6d25a499b2
BREAKING CHANGE: Replaced all direct useNavigate() usage with HandleNavigate hook
## Summary
- Complete frontend navigation refactoring
- Centralized route management with routes.js
- Converted 18+ components to use HandleNavigate
- Enhanced navigation with 20+ type-safe functions
## New Files
- src/utils/routes.js - Central route constants and helpers
- Documentations/FRONTEND_CODING_GUIDELINES.md - Frontend best practices (300+ lines)
- Documentations/NAVIGATION_REFACTORING_REPORT.md - Detailed refactoring report (400+ lines)
## Modified Components (18+)
### Pages
- Home.jsx, LoginForm.jsx, RegisterForm.jsx
- ResetPassword.jsx, VerifyEmailPage.jsx
- DeckCreator.jsx, Card_display.jsx
- Lobby.jsx, GameTest.jsx, ChooseDeck.jsx, PlayerSetup.jsx
- Landingpage.jsx
### Components
- Userdetails.jsx, DeckInfoPopUp.jsx
- PlayMenu.jsx, LandingPage.jsx, DeckManager.jsx
### Hooks
- useRequireAuth.jsx
### Core
- App.jsx - Route constants integration
- HandleNavigate.jsx - Enhanced with 20+ navigation functions
## Key Improvements
Type-safe navigation (goDeckDetails(id) vs navigate('/deck/'+id))
Automatic scroll management
Centralized state passing
Single source of truth for routes
Backwards compatibility aliases
Zero compile errors
Production ready
## Validation
- useNavigate: Only in HandleNavigate.jsx
- navigate() calls: 0 direct usage
- Compile errors: 0
- Documentation: Complete
571 lines
14 KiB
Markdown
571 lines
14 KiB
Markdown
# Frontend Kódolási Útmutató - SerpentRace
|
|
|
|
## Tartalomjegyzék
|
|
1. [Navigáció és Routing](#navigáció-és-routing)
|
|
2. [Fájl és Mappa Struktúra](#fájl-és-mappa-struktúra)
|
|
3. [Komponens Konvenciók](#komponens-konvenciók)
|
|
4. [State Management](#state-management)
|
|
5. [API Hívások](#api-hívások)
|
|
6. [Hibakezelés](#hibakezelés)
|
|
7. [Elnevezési Konvenciók](#elnevezési-konvenciók)
|
|
|
|
---
|
|
|
|
## Navigáció és Routing
|
|
|
|
### ✅ Helyes gyakorlat: HandleNavigate használata
|
|
|
|
**MINDIG használd a központosított HandleNavigate hook-ot navigációhoz:**
|
|
|
|
```jsx
|
|
import HandleNavigate from "../../utils/HandleNavigate/HandleNavigate"
|
|
|
|
const MyComponent = () => {
|
|
const { goHome, goLogin, goDeckDetails } = HandleNavigate()
|
|
|
|
const handleClick = () => {
|
|
goHome() // Egyszerű navigáció
|
|
}
|
|
|
|
const handleDeckView = (deckId) => {
|
|
goDeckDetails(deckId) // Dinamikus route paraméterrel
|
|
}
|
|
|
|
const handleLobby = (gameCode) => {
|
|
goLobby({ gameCode }) // State passzolással
|
|
}
|
|
}
|
|
```
|
|
|
|
### ❌ Kerülendő: Direkt useNavigate használat
|
|
|
|
```jsx
|
|
// SOHA NE HASZNÁLD EZT!
|
|
import { useNavigate } from "react-router-dom"
|
|
|
|
const MyComponent = () => {
|
|
const navigate = useNavigate()
|
|
navigate("/home") // ❌ NEM JÓ!
|
|
}
|
|
```
|
|
|
|
### Elérhető Navigációs Függvények
|
|
|
|
**HandleNavigate által biztosított függvények:**
|
|
|
|
```jsx
|
|
const {
|
|
// Általános
|
|
goTo, // goTo('/any-path', { state: {...} })
|
|
goBack, // Vissza az előző oldalra
|
|
|
|
// Authentikáció
|
|
goHome, // → /home
|
|
goLogin, // → /login, state: { success, message }
|
|
goRegister, // → /register (alias: goAuth)
|
|
goLanding, // → / (landing page)
|
|
|
|
// Deck Management
|
|
goDecks, // → /decks
|
|
goDeckDetails, // goDeckDetails(deckId) → /deck/:deckId
|
|
goDeckCreator, // → /deck-creator
|
|
goDeckCreatorEdit, // goDeckCreatorEdit(deckId) → /deck-creator/:deckId
|
|
|
|
// Game Flow
|
|
goLobby, // goLobby({ gameCode }) → /lobby
|
|
goChooseDeck, // goChooseDeck({ username, deckIds }) → /choosedeck
|
|
goPlayerSetup, // goPlayerSetup({ deckIds }) → /player-setup
|
|
goGame, // goGame({ players, gameState }) → /game
|
|
|
|
// Egyéb
|
|
goContacts // → /contacts (alias: goCompanies)
|
|
} = HandleNavigate()
|
|
```
|
|
|
|
### Route Konstansok
|
|
|
|
**Használd a centralizált route konstansokat:**
|
|
|
|
```jsx
|
|
// src/utils/routes.js
|
|
import { ROUTES } from '../../utils/routes'
|
|
|
|
// App.jsx-ben
|
|
<Route path={ROUTES.HOME} element={<Home />} />
|
|
<Route path={ROUTES.DECK_DETAILS} element={<DeckDetails />} />
|
|
|
|
// ❌ NE használj string literálokat:
|
|
<Route path="/home" element={<Home />} /> // NEM JÓ!
|
|
```
|
|
|
|
### State Passzolás
|
|
|
|
**Így adj át adatokat navigáció során:**
|
|
|
|
```jsx
|
|
// Régi mód (useNavigate) - ❌ NE!
|
|
navigate('/lobby', { state: { gameCode: 'ABC123' } })
|
|
|
|
// Új mód (HandleNavigate) - ✅ JÓ!
|
|
goLobby({ gameCode: 'ABC123' })
|
|
|
|
// Fogadó oldalon:
|
|
import { useLocation } from 'react-router-dom'
|
|
|
|
const Lobby = () => {
|
|
const location = useLocation()
|
|
const gameCode = location.state?.gameCode
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Fájl és Mappa Struktúra
|
|
|
|
### Mappa Szervezés
|
|
|
|
```
|
|
src/
|
|
├── api/ # API hívások
|
|
│ ├── userApi.js
|
|
│ ├── deckApi.js
|
|
│ └── gameApi.js
|
|
├── assets/ # Statikus fájlok
|
|
│ ├── backgrounds/
|
|
│ ├── images/
|
|
│ └── icons/
|
|
├── components/ # Újrahasználható komponensek
|
|
│ ├── Buttons/
|
|
│ ├── Inputs/
|
|
│ ├── Navbar/
|
|
│ └── PopUp/
|
|
├── hooks/ # Custom Hooks
|
|
│ └── useRequireAuth.jsx
|
|
├── pages/ # Oldal komponensek
|
|
│ ├── Auth/
|
|
│ ├── Game/
|
|
│ ├── Decks/
|
|
│ └── Landing/
|
|
├── utils/ # Utility függvények
|
|
│ ├── HandleNavigate/
|
|
│ └── routes.js
|
|
└── App.jsx # Fő alkalmazás komponens
|
|
```
|
|
|
|
### Fájl Elnevezési Konvenciók
|
|
|
|
- **Komponensek**: PascalCase
|
|
- `LoginForm.jsx`, `DeckCreator.jsx`, `ButtonGreen.jsx`
|
|
|
|
- **Utility fájlok**: camelCase
|
|
- `routes.js`, `randomUtils.js`, `userApi.js`
|
|
|
|
- **Hook fájlok**: camelCase, "use" prefix
|
|
- `useRequireAuth.jsx`, `useLocalStorage.jsx`
|
|
|
|
---
|
|
|
|
## Komponens Konvenciók
|
|
|
|
### Funkcionális Komponens Sablon
|
|
|
|
```jsx
|
|
import React, { useState, useEffect } from 'react'
|
|
import HandleNavigate from '../../utils/HandleNavigate/HandleNavigate'
|
|
|
|
/**
|
|
* Komponens rövid leírása
|
|
* @returns {JSX.Element}
|
|
*/
|
|
const MyComponent = () => {
|
|
// 1. Hooks (HandleNavigate, useState, useEffect, stb.)
|
|
const { goHome } = HandleNavigate()
|
|
const [data, setData] = useState(null)
|
|
|
|
// 2. Effect hooks
|
|
useEffect(() => {
|
|
// Component mount logic
|
|
}, [])
|
|
|
|
// 3. Event handlers
|
|
const handleClick = () => {
|
|
// Logic
|
|
}
|
|
|
|
// 4. Render
|
|
return (
|
|
<div>
|
|
{/* JSX */}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default MyComponent
|
|
```
|
|
|
|
### Import Sorrend
|
|
|
|
```jsx
|
|
// 1. React és third-party library-k
|
|
import React, { useState } from 'react'
|
|
import { motion } from 'framer-motion'
|
|
|
|
// 2. React Router hooks (useLocation, useParams - NEM useNavigate!)
|
|
import { useLocation } from 'react-router-dom'
|
|
|
|
// 3. Custom hooks és utils
|
|
import HandleNavigate from '../../utils/HandleNavigate/HandleNavigate'
|
|
import useRequireAuth from '../../hooks/useRequireAuth'
|
|
|
|
// 4. API
|
|
import { getUserData } from '../../api/userApi'
|
|
|
|
// 5. Komponensek
|
|
import Button from '../../components/Buttons/Button'
|
|
import Navbar from '../../components/Navbar/Navbar'
|
|
|
|
// 6. Assets
|
|
import Background from '../../assets/backgrounds/Background'
|
|
```
|
|
|
|
---
|
|
|
|
## State Management
|
|
|
|
### Local State
|
|
|
|
```jsx
|
|
// Egyszerű state
|
|
const [count, setCount] = useState(0)
|
|
|
|
// Object state
|
|
const [user, setUser] = useState({
|
|
name: '',
|
|
email: ''
|
|
})
|
|
|
|
// Array state
|
|
const [items, setItems] = useState([])
|
|
```
|
|
|
|
### LocalStorage Használat
|
|
|
|
**useRequireAuth hook használata auth kezeléshez:**
|
|
|
|
```jsx
|
|
import useRequireAuth from '../../hooks/useRequireAuth'
|
|
|
|
const MyComponent = () => {
|
|
const [username] = useRequireAuth({
|
|
key: 'username',
|
|
redirectTo: '/login'
|
|
})
|
|
|
|
// username automatikusan szinkronizálva van localStorage-el
|
|
// Ha nincs username, automatikus redirect /login-re
|
|
}
|
|
```
|
|
|
|
**Manuális localStorage:**
|
|
|
|
```jsx
|
|
// Írás
|
|
localStorage.setItem('gameToken', token)
|
|
|
|
// Olvasás
|
|
const token = localStorage.getItem('gameToken')
|
|
|
|
// Törlés
|
|
localStorage.removeItem('gameToken')
|
|
```
|
|
|
|
---
|
|
|
|
## API Hívások
|
|
|
|
### API File Struktúra
|
|
|
|
**Minden API endpoint egy külön file-ban (`userApi.js`, `deckApi.js`, `gameApi.js`):**
|
|
|
|
```jsx
|
|
// src/api/userApi.js
|
|
import axiosInstance from './axiosInstance'
|
|
|
|
export const getUserData = async (userId) => {
|
|
try {
|
|
const response = await axiosInstance.get(`/users/${userId}`)
|
|
return response.data
|
|
} catch (error) {
|
|
console.error('Error fetching user data:', error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
export const updateUser = async (userId, userData) => {
|
|
try {
|
|
const response = await axiosInstance.put(`/users/${userId}`, userData)
|
|
return response.data
|
|
} catch (error) {
|
|
console.error('Error updating user:', error)
|
|
throw error
|
|
}
|
|
}
|
|
```
|
|
|
|
### API Hívás Komponensben
|
|
|
|
```jsx
|
|
import { getUserData } from '../../api/userApi'
|
|
|
|
const MyComponent = () => {
|
|
const [loading, setLoading] = useState(false)
|
|
const [error, setError] = useState(null)
|
|
const [data, setData] = useState(null)
|
|
|
|
const fetchData = async () => {
|
|
setLoading(true)
|
|
setError(null)
|
|
|
|
try {
|
|
const result = await getUserData(userId)
|
|
setData(result)
|
|
} catch (err) {
|
|
setError(err.message || 'Hiba történt')
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
useEffect(() => {
|
|
fetchData()
|
|
}, [userId])
|
|
|
|
if (loading) return <div>Betöltés...</div>
|
|
if (error) return <div>Hiba: {error}</div>
|
|
|
|
return <div>{/* data megjelenítése */}</div>
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Hibakezelés
|
|
|
|
### Try-Catch Blokkok
|
|
|
|
```jsx
|
|
const handleSubmit = async () => {
|
|
try {
|
|
const response = await createDeck(deckData)
|
|
// Siker kezelése
|
|
notifySuccess('Deck sikeresen létrehozva!')
|
|
goDecks()
|
|
} catch (error) {
|
|
// Hiba kezelése
|
|
const errorMessage = error.response?.data?.message || 'Ismeretlen hiba'
|
|
setError(errorMessage)
|
|
notifyError(errorMessage)
|
|
}
|
|
}
|
|
```
|
|
|
|
### Toast Notifications
|
|
|
|
```jsx
|
|
import { notifySuccess, notifyError } from '../../components/Toastify/toastifyServices'
|
|
|
|
// Siker üzenet
|
|
notifySuccess('✅ Művelet sikeres!')
|
|
|
|
// Hiba üzenet
|
|
notifyError('❌ Hiba történt!')
|
|
|
|
// Egyedi konfiguráció
|
|
notifySuccess('Mentve!', { autoClose: 2000 })
|
|
```
|
|
|
|
---
|
|
|
|
## Elnevezési Konvenciók
|
|
|
|
### JavaScript/React
|
|
|
|
| Típus | Konvenció | Példa |
|
|
|-------|-----------|-------|
|
|
| Komponensek | PascalCase | `LoginForm`, `DeckCreator` |
|
|
| Függvények | camelCase | `handleClick`, `fetchUserData` |
|
|
| Változók | camelCase | `userName`, `isLoading` |
|
|
| Konstansok | UPPER_SNAKE_CASE | `API_BASE_URL`, `MAX_PLAYERS` |
|
|
| Private változók | _camelCase | `_internalState` |
|
|
| Event handlers | handle + PascalCase | `handleSubmit`, `handleInputChange` |
|
|
| Boolean változók | is/has/can prefix | `isLoading`, `hasError`, `canEdit` |
|
|
|
|
### CSS Classes (Tailwind)
|
|
|
|
```jsx
|
|
// Használj explicit class neveket
|
|
<div className="flex items-center justify-between p-4 bg-white rounded-lg shadow-md">
|
|
|
|
// Kerüld a túl hosszú class stringeket - bontsd több sorra
|
|
<div
|
|
className="
|
|
flex items-center justify-between
|
|
p-4 bg-white rounded-lg shadow-md
|
|
hover:shadow-lg transition-shadow duration-200
|
|
"
|
|
>
|
|
```
|
|
|
|
### Fájl Nevek
|
|
|
|
- **Egyedi komponens**: `LoginForm.jsx` (nem `login-form.jsx`)
|
|
- **Index fájlok**: `index.jsx` (ha könyvtárban több file van)
|
|
- **Utility fájlok**: `randomUtils.js` (camelCase)
|
|
- **API fájlok**: `userApi.js` (camelCase + Api postfix)
|
|
|
|
---
|
|
|
|
## Teljes Példa - Best Practices
|
|
|
|
```jsx
|
|
// src/pages/Example/ExamplePage.jsx
|
|
|
|
import React, { useState, useEffect } from 'react'
|
|
import { useLocation } from 'react-router-dom'
|
|
import { motion } from 'framer-motion'
|
|
|
|
import HandleNavigate from '../../utils/HandleNavigate/HandleNavigate'
|
|
import useRequireAuth from '../../hooks/useRequireAuth'
|
|
import { fetchExampleData, updateExampleData } from '../../api/exampleApi'
|
|
import { notifySuccess, notifyError } from '../../components/Toastify/toastifyServices'
|
|
|
|
import Navbar from '../../components/Navbar/Navbar'
|
|
import Button from '../../components/Buttons/Button'
|
|
import Background from '../../assets/backgrounds/Background'
|
|
|
|
/**
|
|
* Example Page - Komponens rövid leírása
|
|
* @returns {JSX.Element}
|
|
*/
|
|
const ExamplePage = () => {
|
|
// 1. Auth & Navigation
|
|
const [username] = useRequireAuth({ key: 'username', redirectTo: '/login' })
|
|
const { goHome, goBack } = HandleNavigate()
|
|
const location = useLocation()
|
|
|
|
// 2. State
|
|
const [data, setData] = useState(null)
|
|
const [loading, setLoading] = useState(false)
|
|
const [error, setError] = useState(null)
|
|
|
|
// 3. Effects
|
|
useEffect(() => {
|
|
loadData()
|
|
}, [])
|
|
|
|
// 4. Functions
|
|
const loadData = async () => {
|
|
setLoading(true)
|
|
setError(null)
|
|
|
|
try {
|
|
const result = await fetchExampleData()
|
|
setData(result)
|
|
} catch (err) {
|
|
const errorMsg = err.response?.data?.message || 'Hiba történt'
|
|
setError(errorMsg)
|
|
notifyError(errorMsg)
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
const handleSave = async () => {
|
|
try {
|
|
await updateExampleData(data)
|
|
notifySuccess('✅ Sikeresen mentve!')
|
|
goHome()
|
|
} catch (err) {
|
|
notifyError('❌ Mentés sikertelen')
|
|
}
|
|
}
|
|
|
|
const handleCancel = () => {
|
|
goBack()
|
|
}
|
|
|
|
// 5. Conditional Rendering
|
|
if (loading) {
|
|
return (
|
|
<div className="flex items-center justify-center min-h-screen">
|
|
<div>Betöltés...</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<div className="flex items-center justify-center min-h-screen">
|
|
<div className="text-red-500">Hiba: {error}</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
// 6. Main Render
|
|
return (
|
|
<div className="min-h-screen bg-gray-100">
|
|
<Background />
|
|
<Navbar />
|
|
|
|
<main className="container mx-auto px-4 py-8">
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.5 }}
|
|
>
|
|
<h1 className="text-3xl font-bold mb-6">Example Page</h1>
|
|
|
|
{/* Content */}
|
|
<div className="bg-white rounded-lg shadow-md p-6">
|
|
{data && (
|
|
<div>
|
|
{/* Render data */}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Actions */}
|
|
<div className="flex gap-4 mt-6">
|
|
<Button onClick={handleSave} text="Mentés" />
|
|
<Button onClick={handleCancel} text="Mégse" variant="secondary" />
|
|
</div>
|
|
</motion.div>
|
|
</main>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default ExamplePage
|
|
```
|
|
|
|
---
|
|
|
|
## Összefoglalás - Legfontosabb Szabályok
|
|
|
|
1. ✅ **MINDIG használd HandleNavigate-et** navigációhoz
|
|
2. ✅ **Használd a ROUTES konstansokat** az App.jsx-ben
|
|
3. ✅ **API hívások külön file-okban** (userApi.js, deckApi.js, stb.)
|
|
4. ✅ **Try-catch minden async műveletnél**
|
|
5. ✅ **Toast notifications** a felhasználói visszajelzéshez
|
|
6. ✅ **useRequireAuth hook** auth védett oldalaknál
|
|
7. ✅ **Konzisztens import sorrend**
|
|
8. ✅ **PascalCase komponenseknek, camelCase változóknak**
|
|
9. ❌ **SOHA ne használj useNavigate közvetlen**
|
|
10. ❌ **Ne használj string literal route-okat**
|
|
|
|
---
|
|
|
|
**Verzió:** 1.0
|
|
**Utolsó frissítés:** 2025-01-17
|
|
**Készítette:** SerpentRace Development Team
|