Init: projet by Cursor
This commit is contained in:
23
.gitignore
vendored
Normal file
23
.gitignore
vendored
Normal file
@@ -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*
|
||||
180
README.md
Normal file
180
README.md
Normal file
@@ -0,0 +1,180 @@
|
||||
# Initial D Arcade Stage - Site de Classement
|
||||
|
||||
Un site web moderne et responsive reproduisant l'interface du site officiel Initial D Arcade Stage, créé avec React et TypeScript.
|
||||
|
||||
## 🏁 Fonctionnalités
|
||||
|
||||
- **Page d'accueil** : Présentation du jeu avec design inspiré de l'univers Initial D
|
||||
- **Page de classement** : Tableau des meilleurs temps avec filtres et onglets
|
||||
- **Design responsive** : Interface adaptée à tous les écrans
|
||||
- **Animations fluides** : Utilisation de Framer Motion pour les transitions
|
||||
- **Thème sombre** : Interface moderne avec couleurs d'Initial D
|
||||
- **Navigation intuitive** : Menu de navigation avec indicateurs visuels
|
||||
|
||||
## 🚗 Technologies Utilisées
|
||||
|
||||
- **React 18** : Framework JavaScript pour l'interface utilisateur
|
||||
- **TypeScript** : Typage statique pour une meilleure maintenabilité
|
||||
- **Material-UI (MUI)** : Composants UI modernes et personnalisables
|
||||
- **Framer Motion** : Animations et transitions fluides
|
||||
- **React Router** : Navigation entre les pages
|
||||
- **Emotion** : Styling CSS-in-JS
|
||||
|
||||
## 🎨 Design
|
||||
|
||||
Le site reprend l'esthétique du site officiel Initial D avec :
|
||||
- Couleurs principales : Orange (#ff6b35) et noir (#0a0a0a)
|
||||
- Police Orbitron pour les titres
|
||||
- Effets de lueur et animations
|
||||
- Interface sombre et moderne
|
||||
- Icônes Material Design
|
||||
|
||||
## 📱 Pages
|
||||
|
||||
### Page d'Accueil
|
||||
- Hero section avec titre animé
|
||||
- Section des caractéristiques du jeu
|
||||
- Call-to-action vers le classement
|
||||
- Design responsive avec animations
|
||||
|
||||
### Page de Classement
|
||||
- Tableau des meilleurs temps
|
||||
- Filtres par circuit
|
||||
- Onglets pour différents types de classement
|
||||
- Statistiques en temps réel
|
||||
- Avatars et badges pour les joueurs
|
||||
|
||||
## 🚀 Installation et Démarrage
|
||||
|
||||
1. **Cloner le projet**
|
||||
```bash
|
||||
git clone [url-du-repo]
|
||||
cd initiald-ranking
|
||||
```
|
||||
|
||||
2. **Installer les dépendances**
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
3. **Démarrer le serveur de développement**
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
4. **Ouvrir dans le navigateur**
|
||||
```
|
||||
http://localhost:3000
|
||||
```
|
||||
|
||||
## 📦 Scripts Disponibles
|
||||
|
||||
- `npm start` : Démarre le serveur de développement
|
||||
- `npm run build` : Construit l'application pour la production
|
||||
- `npm test` : Lance les tests
|
||||
- `npm run eject` : Éjecte la configuration (irréversible)
|
||||
|
||||
## 🏗️ Structure du Projet
|
||||
|
||||
```
|
||||
src/
|
||||
├── components/ # Composants réutilisables
|
||||
│ └── Header.tsx # En-tête avec navigation
|
||||
├── pages/ # Pages de l'application
|
||||
│ ├── Home.tsx # Page d'accueil
|
||||
│ └── Ranking.tsx # Page de classement
|
||||
├── types/ # Définitions TypeScript
|
||||
│ └── index.ts # Interfaces et types
|
||||
├── assets/ # Ressources statiques
|
||||
├── App.tsx # Composant principal
|
||||
├── index.tsx # Point d'entrée
|
||||
└── index.css # Styles globaux
|
||||
```
|
||||
|
||||
## 🎯 Fonctionnalités Principales
|
||||
|
||||
### Navigation
|
||||
- Menu de navigation responsive
|
||||
- Indicateurs visuels pour la page active
|
||||
- Logo animé avec icône de voiture
|
||||
|
||||
### Classement
|
||||
- Données simulées de joueurs Initial D
|
||||
- Filtrage par circuit
|
||||
- Onglets pour différents vues
|
||||
- Statistiques en temps réel
|
||||
|
||||
### Design
|
||||
- Thème sombre cohérent
|
||||
- Animations fluides
|
||||
- Effets de survol
|
||||
- Typographie optimisée
|
||||
|
||||
## 🎨 Personnalisation
|
||||
|
||||
### Couleurs
|
||||
Les couleurs principales peuvent être modifiées dans :
|
||||
- `src/App.tsx` : Thème Material-UI
|
||||
- `src/App.css` : Variables CSS personnalisées
|
||||
|
||||
### Données
|
||||
Les données de classement sont simulées dans :
|
||||
- `src/pages/Ranking.tsx` : Tableau `rankingData`
|
||||
|
||||
### Styles
|
||||
Les styles peuvent être personnalisés dans :
|
||||
- `src/App.css` : Styles globaux
|
||||
- `src/index.css` : Reset CSS et styles de base
|
||||
|
||||
## 📱 Responsive Design
|
||||
|
||||
Le site est entièrement responsive avec :
|
||||
- Breakpoints Material-UI
|
||||
- Grille CSS adaptative
|
||||
- Typographie responsive
|
||||
- Navigation mobile optimisée
|
||||
|
||||
## 🔧 Configuration
|
||||
|
||||
### Variables d'Environnement
|
||||
Créer un fichier `.env` pour les variables d'environnement :
|
||||
```
|
||||
REACT_APP_API_URL=your-api-url
|
||||
REACT_APP_VERSION=1.0.0
|
||||
```
|
||||
|
||||
### Build de Production
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
## 🚀 Déploiement
|
||||
|
||||
Le projet peut être déployé sur :
|
||||
- **Netlify** : Drag & drop du dossier `build`
|
||||
- **Vercel** : Connexion GitHub automatique
|
||||
- **GitHub Pages** : Utilisation de `gh-pages`
|
||||
- **Firebase Hosting** : Configuration Firebase
|
||||
|
||||
## 📄 Licence
|
||||
|
||||
Ce projet est créé à des fins éducatives et de démonstration. Initial D est une propriété de SEGA.
|
||||
|
||||
## 🤝 Contribution
|
||||
|
||||
Les contributions sont les bienvenues ! N'hésitez pas à :
|
||||
1. Fork le projet
|
||||
2. Créer une branche feature
|
||||
3. Commiter vos changements
|
||||
4. Pousser vers la branche
|
||||
5. Ouvrir une Pull Request
|
||||
|
||||
## 📞 Support
|
||||
|
||||
Pour toute question ou problème :
|
||||
- Ouvrir une issue sur GitHub
|
||||
- Contacter l'équipe de développement
|
||||
|
||||
---
|
||||
|
||||
**Initial D Arcade Stage** - Reproduit avec ❤️ en React
|
||||
44
index.html
Normal file
44
index.html
Normal file
@@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#ff6b35" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Initial D Arcade Stage - Site de classement officiel. Consultez les meilleurs temps des pilotes du monde entier sur les circuits légendaires du Japon."
|
||||
/>
|
||||
<meta name="keywords" content="Initial D, Arcade Stage, course, automobile, classement, Japon, circuits, voitures" />
|
||||
<meta name="author" content="Initial D Team" />
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="https://initiald-ranking.com/" />
|
||||
<meta property="og:title" content="Initial D Arcade Stage - Classement Mondial" />
|
||||
<meta property="og:description" content="Découvrez les meilleurs temps des pilotes Initial D sur les circuits légendaires du Japon." />
|
||||
<meta property="og:image" content="/og-image.jpg" />
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta property="twitter:card" content="summary_large_image" />
|
||||
<meta property="twitter:url" content="https://initiald-ranking.com/" />
|
||||
<meta property="twitter:title" content="Initial D Arcade Stage - Classement Mondial" />
|
||||
<meta property="twitter:description" content="Découvrez les meilleurs temps des pilotes Initial D sur les circuits légendaires du Japon." />
|
||||
<meta property="twitter:image" content="/og-image.jpg" />
|
||||
|
||||
<link rel="apple-touch-icon" href="/logo192.png" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
|
||||
<!-- Google Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;600;700;900&display=swap" rel="stylesheet">
|
||||
|
||||
<title>Initial D Arcade Stage - Classement Mondial</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>Vous devez activer JavaScript pour utiliser cette application.</noscript>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/index.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
2994
package-lock.json
generated
Normal file
2994
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
55
package.json
Normal file
55
package.json
Normal file
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"name": "initiald-ranking",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.1",
|
||||
"@mui/icons-material": "^7.2.0",
|
||||
"@mui/material": "^7.2.0",
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"@types/jest": "^27.5.2",
|
||||
"@types/node": "^20.0.0",
|
||||
"@types/react": "^19.1.8",
|
||||
"@types/react-dom": "^19.1.6",
|
||||
"framer-motion": "^12.23.9",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-router-dom": "^7.7.1",
|
||||
"typescript": "^4.9.5",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"start": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"preview": "vite preview",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-react": "^4.7.0",
|
||||
"vite": "^6.3.5"
|
||||
}
|
||||
}
|
||||
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
43
public/index.html
Normal file
43
public/index.html
Normal file
@@ -0,0 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#ff6b35" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Initial D Arcade Stage - Site de classement officiel. Consultez les meilleurs temps des pilotes du monde entier sur les circuits légendaires du Japon."
|
||||
/>
|
||||
<meta name="keywords" content="Initial D, Arcade Stage, course, automobile, classement, Japon, circuits, voitures" />
|
||||
<meta name="author" content="Initial D Team" />
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="https://initiald-ranking.com/" />
|
||||
<meta property="og:title" content="Initial D Arcade Stage - Classement Mondial" />
|
||||
<meta property="og:description" content="Découvrez les meilleurs temps des pilotes Initial D sur les circuits légendaires du Japon." />
|
||||
<meta property="og:image" content="%PUBLIC_URL%/og-image.jpg" />
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta property="twitter:card" content="summary_large_image" />
|
||||
<meta property="twitter:url" content="https://initiald-ranking.com/" />
|
||||
<meta property="twitter:title" content="Initial D Arcade Stage - Classement Mondial" />
|
||||
<meta property="twitter:description" content="Découvrez les meilleurs temps des pilotes Initial D sur les circuits légendaires du Japon." />
|
||||
<meta property="twitter:image" content="%PUBLIC_URL%/og-image.jpg" />
|
||||
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
|
||||
<!-- Google Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;600;700;900&display=swap" rel="stylesheet">
|
||||
|
||||
<title>Initial D Arcade Stage - Classement Mondial</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>Vous devez activer JavaScript pour utiliser cette application.</noscript>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
BIN
public/logo192.png
Normal file
BIN
public/logo192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.2 KiB |
BIN
public/logo512.png
Normal file
BIN
public/logo512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.4 KiB |
30
public/manifest.json
Normal file
30
public/manifest.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"short_name": "Initial D",
|
||||
"name": "Initial D Arcade Stage - Classement Mondial",
|
||||
"description": "Site de classement officiel Initial D Arcade Stage. Consultez les meilleurs temps des pilotes du monde entier.",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#ff6b35",
|
||||
"background_color": "#0a0a0a",
|
||||
"orientation": "portrait-primary",
|
||||
"categories": ["games", "sports", "entertainment"],
|
||||
"lang": "fr",
|
||||
"dir": "ltr"
|
||||
}
|
||||
3
public/robots.txt
Normal file
3
public/robots.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
187
src/App.css
Normal file
187
src/App.css
Normal file
@@ -0,0 +1,187 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;600;700;900&display=swap');
|
||||
|
||||
.App {
|
||||
text-align: center;
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 50%, #0a0a0a 100%);
|
||||
}
|
||||
|
||||
/* Scrollbar personnalisée */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: #1a1a1a;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #ff6b35;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #e55a2b;
|
||||
}
|
||||
|
||||
/* Animations personnalisées */
|
||||
@keyframes glow {
|
||||
0%, 100% {
|
||||
box-shadow: 0 0 5px rgba(255, 107, 53, 0.5);
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0 0 20px rgba(255, 107, 53, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
|
||||
.glow-effect {
|
||||
animation: glow 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.pulse-effect {
|
||||
animation: pulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* Styles pour les tableaux */
|
||||
.ranking-table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ranking-table th,
|
||||
.ranking-table td {
|
||||
padding: 12px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid rgba(255, 107, 53, 0.2);
|
||||
}
|
||||
|
||||
.ranking-table th {
|
||||
background-color: rgba(255, 107, 53, 0.1);
|
||||
color: #ff6b35;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.ranking-table tr:hover {
|
||||
background-color: rgba(255, 107, 53, 0.05);
|
||||
}
|
||||
|
||||
/* Styles pour les cartes */
|
||||
.card-hover {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.card-hover:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 10px 30px rgba(255, 107, 53, 0.2);
|
||||
}
|
||||
|
||||
/* Styles pour les boutons */
|
||||
.btn-primary {
|
||||
background: linear-gradient(45deg, #ff6b35, #f7931e);
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 12px 24px;
|
||||
border-radius: 8px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: linear-gradient(45deg, #e55a2b, #e0851a);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(255, 107, 53, 0.3);
|
||||
}
|
||||
|
||||
.btn-outline {
|
||||
background: transparent;
|
||||
border: 2px solid #ff6b35;
|
||||
color: #ff6b35;
|
||||
padding: 12px 24px;
|
||||
border-radius: 8px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-outline:hover {
|
||||
background: rgba(255, 107, 53, 0.1);
|
||||
border-color: #e55a2b;
|
||||
color: #e55a2b;
|
||||
}
|
||||
|
||||
/* Styles pour les titres */
|
||||
.title-main {
|
||||
font-family: 'Orbitron', monospace;
|
||||
font-weight: 900;
|
||||
text-shadow: 3px 3px 6px rgba(255, 107, 53, 0.5);
|
||||
}
|
||||
|
||||
.title-secondary {
|
||||
font-family: 'Orbitron', monospace;
|
||||
font-weight: 600;
|
||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
/* Responsive design */
|
||||
@media (max-width: 768px) {
|
||||
.title-main {
|
||||
font-size: 2rem !important;
|
||||
}
|
||||
|
||||
.title-secondary {
|
||||
font-size: 1.5rem !important;
|
||||
}
|
||||
|
||||
.btn-primary,
|
||||
.btn-outline {
|
||||
padding: 10px 20px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Effets de particules */
|
||||
.particles {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.particle {
|
||||
position: absolute;
|
||||
width: 2px;
|
||||
height: 2px;
|
||||
background: #ff6b35;
|
||||
border-radius: 50%;
|
||||
animation: float 6s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% {
|
||||
transform: translateY(0px) rotate(0deg);
|
||||
opacity: 0;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(-100px) rotate(360deg);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
9
src/App.test.tsx
Normal file
9
src/App.test.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import App from './App';
|
||||
|
||||
test('renders learn react link', () => {
|
||||
render(<App />);
|
||||
const linkElement = screen.getByText(/learn react/i);
|
||||
expect(linkElement).toBeInTheDocument();
|
||||
});
|
||||
64
src/App.tsx
Normal file
64
src/App.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import React from 'react';
|
||||
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
|
||||
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
||||
import CssBaseline from '@mui/material/CssBaseline';
|
||||
import Header from './components/Header';
|
||||
import Footer from './components/Footer';
|
||||
import Home from './pages/Home';
|
||||
import Ranking from './pages/Ranking';
|
||||
import './App.css';
|
||||
|
||||
const theme = createTheme({
|
||||
palette: {
|
||||
mode: 'dark',
|
||||
primary: {
|
||||
main: '#ff6b35',
|
||||
},
|
||||
secondary: {
|
||||
main: '#f7931e',
|
||||
},
|
||||
background: {
|
||||
default: '#0a0a0a',
|
||||
paper: '#1a1a1a',
|
||||
},
|
||||
text: {
|
||||
primary: '#ffffff',
|
||||
secondary: '#cccccc',
|
||||
},
|
||||
},
|
||||
typography: {
|
||||
fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
|
||||
h1: {
|
||||
fontWeight: 700,
|
||||
fontSize: '2.5rem',
|
||||
},
|
||||
h2: {
|
||||
fontWeight: 600,
|
||||
fontSize: '2rem',
|
||||
},
|
||||
h3: {
|
||||
fontWeight: 600,
|
||||
fontSize: '1.5rem',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
<Router>
|
||||
<div className="App">
|
||||
<Header />
|
||||
<Routes>
|
||||
<Route path="/" element={<Home />} />
|
||||
<Route path="/ranking" element={<Ranking />} />
|
||||
</Routes>
|
||||
<Footer />
|
||||
</div>
|
||||
</Router>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
231
src/components/Footer.tsx
Normal file
231
src/components/Footer.tsx
Normal file
@@ -0,0 +1,231 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Box,
|
||||
Container,
|
||||
Typography,
|
||||
Link,
|
||||
IconButton,
|
||||
} from '@mui/material';
|
||||
|
||||
import DirectionsCarIcon from '@mui/icons-material/DirectionsCar';
|
||||
import SpeedIcon from '@mui/icons-material/Speed';
|
||||
import EmojiEventsIcon from '@mui/icons-material/EmojiEvents';
|
||||
|
||||
const Footer: React.FC = () => {
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
return (
|
||||
<Box
|
||||
component="footer"
|
||||
sx={{
|
||||
backgroundColor: 'rgba(26, 26, 26, 0.95)',
|
||||
borderTop: '2px solid #ff6b35',
|
||||
py: 4,
|
||||
mt: 'auto',
|
||||
}}
|
||||
>
|
||||
<Container maxWidth="lg">
|
||||
<Box sx={{ display: 'grid', gridTemplateColumns: { xs: '1fr', md: 'repeat(3, 1fr)' }, gap: 4 }}>
|
||||
{/* Logo et Description */}
|
||||
<Box>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
|
||||
<DirectionsCarIcon sx={{ fontSize: 40, color: '#ff6b35', mr: 2 }} />
|
||||
<Typography
|
||||
variant="h5"
|
||||
sx={{
|
||||
color: '#ffffff',
|
||||
fontWeight: 700,
|
||||
fontFamily: '"Orbitron", monospace',
|
||||
}}
|
||||
>
|
||||
INITIAL D
|
||||
</Typography>
|
||||
</Box>
|
||||
<Typography
|
||||
variant="body2"
|
||||
sx={{
|
||||
color: '#cccccc',
|
||||
lineHeight: 1.6,
|
||||
mb: 2,
|
||||
}}
|
||||
>
|
||||
Découvrez l'univers de la course automobile japonaise avec les meilleurs pilotes du monde.
|
||||
Affrontez les circuits légendaires et établissez de nouveaux records.
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
{/* Liens Rapides */}
|
||||
<Box>
|
||||
<Typography
|
||||
variant="h6"
|
||||
sx={{
|
||||
color: '#ff6b35',
|
||||
fontWeight: 600,
|
||||
mb: 3,
|
||||
textTransform: 'uppercase',
|
||||
}}
|
||||
>
|
||||
Liens Rapides
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
|
||||
<Link
|
||||
href="/"
|
||||
sx={{
|
||||
color: '#cccccc',
|
||||
textDecoration: 'none',
|
||||
'&:hover': {
|
||||
color: '#ff6b35',
|
||||
},
|
||||
transition: 'color 0.3s ease',
|
||||
}}
|
||||
>
|
||||
Accueil
|
||||
</Link>
|
||||
<Link
|
||||
href="/ranking"
|
||||
sx={{
|
||||
color: '#cccccc',
|
||||
textDecoration: 'none',
|
||||
'&:hover': {
|
||||
color: '#ff6b35',
|
||||
},
|
||||
transition: 'color 0.3s ease',
|
||||
}}
|
||||
>
|
||||
Classement
|
||||
</Link>
|
||||
<Link
|
||||
href="#"
|
||||
sx={{
|
||||
color: '#cccccc',
|
||||
textDecoration: 'none',
|
||||
'&:hover': {
|
||||
color: '#ff6b35',
|
||||
},
|
||||
transition: 'color 0.3s ease',
|
||||
}}
|
||||
>
|
||||
Circuits
|
||||
</Link>
|
||||
<Link
|
||||
href="#"
|
||||
sx={{
|
||||
color: '#cccccc',
|
||||
textDecoration: 'none',
|
||||
'&:hover': {
|
||||
color: '#ff6b35',
|
||||
},
|
||||
transition: 'color 0.3s ease',
|
||||
}}
|
||||
>
|
||||
Voitures
|
||||
</Link>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Statistiques */}
|
||||
<Box>
|
||||
<Typography
|
||||
variant="h6"
|
||||
sx={{
|
||||
color: '#ff6b35',
|
||||
fontWeight: 600,
|
||||
mb: 3,
|
||||
textTransform: 'uppercase',
|
||||
}}
|
||||
>
|
||||
Statistiques
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<SpeedIcon sx={{ color: '#ff6b35', fontSize: 20 }} />
|
||||
<Typography variant="body2" sx={{ color: '#cccccc' }}>
|
||||
1,234+ Joueurs Actifs
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<EmojiEventsIcon sx={{ color: '#ff6b35', fontSize: 20 }} />
|
||||
<Typography variant="body2" sx={{ color: '#cccccc' }}>
|
||||
8 Circuits Légendaires
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<DirectionsCarIcon sx={{ color: '#ff6b35', fontSize: 20 }} />
|
||||
<Typography variant="body2" sx={{ color: '#cccccc' }}>
|
||||
50+ Voitures Disponibles
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Ligne de s<>paration */}
|
||||
<Box
|
||||
sx={{
|
||||
borderTop: '1px solid rgba(255, 107, 53, 0.3)',
|
||||
mt: 4,
|
||||
pt: 3,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Copyright et Liens Sociaux */}
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
flexWrap: 'wrap',
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="body2"
|
||||
sx={{
|
||||
color: '#888888',
|
||||
}}
|
||||
>
|
||||
© {currentYear} Initial D Arcade Stage. Tous droits réservés.
|
||||
</Typography>
|
||||
|
||||
<Box sx={{ display: 'flex', gap: 1 }}>
|
||||
<IconButton
|
||||
sx={{
|
||||
color: '#cccccc',
|
||||
'&:hover': {
|
||||
color: '#ff6b35',
|
||||
backgroundColor: 'rgba(255, 107, 53, 0.1)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Typography variant="body2">SEGA</Typography>
|
||||
</IconButton>
|
||||
<IconButton
|
||||
sx={{
|
||||
color: '#cccccc',
|
||||
'&:hover': {
|
||||
color: '#ff6b35',
|
||||
backgroundColor: 'rgba(255, 107, 53, 0.1)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Typography variant="body2">Support</Typography>
|
||||
</IconButton>
|
||||
<IconButton
|
||||
sx={{
|
||||
color: '#cccccc',
|
||||
'&:hover': {
|
||||
color: '#ff6b35',
|
||||
backgroundColor: 'rgba(255, 107, 53, 0.1)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Typography variant="body2">Mentions Légales</Typography>
|
||||
</IconButton>
|
||||
</Box>
|
||||
</Box>
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
||||
72
src/components/Header.tsx
Normal file
72
src/components/Header.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
AppBar,
|
||||
Toolbar,
|
||||
Typography,
|
||||
Button,
|
||||
Box,
|
||||
Container,
|
||||
} from '@mui/material';
|
||||
import { Link as RouterLink, useLocation } from 'react-router-dom';
|
||||
import DirectionsCarIcon from '@mui/icons-material/DirectionsCar';
|
||||
|
||||
const Header: React.FC = () => {
|
||||
const location = useLocation();
|
||||
|
||||
return (
|
||||
<AppBar position="static" sx={{ backgroundColor: '#1a1a1a', borderBottom: '2px solid #ff6b35' }}>
|
||||
<Container maxWidth="lg">
|
||||
<Toolbar sx={{ justifyContent: 'space-between' }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<DirectionsCarIcon sx={{ fontSize: 40, color: '#ff6b35', mr: 2 }} />
|
||||
<Typography
|
||||
variant="h4"
|
||||
component={RouterLink}
|
||||
to="/"
|
||||
sx={{
|
||||
color: '#ffffff',
|
||||
textDecoration: 'none',
|
||||
fontWeight: 700,
|
||||
fontFamily: '"Orbitron", monospace',
|
||||
textShadow: '2px 2px 4px rgba(255, 107, 53, 0.5)',
|
||||
}}
|
||||
>
|
||||
INITIAL D
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ display: 'flex', gap: 2 }}>
|
||||
<Button
|
||||
component={RouterLink}
|
||||
to="/"
|
||||
sx={{
|
||||
color: location.pathname === '/' ? '#ff6b35' : '#ffffff',
|
||||
fontWeight: location.pathname === '/' ? 600 : 400,
|
||||
'&:hover': {
|
||||
backgroundColor: 'rgba(255, 107, 53, 0.1)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
ACCUEIL
|
||||
</Button>
|
||||
<Button
|
||||
component={RouterLink}
|
||||
to="/ranking"
|
||||
sx={{
|
||||
color: location.pathname === '/ranking' ? '#ff6b35' : '#ffffff',
|
||||
fontWeight: location.pathname === '/ranking' ? 600 : 400,
|
||||
'&:hover': {
|
||||
backgroundColor: 'rgba(255, 107, 53, 0.1)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
CLASSEMENT
|
||||
</Button>
|
||||
</Box>
|
||||
</Toolbar>
|
||||
</Container>
|
||||
</AppBar>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
329
src/data/rankingData.ts
Normal file
329
src/data/rankingData.ts
Normal file
@@ -0,0 +1,329 @@
|
||||
import { RankingData, Track, Car, Player } from '../types';
|
||||
|
||||
export const rankingData: RankingData[] = [
|
||||
{
|
||||
rank: 1,
|
||||
playerName: 'Takumi_Fujiwara',
|
||||
car: 'Toyota AE86 Trueno',
|
||||
time: '2:45.123',
|
||||
track: 'Mount Akina',
|
||||
date: '2024-01-15',
|
||||
country: 'Japon',
|
||||
},
|
||||
{
|
||||
rank: 2,
|
||||
playerName: 'Keisuke_Takahashi',
|
||||
car: 'Mazda RX-7 FD3S',
|
||||
time: '2:45.456',
|
||||
track: 'Mount Akina',
|
||||
date: '2024-01-14',
|
||||
country: 'Japon',
|
||||
},
|
||||
{
|
||||
rank: 3,
|
||||
playerName: 'Ryosuke_Takahashi',
|
||||
car: 'Mazda RX-7 FC3S',
|
||||
time: '2:46.789',
|
||||
track: 'Mount Akina',
|
||||
date: '2024-01-13',
|
||||
country: 'Japon',
|
||||
},
|
||||
{
|
||||
rank: 4,
|
||||
playerName: 'Bunta_Fujiwara',
|
||||
car: 'Subaru Impreza WRX STI',
|
||||
time: '2:47.012',
|
||||
track: 'Mount Akina',
|
||||
date: '2024-01-12',
|
||||
country: 'Japon',
|
||||
},
|
||||
{
|
||||
rank: 5,
|
||||
playerName: 'Itsuki_Takeuchi',
|
||||
car: 'Toyota AE85 Levin',
|
||||
time: '2:48.345',
|
||||
track: 'Mount Akina',
|
||||
date: '2024-01-11',
|
||||
country: 'Japon',
|
||||
},
|
||||
{
|
||||
rank: 6,
|
||||
playerName: 'Kenta_Nakamura',
|
||||
car: 'Nissan Silvia S13',
|
||||
time: '2:48.678',
|
||||
track: 'Mount Akina',
|
||||
date: '2024-01-10',
|
||||
country: 'Japon',
|
||||
},
|
||||
{
|
||||
rank: 7,
|
||||
playerName: 'Shingo_Shoji',
|
||||
car: 'Honda Civic EG6',
|
||||
time: '2:49.001',
|
||||
track: 'Mount Akina',
|
||||
date: '2024-01-09',
|
||||
country: 'Japon',
|
||||
},
|
||||
{
|
||||
rank: 8,
|
||||
playerName: 'Mako_Sato',
|
||||
car: 'Nissan 180SX',
|
||||
time: '2:49.234',
|
||||
track: 'Mount Akina',
|
||||
date: '2024-01-08',
|
||||
country: 'Japon',
|
||||
},
|
||||
{
|
||||
rank: 9,
|
||||
playerName: 'Sayuki',
|
||||
car: 'Toyota MR2 SW20',
|
||||
time: '2:49.567',
|
||||
track: 'Mount Akina',
|
||||
date: '2024-01-07',
|
||||
country: 'Japon',
|
||||
},
|
||||
{
|
||||
rank: 10,
|
||||
playerName: 'Wataru_Akiyama',
|
||||
car: 'Toyota AE86 Levin',
|
||||
time: '2:49.890',
|
||||
track: 'Mount Akina',
|
||||
date: '2024-01-06',
|
||||
country: 'Japon',
|
||||
},
|
||||
{
|
||||
rank: 11,
|
||||
playerName: 'Seiji_Iwaki',
|
||||
car: 'Nissan Skyline GT-R R32',
|
||||
time: '2:50.123',
|
||||
track: 'Mount Akina',
|
||||
date: '2024-01-05',
|
||||
country: 'Japon',
|
||||
},
|
||||
{
|
||||
rank: 12,
|
||||
playerName: 'Kyoichi_Sudo',
|
||||
car: 'Mitsubishi Lancer Evolution III',
|
||||
time: '2:50.456',
|
||||
track: 'Mount Akina',
|
||||
date: '2024-01-04',
|
||||
country: 'Japon',
|
||||
},
|
||||
{
|
||||
rank: 13,
|
||||
playerName: 'Eiji_Kubota',
|
||||
car: 'Honda Integra Type R',
|
||||
time: '2:50.789',
|
||||
track: 'Mount Akina',
|
||||
date: '2024-01-03',
|
||||
country: 'Japon',
|
||||
},
|
||||
{
|
||||
rank: 14,
|
||||
playerName: 'Tomoyuki_Tachi',
|
||||
car: 'Nissan Silvia S14',
|
||||
time: '2:51.012',
|
||||
track: 'Mount Akina',
|
||||
date: '2024-01-02',
|
||||
country: 'Japon',
|
||||
},
|
||||
{
|
||||
rank: 15,
|
||||
playerName: 'Miki',
|
||||
car: 'Toyota Celica GT-Four',
|
||||
time: '2:51.345',
|
||||
track: 'Mount Akina',
|
||||
date: '2024-01-01',
|
||||
country: 'Japon',
|
||||
},
|
||||
];
|
||||
|
||||
export const tracks: Track[] = [
|
||||
{
|
||||
id: 'mount-akina',
|
||||
name: 'Mount Akina',
|
||||
difficulty: 'Hard',
|
||||
length: '5.2 km',
|
||||
bestTime: '2:45.123',
|
||||
},
|
||||
{
|
||||
id: 'mount-haruna',
|
||||
name: 'Mount Haruna',
|
||||
difficulty: 'Expert',
|
||||
length: '6.8 km',
|
||||
bestTime: '3:12.456',
|
||||
},
|
||||
{
|
||||
id: 'mount-myogi',
|
||||
name: 'Mount Myogi',
|
||||
difficulty: 'Hard',
|
||||
length: '4.9 km',
|
||||
bestTime: '2:38.789',
|
||||
},
|
||||
{
|
||||
id: 'usui-pass',
|
||||
name: 'Usui Pass',
|
||||
difficulty: 'Medium',
|
||||
length: '3.7 km',
|
||||
bestTime: '2:15.012',
|
||||
},
|
||||
{
|
||||
id: 'irohazaka',
|
||||
name: 'Irohazaka',
|
||||
difficulty: 'Expert',
|
||||
length: '7.2 km',
|
||||
bestTime: '3:45.345',
|
||||
},
|
||||
{
|
||||
id: 'happogahara',
|
||||
name: 'Happogahara',
|
||||
difficulty: 'Easy',
|
||||
length: '2.8 km',
|
||||
bestTime: '1:52.678',
|
||||
},
|
||||
{
|
||||
id: 'tsuchisaka',
|
||||
name: 'Tsuchisaka',
|
||||
difficulty: 'Medium',
|
||||
length: '4.1 km',
|
||||
bestTime: '2:28.001',
|
||||
},
|
||||
{
|
||||
id: 'akagi',
|
||||
name: 'Akagi',
|
||||
difficulty: 'Expert',
|
||||
length: '8.5 km',
|
||||
bestTime: '4:12.234',
|
||||
},
|
||||
];
|
||||
|
||||
export const cars: Car[] = [
|
||||
{
|
||||
id: 'ae86-trueno',
|
||||
name: 'Toyota AE86 Trueno',
|
||||
manufacturer: 'Toyota',
|
||||
power: 130,
|
||||
weight: 950,
|
||||
drivetrain: 'FR',
|
||||
},
|
||||
{
|
||||
id: 'rx7-fd3s',
|
||||
name: 'Mazda RX-7 FD3S',
|
||||
manufacturer: 'Mazda',
|
||||
power: 280,
|
||||
weight: 1270,
|
||||
drivetrain: 'FR',
|
||||
},
|
||||
{
|
||||
id: 'rx7-fc3s',
|
||||
name: 'Mazda RX-7 FC3S',
|
||||
manufacturer: 'Mazda',
|
||||
power: 200,
|
||||
weight: 1200,
|
||||
drivetrain: 'FR',
|
||||
},
|
||||
{
|
||||
id: 'impreza-wrx-sti',
|
||||
name: 'Subaru Impreza WRX STI',
|
||||
manufacturer: 'Subaru',
|
||||
power: 300,
|
||||
weight: 1470,
|
||||
drivetrain: '4WD',
|
||||
},
|
||||
{
|
||||
id: 'ae85-levin',
|
||||
name: 'Toyota AE85 Levin',
|
||||
manufacturer: 'Toyota',
|
||||
power: 83,
|
||||
weight: 920,
|
||||
drivetrain: 'FR',
|
||||
},
|
||||
{
|
||||
id: 'silvia-s13',
|
||||
name: 'Nissan Silvia S13',
|
||||
manufacturer: 'Nissan',
|
||||
power: 205,
|
||||
weight: 1240,
|
||||
drivetrain: 'FR',
|
||||
},
|
||||
{
|
||||
id: 'civic-eg6',
|
||||
name: 'Honda Civic EG6',
|
||||
manufacturer: 'Honda',
|
||||
power: 170,
|
||||
weight: 1050,
|
||||
drivetrain: 'FF',
|
||||
},
|
||||
{
|
||||
id: '180sx',
|
||||
name: 'Nissan 180SX',
|
||||
manufacturer: 'Nissan',
|
||||
power: 205,
|
||||
weight: 1260,
|
||||
drivetrain: 'FR',
|
||||
},
|
||||
{
|
||||
id: 'mr2-sw20',
|
||||
name: 'Toyota MR2 SW20',
|
||||
manufacturer: 'Toyota',
|
||||
power: 220,
|
||||
weight: 1190,
|
||||
drivetrain: 'MR',
|
||||
},
|
||||
{
|
||||
id: 'ae86-levin',
|
||||
name: 'Toyota AE86 Levin',
|
||||
manufacturer: 'Toyota',
|
||||
power: 130,
|
||||
weight: 950,
|
||||
drivetrain: 'FR',
|
||||
},
|
||||
];
|
||||
|
||||
export const players: Player[] = [
|
||||
{
|
||||
id: 'takumi-fujiwara',
|
||||
name: 'Takumi_Fujiwara',
|
||||
country: 'Japon',
|
||||
totalRaces: 156,
|
||||
wins: 89,
|
||||
bestTime: '2:45.123',
|
||||
favoriteCar: 'Toyota AE86 Trueno',
|
||||
},
|
||||
{
|
||||
id: 'keisuke-takahashi',
|
||||
name: 'Keisuke_Takahashi',
|
||||
country: 'Japon',
|
||||
totalRaces: 142,
|
||||
wins: 67,
|
||||
bestTime: '2:45.456',
|
||||
favoriteCar: 'Mazda RX-7 FD3S',
|
||||
},
|
||||
{
|
||||
id: 'ryosuke-takahashi',
|
||||
name: 'Ryosuke_Takahashi',
|
||||
country: 'Japon',
|
||||
totalRaces: 203,
|
||||
wins: 134,
|
||||
bestTime: '2:46.789',
|
||||
favoriteCar: 'Mazda RX-7 FC3S',
|
||||
},
|
||||
{
|
||||
id: 'bunta-fujiwara',
|
||||
name: 'Bunta_Fujiwara',
|
||||
country: 'Japon',
|
||||
totalRaces: 89,
|
||||
wins: 67,
|
||||
bestTime: '2:47.012',
|
||||
favoriteCar: 'Subaru Impreza WRX STI',
|
||||
},
|
||||
{
|
||||
id: 'itsuki-takeuchi',
|
||||
name: 'Itsuki_Takeuchi',
|
||||
country: 'Japon',
|
||||
totalRaces: 78,
|
||||
wins: 23,
|
||||
bestTime: '2:48.345',
|
||||
favoriteCar: 'Toyota AE85 Levin',
|
||||
},
|
||||
];
|
||||
460
src/index.css
Normal file
460
src/index.css
Normal file
@@ -0,0 +1,460 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
background: #0a0a0a;
|
||||
color: #ffffff;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
/* Reset des styles par défaut */
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Styles pour les liens */
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* Styles pour les boutons */
|
||||
button {
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
/* Styles pour les inputs */
|
||||
input, select, textarea {
|
||||
font-family: inherit;
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Styles pour les listes */
|
||||
ul, ol {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Styles pour les images */
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* Styles pour les tableaux */
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
/* Styles pour les formulaires */
|
||||
form {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Styles pour les sections */
|
||||
section {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Styles pour les articles */
|
||||
article {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Styles pour les aside */
|
||||
aside {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Styles pour les nav */
|
||||
nav {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Styles pour les header */
|
||||
header {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Styles pour les footer */
|
||||
footer {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Styles pour les main */
|
||||
main {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Styles pour les div */
|
||||
div {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Styles pour les span */
|
||||
span {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Styles pour les strong */
|
||||
strong {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Styles pour les em */
|
||||
em {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Styles pour les small */
|
||||
small {
|
||||
font-size: 0.875em;
|
||||
}
|
||||
|
||||
/* Styles pour les mark */
|
||||
mark {
|
||||
background-color: rgba(255, 107, 53, 0.2);
|
||||
color: #ff6b35;
|
||||
padding: 0.2em 0.4em;
|
||||
border-radius: 0.2em;
|
||||
}
|
||||
|
||||
/* Styles pour les del */
|
||||
del {
|
||||
text-decoration: line-through;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
/* Styles pour les ins */
|
||||
ins {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Styles pour les sub */
|
||||
sub {
|
||||
vertical-align: sub;
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
/* Styles pour les sup */
|
||||
sup {
|
||||
vertical-align: super;
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
/* Styles pour les code */
|
||||
code {
|
||||
background-color: rgba(255, 107, 53, 0.1);
|
||||
color: #ff6b35;
|
||||
padding: 0.2em 0.4em;
|
||||
border-radius: 0.2em;
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
|
||||
/* Styles pour les pre */
|
||||
pre {
|
||||
background-color: rgba(26, 26, 26, 0.8);
|
||||
border: 1px solid rgba(255, 107, 53, 0.3);
|
||||
border-radius: 0.5em;
|
||||
padding: 1em;
|
||||
overflow-x: auto;
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
|
||||
/* Styles pour les blockquote */
|
||||
blockquote {
|
||||
border-left: 4px solid #ff6b35;
|
||||
margin: 0;
|
||||
padding-left: 1em;
|
||||
font-style: italic;
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
/* Styles pour les hr */
|
||||
hr {
|
||||
border: none;
|
||||
border-top: 1px solid rgba(255, 107, 53, 0.3);
|
||||
margin: 2em 0;
|
||||
}
|
||||
|
||||
/* Styles pour les fieldset */
|
||||
fieldset {
|
||||
border: 1px solid rgba(255, 107, 53, 0.3);
|
||||
border-radius: 0.5em;
|
||||
padding: 1em;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Styles pour les legend */
|
||||
legend {
|
||||
color: #ff6b35;
|
||||
font-weight: 600;
|
||||
padding: 0 0.5em;
|
||||
}
|
||||
|
||||
/* Styles pour les datalist */
|
||||
datalist {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Styles pour les details */
|
||||
details {
|
||||
border: 1px solid rgba(255, 107, 53, 0.3);
|
||||
border-radius: 0.5em;
|
||||
padding: 1em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
/* Styles pour les summary */
|
||||
summary {
|
||||
cursor: pointer;
|
||||
color: #ff6b35;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Styles pour les dialog */
|
||||
dialog {
|
||||
background-color: rgba(26, 26, 26, 0.95);
|
||||
border: 2px solid #ff6b35;
|
||||
border-radius: 0.5em;
|
||||
color: #ffffff;
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
/* Styles pour les meter */
|
||||
meter {
|
||||
background-color: rgba(26, 26, 26, 0.8);
|
||||
border: 1px solid rgba(255, 107, 53, 0.3);
|
||||
border-radius: 0.2em;
|
||||
}
|
||||
|
||||
/* Styles pour les progress */
|
||||
progress {
|
||||
background-color: rgba(26, 26, 26, 0.8);
|
||||
border: 1px solid rgba(255, 107, 53, 0.3);
|
||||
border-radius: 0.2em;
|
||||
}
|
||||
|
||||
/* Styles pour les output */
|
||||
output {
|
||||
color: #ff6b35;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Styles pour les canvas */
|
||||
canvas {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Styles pour les svg */
|
||||
svg {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Styles pour les iframe */
|
||||
iframe {
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* Styles pour les object */
|
||||
object {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Styles pour les embed */
|
||||
embed {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Styles pour les video */
|
||||
video {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* Styles pour les audio */
|
||||
audio {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Styles pour les source */
|
||||
source {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Styles pour les track */
|
||||
track {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Styles pour les map */
|
||||
map {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Styles pour les area */
|
||||
area {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Styles pour les param */
|
||||
param {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Styles pour les bdo */
|
||||
bdo {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
/* Styles pour les bdi */
|
||||
bdi {
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
/* Styles pour les ruby */
|
||||
ruby {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
/* Styles pour les rt */
|
||||
rt {
|
||||
display: block;
|
||||
text-align: center;
|
||||
font-size: 0.5em;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
/* Styles pour les rp */
|
||||
rp {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Styles pour les wbr */
|
||||
wbr {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
/* Styles pour les time */
|
||||
time {
|
||||
color: #cccccc;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Styles pour les data */
|
||||
data {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Styles pour les var */
|
||||
var {
|
||||
font-style: italic;
|
||||
color: #ff6b35;
|
||||
}
|
||||
|
||||
/* Styles pour les samp */
|
||||
samp {
|
||||
font-family: 'Courier New', monospace;
|
||||
background-color: rgba(255, 107, 53, 0.1);
|
||||
color: #ff6b35;
|
||||
padding: 0.2em 0.4em;
|
||||
border-radius: 0.2em;
|
||||
}
|
||||
|
||||
/* Styles pour les kbd */
|
||||
kbd {
|
||||
background-color: rgba(26, 26, 26, 0.8);
|
||||
border: 1px solid rgba(255, 107, 53, 0.3);
|
||||
border-radius: 0.2em;
|
||||
padding: 0.2em 0.4em;
|
||||
font-family: 'Courier New', monospace;
|
||||
color: #ff6b35;
|
||||
}
|
||||
|
||||
/* Styles pour les abbr */
|
||||
abbr {
|
||||
border-bottom: 1px dotted #ff6b35;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
/* Styles pour les acronym */
|
||||
acronym {
|
||||
border-bottom: 1px dotted #ff6b35;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
/* Styles pour les dfn */
|
||||
dfn {
|
||||
font-style: italic;
|
||||
color: #ff6b35;
|
||||
}
|
||||
|
||||
/* Styles pour les cite */
|
||||
cite {
|
||||
font-style: italic;
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
/* Styles pour les q */
|
||||
q {
|
||||
font-style: italic;
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
/* Styles pour les address */
|
||||
address {
|
||||
font-style: italic;
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
/* Styles pour les figure */
|
||||
figure {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Styles pour les figcaption */
|
||||
figcaption {
|
||||
text-align: center;
|
||||
color: #cccccc;
|
||||
font-style: italic;
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
19
src/index.tsx
Normal file
19
src/index.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import reportWebVitals from './reportWebVitals';
|
||||
|
||||
const root = ReactDOM.createRoot(
|
||||
document.getElementById('root') as HTMLElement
|
||||
);
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
||||
// If you want to start measuring performance in your app, pass a function
|
||||
// to log results (for example: reportWebVitals(console.log))
|
||||
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
||||
reportWebVitals();
|
||||
1
src/logo.svg
Normal file
1
src/logo.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
281
src/pages/Home.tsx
Normal file
281
src/pages/Home.tsx
Normal file
@@ -0,0 +1,281 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Container,
|
||||
Typography,
|
||||
Box,
|
||||
Card,
|
||||
CardContent,
|
||||
CardMedia,
|
||||
Button,
|
||||
Paper,
|
||||
} from '@mui/material';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import { motion } from 'framer-motion';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import DirectionsCarIcon from '@mui/icons-material/DirectionsCar';
|
||||
import SpeedIcon from '@mui/icons-material/Speed';
|
||||
import EmojiEventsIcon from '@mui/icons-material/EmojiEvents';
|
||||
|
||||
const Home: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const features = [
|
||||
{
|
||||
icon: <SpeedIcon sx={{ fontSize: 60, color: '#ff6b35' }} />,
|
||||
title: 'COURSES RAPIDES',
|
||||
description: 'Affrontez les meilleurs pilotes sur les circuits les plus difficiles du Japon.',
|
||||
},
|
||||
{
|
||||
icon: <EmojiEventsIcon sx={{ fontSize: 60, color: '#ff6b35' }} />,
|
||||
title: 'CLASSEMENTS',
|
||||
description: 'Consultez les meilleurs temps et performances des joueurs du monde entier.',
|
||||
},
|
||||
{
|
||||
icon: <DirectionsCarIcon sx={{ fontSize: 60, color: '#ff6b35' }} />,
|
||||
title: 'VOITURES LÉGENDAIRES',
|
||||
description: 'Pilotez les voitures les plus emblématiques de la série Initial D.',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
minHeight: '100vh',
|
||||
background: 'linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 50%, #0a0a0a 100%)',
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
{/* Background effects */}
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
background: 'radial-gradient(circle at 20% 80%, rgba(255, 107, 53, 0.1) 0%, transparent 50%)',
|
||||
}}
|
||||
/>
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
background: 'radial-gradient(circle at 80% 20%, rgba(247, 147, 30, 0.1) 0%, transparent 50%)',
|
||||
}}
|
||||
/>
|
||||
|
||||
<Container maxWidth="lg" sx={{ position: 'relative', zIndex: 1 }}>
|
||||
{/* Hero Section */}
|
||||
<Box
|
||||
component={motion.div}
|
||||
initial={{ opacity: 0, y: 50 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
sx={{
|
||||
textAlign: 'center',
|
||||
py: 8,
|
||||
mt: 4,
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="h1"
|
||||
sx={{
|
||||
fontSize: { xs: '3rem', md: '5rem' },
|
||||
fontWeight: 900,
|
||||
color: '#ffffff',
|
||||
textShadow: '3px 3px 6px rgba(255, 107, 53, 0.5)',
|
||||
mb: 2,
|
||||
fontFamily: '"Orbitron", monospace',
|
||||
}}
|
||||
>
|
||||
INITIAL D
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="h2"
|
||||
sx={{
|
||||
fontSize: { xs: '1.5rem', md: '2.5rem' },
|
||||
color: '#ff6b35',
|
||||
mb: 4,
|
||||
fontWeight: 600,
|
||||
textShadow: '2px 2px 4px rgba(0, 0, 0, 0.5)',
|
||||
}}
|
||||
>
|
||||
ARCADE STAGE
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="h5"
|
||||
sx={{
|
||||
color: '#cccccc',
|
||||
mb: 6,
|
||||
maxWidth: 800,
|
||||
mx: 'auto',
|
||||
lineHeight: 1.6,
|
||||
}}
|
||||
>
|
||||
Découvrez l'univers de la course automobile japonaise avec les meilleurs pilotes du monde.
|
||||
Affrontez les circuits légendaires et établissez de nouveaux records.
|
||||
</Typography>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
size="large"
|
||||
onClick={() => navigate('/ranking')}
|
||||
sx={{
|
||||
backgroundColor: '#ff6b35',
|
||||
color: '#ffffff',
|
||||
fontSize: '1.2rem',
|
||||
px: 4,
|
||||
py: 2,
|
||||
borderRadius: 2,
|
||||
fontWeight: 600,
|
||||
textTransform: 'uppercase',
|
||||
'&:hover': {
|
||||
backgroundColor: '#e55a2b',
|
||||
transform: 'translateY(-2px)',
|
||||
boxShadow: '0 8px 25px rgba(255, 107, 53, 0.3)',
|
||||
},
|
||||
transition: 'all 0.3s ease',
|
||||
}}
|
||||
>
|
||||
Voir le Classement
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
{/* Features Section */}
|
||||
<Box sx={{ py: 8 }}>
|
||||
<Typography
|
||||
variant="h3"
|
||||
sx={{
|
||||
textAlign: 'center',
|
||||
color: '#ffffff',
|
||||
mb: 6,
|
||||
fontWeight: 700,
|
||||
}}
|
||||
>
|
||||
CARACTÉRISTIQUES
|
||||
</Typography>
|
||||
|
||||
<Box sx={{ display: 'grid', gridTemplateColumns: { xs: '1fr', md: 'repeat(3, 1fr)' }, gap: 4 }}>
|
||||
{features.map((feature, index) => (
|
||||
<Box key={index}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: index * 0.2 }}
|
||||
>
|
||||
<Card
|
||||
sx={{
|
||||
backgroundColor: 'rgba(26, 26, 26, 0.8)',
|
||||
border: '2px solid rgba(255, 107, 53, 0.3)',
|
||||
borderRadius: 3,
|
||||
height: '100%',
|
||||
transition: 'all 0.3s ease',
|
||||
'&:hover': {
|
||||
borderColor: '#ff6b35',
|
||||
transform: 'translateY(-5px)',
|
||||
boxShadow: '0 10px 30px rgba(255, 107, 53, 0.2)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<CardContent sx={{ textAlign: 'center', py: 4 }}>
|
||||
<Box sx={{ mb: 3 }}>
|
||||
{feature.icon}
|
||||
</Box>
|
||||
<Typography
|
||||
variant="h5"
|
||||
sx={{
|
||||
color: '#ff6b35',
|
||||
fontWeight: 600,
|
||||
mb: 2,
|
||||
}}
|
||||
>
|
||||
{feature.title}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body1"
|
||||
sx={{
|
||||
color: '#cccccc',
|
||||
lineHeight: 1.6,
|
||||
}}
|
||||
>
|
||||
{feature.description}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</motion.div>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Call to Action */}
|
||||
<Box
|
||||
component={motion.div}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.8, delay: 1 }}
|
||||
sx={{
|
||||
textAlign: 'center',
|
||||
py: 8,
|
||||
}}
|
||||
>
|
||||
<Paper
|
||||
sx={{
|
||||
backgroundColor: 'rgba(255, 107, 53, 0.1)',
|
||||
border: '2px solid #ff6b35',
|
||||
borderRadius: 3,
|
||||
p: 4,
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="h4"
|
||||
sx={{
|
||||
color: '#ffffff',
|
||||
mb: 3,
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
PRÊT À AFFRONTER LA COMPÉTITION ?
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="h6"
|
||||
sx={{
|
||||
color: '#cccccc',
|
||||
mb: 4,
|
||||
}}
|
||||
>
|
||||
Rejoignez les meilleurs pilotes et établissez votre domination sur les circuits.
|
||||
</Typography>
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="large"
|
||||
onClick={() => navigate('/ranking')}
|
||||
sx={{
|
||||
borderColor: '#ff6b35',
|
||||
color: '#ff6b35',
|
||||
fontSize: '1.1rem',
|
||||
px: 4,
|
||||
py: 2,
|
||||
borderRadius: 2,
|
||||
fontWeight: 600,
|
||||
textTransform: 'uppercase',
|
||||
'&:hover': {
|
||||
borderColor: '#e55a2b',
|
||||
backgroundColor: 'rgba(255, 107, 53, 0.1)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
Commencer Maintenant
|
||||
</Button>
|
||||
</Paper>
|
||||
</Box>
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Home;
|
||||
376
src/pages/Ranking.tsx
Normal file
376
src/pages/Ranking.tsx
Normal file
@@ -0,0 +1,376 @@
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Container,
|
||||
Typography,
|
||||
Box,
|
||||
Paper,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Select,
|
||||
MenuItem,
|
||||
FormControl,
|
||||
InputLabel,
|
||||
Button,
|
||||
Chip,
|
||||
Avatar,
|
||||
Tabs,
|
||||
Tab,
|
||||
} from '@mui/material';
|
||||
import { motion } from 'framer-motion';
|
||||
import DirectionsCarIcon from '@mui/icons-material/DirectionsCar';
|
||||
import SpeedIcon from '@mui/icons-material/Speed';
|
||||
import EmojiEventsIcon from '@mui/icons-material/EmojiEvents';
|
||||
import PersonIcon from '@mui/icons-material/Person';
|
||||
import { rankingData, tracks } from '../data/rankingData';
|
||||
|
||||
const Ranking: React.FC = () => {
|
||||
const [selectedTrack, setSelectedTrack] = useState('all');
|
||||
const [selectedTab, setSelectedTab] = useState(0);
|
||||
|
||||
const trackNames = tracks.map(track => track.name);
|
||||
|
||||
const getRankColor = (rank: number) => {
|
||||
switch (rank) {
|
||||
case 1:
|
||||
return '#FFD700'; // Or
|
||||
case 2:
|
||||
return '#C0C0C0'; // Argent
|
||||
case 3:
|
||||
return '#CD7F32'; // Bronze
|
||||
default:
|
||||
return '#ffffff';
|
||||
}
|
||||
};
|
||||
|
||||
const getRankIcon = (rank: number) => {
|
||||
if (rank <= 3) {
|
||||
return <EmojiEventsIcon sx={{ color: getRankColor(rank), fontSize: 20 }} />;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
minHeight: '100vh',
|
||||
background: 'linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 50%, #0a0a0a 100%)',
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
{/* Background effects */}
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
background: 'radial-gradient(circle at 20% 80%, rgba(255, 107, 53, 0.1) 0%, transparent 50%)',
|
||||
}}
|
||||
/>
|
||||
|
||||
<Container maxWidth="lg" sx={{ position: 'relative', zIndex: 1, py: 4 }}>
|
||||
{/* Header */}
|
||||
<Box
|
||||
component={motion.div}
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
sx={{ textAlign: 'center', mb: 4 }}
|
||||
>
|
||||
<Typography
|
||||
variant="h2"
|
||||
sx={{
|
||||
color: '#ffffff',
|
||||
fontWeight: 700,
|
||||
mb: 2,
|
||||
textShadow: '2px 2px 4px rgba(255, 107, 53, 0.5)',
|
||||
}}
|
||||
>
|
||||
CLASSEMENT MONDIAL
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="h5"
|
||||
sx={{
|
||||
color: '#ff6b35',
|
||||
mb: 4,
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
Les meilleurs temps sur les circuits légendaires
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
{/* Tabs */}
|
||||
<Box sx={{ mb: 4 }}>
|
||||
<Tabs
|
||||
value={selectedTab}
|
||||
onChange={(_, newValue) => setSelectedTab(newValue)}
|
||||
sx={{
|
||||
'& .MuiTab-root': {
|
||||
color: '#cccccc',
|
||||
fontWeight: 600,
|
||||
textTransform: 'uppercase',
|
||||
'&.Mui-selected': {
|
||||
color: '#ff6b35',
|
||||
},
|
||||
},
|
||||
'& .MuiTabs-indicator': {
|
||||
backgroundColor: '#ff6b35',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Tab label="Classement Général" />
|
||||
<Tab label="Par Circuit" />
|
||||
<Tab label="Par Voiture" />
|
||||
</Tabs>
|
||||
</Box>
|
||||
|
||||
{/* Filters */}
|
||||
<Box
|
||||
component={motion.div}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
gap: 2,
|
||||
mb: 4,
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<FormControl sx={{ minWidth: 200 }}>
|
||||
<InputLabel sx={{ color: '#cccccc' }}>Circuit</InputLabel>
|
||||
<Select
|
||||
value={selectedTrack}
|
||||
onChange={(e) => setSelectedTrack(e.target.value)}
|
||||
sx={{
|
||||
color: '#ffffff',
|
||||
'& .MuiOutlinedInput-notchedOutline': {
|
||||
borderColor: '#ff6b35',
|
||||
},
|
||||
'&:hover .MuiOutlinedInput-notchedOutline': {
|
||||
borderColor: '#e55a2b',
|
||||
},
|
||||
'& .MuiSvgIcon-root': {
|
||||
color: '#ff6b35',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<MenuItem value="all">Tous les circuits</MenuItem>
|
||||
{trackNames.map((trackName) => (
|
||||
<MenuItem key={trackName} value={trackName}>
|
||||
{trackName}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<Button
|
||||
variant="outlined"
|
||||
sx={{
|
||||
borderColor: '#ff6b35',
|
||||
color: '#ff6b35',
|
||||
'&:hover': {
|
||||
borderColor: '#e55a2b',
|
||||
backgroundColor: 'rgba(255, 107, 53, 0.1)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
Actualiser
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
{/* Ranking Table */}
|
||||
<Box
|
||||
component={motion.div}
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8, delay: 0.4 }}
|
||||
>
|
||||
<TableContainer
|
||||
component={Paper}
|
||||
sx={{
|
||||
backgroundColor: 'rgba(26, 26, 26, 0.9)',
|
||||
border: '2px solid rgba(255, 107, 53, 0.3)',
|
||||
borderRadius: 3,
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow sx={{ backgroundColor: 'rgba(255, 107, 53, 0.1)' }}>
|
||||
<TableCell sx={{ color: '#ff6b35', fontWeight: 600, fontSize: '1.1rem' }}>
|
||||
RANG
|
||||
</TableCell>
|
||||
<TableCell sx={{ color: '#ff6b35', fontWeight: 600, fontSize: '1.1rem' }}>
|
||||
JOUEUR
|
||||
</TableCell>
|
||||
<TableCell sx={{ color: '#ff6b35', fontWeight: 600, fontSize: '1.1rem' }}>
|
||||
VOITURE
|
||||
</TableCell>
|
||||
<TableCell sx={{ color: '#ff6b35', fontWeight: 600, fontSize: '1.1rem' }}>
|
||||
TEMPS
|
||||
</TableCell>
|
||||
<TableCell sx={{ color: '#ff6b35', fontWeight: 600, fontSize: '1.1rem' }}>
|
||||
CIRCUIT
|
||||
</TableCell>
|
||||
<TableCell sx={{ color: '#ff6b35', fontWeight: 600, fontSize: '1.1rem' }}>
|
||||
DATE
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{rankingData.map((row) => (
|
||||
<TableRow
|
||||
key={row.rank}
|
||||
sx={{
|
||||
'&:hover': {
|
||||
backgroundColor: 'rgba(255, 107, 53, 0.05)',
|
||||
},
|
||||
'&:nth-of-type(odd)': {
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.02)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<TableCell sx={{ color: getRankColor(row.rank), fontWeight: 600, fontSize: '1.1rem' }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
{getRankIcon(row.rank)}
|
||||
#{row.rank}
|
||||
</Box>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
||||
<Avatar sx={{ bgcolor: '#ff6b35', width: 32, height: 32 }}>
|
||||
<PersonIcon />
|
||||
</Avatar>
|
||||
<Typography sx={{ color: '#ffffff', fontWeight: 500 }}>
|
||||
{row.playerName}
|
||||
</Typography>
|
||||
<Chip
|
||||
label={row.country}
|
||||
size="small"
|
||||
sx={{
|
||||
backgroundColor: 'rgba(255, 107, 53, 0.2)',
|
||||
color: '#ff6b35',
|
||||
fontSize: '0.7rem',
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<DirectionsCarIcon sx={{ color: '#ff6b35', fontSize: 20 }} />
|
||||
<Typography sx={{ color: '#cccccc' }}>
|
||||
{row.car}
|
||||
</Typography>
|
||||
</Box>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<SpeedIcon sx={{ color: '#ff6b35', fontSize: 20 }} />
|
||||
<Typography
|
||||
sx={{
|
||||
color: '#ffffff',
|
||||
fontWeight: 600,
|
||||
fontFamily: 'monospace',
|
||||
fontSize: '1.1rem',
|
||||
}}
|
||||
>
|
||||
{row.time}
|
||||
</Typography>
|
||||
</Box>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Typography sx={{ color: '#cccccc' }}>
|
||||
{row.track}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Typography sx={{ color: '#cccccc', fontSize: '0.9rem' }}>
|
||||
{row.date}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Box>
|
||||
|
||||
{/* Stats */}
|
||||
<Box
|
||||
component={motion.div}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.8, delay: 0.6 }}
|
||||
sx={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: { xs: '1fr', md: 'repeat(3, 1fr)' },
|
||||
gap: 3,
|
||||
mt: 6,
|
||||
}}
|
||||
>
|
||||
<Paper
|
||||
sx={{
|
||||
backgroundColor: 'rgba(26, 26, 26, 0.8)',
|
||||
border: '2px solid rgba(255, 107, 53, 0.3)',
|
||||
borderRadius: 3,
|
||||
p: 3,
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography variant="h4" sx={{ color: '#ff6b35', fontWeight: 700, mb: 1 }}>
|
||||
1,234
|
||||
</Typography>
|
||||
<Typography variant="body1" sx={{ color: '#cccccc' }}>
|
||||
Joueurs Actifs
|
||||
</Typography>
|
||||
</Paper>
|
||||
|
||||
<Paper
|
||||
sx={{
|
||||
backgroundColor: 'rgba(26, 26, 26, 0.8)',
|
||||
border: '2px solid rgba(255, 107, 53, 0.3)',
|
||||
borderRadius: 3,
|
||||
p: 3,
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography variant="h4" sx={{ color: '#ff6b35', fontWeight: 700, mb: 1 }}>
|
||||
8
|
||||
</Typography>
|
||||
<Typography variant="body1" sx={{ color: '#cccccc' }}>
|
||||
Circuits Disponibles
|
||||
</Typography>
|
||||
</Paper>
|
||||
|
||||
<Paper
|
||||
sx={{
|
||||
backgroundColor: 'rgba(26, 26, 26, 0.8)',
|
||||
border: '2px solid rgba(255, 107, 53, 0.3)',
|
||||
borderRadius: 3,
|
||||
p: 3,
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography variant="h4" sx={{ color: '#ff6b35', fontWeight: 700, mb: 1 }}>
|
||||
2:45.123
|
||||
</Typography>
|
||||
<Typography variant="body1" sx={{ color: '#cccccc' }}>
|
||||
Meilleur Temps
|
||||
</Typography>
|
||||
</Paper>
|
||||
</Box>
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Ranking;
|
||||
1
src/react-app-env.d.ts
vendored
Normal file
1
src/react-app-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="react-scripts" />
|
||||
15
src/reportWebVitals.ts
Normal file
15
src/reportWebVitals.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { ReportHandler } from 'web-vitals';
|
||||
|
||||
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
|
||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||
getCLS(onPerfEntry);
|
||||
getFID(onPerfEntry);
|
||||
getFCP(onPerfEntry);
|
||||
getLCP(onPerfEntry);
|
||||
getTTFB(onPerfEntry);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default reportWebVitals;
|
||||
5
src/setupTests.ts
Normal file
5
src/setupTests.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
||||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom';
|
||||
36
src/types/index.ts
Normal file
36
src/types/index.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
export interface RankingData {
|
||||
rank: number;
|
||||
playerName: string;
|
||||
car: string;
|
||||
time: string;
|
||||
track: string;
|
||||
date: string;
|
||||
country: string;
|
||||
}
|
||||
|
||||
export interface Track {
|
||||
id: string;
|
||||
name: string;
|
||||
difficulty: 'Easy' | 'Medium' | 'Hard' | 'Expert';
|
||||
length: string;
|
||||
bestTime: string;
|
||||
}
|
||||
|
||||
export interface Car {
|
||||
id: string;
|
||||
name: string;
|
||||
manufacturer: string;
|
||||
power: number;
|
||||
weight: number;
|
||||
drivetrain: 'FR' | 'FF' | '4WD' | 'MR' | 'RR';
|
||||
}
|
||||
|
||||
export interface Player {
|
||||
id: string;
|
||||
name: string;
|
||||
country: string;
|
||||
totalRaces: number;
|
||||
wins: number;
|
||||
bestTime: string;
|
||||
favoriteCar: string;
|
||||
}
|
||||
26
tsconfig.json
Normal file
26
tsconfig.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
||||
15
vite.config.ts
Normal file
15
vite.config.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
server: {
|
||||
port: 5173,
|
||||
open: true
|
||||
},
|
||||
build: {
|
||||
outDir: 'build',
|
||||
sourcemap: true
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user