Autosave: 20260125-141514
This commit is contained in:
parent
36e049bd4d
commit
7490a909b5
6
502.html
6
502.html
@ -129,8 +129,8 @@
|
|||||||
<p class="tip">The application is currently launching. The page will automatically refresh once site is
|
<p class="tip">The application is currently launching. The page will automatically refresh once site is
|
||||||
available.</p>
|
available.</p>
|
||||||
<div class="project-info">
|
<div class="project-info">
|
||||||
<h2>AI App Draft</h2>
|
<h2>ADML Exchange</h2>
|
||||||
<p>Crypto trading platform inspired by OKX with exchange, wallets, orders, and KYC.</p>
|
<p>Professional crypto trading platform with spot, perpetual contracts, and second contracts.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="loader-container">
|
<div class="loader-container">
|
||||||
<img src="https://flatlogic.com/blog/wp-content/uploads/2025/05/logo-bot-1.png" alt="App Logo"
|
<img src="https://flatlogic.com/blog/wp-content/uploads/2025/05/logo-bot-1.png" alt="App Logo"
|
||||||
@ -184,4 +184,4 @@
|
|||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
BIN
assets/pasted-20260125-132149-bf76a614.png
Normal file
BIN
assets/pasted-20260125-132149-bf76a614.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 84 KiB |
BIN
assets/pasted-20260125-140107-3b747574.png
Normal file
BIN
assets/pasted-20260125-140107-3b747574.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 82 KiB |
@ -1,6 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
const os = require('os');
|
const os = require('os');
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
@ -39,7 +36,7 @@ const config = {
|
|||||||
},
|
},
|
||||||
uploadDir: os.tmpdir(),
|
uploadDir: os.tmpdir(),
|
||||||
email: {
|
email: {
|
||||||
from: 'AI App Draft <app@flatlogic.app>',
|
from: 'ADML Exchange <app@adml-exchange.com>',
|
||||||
host: 'email-smtp.us-east-1.amazonaws.com',
|
host: 'email-smtp.us-east-1.amazonaws.com',
|
||||||
port: 587,
|
port: 587,
|
||||||
auth: {
|
auth: {
|
||||||
@ -76,4 +73,4 @@ config.swaggerUrl = `${config.swaggerUI}${config.swaggerPort}`;
|
|||||||
config.uiUrl = `${config.hostUI}${config.portUI ? `:${config.portUI}` : ``}/#`;
|
config.uiUrl = `${config.hostUI}${config.portUI ? `:${config.portUI}` : ``}/#`;
|
||||||
config.backUrl = `${config.hostUI}${config.portUI ? `:${config.portUI}` : ``}`;
|
config.backUrl = `${config.hostUI}${config.portUI ? `:${config.portUI}` : ``}`;
|
||||||
|
|
||||||
module.exports = config;
|
module.exports = config;
|
||||||
@ -2,11 +2,9 @@
|
|||||||
* @type {import('next').NextConfig}
|
* @type {import('next').NextConfig}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const output = process.env.NODE_ENV === 'production' ? 'export' : 'standalone';
|
const nextConfig = {
|
||||||
const nextConfig = {
|
trailingSlash: true,
|
||||||
trailingSlash: true,
|
|
||||||
distDir: 'build',
|
distDir: 'build',
|
||||||
output,
|
|
||||||
basePath: "",
|
basePath: "",
|
||||||
devIndicators: {
|
devIndicators: {
|
||||||
position: 'bottom-left',
|
position: 'bottom-left',
|
||||||
@ -26,7 +24,6 @@ trailingSlash: true,
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default nextConfig
|
export default nextConfig
|
||||||
@ -2,54 +2,32 @@
|
|||||||
"pages": {
|
"pages": {
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"pageTitle": "Dashboard",
|
"pageTitle": "Dashboard",
|
||||||
"overview": "Übersicht",
|
"overview": "ADML Exchange Übersicht",
|
||||||
"loadingWidgets": "Widgets werden geladen...",
|
"loadingWidgets": "Widgets werden geladen...",
|
||||||
"loading": "Laden..."
|
"loading": "Wird geladen..."
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"pageTitle": "Anmeldung",
|
"pageTitle": "Login",
|
||||||
|
|
||||||
"sampleCredentialsAdmin": "Verwenden Sie {{email}} / {{password}}, um sich als Administrator anzumelden",
|
|
||||||
"sampleCredentialsUser": "Verwenden Sie {{email}} / {{password}}, um sich als Benutzer anzumelden",
|
|
||||||
|
|
||||||
"form": {
|
"form": {
|
||||||
"loginLabel": "Login",
|
"loginLabel": "Login",
|
||||||
"loginHelp": "Bitte geben Sie Ihren Login ein",
|
"loginHelp": "Bitte geben Sie Ihren Login ein",
|
||||||
"passwordLabel": "Passwort",
|
"passwordLabel": "Passwort",
|
||||||
"passwordHelp": "Bitte geben Sie Ihr Passwort ein",
|
"passwordHelp": "Bitte geben Sie Ihr Passwort ein",
|
||||||
"remember": "Angemeldet bleiben",
|
"remember": "Angemeldet bleiben",
|
||||||
"forgotPassword": "Passwort vergessen?",
|
"forgotPassword": "Passwort vergessen?",
|
||||||
"loginButton": "Anmelden",
|
"loginButton": "Anmelden",
|
||||||
"loading": "Wird geladen...",
|
"loading": "Wird geladen...",
|
||||||
"noAccountYet": "Noch kein Konto?",
|
"noAccountYet": "Noch kein Konto?",
|
||||||
"newAccount": "Neues Konto"
|
"newAccount": "Neues Konto registrieren"
|
||||||
},
|
|
||||||
|
|
||||||
"pexels": {
|
|
||||||
"photoCredit": "Foto von {{photographer}} auf Pexels",
|
|
||||||
"videoCredit": "Video von {{name}} auf Pexels",
|
|
||||||
"videoUnsupported": "Ihr Browser unterstützt das Video-Tag nicht."
|
|
||||||
},
|
|
||||||
|
|
||||||
"footer": {
|
|
||||||
"copyright": "© {{year}} {{title}}. Alle Rechte vorbehalten",
|
|
||||||
"privacy": "Datenschutzrichtlinie"
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
"components": {
|
|
||||||
"widgetCreator": {
|
|
||||||
"title": "Diagramm oder Widget erstellen",
|
|
||||||
"helpText": "Beschreiben Sie Ihr neues Widget oder Diagramm in natürlicher Sprache. Zum Beispiel: \"Anzahl der Admin-Benutzer\" ODER \"rotes Diagramm mit der Anzahl geschlossener Verträge gruppiert nach Monat\"",
|
|
||||||
"settingsTitle": "Einstellungen für Widget Creator",
|
|
||||||
"settingsDescription": "Für welche Rolle zeigen wir Widgets an und erstellen sie?",
|
|
||||||
"doneButton": "Fertig",
|
|
||||||
"loading": "Laden..."
|
|
||||||
},
|
},
|
||||||
"search": {
|
"exchange": {
|
||||||
"placeholder": "Suche",
|
"pageTitle": "Spot-Handel",
|
||||||
"required": "Pflichtfeld",
|
"spot": "Spot",
|
||||||
"minLength": "Mindestlänge: {{count}} Zeichen"
|
"perpetual": "Perpetual",
|
||||||
|
"seconds": "Sekunden-Kontrakt",
|
||||||
|
"buy": "Kaufen",
|
||||||
|
"sell": "Verkaufen"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,52 +1,176 @@
|
|||||||
{
|
{
|
||||||
"pages": {
|
"nav": {
|
||||||
"dashboard": {
|
"markets": "Markets",
|
||||||
"pageTitle": "Dashboard",
|
"spot": "Spot",
|
||||||
"overview": "Overview",
|
"perpetual": "Perpetual",
|
||||||
"loadingWidgets": "Loading widgets...",
|
"options": "Options"
|
||||||
"loading": "Loading..."
|
},
|
||||||
|
"landing": {
|
||||||
|
"hero": {
|
||||||
|
"title": "ADML Exchange | The World’s Leading Crypto Exchange",
|
||||||
|
"badge": "Trusted by 20M+ Users Globally",
|
||||||
|
"title_part1": "Trade The Future",
|
||||||
|
"title_highlight": "With ADML",
|
||||||
|
"subtitle": "Securely buy, sell and trade over 400+ cryptocurrencies with low fees and institutional-grade security.",
|
||||||
|
"input_placeholder": "Email / Phone number",
|
||||||
|
"cta": "Get Started"
|
||||||
},
|
},
|
||||||
"login": {
|
"stats": {
|
||||||
"pageTitle": "Login",
|
"24h_vol": "24h Volume",
|
||||||
|
"users": "Active Users",
|
||||||
"form": {
|
"assets": "Assets Listed",
|
||||||
"loginLabel": "Login",
|
"uptime": "System Uptime"
|
||||||
"loginHelp": "Please enter your login",
|
},
|
||||||
"passwordLabel": "Password",
|
"features": {
|
||||||
"passwordHelp": "Please enter your password",
|
"security": {
|
||||||
"remember": "Remember",
|
"title": "Security First",
|
||||||
"forgotPassword": "Forgot password?",
|
"desc": "Cold storage, multi-sig wallets, and 2FA protection for all assets. Your security is our priority."
|
||||||
"loginButton": "Login",
|
|
||||||
"loading": "Loading...",
|
|
||||||
"noAccountYet": "Don’t have an account yet?",
|
|
||||||
"newAccount": "New Account"
|
|
||||||
},
|
},
|
||||||
|
"speed": {
|
||||||
"pexels": {
|
"title": "Instant Execution",
|
||||||
"photoCredit": "Photo by {{photographer}} on Pexels",
|
"desc": "Our high-performance matching engine handles 1M+ transactions per second with sub-millisecond latency."
|
||||||
"videoCredit": "Video by {{name}} on Pexels",
|
|
||||||
"videoUnsupported": "Your browser does not support the video tag."
|
|
||||||
},
|
},
|
||||||
|
"support": {
|
||||||
"footer": {
|
"title": "24/7 Support",
|
||||||
"copyright": "© {{year}} {{title}}. All rights reserved",
|
"desc": "Global customer support available in 12 languages around the clock to assist you with any issues."
|
||||||
"privacy": "Privacy Policy"
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"markets": {
|
||||||
|
"title": "Market Trends",
|
||||||
|
"tab": {
|
||||||
|
"popular": "Popular",
|
||||||
|
"new": "New Listings",
|
||||||
|
"gainers": "Top Gainers"
|
||||||
|
},
|
||||||
|
"view_all": "View All Markets",
|
||||||
|
"col": {
|
||||||
|
"asset": "Asset",
|
||||||
|
"price": "Price",
|
||||||
|
"change": "24h Change",
|
||||||
|
"volume": "Volume",
|
||||||
|
"action": "Action"
|
||||||
|
},
|
||||||
|
"trade_btn": "Trade"
|
||||||
|
},
|
||||||
|
"app": {
|
||||||
|
"title": "Trade Anywhere, Anytime.",
|
||||||
|
"desc": "Stay connected to the markets with our professional mobile app. Available on iOS, Android, and Web.",
|
||||||
|
"qr_title": "Scan to download",
|
||||||
|
"qr_desc": "Instant access to ADML features on your mobile device."
|
||||||
|
},
|
||||||
|
"cta": {
|
||||||
|
"title": "Start Your Trade Now",
|
||||||
|
"desc": "Join the most reliable crypto exchange in the world.",
|
||||||
|
"btn_signup": "Create Account",
|
||||||
|
"btn_login": "Login Demo"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"components": {
|
"exchange": {
|
||||||
"widgetCreator": {
|
"title": "Spot Trading",
|
||||||
"title": "Create Chart or Widget",
|
"header": {
|
||||||
"helpText": "Describe your new widget or chart in natural language. For example: \"Number of admin users\" OR \"red chart with number of closed contracts grouped by month\"",
|
"spot": "Spot",
|
||||||
"settingsTitle": "Widget Creator Settings",
|
"last_price": "Last Price",
|
||||||
"settingsDescription": "What role are we showing and creating widgets for?",
|
"change": "24h Change",
|
||||||
"doneButton": "Done",
|
"high": "24h High",
|
||||||
"loading": "Loading..."
|
"low": "24h Low",
|
||||||
|
"volume": "24h Volume",
|
||||||
|
"deposit": "Deposit"
|
||||||
},
|
},
|
||||||
"search": {
|
"tabs": {
|
||||||
"placeholder": "Search",
|
"open_orders": "Open Orders",
|
||||||
"required": "Required",
|
"order_history": "Order History",
|
||||||
"minLength": "Minimum length: {{count}} characters"
|
"trade_history": "Trade History",
|
||||||
|
"assets": "Assets"
|
||||||
|
},
|
||||||
|
"orderbook": {
|
||||||
|
"title": "Order Book",
|
||||||
|
"price": "Price",
|
||||||
|
"amount": "Amount"
|
||||||
|
},
|
||||||
|
"panel": {
|
||||||
|
"buy": "Buy",
|
||||||
|
"sell": "Sell",
|
||||||
|
"type": "Order Type",
|
||||||
|
"market": "Market",
|
||||||
|
"price": "Price",
|
||||||
|
"amount": "Amount",
|
||||||
|
"available": "Available"
|
||||||
|
},
|
||||||
|
"no_records": "No active records found"
|
||||||
|
},
|
||||||
|
"perpetual": {
|
||||||
|
"title": "Perpetual Trading",
|
||||||
|
"header": {
|
||||||
|
"perp": "Perp",
|
||||||
|
"mark_price": "Mark Price",
|
||||||
|
"funding": "Funding / Countdown",
|
||||||
|
"change": "24h Change",
|
||||||
|
"margin": "Margin",
|
||||||
|
"cross": "Cross",
|
||||||
|
"deposit": "Deposit"
|
||||||
|
},
|
||||||
|
"tabs": {
|
||||||
|
"positions": "Positions",
|
||||||
|
"open_orders": "Open Orders",
|
||||||
|
"order_history": "Order History",
|
||||||
|
"trade_history": "Trade History"
|
||||||
|
},
|
||||||
|
"panel": {
|
||||||
|
"long": "Open Long",
|
||||||
|
"short": "Open Short",
|
||||||
|
"mode": "Margin Mode",
|
||||||
|
"cross": "Cross",
|
||||||
|
"price": "Price",
|
||||||
|
"amount": "Amount",
|
||||||
|
"leverage": "Adjust Leverage",
|
||||||
|
"available": "Available",
|
||||||
|
"max_long": "Max Long",
|
||||||
|
"buy": "BUY / LONG",
|
||||||
|
"sell": "SELL / SHORT"
|
||||||
|
},
|
||||||
|
"orderbook": {
|
||||||
|
"title": "Market Depth"
|
||||||
|
},
|
||||||
|
"no_positions": "No active positions or orders"
|
||||||
|
},
|
||||||
|
"seconds": {
|
||||||
|
"title": "Binary Options",
|
||||||
|
"header": {
|
||||||
|
"contract": "Option Contract",
|
||||||
|
"current_price": "Current Price",
|
||||||
|
"yield": "Estimated Yield",
|
||||||
|
"balance": "Available Balance",
|
||||||
|
"deposit": "Deposit"
|
||||||
|
},
|
||||||
|
"tabs": {
|
||||||
|
"current": "Current Positions",
|
||||||
|
"history": "Trade History"
|
||||||
|
},
|
||||||
|
"panel": {
|
||||||
|
"period": "Execution Period",
|
||||||
|
"yield": "Yield",
|
||||||
|
"amount": "Investment Amount",
|
||||||
|
"expected_profit": "Expected Profit",
|
||||||
|
"call": "CALL (HIGH)",
|
||||||
|
"put": "PUT (LOW)",
|
||||||
|
"risk_warning": "Risk Warning: Trading involves significant risk of loss and is not suitable for all investors."
|
||||||
|
},
|
||||||
|
"no_history": "No active contracts"
|
||||||
|
},
|
||||||
|
"footer": {
|
||||||
|
"about": "Professional and secure digital asset trading platform.",
|
||||||
|
"section": {
|
||||||
|
"about": "About",
|
||||||
|
"product": "Product",
|
||||||
|
"support": "Support",
|
||||||
|
"legal": "Legal"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"login": {
|
||||||
|
"title": "Log in",
|
||||||
|
"demo_login": "Demo Account Login"
|
||||||
|
},
|
||||||
|
"register": {
|
||||||
|
"title": "Sign up"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,55 +1,33 @@
|
|||||||
{
|
{
|
||||||
"pages": {
|
"pages": {
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"pageTitle": "Tablero",
|
"pageTitle": "Panel de control",
|
||||||
"overview": "Resumen",
|
"overview": "Resumen de ADML Exchange",
|
||||||
"loadingWidgets": "Cargando widgets...",
|
"loadingWidgets": "Cargando widgets...",
|
||||||
"loading": "Cargando..."
|
"loading": "Cargando..."
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"pageTitle": "Inicio de sesión",
|
"pageTitle": "Iniciar sesión",
|
||||||
|
|
||||||
"sampleCredentialsAdmin": "Use {{email}} / {{password}} para iniciar sesión como Administrador",
|
|
||||||
"sampleCredentialsUser": "Use {{email}} / {{password}} para iniciar sesión como Usuario",
|
|
||||||
|
|
||||||
"form": {
|
"form": {
|
||||||
"loginLabel": "Usuario",
|
"loginLabel": "Login",
|
||||||
"loginHelp": "Introduzca su usuario",
|
"loginHelp": "Por favor, introduzca su login",
|
||||||
"passwordLabel": "Contraseña",
|
"passwordLabel": "Contraseña",
|
||||||
"passwordHelp": "Introduzca su contraseña",
|
"passwordHelp": "Por favor, introduzca su contraseña",
|
||||||
"remember": "Recuérdame",
|
"remember": "Recordarme",
|
||||||
"forgotPassword": "¿Olvidó su contraseña?",
|
"forgotPassword": "¿Olvidó su contraseña?",
|
||||||
"loginButton": "Acceder",
|
"loginButton": "Entrar",
|
||||||
"loading": "Cargando...",
|
"loading": "Cargando...",
|
||||||
"noAccountYet": "¿Aún no tiene una cuenta?",
|
"noAccountYet": "¿No tiene cuenta?",
|
||||||
"newAccount": "Crear cuenta"
|
"newAccount": "Registrar nueva cuenta"
|
||||||
},
|
|
||||||
|
|
||||||
"pexels": {
|
|
||||||
"photoCredit": "Foto de {{photographer}} en Pexels",
|
|
||||||
"videoCredit": "Vídeo de {{name}} en Pexels",
|
|
||||||
"videoUnsupported": "Su navegador no admite la etiqueta de vídeo."
|
|
||||||
},
|
|
||||||
|
|
||||||
"footer": {
|
|
||||||
"copyright": "© {{year}} {{title}}. Todos los derechos reservados",
|
|
||||||
"privacy": "Política de privacidad"
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
"components": {
|
|
||||||
"widgetCreator": {
|
|
||||||
"title": "Crear gráfico o widget",
|
|
||||||
"helpText": "Describe tu nuevo widget o gráfico en lenguaje natural. Por ejemplo: \"Número de usuarios administradores\" O \"gráfico rojo con el número de contratos cerrados agrupados por mes\"",
|
|
||||||
"settingsTitle": "Configuración del creador de widgets",
|
|
||||||
"settingsDescription": "¿Para qué rol estamos mostrando y creando widgets?",
|
|
||||||
"doneButton": "Listo",
|
|
||||||
"loading": "Cargando..."
|
|
||||||
},
|
},
|
||||||
"search": {
|
"exchange": {
|
||||||
"placeholder": "Buscar",
|
"pageTitle": "Trading Spot",
|
||||||
"required": "Obligatorio",
|
"spot": "Spot",
|
||||||
"minLength": "Longitud mínima: {{count}} caracteres"
|
"perpetual": "Perpetuo",
|
||||||
|
"seconds": "Contrato de segundos",
|
||||||
|
"buy": "Comprar",
|
||||||
|
"sell": "Vender"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2,54 +2,32 @@
|
|||||||
"pages": {
|
"pages": {
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"pageTitle": "Tableau de bord",
|
"pageTitle": "Tableau de bord",
|
||||||
"overview": "Vue d'ensemble",
|
"overview": "Aperçu de ADML Exchange",
|
||||||
"loadingWidgets": "Chargement des widgets...",
|
"loadingWidgets": "Chargement des widgets...",
|
||||||
"loading": "Chargement..."
|
"loading": "Chargement..."
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"pageTitle": "Connexion",
|
"pageTitle": "Connexion",
|
||||||
|
|
||||||
"sampleCredentialsAdmin": "Utilisez {{email}} / {{password}} pour vous connecter en tant qu’administrateur",
|
|
||||||
"sampleCredentialsUser": "Utilisez {{email}} / {{password}} pour vous connecter en tant qu’utilisateur",
|
|
||||||
|
|
||||||
"form": {
|
"form": {
|
||||||
"loginLabel": "Identifiant",
|
"loginLabel": "Identifiant",
|
||||||
"loginHelp": "Veuillez saisir votre identifiant",
|
"loginHelp": "Veuillez entrer votre identifiant",
|
||||||
"passwordLabel": "Mot de passe",
|
"passwordLabel": "Mot de passe",
|
||||||
"passwordHelp": "Veuillez saisir votre mot de passe",
|
"passwordHelp": "Veuillez entrer votre mot de passe",
|
||||||
"remember": "Se souvenir de moi",
|
"remember": "Se souvenir de moi",
|
||||||
"forgotPassword": "Mot de passe oublié ?",
|
"forgotPassword": "Mot de passe oublié?",
|
||||||
"loginButton": "Se connecter",
|
"loginButton": "Se connecter",
|
||||||
"loading": "Chargement…",
|
"loading": "Chargement...",
|
||||||
"noAccountYet": "Vous n’avez pas encore de compte ?",
|
"noAccountYet": "Pas encore de compte?",
|
||||||
"newAccount": "Créer un compte"
|
"newAccount": "Créer un compte"
|
||||||
},
|
|
||||||
|
|
||||||
"pexels": {
|
|
||||||
"photoCredit": "Photo de {{photographer}} sur Pexels",
|
|
||||||
"videoCredit": "Vidéo de {{name}} sur Pexels",
|
|
||||||
"videoUnsupported": "Votre navigateur ne prend pas en charge la balise vidéo."
|
|
||||||
},
|
|
||||||
|
|
||||||
"footer": {
|
|
||||||
"copyright": "© {{year}} {{title}}. Tous droits réservés",
|
|
||||||
"privacy": "Politique de confidentialité"
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
"components": {
|
|
||||||
"widgetCreator": {
|
|
||||||
"title": "Créer un graphique ou un widget",
|
|
||||||
"helpText": "Décrivez votre nouveau widget ou graphique en langage naturel. Par exemple : \"Nombre d'utilisateurs administrateurs\" OU \"graphique rouge avec le nombre de contrats clôturés regroupés par mois\"",
|
|
||||||
"settingsTitle": "Paramètres du créateur de widget",
|
|
||||||
"settingsDescription": "Pour quel rôle affichons-nous et créons-nous des widgets ?",
|
|
||||||
"doneButton": "Terminé",
|
|
||||||
"loading": "Chargement..."
|
|
||||||
},
|
},
|
||||||
"search": {
|
"exchange": {
|
||||||
"placeholder": "Rechercher",
|
"pageTitle": "Trading Spot",
|
||||||
"required": "Champ requis",
|
"spot": "Spot",
|
||||||
"minLength": "Longueur minimale : {{count}} caractères"
|
"perpetual": "Perpétuel",
|
||||||
|
"seconds": "Contrat de secondes",
|
||||||
|
"buy": "Acheter",
|
||||||
|
"sell": "Vendre"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
46
frontend/public/locales/ja/common.json
Normal file
46
frontend/public/locales/ja/common.json
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"nav": {
|
||||||
|
"markets": "マーケット",
|
||||||
|
"spot": "現物取引",
|
||||||
|
"perpetual": "先物取引",
|
||||||
|
"options": "オプション"
|
||||||
|
},
|
||||||
|
"landing": {
|
||||||
|
"hero": {
|
||||||
|
"title": "ADML 取引所 | 世界をリードする暗号資産取引所",
|
||||||
|
"badge": "世界中で2000万人以上のユーザーに信頼されています",
|
||||||
|
"cta": "今すぐ開始"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"exchange": {
|
||||||
|
"title": "現物取引",
|
||||||
|
"header": {
|
||||||
|
"spot": "現物",
|
||||||
|
"deposit": "入金"
|
||||||
|
},
|
||||||
|
"panel": {
|
||||||
|
"buy": "買い",
|
||||||
|
"sell": "売り"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"perpetual": {
|
||||||
|
"title": "先物取引",
|
||||||
|
"panel": {
|
||||||
|
"long": "ロング",
|
||||||
|
"short": "ショート"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"seconds": {
|
||||||
|
"title": "バイナリーオプション",
|
||||||
|
"panel": {
|
||||||
|
"call": "コール (上昇)",
|
||||||
|
"put": "プット (下落)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"login": {
|
||||||
|
"title": "ログイン"
|
||||||
|
},
|
||||||
|
"register": {
|
||||||
|
"title": "新規登録"
|
||||||
|
}
|
||||||
|
}
|
||||||
46
frontend/public/locales/ko/common.json
Normal file
46
frontend/public/locales/ko/common.json
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"nav": {
|
||||||
|
"markets": "마켓",
|
||||||
|
"spot": "현물 거래",
|
||||||
|
"perpetual": "선물 거래",
|
||||||
|
"options": "옵션"
|
||||||
|
},
|
||||||
|
"landing": {
|
||||||
|
"hero": {
|
||||||
|
"title": "ADML 거래소 | 세계 최고의 암호화폐 거래소",
|
||||||
|
"badge": "전 세계 2000만 명 이상의 사용자가 신뢰함",
|
||||||
|
"cta": "시작하기"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"exchange": {
|
||||||
|
"title": "현물 거래",
|
||||||
|
"header": {
|
||||||
|
"spot": "현물",
|
||||||
|
"deposit": "입금"
|
||||||
|
},
|
||||||
|
"panel": {
|
||||||
|
"buy": "매수",
|
||||||
|
"sell": "매도"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"perpetual": {
|
||||||
|
"title": "선물 거래",
|
||||||
|
"panel": {
|
||||||
|
"long": "롱 오픈",
|
||||||
|
"short": "숏 오픈"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"seconds": {
|
||||||
|
"title": "바이너리 옵션",
|
||||||
|
"panel": {
|
||||||
|
"call": "콜 (상승)",
|
||||||
|
"put": "풋 (하락)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"login": {
|
||||||
|
"title": "로그인"
|
||||||
|
},
|
||||||
|
"register": {
|
||||||
|
"title": "회원가입"
|
||||||
|
}
|
||||||
|
}
|
||||||
48
frontend/public/locales/ru/common.json
Normal file
48
frontend/public/locales/ru/common.json
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"pages": {
|
||||||
|
"dashboard": {
|
||||||
|
"pageTitle": "Панель управления",
|
||||||
|
"overview": "Обзор биржи ADML",
|
||||||
|
"loadingWidgets": "Загрузка виджетов...",
|
||||||
|
"loading": "Загрузка..."
|
||||||
|
},
|
||||||
|
"login": {
|
||||||
|
"pageTitle": "Вход",
|
||||||
|
"form": {
|
||||||
|
"loginLabel": "Логин",
|
||||||
|
"loginHelp": "Введите ваш логин",
|
||||||
|
"passwordLabel": "Пароль",
|
||||||
|
"passwordHelp": "Введите ваш пароль",
|
||||||
|
"remember": "Запомнить меня",
|
||||||
|
"forgotPassword": "Забыли пароль?",
|
||||||
|
"loginButton": "Войти",
|
||||||
|
"loading": "Загрузка...",
|
||||||
|
"noAccountYet": "Нет аккаунта?",
|
||||||
|
"newAccount": "Регистрация"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"exchange": {
|
||||||
|
"pageTitle": "Спотовая торговля",
|
||||||
|
"spot": "Спот",
|
||||||
|
"perpetual": "Фьючерсы",
|
||||||
|
"seconds": "Опционы",
|
||||||
|
"buy": "Купить",
|
||||||
|
"sell": "Продать"
|
||||||
|
},
|
||||||
|
"perpetual": {
|
||||||
|
"pageTitle": "Бессрочные контракты",
|
||||||
|
"leverage": "Плечо",
|
||||||
|
"cross": "Кросс",
|
||||||
|
"isolated": "Изолир.",
|
||||||
|
"long": "Лонг",
|
||||||
|
"short": "Шорт"
|
||||||
|
},
|
||||||
|
"seconds": {
|
||||||
|
"pageTitle": "Опционы (Seconds)",
|
||||||
|
"duration": "Длительность",
|
||||||
|
"profit": "Доходность",
|
||||||
|
"call": "Вверх",
|
||||||
|
"put": "Вниз"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
174
frontend/public/locales/zh/common.json
Normal file
174
frontend/public/locales/zh/common.json
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
{
|
||||||
|
"nav": {
|
||||||
|
"markets": "行情",
|
||||||
|
"spot": "现货交易",
|
||||||
|
"perpetual": "合约交易",
|
||||||
|
"options": "秒合约"
|
||||||
|
},
|
||||||
|
"landing": {
|
||||||
|
"hero": {
|
||||||
|
"title": "ADML 交易所 | 全球领先的加密货币交易平台",
|
||||||
|
"badge": "全球 2000 万用户的信赖之选",
|
||||||
|
"title_part1": "交易未来",
|
||||||
|
"title_highlight": "就在 ADML",
|
||||||
|
"subtitle": "安全地购买、出售和交易 400 多种加密货币,享受低手续费和机构级安全保障。",
|
||||||
|
"input_placeholder": "邮箱 / 手机号码",
|
||||||
|
"cta": "立即开始"
|
||||||
|
},
|
||||||
|
"stats": {
|
||||||
|
"24h_vol": "24小时成交量",
|
||||||
|
"users": "活跃用户",
|
||||||
|
"assets": "上架资产",
|
||||||
|
"uptime": "系统运行时间"
|
||||||
|
},
|
||||||
|
"features": {
|
||||||
|
"security": {
|
||||||
|
"title": "安全第一",
|
||||||
|
"desc": "冷存储、多重签名钱包和 2FA 双重验证,保障所有资产安全。您的安全是我们的重中之重。"
|
||||||
|
},
|
||||||
|
"speed": {
|
||||||
|
"title": "秒级执行",
|
||||||
|
"desc": "高性能撮合引擎,每秒处理超过 100 万笔交易,延迟低至亚毫秒级。"
|
||||||
|
},
|
||||||
|
"support": {
|
||||||
|
"title": "24/7 客户支持",
|
||||||
|
"desc": "全球客服团队,支持 12 种语言,全天候为您解决任何问题。"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"markets": {
|
||||||
|
"title": "行情趋势",
|
||||||
|
"tab": {
|
||||||
|
"popular": "热门",
|
||||||
|
"new": "新币",
|
||||||
|
"gainers": "涨幅榜"
|
||||||
|
},
|
||||||
|
"view_all": "查看所有行情",
|
||||||
|
"col": {
|
||||||
|
"asset": "资产",
|
||||||
|
"price": "最新价",
|
||||||
|
"change": "24小时涨跌",
|
||||||
|
"volume": "成交量",
|
||||||
|
"action": "操作"
|
||||||
|
},
|
||||||
|
"trade_btn": "去交易"
|
||||||
|
},
|
||||||
|
"app": {
|
||||||
|
"title": "随时随地,随心交易",
|
||||||
|
"desc": "使用我们的专业移动应用程序,随时关注市场。支持 iOS、Android 和 Web。"
|
||||||
|
},
|
||||||
|
"cta": {
|
||||||
|
"title": "立即开启您的交易之旅",
|
||||||
|
"desc": "加入全球最可靠的加密货币交易所。",
|
||||||
|
"btn_signup": "创建账户",
|
||||||
|
"btn_login": "演示登录"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"exchange": {
|
||||||
|
"title": "现货交易",
|
||||||
|
"header": {
|
||||||
|
"spot": "现货",
|
||||||
|
"last_price": "最新价",
|
||||||
|
"change": "24h 涨跌",
|
||||||
|
"high": "24h 最高",
|
||||||
|
"low": "24h 最低",
|
||||||
|
"volume": "24h 成交量",
|
||||||
|
"deposit": "充值"
|
||||||
|
},
|
||||||
|
"tabs": {
|
||||||
|
"open_orders": "当前委托",
|
||||||
|
"order_history": "历史委托",
|
||||||
|
"trade_history": "成交历史",
|
||||||
|
"assets": "资产"
|
||||||
|
},
|
||||||
|
"orderbook": {
|
||||||
|
"title": "订单簿",
|
||||||
|
"price": "价格",
|
||||||
|
"amount": "数量"
|
||||||
|
},
|
||||||
|
"panel": {
|
||||||
|
"buy": "买入",
|
||||||
|
"sell": "卖出",
|
||||||
|
"type": "订单类型",
|
||||||
|
"market": "市价",
|
||||||
|
"price": "价格",
|
||||||
|
"amount": "数量",
|
||||||
|
"available": "可用"
|
||||||
|
},
|
||||||
|
"no_records": "暂无记录"
|
||||||
|
},
|
||||||
|
"perpetual": {
|
||||||
|
"title": "合约交易",
|
||||||
|
"header": {
|
||||||
|
"perp": "永续",
|
||||||
|
"mark_price": "标记价格",
|
||||||
|
"funding": "资金费率 / 倒计时",
|
||||||
|
"change": "24h 涨跌",
|
||||||
|
"margin": "保证金",
|
||||||
|
"cross": "全仓",
|
||||||
|
"deposit": "充值"
|
||||||
|
},
|
||||||
|
"tabs": {
|
||||||
|
"positions": "持有仓位",
|
||||||
|
"open_orders": "当前委托",
|
||||||
|
"order_history": "历史委托",
|
||||||
|
"trade_history": "成交历史"
|
||||||
|
},
|
||||||
|
"panel": {
|
||||||
|
"long": "看涨/做多",
|
||||||
|
"short": "看跌/做空",
|
||||||
|
"mode": "仓位模式",
|
||||||
|
"cross": "全仓",
|
||||||
|
"price": "价格",
|
||||||
|
"amount": "数量",
|
||||||
|
"leverage": "调整杠杆",
|
||||||
|
"available": "可用平衡",
|
||||||
|
"max_long": "最大可开多",
|
||||||
|
"buy": "买入 / 做多",
|
||||||
|
"sell": "卖出 / 做空"
|
||||||
|
},
|
||||||
|
"orderbook": {
|
||||||
|
"title": "深度图"
|
||||||
|
},
|
||||||
|
"no_positions": "暂无持仓或委托"
|
||||||
|
},
|
||||||
|
"seconds": {
|
||||||
|
"title": "秒合约",
|
||||||
|
"header": {
|
||||||
|
"contract": "期权合约",
|
||||||
|
"current_price": "当前价",
|
||||||
|
"yield": "预期收益",
|
||||||
|
"balance": "可用余额",
|
||||||
|
"deposit": "充值"
|
||||||
|
},
|
||||||
|
"tabs": {
|
||||||
|
"current": "当前持仓",
|
||||||
|
"history": "交易历史"
|
||||||
|
},
|
||||||
|
"panel": {
|
||||||
|
"period": "执行周期",
|
||||||
|
"yield": "收益率",
|
||||||
|
"amount": "投资金额",
|
||||||
|
"expected_profit": "预期利润",
|
||||||
|
"call": "看涨 (买升)",
|
||||||
|
"put": "看跌 (买降)",
|
||||||
|
"risk_warning": "风险提示:交易涉及显著的亏损风险,不适合所有投资者。"
|
||||||
|
},
|
||||||
|
"no_history": "暂无活跃合约"
|
||||||
|
},
|
||||||
|
"footer": {
|
||||||
|
"about": "专业、安全的数字资产交易平台。",
|
||||||
|
"section": {
|
||||||
|
"about": "关于我们",
|
||||||
|
"product": "产品中心",
|
||||||
|
"support": "服务支持",
|
||||||
|
"legal": "法律条款"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"login": {
|
||||||
|
"title": "登录",
|
||||||
|
"demo_login": "演示账户登录"
|
||||||
|
},
|
||||||
|
"register": {
|
||||||
|
"title": "注册"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,6 +5,7 @@ import AsideMenuList from './AsideMenuList'
|
|||||||
import { MenuAsideItem } from '../interfaces'
|
import { MenuAsideItem } from '../interfaces'
|
||||||
import { useAppSelector } from '../stores/hooks'
|
import { useAppSelector } from '../stores/hooks'
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
import Logo from './Logo';
|
||||||
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -29,22 +30,19 @@ export default function AsideMenuLayer({ menu, className = '', ...props }: Props
|
|||||||
return (
|
return (
|
||||||
<aside
|
<aside
|
||||||
id='asideMenu'
|
id='asideMenu'
|
||||||
className={`${className} zzz lg:py-2 lg:pl-2 w-60 fixed flex z-40 top-0 h-screen transition-position overflow-hidden`}
|
className={`${className} zzz lg:py-2 lg:pl-2 w-64 fixed flex z-40 top-0 h-screen transition-position overflow-hidden`}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`flex-1 flex flex-col overflow-hidden dark:bg-dark-900 ${asideStyle} ${corners}`}
|
className={`flex-1 flex flex-col overflow-hidden dark:bg-[#0a0a0a] ${asideStyle} ${corners}`}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`flex flex-row h-14 items-center justify-between ${asideBrandStyle}`}
|
className={`flex flex-row h-16 items-center justify-between px-6 ${asideBrandStyle}`}
|
||||||
>
|
>
|
||||||
<div className="text-center flex-1 lg:text-left lg:pl-6 xl:text-center xl:pl-0">
|
<Link href="/dashboard" className="flex-1 flex items-center justify-center lg:justify-start">
|
||||||
|
<Logo className="scale-75 origin-left" />
|
||||||
<b className="font-black">AI App Draft</b>
|
</Link>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<button
|
<button
|
||||||
className="hidden lg:inline-block xl:hidden p-3"
|
className="hidden lg:inline-block xl:hidden p-3 text-gray-500 hover:text-white"
|
||||||
onClick={handleAsideLgCloseClick}
|
onClick={handleAsideLgCloseClick}
|
||||||
>
|
>
|
||||||
<BaseIcon path={mdiClose} />
|
<BaseIcon path={mdiClose} />
|
||||||
@ -60,4 +58,4 @@ export default function AsideMenuLayer({ menu, className = '', ...props }: Props
|
|||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -1,6 +1,5 @@
|
|||||||
import React, { ReactNode } from 'react'
|
import React, { ReactNode } from 'react'
|
||||||
import { containerMaxW } from '../config'
|
import { containerMaxW } from '../config'
|
||||||
import Logo from './Logo'
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
children?: ReactNode
|
children?: ReactNode
|
||||||
@ -10,26 +9,17 @@ export default function FooterBar({ children }: Props) {
|
|||||||
const year = new Date().getFullYear()
|
const year = new Date().getFullYear()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<footer className={`py-2 px-6 ${containerMaxW}`}>
|
<footer className={`py-6 px-6 ${containerMaxW} border-t border-white/5`}>
|
||||||
<div className="block md:flex items-center justify-between">
|
<div className="flex flex-col md:flex-row items-center justify-between gap-4 text-center md:text-left">
|
||||||
<div className="text-center md:text-left mb-6 md:mb-0">
|
<div className="text-[10px] font-black uppercase tracking-[0.2em] text-gray-500">
|
||||||
<b>
|
© {year} ADML EXCHANGE. ALL RIGHTS RESERVED.
|
||||||
©{year},{` `}
|
</div>
|
||||||
<a href="https://flatlogic.com/" rel="noreferrer" target="_blank">
|
<div className="flex items-center space-x-6 text-[10px] font-black uppercase tracking-widest text-gray-600">
|
||||||
Flatlogic
|
<a href="#" className="hover:text-blue-500 transition-colors">Privacy Policy</a>
|
||||||
</a>
|
<a href="#" className="hover:text-blue-500 transition-colors">Terms of Service</a>
|
||||||
.
|
<a href="#" className="hover:text-blue-500 transition-colors">Risk Warning</a>
|
||||||
</b>
|
|
||||||
{` `}
|
|
||||||
{children}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex item-center md:py-2 gap-4">
|
|
||||||
<a href="https://flatlogic.com/" rel="noreferrer" target="_blank">
|
|
||||||
<Logo className="w-auto h-8 md:h-6 mx-auto" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -1,13 +1,18 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import Select, { components, SingleValueProps, OptionProps } from 'react-select';
|
import Select, { components, SingleValueProps, OptionProps } from 'react-select';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
type LanguageOption = { label: string; value: string };
|
type LanguageOption = { label: string; value: string };
|
||||||
|
|
||||||
const LANGS: LanguageOption[] = [
|
const LANGS: LanguageOption[] = [
|
||||||
{ value: 'en', label: '🇬🇧 EN' },
|
{ value: 'en', label: '🇬🇧 EN' },
|
||||||
|
{ value: 'zh', label: '🇨🇳 ZH' },
|
||||||
{ value: 'fr', label: '🇫🇷 FR' },
|
{ value: 'fr', label: '🇫🇷 FR' },
|
||||||
{ value: 'es', label: '🇪🇸 ES' },
|
{ value: 'es', label: '🇪🇸 ES' },
|
||||||
{ value: 'de', label: '🇩🇪 DE' },
|
{ value: 'de', label: '🇩🇪 DE' },
|
||||||
|
{ value: 'ja', label: '🇯🇵 JA' },
|
||||||
|
{ value: 'ko', label: '🇰🇷 KO' },
|
||||||
|
{ value: 'ru', label: '🇷🇺 RU' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const Option = (props: OptionProps<LanguageOption, false>) => (
|
const Option = (props: OptionProps<LanguageOption, false>) => (
|
||||||
@ -23,22 +28,26 @@ const SingleVal = (props: SingleValueProps<LanguageOption, false>) => (
|
|||||||
);
|
);
|
||||||
|
|
||||||
const LanguageSwitcher: React.FC = () => {
|
const LanguageSwitcher: React.FC = () => {
|
||||||
|
const { i18n } = useTranslation();
|
||||||
const [mounted, setMounted] = useState(false);
|
const [mounted, setMounted] = useState(false);
|
||||||
const [selected, setSelected] = useState<LanguageOption>(LANGS[0]);
|
const [selected, setSelected] = useState<LanguageOption>(LANGS[0]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setMounted(true);
|
setMounted(true);
|
||||||
}, []);
|
const currentLang = LANGS.find(l => l.value === i18n.language) || LANGS[0];
|
||||||
|
setSelected(currentLang);
|
||||||
|
}, [i18n.language]);
|
||||||
|
|
||||||
const handleChange = (opt: LanguageOption | null) => {
|
const handleChange = (opt: LanguageOption | null) => {
|
||||||
if (!opt) return;
|
if (!opt) return;
|
||||||
|
i18n.changeLanguage(opt.value);
|
||||||
setSelected(opt);
|
setSelected(opt);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!mounted) return null;
|
if (!mounted) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ width: 88 }}>
|
<div style={{ width: 100 }}>
|
||||||
<Select
|
<Select
|
||||||
value={selected}
|
value={selected}
|
||||||
options={LANGS}
|
options={LANGS}
|
||||||
@ -53,12 +62,13 @@ const LanguageSwitcher: React.FC = () => {
|
|||||||
styles={{
|
styles={{
|
||||||
control: (base) => ({
|
control: (base) => ({
|
||||||
...base,
|
...base,
|
||||||
minHeight: 28,
|
minHeight: 32,
|
||||||
height: 28,
|
height: 32,
|
||||||
paddingTop: 0,
|
paddingTop: 0,
|
||||||
paddingBottom: 0,
|
paddingBottom: 0,
|
||||||
borderColor: '#d1d5db',
|
borderColor: '#d1d5db',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
|
backgroundColor: 'transparent',
|
||||||
}),
|
}),
|
||||||
valueContainer: (base) => ({
|
valueContainer: (base) => ({
|
||||||
...base,
|
...base,
|
||||||
@ -68,7 +78,7 @@ const LanguageSwitcher: React.FC = () => {
|
|||||||
}),
|
}),
|
||||||
indicatorsContainer: (base) => ({
|
indicatorsContainer: (base) => ({
|
||||||
...base,
|
...base,
|
||||||
height: 24,
|
height: 28,
|
||||||
}),
|
}),
|
||||||
dropdownIndicator: (base) => ({
|
dropdownIndicator: (base) => ({
|
||||||
...base,
|
...base,
|
||||||
@ -78,7 +88,7 @@ const LanguageSwitcher: React.FC = () => {
|
|||||||
...base,
|
...base,
|
||||||
paddingTop: 4,
|
paddingTop: 4,
|
||||||
paddingBottom: 4,
|
paddingBottom: 4,
|
||||||
height: 26,
|
height: 32,
|
||||||
fontSize: '0.875rem',
|
fontSize: '0.875rem',
|
||||||
backgroundColor: state.isFocused ? '#f3f4f6' : 'white',
|
backgroundColor: state.isFocused ? '#f3f4f6' : 'white',
|
||||||
color: '#111827',
|
color: '#111827',
|
||||||
@ -93,4 +103,4 @@ const LanguageSwitcher: React.FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default LanguageSwitcher;
|
export default LanguageSwitcher;
|
||||||
@ -6,10 +6,18 @@ type Props = {
|
|||||||
|
|
||||||
export default function Logo({ className = '' }: Props) {
|
export default function Logo({ className = '' }: Props) {
|
||||||
return (
|
return (
|
||||||
<img
|
<div className={`flex items-center space-x-2 ${className}`}>
|
||||||
src={"https://flatlogic.com/logo.svg"}
|
<div className="relative w-10 h-10 group">
|
||||||
className={className}
|
<div className="absolute inset-0 bg-blue-600 rounded-xl rotate-6 group-hover:rotate-0 transition-transform duration-300"></div>
|
||||||
alt={'Flatlogic logo'}>
|
<div className="absolute inset-0 bg-black border-2 border-blue-500 rounded-xl flex items-center justify-center font-black text-blue-500 text-xl overflow-hidden">
|
||||||
</img>
|
A
|
||||||
|
<div className="absolute -bottom-1 -right-1 w-4 h-4 bg-blue-500 rotate-45"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col leading-none">
|
||||||
|
<span className="font-black text-2xl tracking-tighter italic">ADML</span>
|
||||||
|
<span className="text-[8px] font-bold text-blue-500 tracking-[0.2em] uppercase ml-0.5">Exchange</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import NavBarItemPlain from './NavBarItemPlain'
|
|||||||
import NavBarMenuList from './NavBarMenuList'
|
import NavBarMenuList from './NavBarMenuList'
|
||||||
import { MenuNavBarItem } from '../interfaces'
|
import { MenuNavBarItem } from '../interfaces'
|
||||||
import { useAppSelector } from '../stores/hooks';
|
import { useAppSelector } from '../stores/hooks';
|
||||||
|
import LanguageSwitcher from './LanguageSwitcher'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
menu: MenuNavBarItem[]
|
menu: MenuNavBarItem[]
|
||||||
@ -39,6 +40,9 @@ export default function NavBar({ menu, className = '', children }: Props) {
|
|||||||
>
|
>
|
||||||
<div className={`flex lg:items-stretch ${containerMaxW} ${isScrolled && `border-b border-pavitra-400 dark:border-dark-700`}`}>
|
<div className={`flex lg:items-stretch ${containerMaxW} ${isScrolled && `border-b border-pavitra-400 dark:border-dark-700`}`}>
|
||||||
<div className="flex flex-1 items-stretch h-14">{children}</div>
|
<div className="flex flex-1 items-stretch h-14">{children}</div>
|
||||||
|
<div className="flex items-center px-3 lg:hidden">
|
||||||
|
<LanguageSwitcher />
|
||||||
|
</div>
|
||||||
<div className="flex-none items-stretch flex h-14 lg:hidden">
|
<div className="flex-none items-stretch flex h-14 lg:hidden">
|
||||||
<NavBarItemPlain onClick={handleMenuNavBarToggleClick}>
|
<NavBarItemPlain onClick={handleMenuNavBarToggleClick}>
|
||||||
<BaseIcon path={isMenuNavBarActive ? mdiClose : mdiDotsVertical} size="24" />
|
<BaseIcon path={isMenuNavBarActive ? mdiClose : mdiDotsVertical} size="24" />
|
||||||
@ -49,9 +53,12 @@ export default function NavBar({ menu, className = '', children }: Props) {
|
|||||||
isMenuNavBarActive ? 'block' : 'hidden'
|
isMenuNavBarActive ? 'block' : 'hidden'
|
||||||
} flex items-center max-h-screen-menu overflow-y-auto lg:overflow-visible absolute w-screen top-14 left-0 ${bgColor} shadow-lg lg:w-auto lg:flex lg:static lg:shadow-none dark:bg-dark-800`}
|
} flex items-center max-h-screen-menu overflow-y-auto lg:overflow-visible absolute w-screen top-14 left-0 ${bgColor} shadow-lg lg:w-auto lg:flex lg:static lg:shadow-none dark:bg-dark-800`}
|
||||||
>
|
>
|
||||||
|
<div className="hidden lg:flex items-center px-4">
|
||||||
|
<LanguageSwitcher />
|
||||||
|
</div>
|
||||||
<NavBarMenuList menu={menu} />
|
<NavBarMenuList menu={menu} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -1,6 +1,5 @@
|
|||||||
import React, {useEffect, useRef} from 'react'
|
import React, { useEffect, useRef, useState } from 'react'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useState } from 'react'
|
|
||||||
import { mdiChevronUp, mdiChevronDown } from '@mdi/js'
|
import { mdiChevronUp, mdiChevronDown } from '@mdi/js'
|
||||||
import BaseDivider from './BaseDivider'
|
import BaseDivider from './BaseDivider'
|
||||||
import BaseIcon from './BaseIcon'
|
import BaseIcon from './BaseIcon'
|
||||||
@ -129,4 +128,4 @@ export default function NavBarItem({ item }: Props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return <div className={componentClass} ref={excludedRef}>{NavBarItemComponentContents}</div>
|
return <div className={componentClass} ref={excludedRef}>{NavBarItemComponentContents}</div>
|
||||||
}
|
}
|
||||||
@ -1,15 +1,13 @@
|
|||||||
export const hostApi = process.env.NODE_ENV === 'development' && !process.env.NEXT_PUBLIC_BACK_API ? 'http://localhost' : ''
|
export const containerMaxW = 'container lg:mx-auto'
|
||||||
export const portApi = process.env.NODE_ENV === 'development' && !process.env.NEXT_PUBLIC_BACK_API ? 8080 : '';
|
|
||||||
export const baseURLApi = `${hostApi}${portApi ? `:${portApi}` : ``}/api`
|
export const appTitle = 'ADML Exchange'
|
||||||
|
|
||||||
|
export const getPageTitle = (pageTitle: string) => `${pageTitle} — ${appTitle}`
|
||||||
|
|
||||||
|
export const baseURLApi = process.env.NEXT_PUBLIC_BACK_API || '/api'
|
||||||
|
|
||||||
|
export const tinyKey = process.env.NEXT_PUBLIC_TINY_KEY || ''
|
||||||
|
|
||||||
export const localStorageDarkModeKey = 'darkMode'
|
export const localStorageDarkModeKey = 'darkMode'
|
||||||
|
|
||||||
export const localStorageStyleKey = 'style'
|
export const localStorageStyleKey = 'style'
|
||||||
|
|
||||||
export const containerMaxW = 'xl:max-w-full xl:mx-auto 2xl:mx-20'
|
|
||||||
|
|
||||||
export const appTitle = 'created by Flatlogic generator!'
|
|
||||||
|
|
||||||
export const getPageTitle = (currentPageTitle: string) => `${currentPageTitle} — ${appTitle}`
|
|
||||||
|
|
||||||
export const tinyKey = process.env.NEXT_PUBLIC_TINY_KEY || ''
|
|
||||||
@ -9,6 +9,8 @@ i18n
|
|||||||
.use(initReactI18next)
|
.use(initReactI18next)
|
||||||
.init({
|
.init({
|
||||||
fallbackLng: 'en',
|
fallbackLng: 'en',
|
||||||
|
ns: ['common'],
|
||||||
|
defaultNS: 'common',
|
||||||
detection: {
|
detection: {
|
||||||
order: ['localStorage', 'navigator'],
|
order: ['localStorage', 'navigator'],
|
||||||
lookupLocalStorage: 'app_lang_',
|
lookupLocalStorage: 'app_lang_',
|
||||||
@ -18,4 +20,6 @@ i18n
|
|||||||
loadPath: '/locales/{{lng}}/{{ns}}.json',
|
loadPath: '/locales/{{lng}}/{{ns}}.json',
|
||||||
},
|
},
|
||||||
interpolation: { escapeValue: false },
|
interpolation: { escapeValue: false },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export default i18n;
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import React, { ReactNode, useEffect } from 'react'
|
import React, { ReactNode, useEffect, useState } from 'react'
|
||||||
import { useState } from 'react'
|
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import { mdiForwardburger, mdiBackburger, mdiMenu } from '@mdi/js'
|
import { mdiForwardburger, mdiBackburger, mdiMenu, mdiAlertCircle } from '@mdi/js'
|
||||||
import menuAside from '../menuAside'
|
import menuAside from '../menuAside'
|
||||||
import menuNavBar from '../menuNavBar'
|
import menuNavBar from '../menuNavBar'
|
||||||
import BaseIcon from '../components/BaseIcon'
|
import BaseIcon from '../components/BaseIcon'
|
||||||
@ -13,6 +12,7 @@ import { useAppDispatch, useAppSelector } from '../stores/hooks'
|
|||||||
import Search from '../components/Search';
|
import Search from '../components/Search';
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import {findMe, logoutUser} from "../stores/authSlice";
|
import {findMe, logoutUser} from "../stores/authSlice";
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import {hasPermission} from "../helpers/userPermissions";
|
import {hasPermission} from "../helpers/userPermissions";
|
||||||
|
|
||||||
@ -32,6 +32,7 @@ export default function LayoutAuthenticated({
|
|||||||
}: Props) {
|
}: Props) {
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const { t } = useTranslation('common');
|
||||||
const { token, currentUser } = useAppSelector((state) => state.auth)
|
const { token, currentUser } = useAppSelector((state) => state.auth)
|
||||||
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
||||||
let localToken
|
let localToken
|
||||||
@ -86,18 +87,19 @@ export default function LayoutAuthenticated({
|
|||||||
}, [router.events, dispatch])
|
}, [router.events, dispatch])
|
||||||
|
|
||||||
|
|
||||||
const layoutAsidePadding = 'xl:pl-60'
|
const layoutAsidePadding = 'xl:pl-64'
|
||||||
|
const isDemo = currentUser?.email === 'admin@flatlogic.com'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`${darkMode ? 'dark' : ''} overflow-hidden lg:overflow-visible`}>
|
<div className={`${darkMode ? 'dark' : ''} overflow-hidden lg:overflow-visible`}>
|
||||||
<div
|
<div
|
||||||
className={`${layoutAsidePadding} ${
|
className={`${layoutAsidePadding} ${
|
||||||
isAsideMobileExpanded ? 'ml-60 lg:ml-0' : ''
|
isAsideMobileExpanded ? 'ml-64 lg:ml-0' : ''
|
||||||
} pt-14 min-h-screen w-screen transition-position lg:w-auto ${bgColor} dark:bg-dark-800 dark:text-slate-100`}
|
} pt-14 min-h-screen w-screen transition-position lg:w-auto ${bgColor} dark:bg-[#000000] dark:text-slate-100`}
|
||||||
>
|
>
|
||||||
<NavBar
|
<NavBar
|
||||||
menu={menuNavBar}
|
menu={menuNavBar}
|
||||||
className={`${layoutAsidePadding} ${isAsideMobileExpanded ? 'ml-60 lg:ml-0' : ''}`}
|
className={`${layoutAsidePadding} ${isAsideMobileExpanded ? 'ml-64 lg:ml-0' : ''}`}
|
||||||
>
|
>
|
||||||
<NavBarItemPlain
|
<NavBarItemPlain
|
||||||
display="flex lg:hidden"
|
display="flex lg:hidden"
|
||||||
@ -111,6 +113,12 @@ export default function LayoutAuthenticated({
|
|||||||
>
|
>
|
||||||
<BaseIcon path={mdiMenu} size="24" />
|
<BaseIcon path={mdiMenu} size="24" />
|
||||||
</NavBarItemPlain>
|
</NavBarItemPlain>
|
||||||
|
{isDemo && (
|
||||||
|
<div className="hidden md:flex items-center px-4 py-1.5 bg-blue-600/10 border border-blue-500/20 rounded-full text-blue-500 text-[10px] font-black uppercase tracking-widest ml-4">
|
||||||
|
<BaseIcon path={mdiAlertCircle} size={14} className="mr-2" />
|
||||||
|
{t('demo.badge', { defaultValue: 'Demo Mode' })}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<NavBarItemPlain useMargin>
|
<NavBarItemPlain useMargin>
|
||||||
<Search />
|
<Search />
|
||||||
</NavBarItemPlain>
|
</NavBarItemPlain>
|
||||||
@ -121,8 +129,12 @@ export default function LayoutAuthenticated({
|
|||||||
menu={menuAside}
|
menu={menuAside}
|
||||||
onAsideLgClose={() => setIsAsideLgActive(false)}
|
onAsideLgClose={() => setIsAsideLgActive(false)}
|
||||||
/>
|
/>
|
||||||
{children}
|
<div className="flex-1">
|
||||||
<FooterBar>Hand-crafted & Made with ❤️</FooterBar>
|
{children}
|
||||||
|
</div>
|
||||||
|
<FooterBar>
|
||||||
|
<span className="text-[10px] font-black uppercase tracking-widest opacity-40">© 2026 ADML Exchange. Global Reliable Trading.</span>
|
||||||
|
</FooterBar>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -7,11 +7,10 @@ type Props = {
|
|||||||
|
|
||||||
export default function LayoutGuest({ children }: Props) {
|
export default function LayoutGuest({ children }: Props) {
|
||||||
const darkMode = useAppSelector((state) => state.style.darkMode)
|
const darkMode = useAppSelector((state) => state.style.darkMode)
|
||||||
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={darkMode ? 'dark' : ''}>
|
<div className={darkMode ? 'dark' : ''}>
|
||||||
<div className={`${bgColor} dark:bg-slate-800 dark:text-slate-100`}>{children}</div>
|
<div className={`bg-[#000000] text-white dark:bg-[#000000] dark:text-slate-100 min-h-screen`}>{children}</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -5,127 +5,129 @@ const menuAside: MenuAsideItem[] = [
|
|||||||
{
|
{
|
||||||
href: '/dashboard',
|
href: '/dashboard',
|
||||||
icon: icon.mdiViewDashboardOutline,
|
icon: icon.mdiViewDashboardOutline,
|
||||||
label: 'Dashboard',
|
label: 'Overview',
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
href: '/users/users-list',
|
|
||||||
label: 'Users',
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
icon: icon.mdiAccountGroup ?? icon.mdiTable,
|
|
||||||
permissions: 'READ_USERS'
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: '/roles/roles-list',
|
label: 'Trading',
|
||||||
label: 'Roles',
|
icon: icon.mdiSwapHorizontal,
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
menu: [
|
||||||
// @ts-ignore
|
{
|
||||||
icon: icon.mdiShieldAccountVariantOutline ?? icon.mdiTable,
|
href: '/markets',
|
||||||
permissions: 'READ_ROLES'
|
label: 'Markets',
|
||||||
|
icon: icon.mdiGlobeLight,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/exchange',
|
||||||
|
label: 'Spot Trading',
|
||||||
|
icon: icon.mdiChartLine,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/perpetual',
|
||||||
|
label: 'Perpetual Trading',
|
||||||
|
icon: icon.mdiChartTimelineVariant,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/second-contract',
|
||||||
|
label: 'Seconds Trading',
|
||||||
|
icon: icon.mdiFlash,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/orders/orders-list',
|
||||||
|
label: 'Orders',
|
||||||
|
icon: icon.mdiClipboardListOutline,
|
||||||
|
permissions: 'READ_ORDERS'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/trades/trades-list',
|
||||||
|
label: 'Trade History',
|
||||||
|
icon: icon.mdiHistory,
|
||||||
|
permissions: 'READ_TRADES'
|
||||||
|
},
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: '/permissions/permissions-list',
|
label: 'Wallet',
|
||||||
label: 'Permissions',
|
icon: icon.mdiWallet,
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
menu: [
|
||||||
// @ts-ignore
|
{
|
||||||
icon: icon.mdiShieldAccountOutline ?? icon.mdiTable,
|
href: '/assets/assets-list',
|
||||||
permissions: 'READ_PERMISSIONS'
|
label: 'Assets',
|
||||||
|
icon: icon.mdiCurrencyUsd,
|
||||||
|
permissions: 'READ_ASSETS'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/wallets/wallets-list',
|
||||||
|
label: 'My Wallets',
|
||||||
|
icon: icon.mdiWallet,
|
||||||
|
permissions: 'READ_WALLETS'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/deposits/deposits-list',
|
||||||
|
label: 'Deposits',
|
||||||
|
icon: icon.mdiBankTransferIn,
|
||||||
|
permissions: 'READ_DEPOSITS'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/withdrawals/withdrawals-list',
|
||||||
|
label: 'Withdrawals',
|
||||||
|
icon: icon.mdiBankTransferOut,
|
||||||
|
permissions: 'READ_WITHDRAWALS'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/transactions/transactions-list',
|
||||||
|
label: 'Transactions',
|
||||||
|
icon: icon.mdiReceipt,
|
||||||
|
permissions: 'READ_TRANSACTIONS'
|
||||||
|
},
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: '/assets/assets-list',
|
label: 'Verification',
|
||||||
label: 'Assets',
|
icon: icon.mdiShieldCheckOutline,
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
menu: [
|
||||||
// @ts-ignore
|
{
|
||||||
icon: 'mdiCurrencyUsd' in icon ? icon['mdiCurrencyUsd' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
href: '/kyc_documents/kyc_documents-list',
|
||||||
permissions: 'READ_ASSETS'
|
label: 'KYC Documents',
|
||||||
|
icon: icon.mdiFileDocument,
|
||||||
|
permissions: 'READ_KYC_DOCUMENTS'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/support_tickets/support_tickets-list',
|
||||||
|
label: 'Support Tickets',
|
||||||
|
icon: icon.mdiHeadset,
|
||||||
|
permissions: 'READ_SUPPORT_TICKETS'
|
||||||
|
},
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: '/trading_pairs/trading_pairs-list',
|
label: 'Administration',
|
||||||
label: 'Trading pairs',
|
icon: icon.mdiShieldAccountOutline,
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
menu: [
|
||||||
// @ts-ignore
|
{
|
||||||
icon: 'mdiSwapHorizontal' in icon ? icon['mdiSwapHorizontal' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
href: '/users/users-list',
|
||||||
permissions: 'READ_TRADING_PAIRS'
|
label: 'Users',
|
||||||
},
|
icon: icon.mdiAccountGroup,
|
||||||
{
|
permissions: 'READ_USERS'
|
||||||
href: '/wallets/wallets-list',
|
},
|
||||||
label: 'Wallets',
|
{
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
href: '/roles/roles-list',
|
||||||
// @ts-ignore
|
label: 'Roles',
|
||||||
icon: 'mdiWallet' in icon ? icon['mdiWallet' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
icon: icon.mdiShieldAccountVariantOutline,
|
||||||
permissions: 'READ_WALLETS'
|
permissions: 'READ_ROLES'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: '/orders/orders-list',
|
href: '/permissions/permissions-list',
|
||||||
label: 'Orders',
|
label: 'Permissions',
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
icon: icon.mdiShieldAccountOutline,
|
||||||
// @ts-ignore
|
permissions: 'READ_PERMISSIONS'
|
||||||
icon: 'mdiChartLine' in icon ? icon['mdiChartLine' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
},
|
||||||
permissions: 'READ_ORDERS'
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
href: '/trades/trades-list',
|
|
||||||
label: 'Trades',
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
icon: 'mdiSwapVertical' in icon ? icon['mdiSwapVertical' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
|
||||||
permissions: 'READ_TRADES'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: '/deposits/deposits-list',
|
|
||||||
label: 'Deposits',
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
icon: 'mdiBankTransferIn' in icon ? icon['mdiBankTransferIn' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
|
||||||
permissions: 'READ_DEPOSITS'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: '/withdrawals/withdrawals-list',
|
|
||||||
label: 'Withdrawals',
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
icon: 'mdiBankTransferOut' in icon ? icon['mdiBankTransferOut' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
|
||||||
permissions: 'READ_WITHDRAWALS'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: '/kyc_documents/kyc_documents-list',
|
|
||||||
label: 'Kyc documents',
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
icon: 'mdiFileDocument' in icon ? icon['mdiFileDocument' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
|
||||||
permissions: 'READ_KYC_DOCUMENTS'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: '/transactions/transactions-list',
|
|
||||||
label: 'Transactions',
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
icon: 'mdiReceipt' in icon ? icon['mdiReceipt' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
|
||||||
permissions: 'READ_TRANSACTIONS'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: '/support_tickets/support_tickets-list',
|
|
||||||
label: 'Support tickets',
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
icon: 'mdiHeadset' in icon ? icon['mdiHeadset' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
|
|
||||||
permissions: 'READ_SUPPORT_TICKETS'
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
href: '/profile',
|
href: '/profile',
|
||||||
label: 'Profile',
|
label: 'Profile',
|
||||||
icon: icon.mdiAccountCircle,
|
icon: icon.mdiAccountCircle,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
href: '/api-docs',
|
|
||||||
target: '_blank',
|
|
||||||
label: 'Swagger API',
|
|
||||||
icon: icon.mdiFileCode,
|
|
||||||
permissions: 'READ_API_DOCS'
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
|
|
||||||
export default menuAside
|
export default menuAside
|
||||||
@ -149,9 +149,9 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
|
|||||||
setStepsEnabled(false);
|
setStepsEnabled(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const title = 'AI App Draft'
|
const title = 'ADML Exchange'
|
||||||
const description = "Crypto trading platform inspired by OKX with exchange, wallets, orders, and KYC."
|
const description = "Professional crypto trading platform with spot, perpetual contracts, and second contracts."
|
||||||
const url = "https://flatlogic.com/"
|
const url = "https://adml-exchange.com/"
|
||||||
const image = "https://project-screens.s3.amazonaws.com/screenshots/37805/app-hero-20260125-125615.png"
|
const image = "https://project-screens.s3.amazonaws.com/screenshots/37805/app-hero-20260125-125615.png"
|
||||||
const imageWidth = '1920'
|
const imageWidth = '1920'
|
||||||
const imageHeight = '960'
|
const imageHeight = '960'
|
||||||
@ -164,7 +164,7 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
|
|||||||
<meta name="description" content={description} />
|
<meta name="description" content={description} />
|
||||||
|
|
||||||
<meta property="og:url" content={url} />
|
<meta property="og:url" content={url} />
|
||||||
<meta property="og:site_name" content="https://flatlogic.com/" />
|
<meta property="og:site_name" content="ADML EXCHANGE" />
|
||||||
<meta property="og:title" content={title} />
|
<meta property="og:title" content={title} />
|
||||||
<meta property="og:description" content={description} />
|
<meta property="og:description" content={description} />
|
||||||
<meta property="og:image" content={image} />
|
<meta property="og:image" content={image} />
|
||||||
@ -198,4 +198,4 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default appWithTranslation(MyApp);
|
export default appWithTranslation(MyApp);
|
||||||
@ -93,16 +93,21 @@ const Dashboard = () => {
|
|||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
<title>
|
<title>
|
||||||
{getPageTitle('Overview')}
|
{getPageTitle('Exchange Overview')}
|
||||||
</title>
|
</title>
|
||||||
</Head>
|
</Head>
|
||||||
<SectionMain>
|
<SectionMain>
|
||||||
<SectionTitleLineWithButton
|
<SectionTitleLineWithButton
|
||||||
icon={icon.mdiChartTimelineVariant}
|
icon={icon.mdiChartTimelineVariant}
|
||||||
title='Overview'
|
title='Exchange Overview'
|
||||||
main>
|
main>
|
||||||
{''}
|
{''}
|
||||||
</SectionTitleLineWithButton>
|
</SectionTitleLineWithButton>
|
||||||
|
|
||||||
|
<div className="bg-blue-600/10 border border-blue-500/20 rounded-2xl p-6 mb-8">
|
||||||
|
<h2 className="text-2xl font-bold mb-2">Welcome back, {currentUser?.firstName || 'Trader'}!</h2>
|
||||||
|
<p className="text-gray-500 dark:text-gray-400">Monitor your assets, track orders, and manage your exchange operations from this central hub.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
{hasPermission(currentUser, 'CREATE_ROLES') && <WidgetCreator
|
{hasPermission(currentUser, 'CREATE_ROLES') && <WidgetCreator
|
||||||
currentUser={currentUser}
|
currentUser={currentUser}
|
||||||
@ -523,4 +528,4 @@ Dashboard.getLayout = function getLayout(page: ReactElement) {
|
|||||||
return <LayoutAuthenticated>{page}</LayoutAuthenticated>
|
return <LayoutAuthenticated>{page}</LayoutAuthenticated>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Dashboard
|
export default Dashboard
|
||||||
219
frontend/src/pages/exchange.tsx
Normal file
219
frontend/src/pages/exchange.tsx
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
import React, { ReactElement, useEffect, useState } from 'react';
|
||||||
|
import Head from 'next/head';
|
||||||
|
import * as icon from '@mdi/js';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import LayoutAuthenticated from '../layouts/Authenticated';
|
||||||
|
import BaseIcon from '../components/BaseIcon';
|
||||||
|
import BaseButton from '../components/BaseButton';
|
||||||
|
import { getPageTitle } from '../config';
|
||||||
|
|
||||||
|
const ExchangePage = () => {
|
||||||
|
const { t } = useTranslation('common');
|
||||||
|
const [mounted, setMounted] = useState(false);
|
||||||
|
const [currentTab, setCurrentTab] = useState('Open Orders');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setMounted(true);
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.src = 'https://s3.tradingview.com/tv.js';
|
||||||
|
script.async = true;
|
||||||
|
script.onload = () => {
|
||||||
|
if (typeof window !== 'undefined' && (window as any).TradingView) {
|
||||||
|
new (window as any).TradingView.widget({
|
||||||
|
"autosize": true,
|
||||||
|
"symbol": "BINANCE:BTCUSDT",
|
||||||
|
"interval": "D",
|
||||||
|
"timezone": "Etc/UTC",
|
||||||
|
"theme": "dark",
|
||||||
|
"style": "1",
|
||||||
|
"locale": "en",
|
||||||
|
"toolbar_bg": "#f1f3f6",
|
||||||
|
"enable_publishing": false,
|
||||||
|
"hide_side_toolbar": false,
|
||||||
|
"allow_symbol_change": true,
|
||||||
|
"container_id": "tradingview_chart"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.head.appendChild(script);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>{getPageTitle(t('exchange.title', { defaultValue: 'Spot Trading' }))}</title>
|
||||||
|
</Head>
|
||||||
|
<div className="flex flex-col h-[calc(100vh-64px)] overflow-hidden bg-[#000000] text-white">
|
||||||
|
{/* Trading Header */}
|
||||||
|
<div className="flex items-center justify-between px-4 py-3 border-b border-white/5 bg-[#080808] backdrop-blur-xl">
|
||||||
|
<div className="flex items-center space-x-8">
|
||||||
|
<div className="flex items-center space-x-3 group cursor-pointer">
|
||||||
|
<div className="w-10 h-10 bg-orange-500/10 rounded-xl flex items-center justify-center group-hover:bg-orange-500/20 transition-colors">
|
||||||
|
<BaseIcon path={icon.mdiBitcoin} size={28} className="text-orange-500" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="text-lg font-black tracking-tighter leading-none">BTC/USDT</div>
|
||||||
|
<div className="text-[10px] font-black text-emerald-500 uppercase mt-1 tracking-widest">{t('exchange.header.spot', { defaultValue: 'Spot' })}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center space-x-8">
|
||||||
|
<div>
|
||||||
|
<div className="text-[10px] font-black text-gray-500 uppercase tracking-widest leading-none mb-1">{t('exchange.header.last_price', { defaultValue: 'Last Price' })}</div>
|
||||||
|
<div className="text-emerald-500 text-lg font-black tracking-tighter">43,123.45</div>
|
||||||
|
</div>
|
||||||
|
<div className="hidden sm:block">
|
||||||
|
<div className="text-[10px] font-black text-gray-500 uppercase tracking-widest leading-none mb-1">{t('exchange.header.change', { defaultValue: '24h Change' })}</div>
|
||||||
|
<div className="text-emerald-500 text-sm font-black">+2.45%</div>
|
||||||
|
</div>
|
||||||
|
<div className="hidden lg:block">
|
||||||
|
<div className="text-[10px] font-black text-gray-500 uppercase tracking-widest leading-none mb-1">{t('exchange.header.high', { defaultValue: '24h High' })}</div>
|
||||||
|
<div className="text-white text-sm font-black">44,500.00</div>
|
||||||
|
</div>
|
||||||
|
<div className="hidden lg:block">
|
||||||
|
<div className="text-[10px] font-black text-gray-500 uppercase tracking-widest leading-none mb-1">{t('exchange.header.low', { defaultValue: '24h Low' })}</div>
|
||||||
|
<div className="text-white text-sm font-black">42,000.00</div>
|
||||||
|
</div>
|
||||||
|
<div className="hidden xl:block">
|
||||||
|
<div className="text-[10px] font-black text-gray-500 uppercase tracking-widest leading-none mb-1">{t('exchange.header.volume', { defaultValue: '24h Volume(BTC)' })}</div>
|
||||||
|
<div className="text-white text-sm font-black">32,123.45</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<BaseButton color="info" label={t('exchange.header.deposit', { defaultValue: 'Deposit' })} small className="font-black px-6 rounded-xl" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-1 overflow-hidden">
|
||||||
|
{/* Main Chart Area */}
|
||||||
|
<div className="flex-1 flex flex-col border-r border-white/5">
|
||||||
|
<div id="tradingview_chart" className="flex-1"></div>
|
||||||
|
|
||||||
|
{/* Orders Tabs */}
|
||||||
|
<div className="h-72 border-t border-white/5 bg-[#080808] overflow-hidden flex flex-col">
|
||||||
|
<div className="flex border-b border-white/5 bg-white/[0.02]">
|
||||||
|
{[
|
||||||
|
t('exchange.tabs.open_orders', { defaultValue: 'Open Orders' }),
|
||||||
|
t('exchange.tabs.order_history', { defaultValue: 'Order History' }),
|
||||||
|
t('exchange.tabs.trade_history', { defaultValue: 'Trade History' }),
|
||||||
|
t('exchange.tabs.assets', { defaultValue: 'Assets' })
|
||||||
|
].map((tab) => (
|
||||||
|
<button
|
||||||
|
key={tab}
|
||||||
|
onClick={() => setCurrentTab(tab)}
|
||||||
|
className={`px-8 py-4 text-[10px] font-black uppercase tracking-[0.2em] transition-all ${currentTab === tab ? 'text-blue-500 border-b-2 border-blue-500 bg-blue-500/5' : 'text-gray-500 hover:text-white'}`}
|
||||||
|
>
|
||||||
|
{tab}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 flex flex-col items-center justify-center p-8 text-center">
|
||||||
|
<div className="w-16 h-16 bg-white/[0.02] rounded-full flex items-center justify-center mb-4">
|
||||||
|
<BaseIcon path={icon.mdiInboxOutline} size={32} className="text-gray-700" />
|
||||||
|
</div>
|
||||||
|
<p className="text-gray-600 text-xs font-black uppercase tracking-widest italic">
|
||||||
|
{t('exchange.no_records', { defaultValue: 'No active records found' })}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Order Book & Trade Panel */}
|
||||||
|
<div className="w-[340px] flex flex-col bg-[#080808]">
|
||||||
|
{/* Order Book */}
|
||||||
|
<div className="flex-1 border-b border-white/5 p-4 overflow-hidden flex flex-col">
|
||||||
|
<div className="flex items-center justify-between mb-4">
|
||||||
|
<h4 className="text-[10px] font-black uppercase tracking-[0.2em] text-gray-500">{t('exchange.orderbook.title', { defaultValue: 'Order Book' })}</h4>
|
||||||
|
<div className="flex space-x-1">
|
||||||
|
<div className="w-4 h-4 bg-emerald-500/20 rounded-sm"></div>
|
||||||
|
<div className="w-4 h-4 bg-rose-500/20 rounded-sm"></div>
|
||||||
|
<div className="w-4 h-4 bg-white/10 rounded-sm"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 text-[10px] font-black uppercase text-gray-600 mb-2 px-2">
|
||||||
|
<span>{t('exchange.orderbook.price', { defaultValue: 'Price' })} (USDT)</span>
|
||||||
|
<span className="text-right">{t('exchange.orderbook.amount', { defaultValue: 'Amount' })} (BTC)</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex-1 overflow-y-auto space-y-[2px] font-mono text-[11px] font-bold">
|
||||||
|
{[...Array(12)].map((_, i) => (
|
||||||
|
<div key={`ask-${i}`} className="flex justify-between text-rose-500 hover:bg-rose-500/10 px-2 py-0.5 rounded cursor-pointer transition-colors relative group">
|
||||||
|
<div className="absolute inset-y-0 right-0 bg-rose-500/10 transition-all duration-500" style={{ width: `${Math.random() * 100}%` }}></div>
|
||||||
|
<span className="relative z-10">43,{(123 + (12-i)*5).toString().padStart(3, '0')}.45</span>
|
||||||
|
<span className="relative z-10 text-gray-400">{(Math.random() * 2).toFixed(4)}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<div className="py-4 px-2 my-2 border-y border-white/5 bg-white/[0.01]">
|
||||||
|
<div className="text-xl font-black text-emerald-500 tracking-tighter">43,123.45</div>
|
||||||
|
<div className="text-[10px] text-gray-500 font-black uppercase tracking-widest mt-0.5">≈ $43,123.45</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{[...Array(12)].map((_, i) => (
|
||||||
|
<div key={`bid-${i}`} className="flex justify-between text-emerald-500 hover:bg-emerald-500/10 px-2 py-0.5 rounded cursor-pointer transition-colors relative group">
|
||||||
|
<div className="absolute inset-y-0 right-0 bg-emerald-500/10 transition-all duration-500" style={{ width: `${Math.random() * 100}%` }}></div>
|
||||||
|
<span className="relative z-10">43,{(123 - i*5).toString().padStart(3, '0')}.45</span>
|
||||||
|
<span className="relative z-10 text-gray-400">{(Math.random() * 2).toFixed(4)}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Trade Panel */}
|
||||||
|
<div className="p-6 bg-black/40">
|
||||||
|
<div className="flex bg-white/5 p-1 rounded-xl mb-6">
|
||||||
|
<button className="flex-1 py-2.5 bg-emerald-500 text-black font-black uppercase text-[10px] tracking-widest rounded-lg transition-all shadow-lg shadow-emerald-500/20">{t('exchange.panel.buy', { defaultValue: 'Buy' })}</button>
|
||||||
|
<button className="flex-1 py-2.5 text-gray-500 font-black uppercase text-[10px] tracking-widest rounded-lg hover:text-white transition-all">{t('exchange.panel.sell', { defaultValue: 'Sell' })}</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex justify-between text-[10px] font-black uppercase tracking-widest text-gray-500 mb-1 px-1">
|
||||||
|
<span>{t('exchange.panel.type', { defaultValue: 'Order Type' })}</span>
|
||||||
|
<span className="text-blue-500 cursor-pointer">{t('exchange.panel.market', { defaultValue: 'Market' })}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="relative group">
|
||||||
|
<span className="absolute left-4 top-1/2 -translate-y-1/2 text-[10px] font-black uppercase text-gray-600 group-focus-within:text-blue-500 transition-colors">{t('exchange.panel.price', { defaultValue: 'Price' })}</span>
|
||||||
|
<input type="text" defaultValue="43123.45" className="w-full bg-[#111111] border border-white/10 rounded-xl pl-16 pr-14 py-3 text-white text-xs font-black focus:border-blue-500/50 transition-all outline-none" />
|
||||||
|
<span className="absolute right-4 top-1/2 -translate-y-1/2 text-[10px] font-black uppercase text-gray-600">USDT</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="relative group">
|
||||||
|
<span className="absolute left-4 top-1/2 -translate-y-1/2 text-[10px] font-black uppercase text-gray-600 group-focus-within:text-blue-500 transition-colors">{t('exchange.panel.amount', { defaultValue: 'Amount' })}</span>
|
||||||
|
<input type="text" placeholder="0.00" className="w-full bg-[#111111] border border-white/10 rounded-xl pl-16 pr-14 py-3 text-white text-xs font-black focus:border-blue-500/50 transition-all outline-none" />
|
||||||
|
<span className="absolute right-4 top-1/2 -translate-y-1/2 text-[10px] font-black uppercase text-gray-600">BTC</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Percentage Buttons */}
|
||||||
|
<div className="grid grid-cols-4 gap-2">
|
||||||
|
{[25, 50, 75, 100].map(p => (
|
||||||
|
<button key={p} className="py-1.5 bg-white/5 text-[9px] font-black rounded-lg hover:bg-white/10 text-gray-500 hover:text-white transition-all">{p}%</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-between text-[10px] font-black uppercase tracking-widest text-gray-600 px-1 pt-2">
|
||||||
|
<span>{t('exchange.panel.available', { defaultValue: 'Available' })}</span>
|
||||||
|
<span className="text-white">0.00 USDT</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<BaseButton
|
||||||
|
color="info"
|
||||||
|
label={`${t('exchange.panel.buy', { defaultValue: 'BUY' })} BTC`}
|
||||||
|
className="w-full py-4 font-black bg-emerald-500 border-none text-black rounded-2xl mt-4 shadow-xl shadow-emerald-500/10 hover:scale-[1.02] active:scale-95 transition-all uppercase tracking-[0.2em] text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ExchangePage.getLayout = function getLayout(page: ReactElement) {
|
||||||
|
return <LayoutAuthenticated>{page}</LayoutAuthenticated>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ExchangePage;
|
||||||
@ -1,166 +1,387 @@
|
|||||||
|
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import type { ReactElement } from 'react';
|
import type { ReactElement } from 'react';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
import * as icon from '@mdi/js';
|
||||||
|
import { useTranslation } from 'next-i18next';
|
||||||
import BaseButton from '../components/BaseButton';
|
import BaseButton from '../components/BaseButton';
|
||||||
import CardBox from '../components/CardBox';
|
import BaseIcon from '../components/BaseIcon';
|
||||||
import SectionFullScreen from '../components/SectionFullScreen';
|
|
||||||
import LayoutGuest from '../layouts/Guest';
|
import LayoutGuest from '../layouts/Guest';
|
||||||
import BaseDivider from '../components/BaseDivider';
|
|
||||||
import BaseButtons from '../components/BaseButtons';
|
|
||||||
import { getPageTitle } from '../config';
|
import { getPageTitle } from '../config';
|
||||||
import { useAppSelector } from '../stores/hooks';
|
import LanguageSwitcher from '../components/LanguageSwitcher';
|
||||||
import CardBoxComponentTitle from "../components/CardBoxComponentTitle";
|
import Logo from '../components/Logo';
|
||||||
import { getPexelsImage, getPexelsVideo } from '../helpers/pexels';
|
|
||||||
|
|
||||||
|
const INITIAL_MARKET_DATA = [
|
||||||
|
{ symbol: 'BTCUSDT', name: 'Bitcoin', label: 'BTC', icon: icon.mdiBitcoin, price: '0', change: '0%', color: 'text-gray-400' },
|
||||||
|
{ symbol: 'ETHUSDT', name: 'Ethereum', label: 'ETH', icon: icon.mdiEthereum, price: '0', change: '0%', color: 'text-gray-400' },
|
||||||
|
{ symbol: 'SOLUSDT', name: 'Solana', label: 'SOL', icon: icon.mdiAlphaSCircle, price: '0', change: '0%', color: 'text-gray-400' },
|
||||||
|
{ symbol: 'BNBUSDT', name: 'BNB', label: 'BNB', icon: icon.mdiAlphaBCircle, price: '0', change: '0%', color: 'text-gray-400' },
|
||||||
|
{ symbol: 'ADAUSDT', name: 'Cardano', label: 'ADA', icon: icon.mdiAlphaACircle, price: '0', change: '0%', color: 'text-gray-400' },
|
||||||
|
{ symbol: 'XRPUSDT', name: 'Ripple', label: 'XRP', icon: icon.mdiAlphaXCircle, price: '0', change: '0%', color: 'text-gray-400' },
|
||||||
|
];
|
||||||
|
|
||||||
export default function Starter() {
|
export default function LandingPage() {
|
||||||
const [illustrationImage, setIllustrationImage] = useState({
|
const { t } = useTranslation('common');
|
||||||
src: undefined,
|
const [marketData, setMarketData] = useState(INITIAL_MARKET_DATA);
|
||||||
photographer: undefined,
|
|
||||||
photographer_url: undefined,
|
|
||||||
})
|
|
||||||
const [illustrationVideo, setIllustrationVideo] = useState({video_files: []})
|
|
||||||
const [contentType, setContentType] = useState('video');
|
|
||||||
const [contentPosition, setContentPosition] = useState('right');
|
|
||||||
const textColor = useAppSelector((state) => state.style.linkColor);
|
|
||||||
|
|
||||||
const title = 'AI App Draft'
|
useEffect(() => {
|
||||||
|
const fetchMarketData = async () => {
|
||||||
// Fetch Pexels image/video
|
try {
|
||||||
useEffect(() => {
|
const symbols = INITIAL_MARKET_DATA.map(d => d.symbol);
|
||||||
async function fetchData() {
|
const response = await fetch(`https://api.binance.com/api/v3/ticker/24hr?symbols=${JSON.stringify(symbols)}`);
|
||||||
const image = await getPexelsImage();
|
const data = await response.json();
|
||||||
const video = await getPexelsVideo();
|
|
||||||
setIllustrationImage(image);
|
setMarketData(prev => prev.map(item => {
|
||||||
setIllustrationVideo(video);
|
const ticker = data.find((t: any) => t.symbol === item.symbol);
|
||||||
}
|
if (ticker) {
|
||||||
fetchData();
|
const price = parseFloat(ticker.lastPrice).toLocaleString(undefined, { minimumFractionDigits: 2 });
|
||||||
}, []);
|
const change = parseFloat(ticker.priceChangePercent).toFixed(2);
|
||||||
|
return {
|
||||||
const imageBlock = (image) => (
|
...item,
|
||||||
<div
|
price: price,
|
||||||
className='hidden md:flex flex-col justify-end relative flex-grow-0 flex-shrink-0 w-1/3'
|
change: (parseFloat(change) >= 0 ? '+' : '') + change + '%',
|
||||||
style={{
|
color: parseFloat(change) >= 0 ? 'text-emerald-500' : 'text-rose-500',
|
||||||
backgroundImage: `${
|
};
|
||||||
image
|
}
|
||||||
? `url(${image?.src?.original})`
|
return item;
|
||||||
: 'linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.5))'
|
}));
|
||||||
}`,
|
} catch (error) {
|
||||||
backgroundSize: 'cover',
|
console.error('Failed to fetch market data:', error);
|
||||||
backgroundPosition: 'left center',
|
}
|
||||||
backgroundRepeat: 'no-repeat',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className='flex justify-center w-full bg-blue-300/20'>
|
|
||||||
<a
|
|
||||||
className='text-[8px]'
|
|
||||||
href={image?.photographer_url}
|
|
||||||
target='_blank'
|
|
||||||
rel='noreferrer'
|
|
||||||
>
|
|
||||||
Photo by {image?.photographer} on Pexels
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const videoBlock = (video) => {
|
|
||||||
if (video?.video_files?.length > 0) {
|
|
||||||
return (
|
|
||||||
<div className='hidden md:flex flex-col justify-end relative flex-grow-0 flex-shrink-0 w-1/3'>
|
|
||||||
<video
|
|
||||||
className='absolute top-0 left-0 w-full h-full object-cover'
|
|
||||||
autoPlay
|
|
||||||
loop
|
|
||||||
muted
|
|
||||||
>
|
|
||||||
<source src={video?.video_files[0]?.link} type='video/mp4'/>
|
|
||||||
Your browser does not support the video tag.
|
|
||||||
</video>
|
|
||||||
<div className='flex justify-center w-full bg-blue-300/20 z-10'>
|
|
||||||
<a
|
|
||||||
className='text-[8px]'
|
|
||||||
href={video?.user?.url}
|
|
||||||
target='_blank'
|
|
||||||
rel='noreferrer'
|
|
||||||
>
|
|
||||||
Video by {video.user.name} on Pexels
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fetchMarketData();
|
||||||
|
const interval = setInterval(fetchMarketData, 5000);
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className="min-h-screen bg-[#000000] text-white selection:bg-blue-500/30">
|
||||||
style={
|
|
||||||
contentPosition === 'background'
|
|
||||||
? {
|
|
||||||
backgroundImage: `${
|
|
||||||
illustrationImage
|
|
||||||
? `url(${illustrationImage.src?.original})`
|
|
||||||
: 'linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.5))'
|
|
||||||
}`,
|
|
||||||
backgroundSize: 'cover',
|
|
||||||
backgroundPosition: 'left center',
|
|
||||||
backgroundRepeat: 'no-repeat',
|
|
||||||
}
|
|
||||||
: {}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Head>
|
<Head>
|
||||||
<title>{getPageTitle('Starter Page')}</title>
|
<title>{getPageTitle(t('landing.hero.title', { defaultValue: 'ADML Exchange | The World’s Leading Crypto Exchange' }))}</title>
|
||||||
</Head>
|
</Head>
|
||||||
|
|
||||||
<SectionFullScreen bg='violet'>
|
{/* Navbar */}
|
||||||
<div
|
<nav className="flex items-center justify-between px-6 py-4 border-b border-white/5 bg-black/80 backdrop-blur-md sticky top-0 z-50">
|
||||||
className={`flex ${
|
<div className="flex items-center space-x-10">
|
||||||
contentPosition === 'right' ? 'flex-row-reverse' : 'flex-row'
|
<Link href="/" className="flex items-center space-x-2 group">
|
||||||
} min-h-screen w-full`}
|
<Logo className="text-white" />
|
||||||
>
|
</Link>
|
||||||
{contentType === 'image' && contentPosition !== 'background'
|
<div className="hidden lg:flex space-x-8 text-[14px] font-bold text-gray-400 uppercase tracking-tight">
|
||||||
? imageBlock(illustrationImage)
|
<Link href="/markets" className="hover:text-white transition-colors">{t('nav.markets', { defaultValue: 'Markets' })}</Link>
|
||||||
: null}
|
<Link href="/exchange" className="hover:text-white transition-colors">{t('nav.spot', { defaultValue: 'Spot' })}</Link>
|
||||||
{contentType === 'video' && contentPosition !== 'background'
|
<Link href="/perpetual" className="hover:text-white transition-colors">{t('nav.perpetual', { defaultValue: 'Perpetual' })}</Link>
|
||||||
? videoBlock(illustrationVideo)
|
<Link href="/second-contract" className="hover:text-white transition-colors">{t('nav.options', { defaultValue: 'Options' })}</Link>
|
||||||
: null}
|
</div>
|
||||||
<div className='flex items-center justify-center flex-col space-y-4 w-full lg:w-full'>
|
|
||||||
<CardBox className='w-full md:w-3/5 lg:w-2/3'>
|
|
||||||
<CardBoxComponentTitle title="Welcome to your AI App Draft app!"/>
|
|
||||||
|
|
||||||
<div className="space-y-3">
|
|
||||||
<p className='text-center text-gray-500'>This is a React.js/Node.js app generated by the <a className={`${textColor}`} href="https://flatlogic.com/generator">Flatlogic Web App Generator</a></p>
|
|
||||||
<p className='text-center text-gray-500'>For guides and documentation please check
|
|
||||||
your local README.md and the <a className={`${textColor}`} href="https://flatlogic.com/documentation">Flatlogic documentation</a></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<BaseButtons>
|
|
||||||
<BaseButton
|
|
||||||
href='/login'
|
|
||||||
label='Login'
|
|
||||||
color='info'
|
|
||||||
className='w-full'
|
|
||||||
/>
|
|
||||||
|
|
||||||
</BaseButtons>
|
|
||||||
</CardBox>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="flex items-center space-x-6">
|
||||||
</SectionFullScreen>
|
<LanguageSwitcher />
|
||||||
<div className='bg-black text-white flex flex-col text-center justify-center md:flex-row'>
|
<Link href="/login" className="text-[14px] font-bold uppercase hover:text-blue-500 transition-colors hidden sm:block">{t('login.title', { defaultValue: 'Log in' })}</Link>
|
||||||
<p className='py-6 text-sm'>© 2026 <span>{title}</span>. All rights reserved</p>
|
<BaseButton href="/register" label={t('register.title', { defaultValue: 'Sign up' })} color="info" className="px-6 font-bold" roundedFull />
|
||||||
<Link className='py-6 ml-4 text-sm' href='/privacy-policy/'>
|
</div>
|
||||||
Privacy Policy
|
</nav>
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
{/* Hero Section */}
|
||||||
|
<section className="relative pt-20 pb-32 overflow-hidden">
|
||||||
|
<div className="container mx-auto px-6 relative z-10 text-center">
|
||||||
|
<div className="inline-flex items-center space-x-2 bg-blue-600/10 border border-blue-500/20 px-4 py-1.5 rounded-full text-blue-400 text-xs font-black mb-8 animate-pulse tracking-widest uppercase">
|
||||||
|
<span className="w-2 h-2 bg-blue-500 rounded-full"></span>
|
||||||
|
<span>{t('landing.hero.badge', { defaultValue: 'Trusted by 20M+ Users Globally' })}</span>
|
||||||
|
</div>
|
||||||
|
<h1 className="text-5xl md:text-8xl font-black mb-6 leading-tight tracking-tighter uppercase max-w-5xl mx-auto">
|
||||||
|
{t('landing.hero.title_part1', { defaultValue: 'Trade The Future' })} <br />
|
||||||
|
<span className="text-transparent bg-clip-text bg-gradient-to-r from-blue-500 via-emerald-400 to-blue-500 bg-[length:200%_auto] animate-gradient-x">
|
||||||
|
{t('landing.hero.title_highlight', { defaultValue: 'With ADML' })}
|
||||||
|
</span>
|
||||||
|
</h1>
|
||||||
|
<p className="text-lg md:text-xl text-gray-400 mb-12 max-w-2xl mx-auto leading-relaxed font-medium">
|
||||||
|
{t('landing.hero.subtitle', { defaultValue: 'Securely buy, sell and trade over 400+ cryptocurrencies with low fees and institutional-grade security.' })}
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-col sm:flex-row items-center justify-center gap-4 max-w-lg mx-auto mb-20">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder={t('landing.hero.input_placeholder', { defaultValue: 'Email / Phone number' })}
|
||||||
|
className="w-full sm:flex-1 bg-white/5 border border-white/10 rounded-2xl px-6 py-4 text-white focus:outline-none focus:border-blue-500 transition-all text-lg font-bold backdrop-blur-sm"
|
||||||
|
/>
|
||||||
|
<BaseButton href="/register" label={t('landing.hero.cta', { defaultValue: 'Get Started' })} color="info" className="w-full sm:w-auto px-10 py-4 h-[60px] text-lg font-black rounded-2xl shadow-2xl shadow-blue-600/20" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Quick Stats */}
|
||||||
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-8 max-w-4xl mx-auto py-12 border-y border-white/5 bg-white/[0.01] rounded-[40px]">
|
||||||
|
{[
|
||||||
|
{ label: '24h Vol', value: '$12.4B' },
|
||||||
|
{ label: 'Users', value: '25M+' },
|
||||||
|
{ label: 'Assets', value: '450+' },
|
||||||
|
{ label: 'Uptime', value: '99.99%' },
|
||||||
|
].map((stat, i) => (
|
||||||
|
<div key={i} className="text-center">
|
||||||
|
<div className="text-3xl font-black text-white mb-1 tracking-tighter">{stat.value}</div>
|
||||||
|
<div className="text-[10px] font-black text-gray-500 uppercase tracking-widest">{t(`landing.stats.${stat.label.toLowerCase().replace(' ', '_')}`, { defaultValue: stat.label })}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Background Decorative Elements */}
|
||||||
|
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-full h-full -z-10 pointer-events-none">
|
||||||
|
<div className="absolute top-40 left-10 w-64 h-64 bg-blue-600/10 rounded-full blur-[120px]"></div>
|
||||||
|
<div className="absolute bottom-20 right-10 w-96 h-96 bg-emerald-500/5 rounded-full blur-[150px]"></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Feature Sections (admlplus style) */}
|
||||||
|
<section className="py-24 bg-[#050505]">
|
||||||
|
<div className="container mx-auto px-6">
|
||||||
|
<div className="grid md:grid-cols-3 gap-12">
|
||||||
|
<div className="p-8 bg-white/[0.02] border border-white/5 rounded-[32px] hover:bg-white/[0.04] transition-all group">
|
||||||
|
<div className="w-16 h-16 bg-blue-600/10 rounded-2xl flex items-center justify-center mb-6 group-hover:scale-110 transition-transform">
|
||||||
|
<BaseIcon path={icon.mdiShieldLock} size={32} className="text-blue-500" />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-2xl font-black mb-4 uppercase tracking-tighter">{t('landing.features.security.title', { defaultValue: 'Security First' })}</h3>
|
||||||
|
<p className="text-gray-500 font-medium leading-relaxed">
|
||||||
|
{t('landing.features.security.desc', { defaultValue: 'Cold storage, multi-sig wallets, and 2FA protection for all assets. Your security is our priority.' })}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="p-8 bg-white/[0.02] border border-white/5 rounded-[32px] hover:bg-white/[0.04] transition-all group">
|
||||||
|
<div className="w-16 h-16 bg-emerald-600/10 rounded-2xl flex items-center justify-center mb-6 group-hover:scale-110 transition-transform">
|
||||||
|
<BaseIcon path={icon.mdiLightningBolt} size={32} className="text-emerald-500" />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-2xl font-black mb-4 uppercase tracking-tighter">{t('landing.features.speed.title', { defaultValue: 'Instant Execution' })}</h3>
|
||||||
|
<p className="text-gray-500 font-medium leading-relaxed">
|
||||||
|
{t('landing.features.speed.desc', { defaultValue: 'Our high-performance matching engine handles 1M+ transactions per second with sub-millisecond latency.' })}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="p-8 bg-white/[0.02] border border-white/5 rounded-[32px] hover:bg-white/[0.04] transition-all group">
|
||||||
|
<div className="w-16 h-16 bg-purple-600/10 rounded-2xl flex items-center justify-center mb-6 group-hover:scale-110 transition-transform">
|
||||||
|
<BaseIcon path={icon.mdiLifebuoy} size={32} className="text-purple-500" />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-2xl font-black mb-4 uppercase tracking-tighter">{t('landing.features.support.title', { defaultValue: '24/7 Support' })}</h3>
|
||||||
|
<p className="text-gray-500 font-medium leading-relaxed">
|
||||||
|
{t('landing.features.support.desc', { defaultValue: 'Global customer support available in 12 languages around the clock to assist you with any issues.' })}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Market Table */}
|
||||||
|
<section className="py-24">
|
||||||
|
<div className="container mx-auto px-6">
|
||||||
|
<div className="flex flex-col md:flex-row items-end justify-between mb-12 gap-6">
|
||||||
|
<div>
|
||||||
|
<h2 className="text-4xl md:text-5xl font-black tracking-tighter uppercase mb-4">{t('landing.markets.title', { defaultValue: 'Market Trends' })}</h2>
|
||||||
|
<div className="flex space-x-6 text-sm font-bold text-gray-500">
|
||||||
|
<button className="text-blue-500 border-b-2 border-blue-500 pb-1 uppercase tracking-wider">{t('landing.markets.tab.popular', { defaultValue: 'Popular' })}</button>
|
||||||
|
<button className="hover:text-white uppercase tracking-wider">{t('landing.markets.tab.new', { defaultValue: 'New Listings' })}</button>
|
||||||
|
<button className="hover:text-white uppercase tracking-wider">{t('landing.markets.tab.gainers', { defaultValue: 'Top Gainers' })}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Link href="/markets" className="text-blue-500 font-black uppercase text-xs tracking-widest hover:text-white transition-colors">
|
||||||
|
{t('landing.markets.view_all', { defaultValue: 'View All Markets' })} →
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-[#080808] border border-white/5 rounded-[40px] overflow-hidden">
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<table className="w-full text-left border-collapse">
|
||||||
|
<thead className="bg-white/[0.02]">
|
||||||
|
<tr className="text-[10px] font-black uppercase tracking-[0.2em] text-gray-500 border-b border-white/5">
|
||||||
|
<th className="px-8 py-6">{t('landing.markets.col.asset', { defaultValue: 'Asset' })}</th>
|
||||||
|
<th className="px-8 py-6">{t('landing.markets.col.price', { defaultValue: 'Price' })}</th>
|
||||||
|
<th className="px-8 py-6">{t('landing.markets.col.change', { defaultValue: '24h Change' })}</th>
|
||||||
|
<th className="px-8 py-6 hidden lg:table-cell">{t('landing.markets.col.volume', { defaultValue: 'Volume' })}</th>
|
||||||
|
<th className="px-8 py-6 text-right">{t('landing.markets.col.action', { defaultValue: 'Action' })}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="divide-y divide-white/5">
|
||||||
|
{marketData.map((coin) => (
|
||||||
|
<tr key={coin.symbol} className="hover:bg-white/[0.02] transition-colors group">
|
||||||
|
<td className="px-8 py-6">
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<div className="w-10 h-10 bg-white/5 rounded-xl flex items-center justify-center group-hover:scale-110 transition-transform">
|
||||||
|
<BaseIcon path={coin.icon} size={24} className="text-blue-500" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="font-black text-lg leading-none">{coin.label}</div>
|
||||||
|
<div className="text-[10px] text-gray-500 font-black uppercase mt-1">{coin.name}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td className="px-8 py-6 font-black text-lg tracking-tight">${coin.price}</td>
|
||||||
|
<td className={`px-8 py-6 font-black text-lg ${coin.color}`}>{coin.change}</td>
|
||||||
|
<td className="px-8 py-6 hidden lg:table-cell">
|
||||||
|
<div className="font-bold text-gray-500">$2.4B</div>
|
||||||
|
</td>
|
||||||
|
<td className="px-8 py-6 text-right">
|
||||||
|
<BaseButton href="/exchange" label={t('landing.markets.trade_btn', { defaultValue: 'Trade' })} color="info" small className="font-black px-6 rounded-xl" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* App Download Section (admlplus style) */}
|
||||||
|
<section className="py-24 bg-[#080808]">
|
||||||
|
<div className="container mx-auto px-6">
|
||||||
|
<div className="flex flex-col lg:flex-row items-center gap-20">
|
||||||
|
<div className="flex-1">
|
||||||
|
<h2 className="text-4xl md:text-6xl font-black mb-8 leading-tight tracking-tighter uppercase">
|
||||||
|
{t('landing.app.title', { defaultValue: 'Trade Anywhere, Anytime.' })}
|
||||||
|
</h2>
|
||||||
|
<p className="text-lg text-gray-400 mb-12 font-medium leading-relaxed">
|
||||||
|
{t('landing.app.desc', { defaultValue: 'Stay connected to the markets with our professional mobile app. Available on iOS, Android, and Web.' })}
|
||||||
|
</p>
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
<div className="flex items-center space-x-4 p-4 bg-white/5 border border-white/5 rounded-2xl hover:bg-white/10 transition-colors cursor-pointer group">
|
||||||
|
<BaseIcon path={icon.mdiApple} size={32} />
|
||||||
|
<div>
|
||||||
|
<div className="text-[10px] font-black text-gray-500 uppercase leading-none mb-1">Download on</div>
|
||||||
|
<div className="font-black">App Store</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-4 p-4 bg-white/5 border border-white/5 rounded-2xl hover:bg-white/10 transition-colors cursor-pointer group">
|
||||||
|
<BaseIcon path={icon.mdiGooglePlay} size={32} />
|
||||||
|
<div>
|
||||||
|
<div className="text-[10px] font-black text-gray-500 uppercase leading-none mb-1">Get it on</div>
|
||||||
|
<div className="font-black">Google Play</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-12 flex items-center space-x-6 p-6 bg-blue-600/10 border border-blue-500/20 rounded-3xl">
|
||||||
|
<div className="w-24 h-24 bg-white p-2 rounded-xl">
|
||||||
|
{/* Mock QR Code */}
|
||||||
|
<div className="w-full h-full bg-black flex items-center justify-center">
|
||||||
|
<Logo className="scale-50 opacity-50" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4 className="font-black text-blue-400 uppercase tracking-tighter mb-1">{t('landing.app.qr_title', { defaultValue: 'Scan to download' })}</h4>
|
||||||
|
<p className="text-xs text-gray-500 font-bold">{t('landing.app.qr_desc', { defaultValue: 'Instant access to ADML features on your mobile device.' })}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 relative">
|
||||||
|
<div className="relative z-10 mx-auto w-full max-w-[320px] aspect-[9/19] bg-[#111111] border-[8px] border-[#222222] rounded-[40px] shadow-[0_0_100px_rgba(37,99,235,0.2)] overflow-hidden">
|
||||||
|
<div className="h-6 w-32 bg-[#222222] absolute top-0 left-1/2 -translate-x-1/2 rounded-b-2xl"></div>
|
||||||
|
<div className="p-6 pt-10">
|
||||||
|
<Logo className="scale-75 origin-left mb-8" />
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="h-24 bg-white/5 rounded-2xl p-4 flex flex-col justify-between">
|
||||||
|
<div className="text-[10px] font-black text-gray-500 uppercase">Available Balance</div>
|
||||||
|
<div className="text-2xl font-black">$45,231.50</div>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-2 gap-2">
|
||||||
|
<div className="h-10 bg-blue-600 rounded-xl flex items-center justify-center text-[10px] font-black uppercase">Deposit</div>
|
||||||
|
<div className="h-10 bg-white/10 rounded-xl flex items-center justify-center text-[10px] font-black uppercase">Withdraw</div>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2 pt-4">
|
||||||
|
{[1,2,3].map(i => (
|
||||||
|
<div key={i} className="flex items-center justify-between p-3 bg-white/5 rounded-xl">
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<div className="w-6 h-6 bg-white/10 rounded-full"></div>
|
||||||
|
<div className="w-12 h-2 bg-white/20 rounded"></div>
|
||||||
|
</div>
|
||||||
|
<div className="w-16 h-2 bg-emerald-500/20 rounded"></div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[140%] h-[140%] bg-blue-600/10 rounded-full blur-[100px] -z-10"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* CTA Section */}
|
||||||
|
<section className="py-32 relative overflow-hidden text-center">
|
||||||
|
<div className="container mx-auto px-6 relative z-10">
|
||||||
|
<h2 className="text-5xl md:text-7xl font-black mb-8 leading-tight tracking-tighter uppercase">{t('landing.cta.title', { defaultValue: 'Start Your Trade Now' })}</h2>
|
||||||
|
<p className="text-xl text-gray-500 mb-12 max-w-2xl mx-auto font-medium">{t('landing.cta.desc', { defaultValue: 'Join the most reliable crypto exchange in the world.' })}</p>
|
||||||
|
<div className="flex flex-col sm:flex-row justify-center gap-4">
|
||||||
|
<BaseButton href="/register" label={t('landing.cta.btn_signup', { defaultValue: 'Create Account' })} color="info" className="px-12 py-5 text-xl font-black rounded-2xl" />
|
||||||
|
<BaseButton href="/login" label={t('landing.cta.btn_login', { defaultValue: 'Login Demo' })} color="whiteDark" className="px-12 py-5 text-xl font-black border border-white/10 rounded-2xl" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Footer */}
|
||||||
|
<footer className="py-24 border-t border-white/5 bg-black">
|
||||||
|
<div className="container mx-auto px-6">
|
||||||
|
<div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-5 gap-12 mb-20">
|
||||||
|
<div className="col-span-2 lg:col-span-1">
|
||||||
|
<Link href="/" className="flex items-center space-x-2 mb-8">
|
||||||
|
<Logo className="text-white" />
|
||||||
|
</Link>
|
||||||
|
<p className="text-gray-500 text-xs leading-relaxed max-w-xs font-bold uppercase tracking-widest opacity-60">
|
||||||
|
{t('footer.about', { defaultValue: 'Professional and secure digital asset trading platform.' })}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4 className="text-white font-black uppercase text-xs tracking-[0.2em] mb-8">{t('footer.section.about', { defaultValue: 'About' })}</h4>
|
||||||
|
<ul className="space-y-4 text-xs font-black uppercase tracking-widest text-gray-600">
|
||||||
|
<li><Link href="/login" className="hover:text-blue-500 transition-colors">Company</Link></li>
|
||||||
|
<li><Link href="/login" className="hover:text-blue-500 transition-colors">Careers</Link></li>
|
||||||
|
<li><Link href="/login" className="hover:text-blue-500 transition-colors">News</Link></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4 className="text-white font-black uppercase text-xs tracking-[0.2em] mb-8">{t('footer.section.product', { defaultValue: 'Product' })}</h4>
|
||||||
|
<ul className="space-y-4 text-xs font-black uppercase tracking-widest text-gray-600">
|
||||||
|
<li><Link href="/exchange" className="hover:text-blue-500 transition-colors">Spot</Link></li>
|
||||||
|
<li><Link href="/perpetual" className="hover:text-blue-500 transition-colors">Perpetual</Link></li>
|
||||||
|
<li><Link href="/second-contract" className="hover:text-blue-500 transition-colors">Options</Link></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4 className="text-white font-black uppercase text-xs tracking-[0.2em] mb-8">{t('footer.section.support', { defaultValue: 'Support' })}</h4>
|
||||||
|
<ul className="space-y-4 text-xs font-black uppercase tracking-widest text-gray-600">
|
||||||
|
<li><Link href="/login" className="hover:text-blue-500 transition-colors">Help Center</Link></li>
|
||||||
|
<li><Link href="/login" className="hover:text-blue-500 transition-colors">API</Link></li>
|
||||||
|
<li><Link href="/login" className="hover:text-blue-500 transition-colors">Fees</Link></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4 className="text-white font-black uppercase text-xs tracking-[0.2em] mb-8">{t('footer.section.legal', { defaultValue: 'Legal' })}</h4>
|
||||||
|
<ul className="space-y-4 text-xs font-black uppercase tracking-widest text-gray-600">
|
||||||
|
<li><Link href="/privacy-policy" className="hover:text-blue-500 transition-colors">Privacy</Link></li>
|
||||||
|
<li><Link href="/terms-of-use" className="hover:text-blue-500 transition-colors">Terms</Link></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="pt-12 border-t border-white/5 flex flex-col md:flex-row justify-between items-center gap-6">
|
||||||
|
<p className="text-[10px] font-black text-gray-600 uppercase tracking-[0.3em]">
|
||||||
|
© 2026 ADML EXCHANGE. ALL RIGHTS RESERVED.
|
||||||
|
</p>
|
||||||
|
<div className="flex space-x-8">
|
||||||
|
<BaseIcon path={icon.mdiTwitter} size={20} className="text-gray-600 hover:text-white transition-colors cursor-pointer" />
|
||||||
|
<BaseIcon path={icon.mdiSend} size={20} className="text-gray-600 hover:text-white transition-colors cursor-pointer" />
|
||||||
|
<BaseIcon path={icon.mdiForum} size={20} className="text-gray-600 hover:text-white transition-colors cursor-pointer" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<style jsx global>{`
|
||||||
|
@keyframes gradient-x {
|
||||||
|
0% { background-position: 0% 50%; }
|
||||||
|
50% { background-position: 100% 50%; }
|
||||||
|
100% { background-position: 0% 50%; }
|
||||||
|
}
|
||||||
|
.animate-gradient-x {
|
||||||
|
animation: gradient-x 5s ease infinite;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Starter.getLayout = function getLayout(page: ReactElement) {
|
LandingPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return <LayoutGuest>{page}</LayoutGuest>;
|
return <LayoutGuest>{page}</LayoutGuest>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1,12 +1,10 @@
|
|||||||
|
|
||||||
|
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import type { ReactElement } from 'react';
|
import type { ReactElement } from 'react';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import BaseButton from '../components/BaseButton';
|
import BaseButton from '../components/BaseButton';
|
||||||
import CardBox from '../components/CardBox';
|
import CardBox from '../components/CardBox';
|
||||||
import BaseIcon from "../components/BaseIcon";
|
import BaseIcon from "../components/BaseIcon";
|
||||||
import { mdiInformation, mdiEye, mdiEyeOff } from '@mdi/js';
|
import { mdiInformation, mdiEye, mdiEyeOff, mdiAccountCircle } from '@mdi/js';
|
||||||
import SectionFullScreen from '../components/SectionFullScreen';
|
import SectionFullScreen from '../components/SectionFullScreen';
|
||||||
import LayoutGuest from '../layouts/Guest';
|
import LayoutGuest from '../layouts/Guest';
|
||||||
import { Field, Form, Formik } from 'formik';
|
import { Field, Form, Formik } from 'formik';
|
||||||
@ -21,6 +19,7 @@ import { useAppDispatch, useAppSelector } from '../stores/hooks';
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import {toast, ToastContainer} from "react-toastify";
|
import {toast, ToastContainer} from "react-toastify";
|
||||||
import { getPexelsImage, getPexelsVideo } from '../helpers/pexels'
|
import { getPexelsImage, getPexelsVideo } from '../helpers/pexels'
|
||||||
|
import Logo from '../components/Logo';
|
||||||
|
|
||||||
export default function Login() {
|
export default function Login() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -44,7 +43,7 @@ export default function Login() {
|
|||||||
password: '4b8a182c',
|
password: '4b8a182c',
|
||||||
remember: true })
|
remember: true })
|
||||||
|
|
||||||
const title = 'AI App Draft'
|
const title = 'ADML EXCHANGE'
|
||||||
|
|
||||||
// Fetch Pexels image/video
|
// Fetch Pexels image/video
|
||||||
useEffect( () => {
|
useEffect( () => {
|
||||||
@ -92,6 +91,15 @@ export default function Login() {
|
|||||||
await dispatch(loginUser(rest));
|
await dispatch(loginUser(rest));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleDemoLogin = async () => {
|
||||||
|
const demoValues = {
|
||||||
|
email: 'admin@flatlogic.com',
|
||||||
|
password: '4b8a182c'
|
||||||
|
};
|
||||||
|
setInitialValues({ ...demoValues, remember: true });
|
||||||
|
await dispatch(loginUser(demoValues));
|
||||||
|
};
|
||||||
|
|
||||||
const setLogin = (target: HTMLElement) => {
|
const setLogin = (target: HTMLElement) => {
|
||||||
setInitialValues(prev => ({
|
setInitialValues(prev => ({
|
||||||
...prev,
|
...prev,
|
||||||
@ -158,61 +166,35 @@ export default function Login() {
|
|||||||
</Head>
|
</Head>
|
||||||
|
|
||||||
<SectionFullScreen bg='violet'>
|
<SectionFullScreen bg='violet'>
|
||||||
<div className={`flex ${contentPosition === 'right' ? 'flex-row-reverse' : 'flex-row'} min-h-screen w-full`}>
|
<div className={`flex ${contentPosition === 'right' ? 'flex-row-reverse' : 'flex-row'} min-h-screen w-full bg-black/40 backdrop-blur-sm`}>
|
||||||
{contentType === 'image' && contentPosition !== 'background' ? imageBlock(illustrationImage) : null}
|
{contentType === 'image' && contentPosition !== 'background' ? imageBlock(illustrationImage) : null}
|
||||||
{contentType === 'video' && contentPosition !== 'background' ? videoBlock(illustrationVideo) : null}
|
{contentType === 'video' && contentPosition !== 'background' ? videoBlock(illustrationVideo) : null}
|
||||||
<div className='flex items-center justify-center flex-col space-y-4 w-full lg:w-full'>
|
<div className='flex items-center justify-center flex-col space-y-4 w-full lg:w-full'>
|
||||||
|
|
||||||
<CardBox id="loginRoles" className='w-full md:w-3/5 lg:w-2/3'>
|
<CardBox className='w-full md:w-3/5 lg:w-1/2 shadow-2xl bg-white/90 dark:bg-dark-900/90'>
|
||||||
|
<div className="flex flex-col items-center mb-8">
|
||||||
<h2 className="text-4xl font-semibold my-4">{title}</h2>
|
<Logo className="text-3xl mb-4" />
|
||||||
|
<h2 className="text-2xl font-black tracking-tight uppercase">Welcome to ADML</h2>
|
||||||
<div className='flex flex-row text-gray-500 justify-between'>
|
<p className="text-gray-500 text-sm font-medium">Please sign in to continue trading</p>
|
||||||
<div>
|
|
||||||
|
|
||||||
<p className='mb-2'>Use{' '}
|
|
||||||
<code className={`cursor-pointer ${textColor} `}
|
|
||||||
data-password="4b8a182c"
|
|
||||||
onClick={(e) => setLogin(e.target)}>admin@flatlogic.com</code>{' / '}
|
|
||||||
<code className={`${textColor}`}>4b8a182c</code>{' / '}
|
|
||||||
to login as Admin</p>
|
|
||||||
<p>Use <code
|
|
||||||
className={`cursor-pointer ${textColor} `}
|
|
||||||
data-password="ed8b688f77aa"
|
|
||||||
onClick={(e) => setLogin(e.target)}>client@hello.com</code>{' / '}
|
|
||||||
<code className={`${textColor}`}>ed8b688f77aa</code>{' / '}
|
|
||||||
to login as User</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<BaseIcon
|
|
||||||
className={`${iconsColor}`}
|
|
||||||
w='w-16'
|
|
||||||
h='h-16'
|
|
||||||
size={48}
|
|
||||||
path={mdiInformation}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</CardBox>
|
|
||||||
|
|
||||||
<CardBox className='w-full md:w-3/5 lg:w-2/3'>
|
|
||||||
<Formik
|
<Formik
|
||||||
initialValues={initialValues}
|
initialValues={initialValues}
|
||||||
enableReinitialize
|
enableReinitialize
|
||||||
onSubmit={(values) => handleSubmit(values)}
|
onSubmit={(values) => handleSubmit(values)}
|
||||||
>
|
>
|
||||||
<Form>
|
<Form className="space-y-4">
|
||||||
<FormField
|
<FormField
|
||||||
label='Login'
|
label='Email / Account'
|
||||||
help='Please enter your login'>
|
help='Enter your registered email'>
|
||||||
<Field name='email' />
|
<Field name='email' placeholder="example@adml.com" />
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
<div className='relative'>
|
<div className='relative'>
|
||||||
<FormField
|
<FormField
|
||||||
label='Password'
|
label='Password'
|
||||||
help='Please enter your password'>
|
help='Enter your password'>
|
||||||
<Field name='password' type={showPassword ? 'text' : 'password'} />
|
<Field name='password' type={showPassword ? 'text' : 'password'} placeholder="••••••••" />
|
||||||
</FormField>
|
</FormField>
|
||||||
<div
|
<div
|
||||||
className='absolute bottom-8 right-0 pr-3 flex items-center cursor-pointer'
|
className='absolute bottom-8 right-0 pr-3 flex items-center cursor-pointer'
|
||||||
@ -226,45 +208,59 @@ export default function Login() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={'flex justify-between'}>
|
<div className={'flex justify-between items-center px-1'}>
|
||||||
<FormCheckRadio type='checkbox' label='Remember'>
|
<FormCheckRadio type='checkbox' label='Stay logged in'>
|
||||||
<Field type='checkbox' name='remember' />
|
<Field type='checkbox' name='remember' />
|
||||||
</FormCheckRadio>
|
</FormCheckRadio>
|
||||||
|
|
||||||
<Link className={`${textColor} text-blue-600`} href={'/forgot'}>
|
<Link className={`${textColor} text-sm font-bold`} href={'/forgot'}>
|
||||||
Forgot password?
|
Forgot password?
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<BaseDivider />
|
<BaseDivider />
|
||||||
|
|
||||||
<BaseButtons>
|
<BaseButtons vertical className="space-y-3">
|
||||||
<BaseButton
|
<BaseButton
|
||||||
className={'w-full'}
|
className={'w-full py-3 font-black text-lg'}
|
||||||
type='submit'
|
type='submit'
|
||||||
label={isFetching ? 'Loading...' : 'Login'}
|
label={isFetching ? 'Verifying...' : 'Sign In'}
|
||||||
color='info'
|
color='info'
|
||||||
disabled={isFetching}
|
disabled={isFetching}
|
||||||
/>
|
/>
|
||||||
|
<BaseButton
|
||||||
|
className={'w-full py-3 font-bold border-2 border-blue-600/50 hover:bg-blue-600 hover:text-white transition-all'}
|
||||||
|
type='button'
|
||||||
|
onClick={handleDemoLogin}
|
||||||
|
label='Demo Account Login'
|
||||||
|
outline
|
||||||
|
color='info'
|
||||||
|
icon={mdiAccountCircle}
|
||||||
|
/>
|
||||||
</BaseButtons>
|
</BaseButtons>
|
||||||
<br />
|
|
||||||
<p className={'text-center'}>
|
<p className={'text-center mt-6 text-sm font-medium text-gray-500'}>
|
||||||
Don’t have an account yet?{' '}
|
Don’t have an account yet?{' '}
|
||||||
<Link className={`${textColor}`} href={'/register'}>
|
<Link className={`${textColor} font-black underline decoration-2 underline-offset-4`} href={'/register'}>
|
||||||
New Account
|
Create Account
|
||||||
</Link>
|
</Link>
|
||||||
</p>
|
</p>
|
||||||
</Form>
|
</Form>
|
||||||
</Formik>
|
</Formik>
|
||||||
</CardBox>
|
</CardBox>
|
||||||
|
|
||||||
|
<div className="bg-white/10 backdrop-blur-md rounded-2xl p-4 border border-white/10 text-[10px] text-gray-400 font-bold uppercase tracking-widest text-center max-w-md">
|
||||||
|
Warning: Please verify the URL is adml-exchange.com to prevent phishing.
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</SectionFullScreen>
|
</SectionFullScreen>
|
||||||
<div className='bg-black text-white flex flex-col text-center justify-center md:flex-row'>
|
<div className='bg-black text-white flex flex-col text-center justify-center md:flex-row py-8 border-t border-white/5'>
|
||||||
<p className='py-6 text-sm'>© 2026 <span>{title}</span>. © All rights reserved</p>
|
<p className='text-sm font-bold opacity-60 uppercase tracking-widest'>© 2026 ADML EXCHANGE. ALL RIGHTS RESERVED.</p>
|
||||||
<Link className='py-6 ml-4 text-sm' href='/privacy-policy/'>
|
<div className="flex space-x-6 md:ml-12 mt-4 md:mt-0">
|
||||||
Privacy Policy
|
<Link className='text-xs font-black uppercase tracking-widest hover:text-blue-500' href='/privacy-policy/'>Privacy Policy</Link>
|
||||||
</Link>
|
<Link className='text-xs font-black uppercase tracking-widest hover:text-blue-500' href='/terms/'>Terms of Service</Link>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ToastContainer />
|
<ToastContainer />
|
||||||
</div>
|
</div>
|
||||||
@ -273,4 +269,4 @@ export default function Login() {
|
|||||||
|
|
||||||
Login.getLayout = function getLayout(page: ReactElement) {
|
Login.getLayout = function getLayout(page: ReactElement) {
|
||||||
return <LayoutGuest>{page}</LayoutGuest>;
|
return <LayoutGuest>{page}</LayoutGuest>;
|
||||||
};
|
};
|
||||||
160
frontend/src/pages/markets.tsx
Normal file
160
frontend/src/pages/markets.tsx
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import type { ReactElement } from 'react';
|
||||||
|
import Head from 'next/head';
|
||||||
|
import * as icon from '@mdi/js';
|
||||||
|
import LayoutGuest from '../layouts/Guest';
|
||||||
|
import SectionMain from '../components/SectionMain';
|
||||||
|
import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton';
|
||||||
|
import BaseIcon from '../components/BaseIcon';
|
||||||
|
import BaseButton from '../components/BaseButton';
|
||||||
|
import { getPageTitle } from '../config';
|
||||||
|
|
||||||
|
const INITIAL_MARKET_DATA = [
|
||||||
|
{ symbol: 'BTCUSDT', name: 'Bitcoin', label: 'BTC', icon: icon.mdiBitcoin },
|
||||||
|
{ symbol: 'ETHUSDT', name: 'Ethereum', label: 'ETH', icon: icon.mdiEthereum },
|
||||||
|
{ symbol: 'SOLUSDT', name: 'Solana', label: 'SOL', icon: icon.mdiAlphaSCircle },
|
||||||
|
{ symbol: 'BNBUSDT', name: 'BNB', label: 'BNB', icon: icon.mdiAlphaBCircle },
|
||||||
|
{ symbol: 'ADAUSDT', name: 'Cardano', label: 'ADA', icon: icon.mdiAlphaACircle },
|
||||||
|
{ symbol: 'XRPUSDT', name: 'Ripple', label: 'XRP', icon: icon.mdiAlphaXCircle },
|
||||||
|
{ symbol: 'DOTUSDT', name: 'Polkadot', label: 'DOT', icon: icon.mdiAlphaPCircle },
|
||||||
|
{ symbol: 'DOGEUSDT', name: 'Dogecoin', label: 'DOGE', icon: icon.mdiDog },
|
||||||
|
{ symbol: 'MATICUSDT', name: 'Polygon', label: 'MATIC', icon: icon.mdiHexagonMultiple },
|
||||||
|
{ symbol: 'LINKUSDT', name: 'Chainlink', label: 'LINK', icon: icon.mdiLinkVariant },
|
||||||
|
];
|
||||||
|
|
||||||
|
const MarketsPage = () => {
|
||||||
|
const [marketData, setMarketData] = useState<any[]>([]);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchMarketData = async () => {
|
||||||
|
try {
|
||||||
|
const symbols = INITIAL_MARKET_DATA.map(d => d.symbol);
|
||||||
|
const response = await fetch(`https://api.binance.com/api/v3/ticker/24hr?symbols=${JSON.stringify(symbols)}`);
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
const mergedData = INITIAL_MARKET_DATA.map(item => {
|
||||||
|
const ticker = data.find((t: any) => t.symbol === item.symbol);
|
||||||
|
if (ticker) {
|
||||||
|
const price = parseFloat(ticker.lastPrice).toLocaleString(undefined, { minimumFractionDigits: 2 });
|
||||||
|
const change = parseFloat(ticker.priceChangePercent).toFixed(2);
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
price: price,
|
||||||
|
change: (parseFloat(change) >= 0 ? '+' : '') + change + '%',
|
||||||
|
isPositive: parseFloat(change) >= 0,
|
||||||
|
volume: (parseFloat(ticker.quoteVolume) / 1000000).toFixed(2) + 'M',
|
||||||
|
high: parseFloat(ticker.highPrice).toLocaleString(),
|
||||||
|
low: parseFloat(ticker.lowPrice).toLocaleString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return { ...item, price: '0', change: '0%', isPositive: true, volume: '0', high: '0', low: '0' };
|
||||||
|
});
|
||||||
|
setMarketData(mergedData);
|
||||||
|
setLoading(false);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch market data:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchMarketData();
|
||||||
|
const interval = setInterval(fetchMarketData, 5000);
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>{getPageTitle('Markets')}</title>
|
||||||
|
</Head>
|
||||||
|
<SectionMain>
|
||||||
|
<SectionTitleLineWithButton icon={icon.mdiChartLine} title="Markets" main>
|
||||||
|
<div className="flex space-x-2">
|
||||||
|
<BaseButton label="Spot" color="info" small />
|
||||||
|
<BaseButton label="Futures" color="whiteDark" small />
|
||||||
|
<BaseButton label="Options" color="whiteDark" small />
|
||||||
|
</div>
|
||||||
|
</SectionTitleLineWithButton>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
|
||||||
|
{marketData.slice(0, 3).map((coin) => (
|
||||||
|
<div key={coin.symbol} className="bg-white dark:bg-dark-900 border border-white/5 p-6 rounded-3xl">
|
||||||
|
<div className="flex items-center justify-between mb-4">
|
||||||
|
<div className="flex items-center space-x-3">
|
||||||
|
<BaseIcon path={coin.icon} size={32} className="text-blue-500" />
|
||||||
|
<span className="font-black text-lg">{coin.label}/USDT</span>
|
||||||
|
</div>
|
||||||
|
<span className={`text-sm font-bold ${coin.isPositive ? 'text-emerald-500' : 'text-rose-500'}`}>{coin.change}</span>
|
||||||
|
</div>
|
||||||
|
<div className="text-3xl font-black">${coin.price}</div>
|
||||||
|
<div className="text-xs text-gray-500 font-bold mt-2 uppercase">24h Volume: {coin.volume}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-white dark:bg-dark-900 border border-white/5 rounded-3xl overflow-hidden shadow-xl">
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<table className="w-full text-left border-collapse">
|
||||||
|
<thead>
|
||||||
|
<tr className="bg-gray-50 dark:bg-dark-800 text-gray-500 text-xs font-black uppercase tracking-widest border-b border-white/5">
|
||||||
|
<th className="px-6 py-4">Asset</th>
|
||||||
|
<th className="px-6 py-4">Price</th>
|
||||||
|
<th className="px-6 py-4">24h Change</th>
|
||||||
|
<th className="px-6 py-4">24h High/Low</th>
|
||||||
|
<th className="px-6 py-4">24h Volume</th>
|
||||||
|
<th className="px-6 py-4 text-right">Trade</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="divide-y divide-white/5">
|
||||||
|
{marketData.map((asset) => (
|
||||||
|
<tr key={asset.symbol} className="hover:bg-gray-50 dark:hover:bg-white/[0.02] transition-colors group">
|
||||||
|
<td className="px-6 py-5">
|
||||||
|
<div className="flex items-center space-x-3">
|
||||||
|
<BaseIcon path={asset.icon} size={24} className="text-blue-500" />
|
||||||
|
<div>
|
||||||
|
<div className="font-black text-base">{asset.label}</div>
|
||||||
|
<div className="text-[10px] text-gray-500 font-bold uppercase">{asset.name}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-5">
|
||||||
|
<div className="font-black text-base">${asset.price}</div>
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-5">
|
||||||
|
<div className={`font-black ${asset.isPositive ? 'text-emerald-500' : 'text-rose-500'}`}>
|
||||||
|
{asset.change}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-5">
|
||||||
|
<div className="text-xs font-bold text-gray-500">H: {asset.high}</div>
|
||||||
|
<div className="text-xs font-bold text-gray-500">L: {asset.low}</div>
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-5">
|
||||||
|
<div className="text-sm font-bold text-gray-400">{asset.volume} USDT</div>
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-5 text-right">
|
||||||
|
<BaseButton
|
||||||
|
href="/exchange"
|
||||||
|
label="Trade"
|
||||||
|
color="info"
|
||||||
|
small
|
||||||
|
className="font-black px-6"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</SectionMain>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
MarketsPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
|
return <LayoutGuest>{page}</LayoutGuest>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MarketsPage;
|
||||||
228
frontend/src/pages/perpetual.tsx
Normal file
228
frontend/src/pages/perpetual.tsx
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
import React, { ReactElement, useEffect, useState } from 'react';
|
||||||
|
import Head from 'next/head';
|
||||||
|
import * as icon from '@mdi/js';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import LayoutAuthenticated from '../layouts/Authenticated';
|
||||||
|
import BaseIcon from '../components/BaseIcon';
|
||||||
|
import BaseButton from '../components/BaseButton';
|
||||||
|
import { getPageTitle } from '../config';
|
||||||
|
|
||||||
|
const PerpetualPage = () => {
|
||||||
|
const { t } = useTranslation('common');
|
||||||
|
const [leverage, setLeverage] = useState(20);
|
||||||
|
const [side, setSide] = useState('long');
|
||||||
|
const [currentTab, setCurrentTab] = useState('Positions');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.src = 'https://s3.tradingview.com/tv.js';
|
||||||
|
script.async = true;
|
||||||
|
script.onload = () => {
|
||||||
|
if (typeof window !== 'undefined' && (window as any).TradingView) {
|
||||||
|
new (window as any).TradingView.widget({
|
||||||
|
"autosize": true,
|
||||||
|
"symbol": "BINANCE:BTCUSDT.P",
|
||||||
|
"interval": "D",
|
||||||
|
"timezone": "Etc/UTC",
|
||||||
|
"theme": "dark",
|
||||||
|
"style": "1",
|
||||||
|
"locale": "en",
|
||||||
|
"toolbar_bg": "#f1f3f6",
|
||||||
|
"enable_publishing": false,
|
||||||
|
"hide_side_toolbar": false,
|
||||||
|
"allow_symbol_change": true,
|
||||||
|
"container_id": "tradingview_chart"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.head.appendChild(script);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>{getPageTitle(t('perpetual.title', { defaultValue: 'Perpetual Trading' }))}</title>
|
||||||
|
</Head>
|
||||||
|
<div className="flex flex-col h-[calc(100vh-64px)] overflow-hidden bg-[#000000] text-white">
|
||||||
|
{/* Trading Header */}
|
||||||
|
<div className="flex items-center justify-between px-4 py-3 border-b border-white/5 bg-[#080808]">
|
||||||
|
<div className="flex items-center space-x-10">
|
||||||
|
<div className="flex items-center space-x-3">
|
||||||
|
<div className="w-10 h-10 bg-orange-500/10 rounded-xl flex items-center justify-center">
|
||||||
|
<BaseIcon path={icon.mdiBitcoin} size={28} className="text-orange-500" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="text-lg font-black tracking-tighter leading-none">BTC/USDT</div>
|
||||||
|
<div className="flex items-center space-x-1 mt-1">
|
||||||
|
<span className="text-[9px] font-black bg-blue-600/20 text-blue-500 px-1.5 py-0.5 rounded uppercase tracking-widest">{t('perpetual.header.perp', { defaultValue: 'Perp' })}</span>
|
||||||
|
<span className="text-[9px] font-black bg-white/5 text-gray-500 px-1.5 py-0.5 rounded uppercase tracking-widest">{leverage}x</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center space-x-10">
|
||||||
|
<div>
|
||||||
|
<div className="text-[10px] font-black text-gray-500 uppercase tracking-widest leading-none mb-1">{t('perpetual.header.mark_price', { defaultValue: 'Mark Price' })}</div>
|
||||||
|
<div className="text-emerald-500 text-lg font-black tracking-tighter">43,123.45</div>
|
||||||
|
</div>
|
||||||
|
<div className="hidden sm:block">
|
||||||
|
<div className="text-[10px] font-black text-gray-500 uppercase tracking-widest leading-none mb-1">{t('perpetual.header.funding', { defaultValue: 'Funding / Countdown' })}</div>
|
||||||
|
<div className="text-blue-500 text-xs font-black">0.0100% / 04:21:05</div>
|
||||||
|
</div>
|
||||||
|
<div className="hidden lg:block">
|
||||||
|
<div className="text-[10px] font-black text-gray-500 uppercase tracking-widest leading-none mb-1">{t('perpetual.header.change', { defaultValue: '24h Change' })}</div>
|
||||||
|
<div className="text-emerald-500 text-xs font-black">+2.45%</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<div className="flex items-center space-x-2 bg-white/5 px-4 py-2 rounded-xl border border-white/5 cursor-pointer hover:bg-white/10 transition-all">
|
||||||
|
<span className="text-[10px] font-black text-gray-500 uppercase tracking-widest">{t('perpetual.header.margin', { defaultValue: 'Margin' })}</span>
|
||||||
|
<span className="text-xs font-black text-blue-500 uppercase">{t('perpetual.header.cross', { defaultValue: 'Cross' })}</span>
|
||||||
|
</div>
|
||||||
|
<BaseButton color="info" label={t('perpetual.header.deposit', { defaultValue: 'Deposit' })} small className="font-black px-6 rounded-xl" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-1 overflow-hidden">
|
||||||
|
{/* Main Chart Area */}
|
||||||
|
<div className="flex-1 flex flex-col border-r border-white/5">
|
||||||
|
<div id="tradingview_chart" className="flex-1"></div>
|
||||||
|
|
||||||
|
{/* Positions/Orders Tabs */}
|
||||||
|
<div className="h-72 border-t border-white/5 bg-[#080808] overflow-hidden flex flex-col">
|
||||||
|
<div className="flex border-b border-white/5 bg-white/[0.02]">
|
||||||
|
{[
|
||||||
|
t('perpetual.tabs.positions', { defaultValue: 'Positions' }),
|
||||||
|
t('perpetual.tabs.open_orders', { defaultValue: 'Open Orders' }),
|
||||||
|
t('perpetual.tabs.order_history', { defaultValue: 'Order History' }),
|
||||||
|
t('perpetual.tabs.trade_history', { defaultValue: 'Trade History' })
|
||||||
|
].map((tab) => (
|
||||||
|
<button
|
||||||
|
key={tab}
|
||||||
|
onClick={() => setCurrentTab(tab)}
|
||||||
|
className={`px-8 py-4 text-[10px] font-black uppercase tracking-[0.2em] transition-all ${currentTab === tab ? 'text-blue-500 border-b-2 border-blue-500 bg-blue-500/5' : 'text-gray-500 hover:text-white'}`}
|
||||||
|
>
|
||||||
|
{tab}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 flex flex-col items-center justify-center p-8 text-center opacity-40">
|
||||||
|
<BaseIcon path={icon.mdiClipboardTextOutline} size={48} className="text-gray-700 mb-4" />
|
||||||
|
<p className="text-gray-600 text-[10px] font-black uppercase tracking-widest">
|
||||||
|
{t('perpetual.no_positions', { defaultValue: 'No active positions or orders' })}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Trade Panel */}
|
||||||
|
<div className="w-[340px] flex flex-col bg-[#080808]">
|
||||||
|
{/* Mode Selector */}
|
||||||
|
<div className="p-6 bg-black/40 border-b border-white/5">
|
||||||
|
<div className="flex bg-white/5 p-1 rounded-xl mb-6">
|
||||||
|
<button
|
||||||
|
onClick={() => setSide('long')}
|
||||||
|
className={`flex-1 py-2.5 font-black uppercase text-[10px] tracking-widest rounded-lg transition-all ${side === 'long' ? 'bg-emerald-500 text-black shadow-lg shadow-emerald-500/20' : 'text-gray-500 hover:text-white'}`}
|
||||||
|
>
|
||||||
|
{t('perpetual.panel.long', { defaultValue: 'Open Long' })}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setSide('short')}
|
||||||
|
className={`flex-1 py-2.5 font-black uppercase text-[10px] tracking-widest rounded-lg transition-all ${side === 'short' ? 'bg-rose-500 text-black shadow-lg shadow-rose-500/20' : 'text-gray-500 hover:text-white'}`}
|
||||||
|
>
|
||||||
|
{t('perpetual.panel.short', { defaultValue: 'Open Short' })}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex justify-between items-center text-[10px] font-black uppercase tracking-widest text-gray-500">
|
||||||
|
<span>{t('perpetual.panel.mode', { defaultValue: 'Margin Mode' })}</span>
|
||||||
|
<span className="text-blue-500 cursor-pointer hover:bg-blue-500/10 px-2 py-1 rounded transition-all">{t('perpetual.panel.cross', { defaultValue: 'Cross' })} {leverage}x</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="relative group">
|
||||||
|
<span className="absolute left-4 top-1/2 -translate-y-1/2 text-[10px] font-black uppercase text-gray-600 group-focus-within:text-blue-500">{t('perpetual.panel.price', { defaultValue: 'Price' })}</span>
|
||||||
|
<input type="text" defaultValue="43123.45" className="w-full bg-[#111111] border border-white/10 rounded-xl pl-16 pr-14 py-3 text-white text-xs font-black focus:border-blue-500/50 outline-none transition-all" />
|
||||||
|
<span className="absolute right-4 top-1/2 -translate-y-1/2 text-[10px] font-black uppercase text-gray-600">USDT</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="relative group">
|
||||||
|
<span className="absolute left-4 top-1/2 -translate-y-1/2 text-[10px] font-black uppercase text-gray-600 group-focus-within:text-blue-500">{t('perpetual.panel.amount', { defaultValue: 'Amount' })}</span>
|
||||||
|
<input type="text" placeholder="0.00" className="w-full bg-[#111111] border border-white/10 rounded-xl pl-16 pr-14 py-3 text-white text-xs font-black focus:border-blue-500/50 outline-none transition-all" />
|
||||||
|
<span className="absolute right-4 top-1/2 -translate-y-1/2 text-[10px] font-black uppercase text-gray-600">Cont</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Leverage Slider Mock */}
|
||||||
|
<div className="pt-2">
|
||||||
|
<div className="flex justify-between text-[9px] font-black uppercase text-gray-600 mb-2">
|
||||||
|
<span>{t('perpetual.panel.leverage', { defaultValue: 'Adjust Leverage' })}</span>
|
||||||
|
<span className="text-blue-500">{leverage}x</span>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
type="range" min="1" max="125" value={leverage}
|
||||||
|
onChange={(e) => setLeverage(parseInt(e.target.value))}
|
||||||
|
className="w-full h-1 bg-white/10 rounded-full appearance-none cursor-pointer accent-blue-500"
|
||||||
|
/>
|
||||||
|
<div className="flex justify-between text-[8px] font-black text-gray-700 mt-1 uppercase">
|
||||||
|
<span>1x</span>
|
||||||
|
<span>25x</span>
|
||||||
|
<span>50x</span>
|
||||||
|
<span>75x</span>
|
||||||
|
<span>100x</span>
|
||||||
|
<span>125x</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2 pt-4 border-t border-white/5">
|
||||||
|
<div className="flex justify-between text-[10px] font-black text-gray-600 uppercase tracking-widest">
|
||||||
|
<span>{t('perpetual.panel.available', { defaultValue: 'Available' })}</span>
|
||||||
|
<span className="text-white">0.00 USDT</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between text-[10px] font-black text-gray-600 uppercase tracking-widest">
|
||||||
|
<span>{t('perpetual.panel.max_long', { defaultValue: 'Max Long' })}</span>
|
||||||
|
<span className="text-white">0.000 BTC</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<BaseButton
|
||||||
|
color="info"
|
||||||
|
label={side === 'long' ? `${t('perpetual.panel.buy', { defaultValue: 'BUY / LONG' })}` : `${t('perpetual.panel.sell', { defaultValue: 'SELL / SHORT' })}`}
|
||||||
|
className={`w-full py-4 font-black border-none text-white rounded-2xl mt-4 shadow-xl uppercase tracking-[0.2em] text-xs transition-all hover:scale-[1.02] active:scale-95 ${side === 'long' ? 'bg-emerald-500 text-black shadow-emerald-500/10' : 'bg-rose-500 text-black shadow-rose-500/10'}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Order Book Minimal */}
|
||||||
|
<div className="flex-1 p-6 overflow-hidden flex flex-col">
|
||||||
|
<h4 className="text-[10px] font-black uppercase tracking-[0.2em] text-gray-500 mb-4">{t('perpetual.orderbook.title', { defaultValue: 'Market Depth' })}</h4>
|
||||||
|
<div className="flex-1 overflow-y-auto space-y-1 font-mono text-[10px] font-bold">
|
||||||
|
{[...Array(6)].map((_, i) => (
|
||||||
|
<div key={`ask-${i}`} className="flex justify-between text-rose-500 hover:bg-rose-500/10 px-2 py-0.5 rounded transition-colors">
|
||||||
|
<span>43,{(123 + (6-i)*5).toString().padStart(3, '0')}.45</span>
|
||||||
|
<span className="text-gray-500">{(Math.random() * 5).toFixed(2)}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="py-2 px-2 text-sm font-black text-emerald-500 border-y border-white/5 my-1">
|
||||||
|
43,123.45
|
||||||
|
</div>
|
||||||
|
{[...Array(6)].map((_, i) => (
|
||||||
|
<div key={`bid-${i}`} className="flex justify-between text-emerald-500 hover:bg-emerald-500/10 px-2 py-0.5 rounded transition-colors">
|
||||||
|
<span>43,{(123 - i*5).toString().padStart(3, '0')}.45</span>
|
||||||
|
<span className="text-gray-500">{(Math.random() * 5).toFixed(2)}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
PerpetualPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
|
return <LayoutAuthenticated>{page}</LayoutAuthenticated>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PerpetualPage;
|
||||||
198
frontend/src/pages/second-contract.tsx
Normal file
198
frontend/src/pages/second-contract.tsx
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
import React, { ReactElement, useEffect, useState } from 'react';
|
||||||
|
import Head from 'next/head';
|
||||||
|
import * as icon from '@mdi/js';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import LayoutAuthenticated from '../layouts/Authenticated';
|
||||||
|
import BaseIcon from '../components/BaseIcon';
|
||||||
|
import BaseButton from '../components/BaseButton';
|
||||||
|
import { getPageTitle } from '../config';
|
||||||
|
|
||||||
|
const SecondContractPage = () => {
|
||||||
|
const { t } = useTranslation('common');
|
||||||
|
const [amount, setAmount] = useState('100');
|
||||||
|
const [period, setPeriod] = useState(60);
|
||||||
|
const [profit, setProfit] = useState(85);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.src = 'https://s3.tradingview.com/tv.js';
|
||||||
|
script.async = true;
|
||||||
|
script.onload = () => {
|
||||||
|
if (typeof window !== 'undefined' && (window as any).TradingView) {
|
||||||
|
new (window as any).TradingView.widget({
|
||||||
|
"autosize": true,
|
||||||
|
"symbol": "BINANCE:BTCUSDT",
|
||||||
|
"interval": "1",
|
||||||
|
"timezone": "Etc/UTC",
|
||||||
|
"theme": "dark",
|
||||||
|
"style": "1",
|
||||||
|
"locale": "en",
|
||||||
|
"toolbar_bg": "#f1f3f6",
|
||||||
|
"enable_publishing": false,
|
||||||
|
"hide_side_toolbar": false,
|
||||||
|
"allow_symbol_change": true,
|
||||||
|
"container_id": "tradingview_chart"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.head.appendChild(script);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>{getPageTitle(t('seconds.title', { defaultValue: 'Binary Options' }))}</title>
|
||||||
|
</Head>
|
||||||
|
<div className="flex flex-col h-[calc(100vh-64px)] overflow-hidden bg-[#000000] text-white">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="flex items-center justify-between px-6 py-4 border-b border-white/5 bg-[#080808]">
|
||||||
|
<div className="flex items-center space-x-12">
|
||||||
|
<div className="flex items-center space-x-3">
|
||||||
|
<div className="w-10 h-10 bg-orange-500/10 rounded-xl flex items-center justify-center">
|
||||||
|
<BaseIcon path={icon.mdiBitcoin} size={28} className="text-orange-500" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="text-lg font-black tracking-tighter leading-none">BTC/USDT</div>
|
||||||
|
<div className="text-[10px] font-black text-blue-500 uppercase mt-1 tracking-[0.2em]">{t('seconds.header.contract', { defaultValue: 'Option Contract' })}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center space-x-12">
|
||||||
|
<div>
|
||||||
|
<div className="text-[10px] font-black text-gray-500 uppercase tracking-widest leading-none mb-1">{t('seconds.header.current_price', { defaultValue: 'Current Price' })}</div>
|
||||||
|
<div className="text-emerald-500 text-xl font-black tracking-tighter animate-pulse">43,123.45</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="text-[10px] font-black text-gray-500 uppercase tracking-widest leading-none mb-1">{t('seconds.header.yield', { defaultValue: 'Estimated Yield' })}</div>
|
||||||
|
<div className="text-emerald-500 text-lg font-black tracking-tighter">+{profit}%</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-6">
|
||||||
|
<div className="text-right">
|
||||||
|
<div className="text-[10px] font-black text-gray-500 uppercase tracking-widest leading-none mb-1">{t('seconds.header.balance', { defaultValue: 'Available Balance' })}</div>
|
||||||
|
<div className="text-white text-sm font-black tracking-tight">0.00 USDT</div>
|
||||||
|
</div>
|
||||||
|
<BaseButton color="info" label={t('seconds.header.deposit', { defaultValue: 'Deposit' })} small className="font-black px-6 rounded-xl" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-1 overflow-hidden">
|
||||||
|
{/* Chart Area */}
|
||||||
|
<div className="flex-1 flex flex-col border-r border-white/5 relative">
|
||||||
|
<div id="tradingview_chart" className="flex-1"></div>
|
||||||
|
|
||||||
|
{/* Quick Stats Overlay */}
|
||||||
|
<div className="absolute top-4 left-4 flex space-x-2 z-10">
|
||||||
|
{[
|
||||||
|
{ label: '30s', yield: '75%' },
|
||||||
|
{ label: '60s', yield: '85%' },
|
||||||
|
{ label: '120s', yield: '90%' },
|
||||||
|
{ label: '300s', yield: '95%' }
|
||||||
|
].map((t) => (
|
||||||
|
<div key={t.label} className="bg-black/60 backdrop-blur-md border border-white/10 px-4 py-2 rounded-xl">
|
||||||
|
<div className="text-[10px] font-black text-gray-500 uppercase">{t.label}</div>
|
||||||
|
<div className="text-xs font-black text-emerald-500">{t.yield}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* History Tabs */}
|
||||||
|
<div className="h-64 border-t border-white/5 bg-[#080808] overflow-hidden flex flex-col">
|
||||||
|
<div className="flex border-b border-white/5">
|
||||||
|
{[
|
||||||
|
t('seconds.tabs.current', { defaultValue: 'Current Positions' }),
|
||||||
|
t('seconds.tabs.history', { defaultValue: 'Trade History' })
|
||||||
|
].map((tab, i) => (
|
||||||
|
<button key={tab} className={`px-8 py-4 text-[10px] font-black uppercase tracking-[0.2em] ${i === 0 ? 'text-blue-500 border-b-2 border-blue-500' : 'text-gray-500 hover:text-white'}`}>
|
||||||
|
{tab}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 flex flex-col items-center justify-center p-8 opacity-40">
|
||||||
|
<BaseIcon path={icon.mdiHistory} size={48} className="text-gray-700 mb-2" />
|
||||||
|
<p className="text-gray-600 text-[10px] font-black uppercase tracking-widest">
|
||||||
|
{t('seconds.no_history', { defaultValue: 'No active contracts' })}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Trade Panel */}
|
||||||
|
<div className="w-[360px] flex flex-col bg-[#080808] p-8">
|
||||||
|
<div className="space-y-8">
|
||||||
|
{/* Period Selection */}
|
||||||
|
<div>
|
||||||
|
<h4 className="text-[10px] font-black uppercase tracking-[0.2em] text-gray-500 mb-4">{t('seconds.panel.period', { defaultValue: 'Execution Period' })}</h4>
|
||||||
|
<div className="grid grid-cols-2 gap-3">
|
||||||
|
{[30, 60, 120, 300].map(s => (
|
||||||
|
<button
|
||||||
|
key={s}
|
||||||
|
onClick={() => setPeriod(s)}
|
||||||
|
className={`py-4 rounded-2xl font-black transition-all border ${period === s ? 'bg-blue-600 border-blue-500 text-white shadow-lg shadow-blue-600/20' : 'bg-white/5 border-white/5 text-gray-500 hover:bg-white/10'}`}
|
||||||
|
>
|
||||||
|
<div className="text-lg">{s}s</div>
|
||||||
|
<div className="text-[9px] uppercase opacity-60 mt-1">{profit}% {t('seconds.panel.yield', { defaultValue: 'Yield' })}</div>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Amount Input */}
|
||||||
|
<div>
|
||||||
|
<h4 className="text-[10px] font-black uppercase tracking-[0.2em] text-gray-500 mb-4">{t('seconds.panel.amount', { defaultValue: 'Investment Amount' })}</h4>
|
||||||
|
<div className="relative group">
|
||||||
|
<span className="absolute left-4 top-1/2 -translate-y-1/2 text-[10px] font-black uppercase text-gray-600 group-focus-within:text-blue-500">USDT</span>
|
||||||
|
<input
|
||||||
|
type="text" value={amount} onChange={(e) => setAmount(e.target.value)}
|
||||||
|
className="w-full bg-[#111111] border border-white/10 rounded-2xl pl-16 pr-4 py-4 text-xl font-black text-white focus:border-blue-500/50 outline-none transition-all"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-4 gap-2 mt-3">
|
||||||
|
{[100, 500, 1000, 5000].map(v => (
|
||||||
|
<button key={v} onClick={() => setAmount(v.toString())} className="py-2 bg-white/5 text-[10px] font-black rounded-xl hover:bg-white/10 text-gray-500 hover:text-white transition-all">{v}</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Expected Profit */}
|
||||||
|
<div className="p-6 bg-emerald-500/5 border border-emerald-500/20 rounded-3xl">
|
||||||
|
<div className="flex justify-between items-center mb-2">
|
||||||
|
<span className="text-[10px] font-black text-gray-500 uppercase tracking-widest">{t('seconds.panel.expected_profit', { defaultValue: 'Expected Profit' })}</span>
|
||||||
|
<span className="text-emerald-500 font-black">+{profit}%</span>
|
||||||
|
</div>
|
||||||
|
<div className="text-3xl font-black text-emerald-500 tracking-tighter">
|
||||||
|
+${(parseFloat(amount || '0') * profit / 100).toFixed(2)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Trade Buttons */}
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
<BaseButton
|
||||||
|
label={t('seconds.panel.call', { defaultValue: 'CALL (HIGH)' })}
|
||||||
|
color="info"
|
||||||
|
className="w-full py-6 font-black bg-emerald-500 border-none text-black rounded-2xl shadow-xl shadow-emerald-500/10 hover:scale-[1.02] active:scale-95 transition-all text-lg uppercase tracking-widest"
|
||||||
|
/>
|
||||||
|
<BaseButton
|
||||||
|
label={t('seconds.panel.put', { defaultValue: 'PUT (LOW)' })}
|
||||||
|
color="danger"
|
||||||
|
className="w-full py-6 font-black bg-rose-500 border-none text-black rounded-2xl shadow-xl shadow-rose-500/10 hover:scale-[1.02] active:scale-95 transition-all text-lg uppercase tracking-widest"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="text-[10px] text-center text-gray-600 font-black uppercase tracking-widest leading-relaxed">
|
||||||
|
{t('seconds.panel.risk_warning', { defaultValue: 'Risk Warning: Trading involves significant risk of loss and is not suitable for all investors.' })}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
SecondContractPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
|
return <LayoutAuthenticated>{page}</LayoutAuthenticated>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SecondContractPage;
|
||||||
@ -25,34 +25,56 @@ interface StyleObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const white: StyleObject = {
|
export const white: StyleObject = {
|
||||||
aside: 'bg-white dark:text-white',
|
aside: 'bg-white dark:text-white border-r border-gray-100',
|
||||||
asideScrollbars: 'aside-scrollbars-light',
|
asideScrollbars: 'aside-scrollbars-light',
|
||||||
asideBrand: '',
|
asideBrand: 'border-b border-gray-100',
|
||||||
asideMenuItem: 'text-gray-700 hover:bg-gray-100/70 dark:text-dark-500 dark:hover:text-white dark:hover:bg-dark-800',
|
asideMenuItem: 'text-gray-700 hover:bg-gray-50 dark:text-dark-500 dark:hover:text-white dark:hover:bg-dark-800',
|
||||||
asideMenuItemActive: 'font-bold text-black dark:text-white',
|
asideMenuItemActive: 'font-bold text-blue-600 dark:text-white bg-blue-50/50',
|
||||||
asideMenuDropdown: 'bg-gray-100/75',
|
asideMenuDropdown: 'bg-gray-50',
|
||||||
navBarItemLabel: 'text-blue-600',
|
navBarItemLabel: 'text-gray-600',
|
||||||
navBarItemLabelHover: 'hover:text-black',
|
navBarItemLabelHover: 'hover:text-blue-600',
|
||||||
navBarItemLabelActiveColor: 'text-black',
|
navBarItemLabelActiveColor: 'text-blue-600',
|
||||||
overlay: 'from-white via-gray-100 to-white',
|
overlay: 'from-white via-gray-100 to-white',
|
||||||
activeLinkColor: 'bg-gray-100/70',
|
activeLinkColor: 'bg-gray-50',
|
||||||
bgLayoutColor: 'bg-gray-50',
|
bgLayoutColor: 'bg-gray-50',
|
||||||
iconsColor: 'text-blue-500',
|
iconsColor: 'text-blue-600',
|
||||||
cardsColor: 'bg-white',
|
cardsColor: 'bg-white',
|
||||||
focusRingColor: 'focus:ring focus:ring-blue-600 focus:border-blue-600 focus:outline-none border-gray-300 dark:focus:ring-blue-600 dark:focus:border-blue-600',
|
focusRingColor: 'focus:ring focus:ring-blue-600 focus:border-blue-600 focus:outline-none border-gray-200',
|
||||||
corners: 'rounded',
|
corners: 'rounded-2xl',
|
||||||
cardsStyle: 'bg-white border border-pavitra-400',
|
cardsStyle: 'bg-white border border-gray-100 shadow-sm',
|
||||||
linkColor: 'text-blue-600',
|
linkColor: 'text-blue-600',
|
||||||
websiteHeder: 'border-b border-gray-200',
|
websiteHeder: 'border-b border-gray-100 bg-white/80 backdrop-blur-md',
|
||||||
borders: 'border-gray-200',
|
borders: 'border-gray-100',
|
||||||
shadow: '',
|
shadow: 'shadow-sm',
|
||||||
websiteSectionStyle: '',
|
websiteSectionStyle: 'bg-gray-50',
|
||||||
textSecondary: 'text-gray-500',
|
textSecondary: 'text-gray-400',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const basic: StyleObject = {
|
||||||
|
aside: 'bg-[#0a0a0a] border-r border-white/5',
|
||||||
|
asideScrollbars: 'aside-scrollbars-gray',
|
||||||
|
asideBrand: 'bg-[#0a0a0a] border-b border-white/5',
|
||||||
|
asideMenuItem: 'text-gray-400 hover:text-white hover:bg-white/5',
|
||||||
|
asideMenuItemActive: 'font-bold text-white bg-blue-600/10',
|
||||||
|
asideMenuDropdown: 'bg-white/5',
|
||||||
|
navBarItemLabel: 'text-gray-400',
|
||||||
|
navBarItemLabelHover: 'hover:text-white',
|
||||||
|
navBarItemLabelActiveColor: 'text-blue-500',
|
||||||
|
overlay: 'from-[#0a0a0a] via-[#111111] to-[#0a0a0a]',
|
||||||
|
activeLinkColor: 'bg-white/5',
|
||||||
|
bgLayoutColor: 'bg-[#000000]',
|
||||||
|
iconsColor: 'text-blue-500',
|
||||||
|
cardsColor: 'bg-[#0d0d0d]',
|
||||||
|
focusRingColor: 'focus:ring focus:ring-blue-600 focus:border-blue-600 focus:outline-none border-white/10',
|
||||||
|
corners: 'rounded-2xl',
|
||||||
|
cardsStyle: 'bg-[#0d0d0d] border border-white/5 shadow-2xl',
|
||||||
|
linkColor: 'text-blue-500',
|
||||||
|
websiteHeder: 'bg-black/80 backdrop-blur-md border-b border-white/5',
|
||||||
|
borders: 'border-white/5',
|
||||||
|
shadow: 'shadow-2xl',
|
||||||
|
websiteSectionStyle: 'bg-black',
|
||||||
|
textSecondary: 'text-gray-500',
|
||||||
|
}
|
||||||
|
|
||||||
export const dataGridStyles = {
|
export const dataGridStyles = {
|
||||||
'& .MuiDataGrid-cell': {
|
'& .MuiDataGrid-cell': {
|
||||||
@ -78,30 +100,4 @@ export const dataGridStyles = {
|
|||||||
'& .MuiDataGrid-root': {
|
'& .MuiDataGrid-root': {
|
||||||
border: 'none',
|
border: 'none',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const basic: StyleObject = {
|
|
||||||
aside: 'bg-gray-800',
|
|
||||||
asideScrollbars: 'aside-scrollbars-gray',
|
|
||||||
asideBrand: 'bg-gray-900 text-white',
|
|
||||||
asideMenuItem: 'text-gray-300 hover:text-white',
|
|
||||||
asideMenuItemActive: 'font-bold text-white',
|
|
||||||
asideMenuDropdown: 'bg-gray-700/50',
|
|
||||||
navBarItemLabel: 'text-black',
|
|
||||||
navBarItemLabelHover: 'hover:text-blue-500',
|
|
||||||
navBarItemLabelActiveColor: 'text-blue-600',
|
|
||||||
overlay: 'from-gray-700 via-gray-900 to-gray-700',
|
|
||||||
activeLinkColor: 'bg-gray-100/70',
|
|
||||||
bgLayoutColor: 'bg-gray-50',
|
|
||||||
iconsColor: 'text-blue-500',
|
|
||||||
cardsColor: 'bg-white',
|
|
||||||
focusRingColor: 'focus:ring focus:ring-blue-600 focus:border-blue-600 focus:outline-none dark:focus:ring-blue-600 border-gray-300 dark:focus:border-blue-600',
|
|
||||||
corners: 'rounded',
|
|
||||||
cardsStyle: 'bg-white border border-pavitra-400',
|
|
||||||
linkColor: 'text-black',
|
|
||||||
websiteHeder: '',
|
|
||||||
borders: '',
|
|
||||||
shadow: '',
|
|
||||||
websiteSectionStyle: '',
|
|
||||||
textSecondary: '',
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user