fixes and architecture
This commit is contained in:
1 parent
ce98f270d5
commit
dd43fa04c1
104 files changed
+7082
-5182
No files matched your search
@@ -11,6 +11,8 @@ node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
.next
|
||||
.claude
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
|
||||
+16
-10
@@ -1,13 +1,19 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>courses-rynext</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Quicksand:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>RyCourses</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
import "./.next/dev/types/routes.d.ts";
|
||||
import "./.next/dev/types/root-params.d.ts";
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
reactStrictMode: true,
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
Generated
+936
-763
File diff suppressed because it is too large.
Load diff
+10
-10
@@ -4,32 +4,32 @@
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tailwindcss/vite": "^4.3.3",
|
||||
"@tailwindcss/postcss": "^4.3.3",
|
||||
"@tanstack/react-query": "^5.101.4",
|
||||
"next": "^16.3.3",
|
||||
"postcss": "^8.5.26",
|
||||
"react": "^19.2.8",
|
||||
"react-dom": "^19.2.8",
|
||||
"react-icons": "^5.7.0",
|
||||
"react-router": "^7.18.2",
|
||||
"tailwindcss": "^4.3.3"
|
||||
"tailwindcss": "^4.3.3",
|
||||
"zustand": "^5.0.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@types/node": "^24.13.3",
|
||||
"@types/react": "^19.2.17",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@vitejs/plugin-react": "^6.0.4",
|
||||
"eslint": "^10.8.0",
|
||||
"eslint-plugin-react-hooks": "^7.1.1",
|
||||
"eslint-plugin-react-refresh": "^0.5.3",
|
||||
"globals": "^17.7.0",
|
||||
"typescript": "~6.0.2",
|
||||
"typescript-eslint": "^8.65.0",
|
||||
"vite": "^8.2.0"
|
||||
"typescript-eslint": "^8.65.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
const config = {
|
||||
plugins: {
|
||||
"@tailwindcss/postcss": {},
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -0,0 +1,568 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 1000 1000" style="enable-background:new 0 0 1000 1000;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{display:none;}
|
||||
.st1{display:none;stroke:#000000;stroke-miterlimit:10;}
|
||||
.st2{fill:#00B5FF;}
|
||||
.st3{fill:#008EFF;}
|
||||
.st4{fill:#0063EE;}
|
||||
.st5{display:inline;}
|
||||
.st6{display:inline;fill:#00B5FF;}
|
||||
.st7{display:inline;fill:#008EFF;}
|
||||
.st8{display:inline;fill:#0063EE;}
|
||||
.st9{fill:#FFFFFF;}
|
||||
.st10{display:inline;fill:#FFFFFF;}
|
||||
</style>
|
||||
<g id="Capa_2">
|
||||
<polygon class="st0" points="374.5,793.5 451.5,839.5 451.5,497.5 374.5,448.23 "/>
|
||||
<polygon class="st1" points="221.5,701.5 294.5,746.5 294.5,366.5 221.5,320.23 "/>
|
||||
<path class="st0" d="M294.5,366.5l242-148l102,68l-264,161.73l77,49.27l223-133c0,0,48-35,43-82c0,0-2-48-33-62l-150-90
|
||||
l-313,189.73L294.5,366.5z"/>
|
||||
<polygon class="st2" points="464,846.5 464,728.4 535,790.5 553,777.5 631,830.5 539,890.5 "/>
|
||||
<polyline class="st3" points="848,702 541,506 464,558 778,747 "/>
|
||||
<polygon class="st4" points="464,558 464,503.53 500,478 541,506 "/>
|
||||
<polygon class="st3" points="778,278 778,747 848,702 848,321.43 "/>
|
||||
<path d="M374.5,793.5l77,46l0-342l223-133c0,0,87.22-69.98,15.86-140.74L534.5,130.5l-313,189.73l0,381.27l73,45l0-380l242-148
|
||||
l102,68l-264,161.73L374.5,793.5z"/>
|
||||
</g>
|
||||
<g id="Capa_1" class="st0">
|
||||
|
||||
<image style="overflow:visible;" width="242" height="207" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPIAAADPCAYAAADPjIzXAAAAAXNSR0IArs4c6QAAAARnQU1BAACx
|
||||
jwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAHPqSURBVHhe3b153BxF8T/+7t3nyB0SQhLCDQEU
|
||||
BQRRRBQVREAU5PTAWxQBFUWEjyKKIIoKgoCKciigghdyC0K4kRsSAoRwQwKBhITceZ7k2e3fH93V
|
||||
U11TPTO7z4Hf3/t5zbPT1dXVVdVd08fMzhqMmmrRFowkMBTlFYHKpVTidOPTZWUq6KKyJORRlWo2
|
||||
EVMMakWerPGDlTG+bgNYzusVMjZ2R8jTkNCDkCoG6JmRuSWygYo8AwTj/wWf8Tbi6UEE949ptT6v
|
||||
p5U+441tURO5LcCKgyArLALjtZosDknnDcPyWqmeoFZr4oPzRLxCb5Nj8BCKGZsdKj9B2CmD2LDO
|
||||
EaqoKlNByOJ2saNQtOZ8TiN/FqEKTwuwEEHMZScNEfBG8zbTjhQs/SvgKQNv29Demcz2A3lAfM0M
|
||||
MyJYAjhPop0pLel5QjFk3aSPRVoW18fIvpJQuKjRVVDDCXKUjpStAMFHRY2Sp0LaFCcduGNUhjcA
|
||||
ZGQVG+H4qqqfbNdE+7UMJl+YUMuUTCmhICjkjewXfA+i6lV5PjNSsUBfVUZFUIdWxSstSry8zwLu
|
||||
wiRhihq7KgoVbA+kP3UObos0wyRsy6mj8FTCANsWQTOSI1FvgpxHgcyQVVlYLK+kWDYiG5Rzgzoi
|
||||
Kecr0vRPwk8Pw1HgV1UfXyDqTL6BLB1EG0iQolKnVD1iulXVv2Wg6lLVtgpSiT5z8lMXa2aL6m/p
|
||||
rwGwvTK0duIwSj7Xkx2+a5WD+l0CYekIxTcaWKVhfZxWhE2tK2vMHFGkiAZtQybLCvl0LpGqVqbV
|
||||
CvqLEv/kdPAIxVIMVcHKF6iRoUp9gscivwZnHzl6ADVMCpSnNV4rUHTLwdODz1N8HAoPtZuSlUdR
|
||||
EFdpLFGYBiPrz1NgXVJZIwcJ+SOMMPSpVZKyiNFVFi5bZYirTbAMHgr0KgzyonKtolU5ZXWndJag
|
||||
ti+SV1VWm6DZW0xk+rDzSqowJt58silLZZUxlOkkjUoy6vDFRSBLoQIhW1orIeVYx+8/AMVxMp2E
|
||||
nz4X8gwUeCf5X0FKF+mQUkc6RCxStpdhZJbkK0Or/BIUDCl7pHziF+kICi0lvgwWFfxdIc9KtYrK
|
||||
cF4jA1krSDTjHVk0jeDgWnkZTFT0SZDpIlRyXjsgvUn3SsYOwCZWK8i1uJImJPzDp28auBtsq372
|
||||
QmkUN/31D9VdRQZvN7bGjWYUfN1fRSZYX2NHtB+jOJPfJoqWLAxBBhQfKzIT5DiQTfjHYB2N6ypZ
|
||||
QDSlUpWmgYQrWv7P4v8VPSW0BkRGT5qVKqcgdGKfTsqsCBpEXKIFXVjFVIyL0RDpWlaXkQU8uP00
|
||||
CBKBHXRatBaO5Es+l84C2XJhojJQnjw4UrubVVDgiAhF9Q8mxI3/cCA7f0NRtX4+iqRQ5lfK14TY
|
||||
jE67tBpbv1EkVNOf9FKOcIHwvkGRCH5nhPdBpYBCShBZ3JFdXEcNTGcPZbMrURkXGuqIhQEYxE6d
|
||||
0muw0Z8L1GCjqLElBtMITQ92wZBZ4CNUCrlCChIyqhQFqe2Zk7q2AhmQrYLK5wO1DLXQBqWOZcjV
|
||||
UaVskXGp8rwMKarRNMg8Xl6T5SF9yK/YST2HEIbrVBWeVzE3hsIQ3NRKfQReRpSXMnO70lp9LdAK
|
||||
p6oexKKYnYOBMvuic/ZpkNUfjdyaPil6FYViuCe7ULVsovMDmeOsNziAKuAGpQzQ4OuMivKpLdUn
|
||||
D1a+cJbAykg+rmJg4w3UD/CHYirBj2LR1LiKHz1/shrNZ6Kdkhd54WcVmo5KncSWEgMUZEr5HtKs
|
||||
gUIU0KICrb5AE35NoihPhxuRAaUhBSRJaQsAyhNXRSjIz4mRFTHYAlFED/6TFwV2pGCRYCgr2F8o
|
||||
srk9haiiW4FPUZ7toHUCglY/p/Gynh7sMzD+aB8VypayFNhXWpah9MJfll+MbETm4DTeH5J9wxNp
|
||||
tChVWoNoSDqPRBU4NJGVNQRvkILGkXQLVkE7diVgaMcyJVNxujShCpJ8CUE5fWjWkOBPQspJgIvj
|
||||
ddsmrLWwrcxYUggiuB0M5OJcf+OgsvwogxSaFK7I47yynGwP+hpjqMtn2gpG8TzV2cnC5UgWTWbk
|
||||
we1JIpXP/BCflDmmRQyUnARS5uVAjKzDWU6vIkvaUlSA5UXFZBmZTsEW80rVcigoW4ZC2cLOlqqx
|
||||
Sl+Tdmbn+fvIuQV9GYrWUAMBMkbWwTcUlGy0utusOClXreQhWksV9QPcF7xOrX6pJ0GzgcGGf5nc
|
||||
qDpZl9SlCoifPn19qloqsQRkY9WypkV+Dl8uuEHKYLa16qYWdFJuP1VAJJ92G3kHq6qxoiSRLNhU
|
||||
nc5F8BIMC2aDFi9ERc4KlSt0AnWCFtDSJhehaNNKISqkGI6B1qFuLSoqMORb3p6lghk0hXlankte
|
||||
tNCXCIrMsDRI9U2t3irw5UIAk5zEYGgh6vdlcpu1EmX0dt8QEhRvFbyMKM99nBOtOZ/B8tE3V1hB
|
||||
qkETMIgbpsXiMaroh4zPwlUmXReJEcpQmVxdcdoYF8j1eo0FtOfju7Jap6zsBI0nZwCDlsd0ydGl
|
||||
Y/i5r9ugzX2bNsDdEs69XkEFrktqRlkC2rT1qKN7/Ik8vzqEMklI5ydAnSXqH9wjKbQTxNp5CSLR
|
||||
RtSbgKYOX4aULUlC55N8UjBP8zLUSeKeUqu5wO3oqOOtW22Bjx+wN0aNGoGX5r2KRqMhRmgCyaCj
|
||||
VZSVSfVmSZPpFIQfJHmwEV04ZP2y/TSUKRrbVUf32ifGDSQM12CR4OG0CsoSu7ziR/K1enyjR1el
|
||||
CvWpsjSaQEq0LEp90Rb4MZD8iYmIApwueVJKIeOl6buvw9QMaqYGay1GjRyOvffcFd875nAcsO8e
|
||||
2HGHbTF61EjMeekVLFm6TLntI9MEbqvGQzQtT0MRX1EelHzFf5JlMJCLD3Ye1c99pilmCi5wBCfD
|
||||
YPTmrEdQQQ6Z9sgpy9OijFFoQKI+Tbash3dQli6FlEMoKy+fOvKQnT2ntycWtgXL4Be0qD7i8Z+0
|
||||
5jM8zxewXK+s7lqt7j4NsNWbNsfnD9kfH/vo7pgyeSI6OupoNi2WLluOO+9+AOf94a+47c57sWLl
|
||||
Ki+HLemDXsIgasqILJ2mOUHyIMHXAuT+QzQLQqLOgQJfBik+AvL1h2RCL/JtSOTBApkxVFlzWgih
|
||||
/FyKTMgoQm5NE1mTkSStEFImoURGtFnCUBrI7AJA5Chb6sP8TuV4A0Zlraif15UJqdXcNoi1wNix
|
||||
o/CFTx+AQz93MDbacD0M6+4K0+xm09237etrYN6r83H1dTfjgov/jsefeAqNhhPcbDZDVTlfcBS2
|
||||
uyxXhUfCJMoRmecJvxmkyw4I+hPIuYRDYZ9xMBi9hSsZyvuTIoNTikqkFC+DRblsQtCzoHEDymQq
|
||||
gQekAxkQDzFA5wGEXwWZj6CcTvoYsILezqJyfhOLAhQA3r3j9vjuMV/Fbu/bCV1dnQBbK4ei/gEM
|
||||
CujnXpiL8y/6Gy7+y7/w2sLFjtcCzWaD6UMVBjGMXoLCJssJbANKBQZ52oDC6x1V4WnBJNnOakJ3
|
||||
gYWa4QI5NxWpYGhgyQsFJLmCPAnNEblzz6SqUFSnWiCD9f+oHxjkp2tAhRGZIP0bJ7OKBKyclhvB
|
||||
Sxm8bDYKN5tNrDdlEo75+pfxhU8fgNFjRgJNd9OReDRYa9FsNmGMQaPRxG133ofTz74At9x+L3p7
|
||||
elGruXV29tSV36+gc82WFFpizzmuAEo/IVTp3xG8LNtqWTY6a6onRbGMAjM4DEZtkQ1DBsXcEhGr
|
||||
0DRKtiAzgrjtEqB04Jyj1IIeOeYYNvxjVUl51mUmR2SfX6gjQfARcnWaNC8sjKkFdYYN68YnD/wI
|
||||
/u/bX8Vmm2wYgi7bkS4HBWutVsOKlatw9XXTcMY5f8BDMx5Do+ECvdloeh+QvdD1S6lt4DIsnByN
|
||||
J6Ca3hk4P2+HwkpiWDbboGKtlNd0lsVTvoG4M8J5RBmDUXyzq0UlI1aq0X8m5VimBddGs4bxqXVx
|
||||
iLKBRcrkILla3T5tEO8ZSD1UugaFQVYr0yryTDw4u7u7sNM7t8M3Dv8c9vzgLuju7kJfXx9qtVrh
|
||||
KJwCBTPVsej1JTj73Ivx69//GYuXLEWj4abubrqdAm8vrjtf5/t2Dvns/qr0eX8QFS9xdmI96vSW
|
||||
tDKwAkEu62NkbFKl4n2rLJCDsBYRilAt/rNdeWoZzcCUJ2VHYckIPsPSea4CRuNyeDnv/ErQ5CeQ
|
||||
UkcBBVh3dzc2XH9dfPoTH8PnDzkAU9ZdJ/C0E8ASNN0mWffcPx2nn+Wm20uXLUOzma2v8xAdN5yX
|
||||
TMlD23DINEEpn6IHERp/ql4OX66IJQJjDFUKvfi9flUtqlPzlxEjsuFrMl6AIXmVStHhZMnshHjd
|
||||
Cq90zsG5wsI5Ih2t6bSLAxL1e1B1gYXzarpAkScaUEWuohwogDs7O7DOhLWx+wd2xqGfPxjbb/sW
|
||||
dHV1wlqLer0ui/UbtLtdq9WwclUP/nnF9fjdhZfhsVlPYfnylWj6by3FSNhjfJtw11nporL2JlAh
|
||||
Xjh1nkAFljz8rEJWwz8Za8YUEeIAtYIn8pPnC3W4WWE+kFOIhGsoyiuDDHTpFUGOmGW93AY5JWZy
|
||||
1UDOERJI8JV2BD8Vj3RheSpiPppC1+t1jFtrLN7x9m1wyMH7YM/dd8H4cWPDJlXVdXA74Jth1lo8
|
||||
+/wcXPTnf+Gq66bhuefnYOWqnvxmmCvJpGhpjZUIVeypwmO1Skog7egPuAxWvxrIRPOfUVEKcEdk
|
||||
U+sSo4Jw/jmABubqJ4dLOpixLA3ovDTLiGj+X0RnzpL8OSgMxmQjflBHE5aiKTC+wUDPQAPG1DBq
|
||||
5HBsucVm+NhHPoiD9/8wNtlo/TDlHcwAluDT7Z6eXtz34CO45NIrcOMtd2HeK/PR1+fWzd6EePNO
|
||||
pnOQdsh0ClX5ysB14+eafK3/aXwS3B/eSTmXyEFOwML1j2xELiuQykzRW0Wq/pxlGXI6aQ5l6Yhd
|
||||
TIdkw+VkEyzjZTwhkH2DtIQEvycbGAAWXV2d2GiD9bHr+96Fg/f/MN65w7YYPqzb8QzyKFwEPvou
|
||||
eG0Rrrz2JvzjiusxY+YTWLhocbiXbclv5KJcIPP202zRaBJVeKpA9IeAqvKr8PF+yXwDNnOjAU5z
|
||||
CfECZVNr7QrBISUPAHI6yDSDRWKKDK+bzEhdLCSUQA59TMokGMD6J59CUZPQg5BQxpMpMGumhvHj
|
||||
xuJd73gbDt7/w9h9150xcZ21Q/AMxGZWf8GDubd3NWbNfgZX//tm/Ps/t+GxWU+Fxz0t+cilmI/I
|
||||
TeQr7SETpH2WA/mdfxZB46HpK0sDrIFEMV5dThaH0h9y/V4gkk3I6kiPyLIjq6jC0ypksJUYiCLH
|
||||
Scul7BREo1r/j0+fg2zZkpRXFXleY2gDw2D4sG68bZutsOfuu2CfD++Krd60OWq1bPR9o0bhFHhA
|
||||
L1+xEvfcNx1/u/xa3HL7PZj78itYs2YNALe2znznbSA/A87XpZDtq5UhHslLeQTR5hFkOdJRkAER
|
||||
N5JB1sHklgUyOLuUi1Qgy1FOcwIhL7R/kMGWqleg8MLjb3PQBkElsHotso7FA5mPLpU6nkS+jDHu
|
||||
W0oAsNH6U/DhPd6PA/bdAztsvzVGjRzxPzUKF4Gm081mE3NeegU33XIXrr5uGu6+7yEsen1JcGNw
|
||||
pw3/fHv50yxT9Vcekqeo70rIsgRZPtWPtLjhkHI4UjIZQvE8ow9kP9pwhxUUKlZI4y+DkKc2YhGk
|
||||
A1MgZ9msTBV1U9NpTjd8JzzBHyAq9aMvYfTokXjvTjvgwP32wod2fQ8mT5rwPzsCF0Gbbl913U24
|
||||
4pqbMGv201izpi/4wlralPSNkuwDmv28IbX8/oDL83rI0dP69s81u9Qlx8Cg5FFxMq9gsDIYNdVJ
|
||||
iHh8IifbaMSKSJVN0IM+Sl6AYhhvUxv+ZZBFOL8GC7gHlJFnjAI5d5JA3Do8MOv1Gt62zVbYf58P
|
||||
Yc8P7oKt3jQ13BN+Izez+gse0AsXLcbd9z2MK6+5CTfdehdeevlVWMt4wqwpakQG6i/cFzI90H6i
|
||||
Okm00MmGjAIwGSqK8lAq3wWyysOIhXVwAzkEIZKRaiReTMmLkKtQoKw8nIzApkxtbPjnP2v+09Os
|
||||
poZxR2g3TQ+XaUw2PV5vyiR8dK9dcdB+e+Ht270Vo0aO+H9yFC4CBXNfXwMvzn0Z99z3MP5xxfX4
|
||||
z813YtWqnnBhdLvb3Gb5bS+G4Geep/AVgrVRVJQl6FZiIFEZbSQuQ1CagckrBS/r+0hpIMv6clAC
|
||||
IIeKxhpUqdBDq7RqWQKzMXqqDenptArW0QzfENOQvRvLWmDkiOHY9X074RMHfgQ777Q9pkyeGJ7I
|
||||
GqoAphF/qECj7+rVa/DMcy/iqutuwiWXXoEnZj8d8WT7Ep4Y2oada1Na69tBAy8bERPy88yinylx
|
||||
YigtFeeypNJVIcs5mf0IZG+tXC+oKJNFLEUMEprSrZRHXi9ui1z/WusYrU+7jKxw6HicxtV0dBcw
|
||||
LpC3fsuW+NTB+2Cv3XfB1E03QldXZ5hCD1Vg0ShJGMp66QKyeMlSPPLoE/jTZVfgn1f8G4uXLPV+
|
||||
c1+XVJs18iuD9bSW7CgajLSM0GFYkrU7O41BstTMCuB9kvUva2AweqoYiojLI1lnkfEaNIdzcrKi
|
||||
AnCZ7ZQncM8nOg6Q5fGRIhT1CaO1YvZkFgBMmrgO9t17N3ziwI9g263fhNGjRoVbSkMdSNZarFm9
|
||||
BvWODtTr2dNhQ6UHADQaDVgLzHtlPm67816cde4f8cBDj8L4R0BtU/qT3QqMLqr0WdQvNLuUvkwb
|
||||
S8bnR32Eg2YErdYJhS8Fz6eKcfX7t2hKDp/m9ZB/jDdaFmkVAyFjUFCkFF/7soY2LM1YARcUdKuo
|
||||
u7sb73/PjvjuMV/FZz65H960xaYYPqwb9br7iuFQBE/YVPJr1Qcenolzfncx5sydh4032gAjRgwP
|
||||
+UOhD4KPDEaNGoEtpm6CHbbfBqt6ejH7qWf9955zBeJPsDaJAps6MAUigZ/LYAoN6qEEeQTJz2la
|
||||
HkdBHo+zQjEuI/E6XFYqEiKNHkQkFR8CmCLP+TxDfMr9XF8028yy2GSj9fG1r3wWx3zjULzrHW/D
|
||||
mDGjUK/XhyyAwb65ZIzBq/Nfw5//ehVO/eXvcMNNd+Ce+6fjuefnYuI6a2PSxAnhLSBDoRvNAIx/
|
||||
Re+kietgh+23xsgRIzBj5iz09q52ekTLHW2gDI7P+MAnnFVtYXy8HhW+P0hSJWiCfQBrWSmYMLXW
|
||||
oElJsA4kqBGs/2e8VUNQdQSqPwnyD+mX8Ro/TbbWYuyY0dh7zw/gsC9+Atu/7a3o7uoMwTsUQQK4
|
||||
UbjRaKBer6Onpxf/vvE2/Pb8v+C+B2ZgxcqVQfXOzg5suP56+ORBH8EXP3MgNtxgSviW01A+gNK0
|
||||
Fn2NJhYtXIQLLv47Tj39t+4RT3+bCvD9JApk3lZiiaNOwSUSkcPEaNl5Ikvn9ILgl3meJkVWQGJE
|
||||
JnArhgA8iA18gwieCIYdAwEur4pMxuOL1PyvNtRqBtttuxV+fMK3cNQRn8dmm26Izs4O1GtDPwrD
|
||||
Pwk296VXcNLPzsHJp56D2U89h9Wr10Rr5WbT4vXFS3H3fQ/h/gcfwVprjcGGG6yHzs6OIRudAT9C
|
||||
Axg+fDg222RDrFzVgwcenpm9xdNxsXMJ1m9C4Id/BWVTdJ+lxnqOkCHZjVJ6tBfEKA/kIYScDpHj
|
||||
k7vi0mKZbgfa017J1mBwo2u9Xoe1FpMnrYOvH/ZZnHrSd/CenXZAV2cnDFz+UAVDs9kMo/Cy5Stw
|
||||
4cX/wJFH/xDX/edW9K5e7Xno9TzMRuPKvjDnZVx7/S2Y+9I8bD51E6w9fq0QSENhA81YRo0agfXW
|
||||
nYxnnnsRzz43h9XNL/KJPmKQrTUrtSOhhE/NVokOxs8Icv2KnbainoL/oUD2/8IUlRsurVQspgC0
|
||||
3nGAzpdEK7wxaIQdPqwbH9x1Z/z2zJPw2U/uh7FjRsP6t2kM1dTUjazZGzDvvu9hfOXrx+M35/0J
|
||||
CxctDnpY2/S+qsWmW38dNcCavj48NP0x3HbnvVhnwnhsvNH66OzsGJKXFwBZMI8fNxadnR144KGZ
|
||||
WLp0udt7kFUbttEVdReekIXKIPsck98WhH6RnomLUUX8jwWypBl/c18ayZgpgHlb8al5ZSgPFgCF
|
||||
DqYAdt8TXg/HfvMrOOWHR2PTjTdAo+FGu6GcRtMUmZ6cOvvci3HsCT/DY7OecnrUatn0lMzyX9CI
|
||||
QK/rMU7/BQtfx/X/uR2vzn8NG20wBWuNHYOODjf7GArbarUa1ho7Bi+/Mh/TH5mFJr/YkyE8iMOJ
|
||||
YZuRJXqq2RpRjqxVwQq1Vb4Y7refLBmdOCpXzMu1AV4sJyKRmePjNC0zgeiBdB68eZuM3/xxO6wT
|
||||
8KkDP4rTf/I97PuRD6KrM3tf1lAFMQ/gBa8twlXXTcN3f3ga/vav67B8xUrUfAA7q4Q+cknDadbA
|
||||
+odY1qzpwwMPP4obbrodgMEG66+LESOGh5KDZSfJHTNmFJrWYvrMWVi4yL8sH2xNaeifPDQ6Q1Re
|
||||
IMiUtFZQUGdAerCoijq61q4wIlcJ5lKG1mD6a18r+vhRn3fGMKrHoCCesPY47LzT2/HjE47GkV/5
|
||||
DCZPmhDt8A5Wx+agHdxGo4klS5fjv/c+hF+ceR7O/u0lePb5OSHfNvk6kYLVCwl68lHO06mMhXtn
|
||||
dg14ffESTLv1v5gxcxYmrjMeE9Yej2HDuoO9g2l3Z2cnZj/1LB6b9TTb+KJbgTVuFCtVoo+J3ZLL
|
||||
432jRJQOUSgnQ6u4ddTRNSHxa4x0EIoM0TI0WgVUKlbGRPkKX+S3hBP5LQ4PYwxq9RrGjBqFrd+6
|
||||
JQ7/8iE4/jtHYNut3xR14sHsyIQQoNZixcpVeHzWU7jk0itw2lnn446778eqnl4Yf/srPFqq6aXR
|
||||
ku3vbgHSS/Cfe2Eubrn9XqxYsQoT1h6H0aNHoqMje2vnQPvBGIMRw4fhpZdfwYMzHsWqnp7wCiTH
|
||||
QPZQQHP9pS0Ev65OdINMJiVaRVGZVKXtoeKI7JHUK5WRcmABWmTX0Wq9gtdkGycUnN3DurDJRhvg
|
||||
gH33wHHf+gr22Ws3jBkzKmxmDWUQW2vRaDQx75X5uOb6m3H62Rfg75dfh1deXRCuQeGVOqEf0glN
|
||||
NaTN8oT4JLIOuHzFStz7wHRMf2QWuro6MWHt8Rg5YgRqft09UP4gOR0dHVi8eCnuf+gRzJ+/UAQD
|
||||
2VSlzqp8cHz9sqM/ZaujQiBzRaSzShxCWbQGN7J8P2F9ZzNQdCMwemh3OmH8nIQsgOv1GqZMmYhd
|
||||
37cTvvaVz+BLnzsIm22yYXg2+o2YRi94bRHuuW86LvrL5fjdhZdhxsxZWNPXx/h4B0/dmwyNo/iM
|
||||
0blLxaajMQbNpsVLL7+Kex+YjvkLFqG7uwtrjR2DEcOHRXwDAWstenp7cd+DM/DUM8+znEhJRvd9
|
||||
JCJxPtb2SRVZhinjTYEXiH0Y0LLMGCKQW5EmeaWDfJqzRUVk+RYR/EH1+E9LNKFDzn+ifs/OR9a1
|
||||
xo7Bjjtsi88fsj8OP/RT2Omd22H48GFDGsBgQbxqVQ9mzJyFSy67Ar89/8/4z813YuGixZ6nqfSy
|
||||
nNFp5GwRIxF9zVODMVixchVmPj4b02c+gZUrVmHcuDFYa+wY1Ov+G0zof0Abf0vtvgdmYObjs93a
|
||||
33LdWZtzhGxvk+FEwZODkqGQ3mhUDGTuII0nlUfl/BFGZJnfKmSPKtJPpllx0bEMXCN3d3fhTVts
|
||||
ioP3/zCO/PIh2HvPD2DiOmsP6TeUaAoN/+WGF+a8jOv+cyt+d+Gl+Nvl1+HFufP8e6MzPgfp3zJQ
|
||||
24A7xn+w8tLlAY7Hjc5NzF+wEDNmzsKLc+ehq6sLE9YehxH+4jcQfms0G7jvwRmYPmMW+hrZq4Li
|
||||
T3/OzTEGGD0e2GQboG810LvSsxnH6D8y+ETUZ5n+4VT47A1Chak1v8pLZ3EjK6JF9jyUkSGoKPXk
|
||||
8IX4VMuXoU5Wq9cwZd2J+PAe78eXP/9xfPLAj2CLzTdBZ2fHkI7CFMTWWixbvgLTbrsbv7/wMlxy
|
||||
6RV44OGZ6Ol1T2Zlr5Ztsy0IckSjkUtCXjCMYZtLAGBhYNDT24tnnnsRjz7+FBYvXorRo0dh/Lix
|
||||
6OhwfuwPrLV44OFHcf+DM9G7eg2Tp9nPgswYYMpUYL8jgIkbAiuWuqPh3uqpI+GHKMgR2Z+WNbgo
|
||||
CGR5lR8gJXMiZFS2AKliJJw7lz7Y7i1No+EaZNTIEdhpx+1w6OcOwhc+c6D7htLoUSHI+9sBq4Kv
|
||||
hR+b9RQuuewKnHvBX3DzbXfjtYWvh3z3KTuShqI8Ao3IWVIvZ+JD84l3N43Ojz7+JJ5+5gWMGjkC
|
||||
668/Gd1dXW35kspYa3HfgzNwz/3T0dPTyznYOYdv81oNWGd9YNdPAVtuDzNpY6DeDSxdCPQsUy5e
|
||||
3idW+CaCnKabNuOE+1WD0Ct3JAM5FVypilKgKxcV9eVbFVME2oDJXR0ZrM83meHG1AALdHTUsdWb
|
||||
puLznz4AX/7Cx/Gh3d6LKZMnhtF3qEdhYwxeePEl/OVvV+G3F/wFV1xzI559bk74+dJ4Gk3nbBSN
|
||||
2lx2tCIwHmlv2KyEuL0TGFhbk3/dxa+npxfPvzgXa40Zg3dsvzXGjh3dL3/29TVw1z0P4u57H0ZP
|
||||
byqQxXnN+2b8FODtewBjxsOsPQVmoy2BcZOB3h5g2UI3OtPjn3Rf2tAmKfO7VD+kyS+tBLPkS6W9
|
||||
bC00TTKQU5CVtACysR+NCIhptaFO5hORaJbgp2x0HTNmFD72kQ/hW1/7Avb76Iew5eaboLu7CxjC
|
||||
Ryt5APf2rsa0W+/Gz874HS775zV45NHZWLGCfqFBa0EUt4nh/a+AL8DzyJFJPZfJrCwvPmniBOyz
|
||||
9244eP8PY8stNg2vM2oVZH/v6tW49Y57cc/907F69RrK9Z8JuXSRG78u8M69gGEjgSZgRo4B1t8U
|
||||
2HBLYORYYPECYNUy35e8rOgugA9QrRoT/qX1yCHFJ3xuOU0pYwCDUVskeohGVoQUQsqgxpZ5kk+D
|
||||
zd5kKBGmmUw+78DGBXCg1IB3bLc1Pv2JfbHHbu/FRhush45O982kdjpZO5CBOfOx2fjTZVfihml3
|
||||
YPZTz2L16tXBHmttNvOIXkGju8OBfOJhyE8S3FnEzzsy8Xk6yeBu8vsO5D9rm36psj0O3v/DeN97
|
||||
3okN1ls3XCTbAfnr9cVL8KOfno3z/vhX9KzqhQ2vKpbPVNNFn5ZTBthsO+CIM4AJ6wJhaWyBWhNY
|
||||
+Trw5EPAHVcBs+4GVi33o3Ez/iEC358CLF+utdp3OD9vB2TnNLVX32lNbWGLAnmQEe5JllSfy7Zx
|
||||
x+OODLzkFApiN7pa28R6UybhM5/4GA7Ydw9sPnVjjBo5wpV4g4L4lVcX4Mprb8Jl/7gGM2bOwtJl
|
||||
y8U6mCO2K++bMv257zT4Dg/fKXOdx5dl7ieQ6zo7O7Dl5pvg4wfsjb33+AA223TDsGtd68c3wGjm
|
||||
MmfuPBx/0i9x2T+uDj+unoH3A0GvAdh4G+BrZ7q18mqy0V8kaxZorgEWvQw8chdw17+AF2YBjT4n
|
||||
iwYLze/G/2PNwzKUAgTpX4LQ3frPHDyfsTAYvbn/kTyNcRARPVxQYGiuUeK3cQDUi2i0yOwwJrt6
|
||||
dnV1Yo/d3ouvf/WzeNs2b8a4tcYAQziFBuuMxhisWtWDW++4F3/40z9wz/3T8er819BoNNl3hFOQ
|
||||
ulJa0iUs8yP5SXQY+JE4F8AEtrTxLJnvLCasPQ777/MhHPLxffCWN2+OMaNHD9gtO3q2+v6HZuJ7
|
||||
J56GW26/B/A+dSeKSSA9jetvG28NHHEmMGlDNyJznxnvH2OBxipg3gvAHVcAd14OrFySBb10mQUb
|
||||
kRH7LjJZKlaEIJgV0/znM0MgQygw6OBP26QMZPqEwE3xIuI3xsDAfXPHWos3b7kZjvjyIdh37w9i
|
||||
8qR1UKtlo0N/O1gVWPHD4E88+Sz+dNmVuOKaG/H8i3OjN3XkQY1Faa1xq9ig+JCKWZbwm4AKQ87/
|
||||
tVoN1jbR2dmBd759W3ztsE9j1/ftFB4EGYgAJpB//nb5dfjBj8/E08++4Fwh37BJyahaFshH/gqY
|
||||
uEEcyJzXWjeVrlmgsRx4/EHguguA2ff7fmsAXqeRAoS9Iam1bRm0tubI2oX9iJvG2B+oHvXOEGkV
|
||||
jMmC/WyLBhPxu75jMWrESHzq4x/FYV/8JLbYfJPwW8L9meK1Cj4KL122HFddOw2/u/BSPPLobKxc
|
||||
1eO+XmiZT4I7pF9S/qS07EyyPDIaZ43YYj/GyBjJf7RU+cKnD8BnP7UfNlhv3XDPfaACGN6HALDo
|
||||
9cX4xZnn45zfXYyVq3rdL1LIi5/qJj8z23gb4MgzSgKZTvyTch1NYNFrwK1/A266BFj+uruVJaf0
|
||||
kcuZwEgPrpwtaCdkBTWZAVnZ/q+Rk7ooRE0XjS+ACbf0j6bLtPlFrNk0vKurE+98+zY48iufxod2
|
||||
ey/G+i83DOWrduCng8bfgrn/oZm48JK/44ab7sCCBYtcgINNDVWwPOPtVTudEc5VmRJpMP6Ub1wZ
|
||||
F6DOro6OOg7aby98++tfwlvevDm6ujoZT0pOe6CL4YyZT+D7J/8S1994e+Y7a5lNvg8Yf87pxgIb
|
||||
beOm1pN9IGt6cvdYALYB1AHUG6g9/zSaV50DPHgDY4KX72WFKhXZKhLtEZELZBkA1mrvcq0IA9+5
|
||||
yJHyYHz8M4JmBIeQY3hHFgI9qzEGnz/kAFx83mk4aL+9MGb0SBhjBuSpoqqgUbZWq+HFOS/jxJ+c
|
||||
hY9/7hu45NIrMX/BIjSbTcfTbPqpHB2KSww1lve3BgOtB7I0BwnkRxHcbMJ9o8ktEXbYfmv86fzT
|
||||
8Yff/gzbv+0t4euLg73fMPupZzHriad9APMcYYe6TDR+lC2a2TGQ/FodaBqgtwZs/CbUjvwFsNtn
|
||||
XB777a6sA1J7Cf9Tk2iHlh8gbdHbtf1AJkVlX6Bzg3gDIacpV0iMNFVgZd2unmHDurDl5ptgwtrj
|
||||
ACC8qWMoYK1FX18frLVYumw5rrjmRnzh8OPwy3MuwKvzX3M8YadVMTjlS0pYseEi+SGXLWBMOWYl
|
||||
n5C1EY3CADBh7XH48Q++hZuuvggfP2Dv8HbNjo6OQfOx9fsLc196BdNu/S+ef/ElP0L7i6CGYIq0
|
||||
169/BUk95zRTA+odsD1NNOvDgLfu7NujmdVhwz8H49NRV9d8nag3QMtkNH9a4v2UlUqHkWlVgQRM
|
||||
+KfISYD4IrVcIK1YuRJr1tDDAoMPCuBGo4E1a/ow/ZFZOOrYk/H5w47FrXfci6b/lk6z4dfDstEl
|
||||
oo7IHUJl5IjEkKIH8M4knZ0VdqNwDbWa+0L/3nu8H//88zk47luHYfSokejr6ws8g41m0+L+hx7B
|
||||
rXfcC3jdqkE6o2hHXkBxuzU1P6gbN1LnGNg5IXd3hg5Ke0RqSb1lOaJlnwWtwBnFecoXRE/lR2Dr
|
||||
ikDSOpcG1hGDDKcbXcGHCrR+azSaeHX+Qlxy2RX47FeOwUV/vhxLly4DLB+FPcpMVNo5LpQ6l5Ad
|
||||
QgNvXzf6ugCuYeSIYdhhu7fi9J9+Fxf9/ud4z047hNtJQ7FUcX5t4Mmnn8Nf/3mt26nOfdtLIKgk
|
||||
eawbWfmFR+viHNw8C/9wCBJhIwREyYKYIeTqzxE8pFIOmkYZA4uX6ChFSgkGkmP4vWK+C6kEOtE5
|
||||
LNHcYa27ghe19UCAOlmz2cTCRYtx7Q234PBvnoCjjj0Zs2Y/ne3sho7n9Tb+n+EHqe+XIdLPtMlF
|
||||
/PDn8L4Ltmr+0hxBhXie9eINhg3rxuabbohvHvl5XHzeL/DFzxwUXu07GJtZGuiC/Pripbjqumm4
|
||||
8ea7YOj1RTndJSjP81V9+EiDZmr03W92BF4RuMbTSBfezjGTgGYnrzeDHsh5PiY0bnz9KECQzfgN
|
||||
/PrPd1DjdxmtZcGcU0iB8Rfd+HnfgQSNwJbel/XE0zj5Z+fgS0d+F9dcfwt6e92jlW4aDd/ArJGp
|
||||
HTKJir+9D1XTvbzI1VyA5n+NRnC/qmsM0NlZx7qTJ2C/j34QZ5/2Qxx39GGYuulGIcAHezOLQP7t
|
||||
6V2NG266Axde/HcsWbpMslWHNF+mUyjqznRBIdfn3M/6N6W1zcqo7aVvWVkVGX8cyAaVChVbWIKo
|
||||
KLMiMohlh2C2RBBMHqH84I0YtNvc27saTz/7Ai768+U49Mjv4ZzfXYLFi5c6JuM6YtIeScsTPDQ7
|
||||
Ga9BNipHkGmOuN3cb1TVUO+oYfy4sXjPTjvg+O8cgZ+ffBx2+8BO4b77UN62oyBevXoN7rjrfpzx
|
||||
6wvxzHMvhry2ENzEyrcpCoAPyFb8wflF7BSGEpUpqssZV4uGesjhvh1onUsg2MSn0pRBaZLjd2pd
|
||||
AfZJ+sZXOmvdj4ANJKhzNZsWL8+bj6uum4b/+8Ev8N0Tf4H7H34EMC4orLV+U5T7oEQXQ/8q+I1g
|
||||
kc1gKiNjphHWGIPRo0Zgu23fgq9+6VM47ZT/w5c+exAmT5oQptFDsZlFCCNxTy9uu/Ne/PzM3+Hh
|
||||
GY+FvBYNZv2H+6pAButWOptvn2idzW8fJgumETW7lQRGl+n4aLGVZAWEFjsigYoUOTm3llDA/JdN
|
||||
fSVT68hkWSxeshT/vfchnHbW+Tj2hJ/jquumYdUq931Y4nF6cn0r+CSnJy8jMiv3k3SdFMSdnXVs
|
||||
tvEG+NTBH8UpPzga3/nmoXjbNm9GR4cbfYdqGg3vP5rtLF6yFFdccyN+9NOzwi51NhJX8CeQ4JMB
|
||||
XRGRz71M7SdrOKI6eFsWFKLBKGIp4A9wtvpATvQQIquK0XmVysogNrkkFNUihCIW1roOMRCw1qK3
|
||||
dzUeffxJXHjxP/D9k36J8y/6O16c83J46CPrZExPaYpikqPR5pXGgBboXIZRHeYC2O1Irz1+Lez5
|
||||
wV3wve8cjhOO+xp2e/9OGD1qZBiFhyqA4X1MPnxhzsv4/R8uw4k/+RXuuX96yM8jpZ/0ZYpPgVYN
|
||||
p8m2Tcrmm15ICE6Ujci8PalNNVkO/An5PDPJCBVIYTJdAunnQPcZOVE2K6CVi1Bwb7UNWGvDz6/8
|
||||
+Oe/xi9+dT7+e+/DWLWqJ+TnOlnkJ6Z+YCvylzSQ0pJf8hGIL+Z3genOhw3rxjvfvg2OOuJz+NHx
|
||||
R+FTB30UkydNCHxDOQrTCAzAvZfs1v/ilJ//Bqf96nw8/SxfE3N9uP+knpSmTsbTAw1290DSJBkQ
|
||||
OqgMGWRzB3CbOFyBFqfWKZCD+dEKEmU4WckOsPCOLOGrCOt3pK/59zT85LTf4qrrpmH+gtf0UZgj
|
||||
ItvM+aHRRUOU3hZh+SoLyVMzs6A0wEYbTMGXPnsQfvyDb+Frh30G27x1yzdsGk3+W7OmD/c9+AhO
|
||||
P/sCfO9Hp+OSv/4Li15f4vmQ91eJvWkwOdJO3sckPQUuI9G0MbgwKdj3EcsfVvG0UmR8iUAm6+Qx
|
||||
lLBOSW3LniPKzgxLBlsJqNzy5Stw30Mz8fisJ90rZYz7WmTOv0n3eEbK48EcyaCCOQEVeon3kQCf
|
||||
Hq81djQO+the+MmJx+C4b30F73/vjhg9yj1/PpQBDPYMOgC8PG8+/vjny/H9k3+Js357ER6c/ij6
|
||||
1rhHHttruiI/MlqZcLUtJZjfZbXW1xHJIB97ItWhrtl5e6TaJuvnhEQgvxHgjvGOMiVOVfNUYkuw
|
||||
/kmt1b1r0PBPZVnrXvWaD8QKCCOjNiUrA29srWxmLw/ger2GHXfYFj/87tfxo+OPwoEf2xPrTZmU
|
||||
4xsK8Gn00mXLcd1/bsMPTjkTp/7yXNxy+z1YumyFC+Cyp7YGGrIqmU5CC8Ay+L4Tjbr+s/Cx0aK8
|
||||
DC0EcjWB7UHItv6qlvIWkaNi9ACJO223o1JHstaiyX4nGKo2eYoDr5s9vBFMakG3UAUro1Tr7HUV
|
||||
bLzhFHztsE/j5z8+Fl/87IHYfLONhuwbShyWvVDBGIOnn30Bp599AX7w4zPxt8uvxYtz/aZhsyle
|
||||
EOAdxYM6tTwJKMtPIGqXqigoYLyusm/y/CghZXGF6BD9SUFJIPNCUuAAwSIOWj6VTvgKhq5iiRFO
|
||||
IbULG6ZJ0rlgDSGh0aRSXv8UQjWCj0STu8K9Xou1xo7Bxw/YG2f87Hh856gvY6d3bodRI0eE4B2q
|
||||
AAabRhtj8MqrC3DpP67GcSf8HL+78FLMmDkLK1f2uGfQw0++yvuwrG3DJ0lPgRgkI0+zC2vboDZh
|
||||
B1URyU3oEwKdPThEhwre33SIQGYSaf5Oi3CZ7hdYHVK5pIN5GQ8LcRHIsgYWPpgt3fhn9GSlxKQY
|
||||
FPwo/csOIC/bhsIsgN00+p1v3xY/OfHbOOn7R2GP3d6LSRPXHvDX7VQBH4V7entx48134ZjjT8X3
|
||||
T/olbph2BxYuXMw2Df1twshF1Ja0C2yCzRkUWmSj4nNInorgoui8aRPfa5byrUIjsIew+MEf0Crb
|
||||
H2IoGZGRBVBSoRZAevE2aEHZgFAkCAlZxrhv7gwE8us1rjgq+ESW5yjKY+Dt7EGBaa3FupPXwRFf
|
||||
PgRnn3YCDvn4Pthko/XR1dU5pFNoAr9//8Kcl3DWby/CsSf8DFdccyNemPMSenp6w35DGlV1Fn2y
|
||||
UCaBXYRlU6Zokm7hXngvQ8cyHTQZEUoZHCqywWnTZqBqo2kp/ChExYyXU9jhlHpUdoWvn0gHgmxZ
|
||||
TteQ6iFF8L5iSVMzMLUaYAxGjhyGD39oF/zmjBNx/HcOx9u22QojRwwPL1JI6z7wcJuDDRj/XrK/
|
||||
XX4dDj3ye/jZGb/HY088iVWretw3AHNBXKBjqbs0BkkT8itWrTYth83HsQO1M10wiiqpgurlVXWS
|
||||
BlRCQUAZ6zsoWxcBBVdTL0c2QEhTXQpfP0FT0iwgvO45aDQwOmvccKSQ8J0BarU6APd7zW/danOc
|
||||
8sOj8ZszfoS9dn8fxo8bi3rdzUSGMoDB1sKNRhMPTn8U3zz2x/jGd07C7Xfdh6VLl6PhX6aQ/eBc
|
||||
BSTcAHB6iqEEVZoBJflRH+Rg/bFl9VhZtLh8NdGz1r6g5Uq0ICwHrhincRJ5RKuH9PFJS/9UDwbn
|
||||
0Rqtv6DRIz8N5LpyfRgfbWYYzsN3YaVMicxPxr+6t9lsYPy4Mfjqlz6Jv170Kxzx5U9jvSmT0NHh
|
||||
RuCBWk5URbPZDG8JWfT6Epz9u4vde8kuuwILFy1GX5/7rnbsP3mutafsMzyf9Z/oSPG3gFaKVeLl
|
||||
OkmFJTxNc0cSsf9q2QYLsqtA1WlzKOOPsqtI2JgiPn4uDdeM8vSwSaBjoEalrBOm/MGXBTzf6ycv
|
||||
AsbnEZm3hWUEv6FGu9GdnXXssvMO+MO5p+KXP/0e3rTFpuHd3EM9Clv/WiNj3IMbN95yFw76zNdx
|
||||
7Ak/x3PP0/u02AUwckFKT+kI2bbc/3TOjwLRAwGujlSNQ6hUDZzZF07WkfJPamrdDvKyPbQM6qym
|
||||
ULkIwV5uOCvjN82GZpeWyedqU71l5qTU8+VqtRpq9RrqdYOpm26EU35wNP7xp3PwkT0/gHrdNdlQ
|
||||
BzDYNNpat5l1zPE/xYGHHIlb77w32JsbhWl2kjOa93qRF/lOliuAWg+HaJ+yrleUl0JL/EW6VoQX
|
||||
MTCBzJWPjE9YZcGMUBqy1INs+p/7PrI2HW4PxYHi8zhLW9VmtpqaQb1eR71ew7i1RuPg/T+Mv19y
|
||||
No7++hex9vi1oocrhhLNZhONRgONRhMLXluE3//hMuy53xdx1m8vwvIVK1EztfwrjZJQ8qMAbMuJ
|
||||
Djb8S6AojzWF1v1yNKMRM3KEHCHvh5xs/lmOioGsKByBgrF6xYVeCyR2wzyigy0DGK8/BiqQk+B+
|
||||
pqqqmm7hZyPN7F6kMWEtPGbMSLx/l3fivHNOwXlnn4Jt3rplCOChfFMHkO03WGuxbPkK3PHf+/Gl
|
||||
I7+Lbxx7EmY/+ayf+hv/dBZEP+B6Sprgs3RK944hGrsCAiuvV5YX09YWqwiIyvj6pLk5wTztmbMu
|
||||
y8AJUlkFnt1/jTHBJCHbIxAJVrOoTfCdbf4QBlNX1adsJK0G6sQqNHdpNAnpHtBmlsHIEcPw5i03
|
||||
w3FHfwV/ufAMfOwjuw/5bzVzkP0rVq7CzMeexC9+dR6+cPhx+PeNtwHwG3B89mPCv7yRhb6xYrOK
|
||||
N2oLNgdWrTLheNnlZZojRecIovmFQtNdCuM2avwSsnwGNiKnmBidTpPKFjirFRjkG1dWBSj1DyyS
|
||||
wRNGjYrtEPnClaUpcldXBzbecAo+dfA+OP/Xp+CYb3wpN41O6jEIoADu62tg3isL8Nd/XouvfvP7
|
||||
OO2s8zHnpXlh6dL0G3JZwfDPw3fS0j5gRLnSAhUhdemHXGq/qB3phJ768+eFTcXjQ/TvIEQK8HTv
|
||||
Tt2O8IYQgs7UGoSMsCMek1uDKBypJCPJvzurn9PrEDzSfOlMvmufg6DRzNG4zazJk9fBXru/Dz85
|
||||
8Rj8/MfH4h3bbx3qfaOm0Y1GE/MXLMRtd96Ln57+W5zw4zNw3wOPYM2aPsdHz0dbMLtTeqboElX5
|
||||
Bhmyn6a6UKC3qbd6d0ejKZD9Dy7NptaUKZkQN1jUgCnIPH6bph34srxa+ozU7U8dMcKFQHNHAN0v
|
||||
1piYz7zt1AZjRo/EO9++LY464nM47SffxQH77vmGv2rHWouVq3rw4PRHcfa5F+Nb3z0FF1zyD7w6
|
||||
fyHgbzW5vayK2yoBRX1F9j0U8JYg1wQFcni1mgpamoNeaw3EfRNKWSDzgUUiiAmUx3kU/1FHYki0
|
||||
Sk4ThiJFClAkshQpj1N2rFM/B+OAeFSXdktnF1Xq9O7q6sQWUzfGpz+xL0754dE48iufwaYbbxB+
|
||||
vWEoR2G+B7B69Ro889yL+Nvl1+JHPz0Lv/79n/DY40+7FyoEf/IOxXttuxAbmUko9+M1lMppQ12t
|
||||
u1Fa1sfpMq/fkEoQMnoikNuF37gwKLEmpVhV+PJ2gPpUAZKBFXUurghvSdcTjDEYM2YUdvvAu3H8
|
||||
sUfie8ccgV12fgdGDB8GDPFmFh+Bm80mXp3/Gq65/mac8ovf4KRTz8GNN9+JpcuWO94m+1GF8OCQ
|
||||
Zieli8DyjQ/i4EKSJWTwdXhZ+5blA46Jf+/ZNU8MoqXoUMzOmZ4jMPAKEt+A4pUbuRlIdP/PZylr
|
||||
ZBKiWVOGovUw3xQYIPDOEHWM/kNdY3OHUn0W7MEW5JRwAWqx6SYb4kufPRgH7bcXJk+a8IZtZJFd
|
||||
PT29uO/BR3Dmr/+Ik049B5f94xq8OOdl8Ww0tyUYy2gcKTpB5Idkkf0UJUU8HoGN88pyVv/6Yco0
|
||||
To/ymE5ENxV05SycVRaL8hMZ/Par2+zSKpYWVQUvlzgP1eW80z/Y1M52+4hjOXt0MiA8jGJFqxBT
|
||||
ZuO4tcZgyrqT0NnZAbwBozDdD240mnjmuRdx4SX/wA9P+RV+d+GlmPnYbDaNFjYm24hsS+VLMFsL
|
||||
14kekVjeaQtgwz9OYKAgULIIZWYR3fCEPG8TFKeAIo/7j9Md/IisOZa4lVJJUCf2ZbSiGq1laPoO
|
||||
NkSdwQ5/ASlcTxPcNHsowR+ZXL5iJa66bhpOOPlM/OJX5+GW2+8Jv6nkNrOYDWo7+balqXFLkAIL
|
||||
gipKU0WMvxBcMR4Z/pNfRKhufkhweshnMsJGJ8nPsjLISqocVE4ko7skmX0Dv0bmSDV2YEsxVISo
|
||||
biDhpr2colRGU2olS8JNVyV18EDTaGMMmk2LBx5+FCedeg5+eMqv8K+r/4M5c+exZ6d55xGwoCjP
|
||||
s/A4qYoyfluBJ4kqBRMXD5CtyqHlpWC8vwYShv6l7ct/jTGCZkURZGWpMpKvFbRbrj2UtklZvkdu
|
||||
xBsk8Gk0AMx+6jmc8ovf4BvHnIQLLvo7Hn/iKT+NptfO+rbg9/uNdrtQjMRVu0SOyZ9L8aC62ScE
|
||||
X6ib6RpEawIlFJ6cHAW5PLaTLsvmeAugqJME90kOud9+0gKsVU0pX+NjsqTYQCMdpB7IaKGsxjOQ
|
||||
0HoTc6oB2+R448FH4cVLluKCi/+Orx51As4+92Lc/9BMLFm6zF9Q5PeECd4W8m8YqYmXj2ZF7UTg
|
||||
dTA5NB2VItRPtg8RdI4aIAtsFawu0kGalYKmvgXzSwFU/wpUYAEYn8rvLq6JqXVRA5mKXpCgqZls
|
||||
PSGKxKcQyaDzgQd19GxNyx/o5x1a+iKt/GCtj+nbSQDQ19fAzbfdjW8e9xOcdOo5uOueB7Fw0WJY
|
||||
a7xmCf0i1cgmZlsIhKoQPiH5kYgW5PXXdVSVbC4OxWyVX27oA+IiA2VGo6CUpYwhUyIRyJr2pCBP
|
||||
Sp4i8OmaJt8bb30duezUVZc70IZZ4UAETW7UsvB2eB2Nsm6MEOuQkzcAIJm1Wg1z583HKaf9Fkce
|
||||
fSL+eeX1mPvSK2g0AdSHw9qG64HGfWMpCS3L0D8tU4O0k7VrEEGEwYTUg4G6ID8kJC3HJ/Sn9q1q
|
||||
lpSfQpE8n6cEsiKdBBlk+VGDaCiqnUG2Z5h2+U/uPHXtxuB3JAciiGMU2FxYVcaXbSoNDGgtDACv
|
||||
LXwdl1x6BT5/2LE46zcX4alnXsDKlSuBdXaA3eEUYOdzgK0OB0ZMcU94hJkFOZf0oukrpxFkOoUU
|
||||
H937jDpTP5CqJ4UKzzFw0yUfc5E7NzETmWYoUQbuh9SR0IXD5/lA5lrq8tyn4KsEZlQki8kP4oT8
|
||||
QOejMZOXg2MaiECmXWv3YAS7R62ZnqyOOTDJ0zrogmCtxW133ocvHv5/+MZ3Tsbtd96DJUuXojls
|
||||
ArDNUbDv+SWw+T7AujsA2xwOvPcsYJN9gfowZ4e6vqcnidhF0y2shfGaIzSagIVSJ0HTh0FmRU9F
|
||||
VYRtZkWqmMP5ZBlC7ll7mU5BGpSCScs0AOLNLs9YKjshUIUQlitapU6WGTpUAUqy20ZoRGU6LdM5
|
||||
FNzyaBOvLXwdv/79n3DYUd/Hv2+8DUuWLkWjWYOdsjvwnrOBrQ8DukcBjWWA7QGay4G1Ngbe+UNg
|
||||
x5OAcZv75y+Nn26LdaxsE0pHdG5Uf42TFUqU+V1makjI4DRKS7M4T07VHMFDVkZI8TNIvXJlYoXE
|
||||
7yNzKPRIeJH1JYiu7gVl1JG1gJ9gw78BRP/kWf/XH1hrsXLlKlx57U3Y/5Aj8J3vn4qnnn4B1hhg
|
||||
zMaw233HBer4rYA1K4BmH2BrLlBNB9DoA5q9wPofAN79C2Dzg4Du0Vkwhw2GUKP7kCN3cgTi/YDR
|
||||
5MU3SjLZUmwErlcKUkCiAGeTqmvgPIQaFPk8nRKm1MkPwNvKfS6ZPCOrTlkjE7ygpD7SiCJIXr7W
|
||||
NbrRkWESMpOdG4RvEw0MSD+ub96n5eifTrQmfvmV+fj7v/6Nu+55EH0NC4ycArvpJ4F3nQZs8Smg
|
||||
eyTQXAX4F9nHHaLmjkYvMGoKsP2xwLtOBibuANS7XZ6l1Va+s/iMhM0a0fMGGf4kJ9MjRSdoVUQo
|
||||
EsB9INquVK7gtcj6heH9VxOUoGnkAF9J7oKZRj6QaUMi3Jujc+oQvGOQVdohIZwcnKHkJUh55OsZ
|
||||
qC8iuJfJ0fVGk+fr5u4oQAWWlmCtRbN7POwWnwC2OQJYe3PArgLsGhfEObCgNh1+06sGrL8r8O5T
|
||||
gK2/CIxdH+h0v9oYl2NJq3SDHFg/iPznfUb9SX2wI9+mWf+DqFweElwWKT4A8O8qC0HM7VDhZyX8
|
||||
4kjP6YfD0yRyfMQb16c8EFKkEEclJoEip0skeMIGxxCiSO3ChxEYqvC0iq6RwMh1gQ4DNFc6mjFi
|
||||
55kfPp+m2qYG2F5g7CRgh8OA958AbPxeYNgYoO5+2SJnNHWgaAOMG6cZqtEUaGwhgJHXhfhl9UEl
|
||||
6s/E4O3h5bj6mhwNkh4CMJfByDLPI5A1pRJlKI+5gwUyv/Lynqv1XoHA4iuuUKQSZBtS2kAxMksP
|
||||
xG2eTIYwRrVNJUawA6hXkFPrAupdbArNgosCLQQbg/EtbwB01IGOJtBdA7Z4L7DXD4B3fRGYuAXQ
|
||||
OTyemifNLOp4RNPyJKrwCH0ssnL8PIWUOpwu8yRSPg15WoY8HyB4VdiPuOV1S0NjZDQtm9fTCqJi
|
||||
cm2hOJSoOYe2CU1lE/4J+Dq1LI3WBqx1P8AOeJn1DsDU2Z6D9Y8e+bcC8E5n4Pj8UhldBhheB0Z2
|
||||
A8M7gTqA8VOAnb8I7H4csNWHgLGTgVrdlQ2i6IR8LPsP3wPhGfxTax8x1Y5GY5/PEXSREPJJl7Bs
|
||||
1NlUcJ7AKwuRczxC3+PG8H7D7AinnE7nGo2dsyqVqTWrRIU0wl/1DfzIoIkggixbBOll7oyEqsa/
|
||||
HM5aJXMgQbu70i6fbsXMFsEfBAkIa19yCksbxOmadQHbXQNGdgKjOoERHUB33Y3OANA5DNjiPcBe
|
||||
xwHvPxzYZEege0xeftLHcqea8/F25R2dDuLVZHM+AtMjFJG6ef4aK8tFSBUoT/JEvETw9eQuOgr4
|
||||
LMn6Mrm9AnkxY7JlPhBs9YEsDecQ1gRWdm5QUB5CRkl1OURa5yHlyHSboA2z0pE92O4rjjp7YBhk
|
||||
sDoiffxBAV2zbj09rAMY3eWCeFgH0AkX3HW/kWMM0LDAqAnA2w8C9jgWePuBwIRNfb6ok5AjcT8g
|
||||
35dy7dqCz6wwM4APJiTfE6oEG4GKhmDiNEX3lE8CivIkBK+B2Oyjz4yP7cW3iOConCdZPgerwyJd
|
||||
rl0wUaXBVwGmldtFgU12Fun8/iNaI1v6F298AOReNnOoAeisASM6gTHdwIguoKvugjfXFH5DrA9A
|
||||
swas+2bgA0cAu34D2HJX96CJesGSB4GNQrm+xtM8YlKHh7SXoNJZeSlOq56n+WeAZKSkZJTtL32l
|
||||
QfLIHXv+mUG7T1EBpHBeoEOKziBtbgeKjIEIYkLlQOZoo0jbsH1A0z9yCKqbTbF5EHf5IB7t18Nd
|
||||
bK2c0tl4hgbc1HrrDwN7fRvY+QvAlLe4nW2qMwfqhFo7SWKOoQAFvAVZALJprYQkcfWSamo2F4Di
|
||||
srScVKYaWCCnBGgVazQCywvKJ2CRbUC0isjRse4DGcyloEYnE9SqrXdFG3YqcBcYf5up2YzdR+6k
|
||||
ow438o7ockd3HehQ2oanpQxTc+3UALD2psB7DwX2OBrY7kBgzGTPIwVAn4JGlcoo4fxaWQGZzUUX
|
||||
QVZh2VcTUwdtS8g6I2gKSCcXQSpWHeIRzQqFA4uiFF+UcyisESxYr2kFrRvcCipdEEjtiFXprKbN
|
||||
EV7AhK8hWhdgNZobgyljgJoB6gbo7gBGdrljWN3RSAQ/QgXsPIIf3Rvua5HY9N3Abn66vfGOQEeX
|
||||
l8V30AUMpKMSbSj7pOTJfNoSwgVQEUlQqonSFvpaW/oxgD8Uw5dA8j48VUafqrAk3M5FpITUXkBt
|
||||
DPiGVvShE+2pFamsFbQqgYTMRxyVgrAA9DOh7QUfrzsr399nrQHEb/eghzrCFJoFcEfdbWSN7HK7
|
||||
08Nqjq6ZQ+2v5eVQA5oG6DPA6MnAth9xo/OOnwXW3ihrM8PqqiK7LH+gwfspPzhN5vNyPBHtWZSA
|
||||
greUt5QhQja1TpbjGXQ1U7bIOauhwJUZshKWtrwxWQ+QRcCL+R5i4w7a3yBGqzKSrNwHAw1/9SJf
|
||||
14zbUa4b95jlsA53f3hER7YeHqhgMX4zrGncM9pTtgF2+QKwxzHAm3cDOke4Dmv8Fzbiwno6uCnl
|
||||
M1lO40mB6qhwReFdlR/g028bz7UNL8jr8p/qLSOhSlI1X1AdCDM4L+d4pAW+8dyJqJU1hEHxI5SR
|
||||
wQp4teSInHFMvlABcAGYu886mCBzVD09yJS2RncNXvbqFcCapW563dEBdNTcenh4h9vQ6q652008
|
||||
lrgKvBlbhfH/rHEL8eFrA1u+D9jrW8CuXwOmbOUD3qan2jDF/SEgVb5KWQbyAxWL+pug8095Hn7E
|
||||
LsHLYyKHVL/20OhEo+esFdRyDUufUQEaGWUGL9AKNAuFbFkVoJfL8Ti0NKIqqBx0OT15jyAM5K9K
|
||||
OBnGAKbnNeD5G4EFD7t7xN2dbh08rCMfxFxPqbNUtwpIBk3rbQ0w3cC4zYAdDwY+8j1gu/2BEeMc
|
||||
Iy0BQmEuRy7vokxJbB0kgr9rq8hm3oR0zmmyHcWMUJctbalglxq4Gg2owTKZ1hem8ySkUoTCQg5a
|
||||
MRjhKTqVzL5evv4aJGT3kTWbNFoKfnfZ9v/iQnBfIzdAYw3w0p3A3T8GHrkQaCx0D3oMA9DpHsAN
|
||||
TcUPCY2moVCG8XunNaA+Clh/O2D3b7jp9npv9beqfEATv6H7utIvNBWVdIaUHhrCup3S7FM7eJ4E
|
||||
p1kn08W1UCaSV0HRUK/nLSqi6JVNuugrUoDjjAQpJYF8bSm2HKSWomArjQTkyg/c6AdFEd96EpEK
|
||||
Sr5pYZQvgLU2vDETgPtu8dLngRl/AKb9AHj+VqBjNdAFwNCz1lK/NqHJkG0VgrUDGDUR2H4fYP+T
|
||||
gXd+xj0pBr+e5zu/hstmHdlAqTREB0uXwcvkRYuKSfH8AOK1Nt+3ivzg4ynQtAqF84JPaF3N/SQ/
|
||||
Y8RT6wiyYplWkJRF8IqrfLRxw9JRnZQpjB8k5HeYZboMsZ4DdQ85erKLfGQtsGYV8PydwFXfBW44
|
||||
HXj9ZRfMUO419wcpN0j59DCJ7QImvxn48DeBA08FNt/FvTOsZrJxxBoRERIykgr0UMH8JUd54cpI
|
||||
rlaHSuNPrim+iCrRwPtKrrCeZ+L+RZ7MGCJIurS0v/DywpWLe1MzyMOC7QoWdYD+IZ4KK85MIWId
|
||||
eOWcXuzWmLWAbQDNBrByEXD/n4BLjwSmXw/UGm693OjLd+JWQP2myHzOYzzB1IGGAWw3sOV7gI//
|
||||
DNjlK8Dwcb5MwbPbQJ7O5bdqDg843pX5p8zTIIIoAysUZl9SkEwTUjI9NF2ZrPQ7uxLkQvApRwRP
|
||||
l05KsQPFChhk6yshYKCm1XEQF+jC7QGYPkKH/qsUkNknWpd+CtVa4JXHgcuPBv56FPDqk8Dougtq
|
||||
2ycV7h+qiDI1t7PdC2DYeOCDXwc+dQ6w+XuzFxgY+WYSgqggmFw2beWo4HwpgtqVt2+ULpFJ/cdA
|
||||
EU6gACiTJT7ziejGRCYwVS+gVCrTRGOHlFdB9zy8EOudFGQyun+IY6A2lTIUKBvZQq3slw8DrQbi
|
||||
C5Vqp7X+NT51dz7rRuCCzwLTLgAaK9w95fAL5oMMrp4x7jZZ0wBrGsDUHYDP/BrY87vA2Cluqh3t
|
||||
bIeCIu1hZL9SfBHg88IMRjkkXULSjEYsQOgnZE/CrjYh79azUTXqoZxBEmIUOSLX6VuFvDGun6sd
|
||||
fEgg/MWSA6WTfqHi7eW3qumlAqbupts3nAZcejTw9L0AeoBa003Fc7IGCNTedE6gzbDepnuY5H2f
|
||||
BT5/PrDN3u5bVRAPkWgPQlj6px0aeENwupIuQ6iGGwh3nrsIeaToyHeZJK0E7vYTkD19MlAIvi1w
|
||||
Yrsw0IV5Uk19+dwgQlEla3Bv/wDdfmp56UDvroYFnroD+OvRwH/OAuY/DZje7OZq0LcNaOVEH4/A
|
||||
R+dVTWDyVOCgnwMf+T4weUugozsbnTXZSNRZCKUf8s+UPJ7HeSR/ZGOBQBP+OeTYCmwugNu15sGW
|
||||
Q1EewRQcgq2/sH4NmJBFHb2lzq7AyagYfMFUz2tTrd9/GP+635ZA020YNzrfczFw+fHAIzcAvQsB
|
||||
438XinzbKsrU0URa3+9M3Y3OawDssC9w8GnAtvsAoycCNf8seVYgE1ZWJyCYSAlPS+qUryrkcR6J
|
||||
HC11d4aplcpvA35EzmnBYEWNbdYeFSuqrwgsgCMRPsGDqZ8YmNtFA6NLayiq06+Lmw3gpUeA608F
|
||||
pv0amPsw0LfUB3QbwUydu6hYUV6t7qfbACZvDuz9f8Ae3wE2fRcwYi3/VhJ6ugVeRyGjtL3EvWsI
|
||||
vXPylDw616qiJYCBXz4YSnAmdp6Cpkg5CuagmiJtolQEr6uk3iI7LdLlBhsldVca2StAn21I2b7H
|
||||
hYcSxA6v9feVVy4CHvwHcM1PgPv/CSx6Hmiu9ncERLDIKlLgnZ8OLU+CpttrAHSMBLb7CLDPD4Gd
|
||||
Pg1MpOk2D5ACBN1lRQm7KC31lofkj+B10spoKDGhVdBNvAqo4MC2UWQxwbr6B0uFdkENxvVK6DhQ
|
||||
waxD9J6EDoAPGgs3QjXWAC89Btx2LnDjmcCsm4Dl8wE03JNhvG3KOudAwBi3dl5tgPEbAe/9IrD7
|
||||
t9wrhkatI0ZmDq9c8LEctX0evVFF2iLPpZ2cZmV7a/qAxYySL+X3E/Tgq6QPItq1wCiNQ4j1z49Y
|
||||
rSP7pYkSWdROkV7ygqMq3Rb0XWsFFViCP40BepYCT9wE3HgGcOeFwLxHgGaP+zKGFtCDCeN3tvss
|
||||
gBHA1F2A3b8J7PxZYMrW7i2fYXTWoi4RP0WjOYkhUTydO1LLj4RsqR+vY4BQMLXuZy3GW528XURI
|
||||
Ge8RFS/izaZdlTp6CVqWYZBoIesyWxSXQrIPRfC+L2IMrvRTbTpenwPc91fgprOAR64Blrzs1s7U
|
||||
nqxYWzZRuVRZohv455WM+yLG+I2BHQ8BPniUe3fY2MlsdPbGWPqnXfBpjSweTOK6yHQReFdM8ae6
|
||||
q0qXClHspITHSAQyOaeakBxI0UjhlCxBl/YYsN6bkuHhsx17CW8JsocuxGiUQpKlH35MoNQ2ar4q
|
||||
swkOktu3CnjmbuDm37jj6TuANcvd6Bxeek9lSsyT7VmUJ/PhbTA195hnfSSw2c7ArkcA7zkU2HB7
|
||||
92sYnDeca8KQPcyoHRJavsZHTxnm2iUhOCIleMDJiXwGfuedHZzWItSriEwr0FgMXEapGuzKoclp
|
||||
A+5rjFmqFJwl11AIrw4aCITpPm8y46fzJpchDgED1plY29mmG41nXAn851fAnX8AFjzjgrnm15rt
|
||||
QKkqCa6u8SNq0wBjNnDv2f7gN913nsdOzt63Tb4h2WEAEJVpdRfpJmm5c1mgCEE5QZMblNVlxj8Z
|
||||
01+oYqorE7PSVY7TW5A1YDCt1xv5QXVK2whLw5xaPlGqqqJPrgt4p1sLNFYD82YB91wC/PvnwGPX
|
||||
AY2V7qX2cnQug8Yata8C6UvrR+facGDDHYD3HQrs+nVgs/e4b1XBZg6yfhAwvmytno3IYHWndEjl
|
||||
WfYUV853HrkglA3mz408T0HKi6E/az3UUHVkjpK0HJiAFEuLiL/G2KJQrfEHENEGXKqzBcieJhm1
|
||||
NNF8MFsLrFoCPH2bWzvfcLoL7g4TT7c1Udp5FTAVIhhk32e2NWDM+sA2HwX2PAZ432HAuI08n3hk
|
||||
0hj/O1lsal0ELZ/KGS+PaGBpImrltX4U5MkMDVJxSlsZyGrtgwfLNiBSiFRKtS4h5cD2YOniXhkt
|
||||
MbcFQ1No6gDcYD6NlO2dBO/sqU7l5TUt8Npz7t7z1ScB9/8VWPk60OkVomCmoz/I6SBAo27Tv553
|
||||
4puBnQ8B9v0h8Lb9gc7u/GzB+FGawHXlOkvfyTTkEs4ry3Xm57RJy2VR2+VGbo8QGxry5RKbXW0i
|
||||
oZOKUl7GkLJHhUWtpj000Roq3+aB0tgC/dVFhY8dwGQPfFA11v+j0ZQ2Y4qU5FDZbPYY5+pVwJzp
|
||||
wC2/Bq4+EXjyLrez3QH3vWgpQJVXghKfZvDr5gaA+hhg452ADx0F7P1D94oh4oEP5FTgEChb1h8V
|
||||
86N9aFaWGbUBg0zDt5mkW3j5rfX7gQ1koLXac1YQUh2BZBfXUTkAC9BSIMN3Ehjv0lhPJ6ZY57YQ
|
||||
RPL6EvWY8E9AsTHpZt+BrQUaTWDpq8ATtwDXnQLc+htg+avuJQZGPHgB/6lUFcDzivgkeN9o+neG
|
||||
DZ8IbLs3cODPgHd9Duga4ab/q1e5N6mkQDqm6o/owjk8j0/ry2TlfKzowXlCE8eCByiQec28goQV
|
||||
EVmWVcqrI5rSaT0p+2nVIYTlu+tU9xDoELlA8VOOJP0m04ycUp9GNtt0T4a99hzw3z8B/zoBePxm
|
||||
oNnrRmf6mmRKjoRs/hSSXcX4Ll0H0AWMnwrsdiSw/6nAlh8AjIFZs8rbJiqS9fI6cvUwkJ+itpeF
|
||||
EkiyiBmWyMpszdBmICc1YEjwlK2JNUROr1C+JLsK9GeaS2DDvxgVVO4ffAVKA6f7VAWFVBYpv+lG
|
||||
395lwNN3AVedDFx/GrDgOf97y2x0HkyQj2lmZOpudO5cC9hqN2C/k4E9j4cds76binM7pG4yzWG8
|
||||
zcQkecnfkl4FlvcVvywiegnKA5nWWSHNP1MaazQULN4lUnI1KDKrFi1BpUDmdaXY/aZZJXklkAOJ
|
||||
gxz1REdNrgu5PopuajGNaN3o2+gDlswD7r8U+PsxwAN/B9Ys85thDb+7LcsOAsgU4zeM+gzQvQ6w
|
||||
4bbuXWENz8S7GZ2n9ON89Gl1twWkZAFZwSKenGxqV7kRltu1VsDn+6AFPm0a5GpK9TSPVB73YooH
|
||||
sSEayoq3jQKhzDXRZ1GZfoHLzVXK0rQhQ/xV9JH+bdGhdNFv9AGvPAb8+2Tgqh8ALz/qpto16zfD
|
||||
hhDGuN/Isn5DrOKDeipkOaPQWoL3NXczl2clIY3yQA6QWsvO45EgO6QyOV3jUTqXxgYLa5toNtkP
|
||||
nbUJerFAXDcDlx/42O5wmUn9QLx5lhCeIFdHQkDkVm9z1BPpuW3rHvV89Frgsq8Dd10E9C7yD5L0
|
||||
+elpRfAqqjSrxmdM9mw25clPDVoetXGO5utJ+S5A5rM0dymlQ7amjEMLgZwSIpRKrYGD8dLLlObl
|
||||
6JzSjF8hqfX1E+7FAsY/a61ATpPDpo5iS+UlRVVwP/ojqoKWQ7LNpP9lvoTSlkbS2TmdGmQ6GAMs
|
||||
fgm4+XTg8u8DT94N2FXZZlkZyJ0DjVbkSjeR2rxduaxwzv3MhUiByBwX4oelI3atDU0rgVwEzSMK
|
||||
LUcib+YyGBRnqewqsW3oP6uqNYaSH0hZnkFtQNbIhTC8kWXn4TAFeRxeX0P/CtrKQJdp4TL7VgNP
|
||||
3Axcfixw07nA6y8Apg+Af982XQg1tWU6Ba0sh8xrhZ/rFlwgCzPfJNwUg5gqMAdW6SSXMUCBjFgZ
|
||||
aR98do7ODeAdRRrm07x8xJIZRrvN/Q2abGpOcmSrSjCalm0wQK8PSsmQG15FKGDk/cSguD2ipOTh
|
||||
NL/JZQAsXwDc9XvgX8e7wO5bDNSb+cVr1qQOmvgyFJhZiqKyMk/2Nboo5XSWBcGYyM+5Qg40w1Ll
|
||||
9iuQeYtLr4udbg6uRKRQgj+ADBH1kHFAcETLL6crBAknmT4dzNZ0UsyxUH6Gph0IGZRsSXRF/6gy
|
||||
U2U1ZnISK0Nr1RceBK78AXDjr4F5jwFYnf1WldZ3FBLA20FmeEi65JX5ZZDljZj6Ruf86S8U+E5C
|
||||
8gklQzKjp39pQkWZ15DlSV2IZrU8F4DFNNo990KosZW1YKMxUJtdRvQpqVO+7rwdbwCCCsJ/kf4J
|
||||
PTlbYJcPuzCbIzGKbOvbiIpYvzY2AFYuBu75s3uj50NXAivmuam28RtmRdBcnwLxcn6elnJkmkD0
|
||||
4Bdvp+pKT0zJKkWoRDaG4HHwI3JZbdILDKEOz0NpjV06LvAUyOfKy3I8ylLF20R6GjzAFbWIsGzg
|
||||
LitVKWVLik4Q7cL7k9avpEJSL5621FcM8MoTwPU/BW44E3jmdmD1Yt8z2dckSRWhUl6HNqHJ5nnR
|
||||
OT09BsUZ0jEyn9PbAa8nQ4WX7ymWGfgH9YX1hrFzsYFFGBtGWE+Phj/pDIKiD+Do1r2cvl6v93uN
|
||||
DLigUZFSYYhgIKdsIcOB9OM+F02VRmVGAaW9NB25XD46r1kJPHI1cO0pwN2XAAufBOwad+9Zrp8J
|
||||
vDsNNLgbuDssgHon+60qRa/Qp4sU4wJbRV6uH5FlD+DIF3KGcWUFj+aAkMF4ozz+4AlEptTLT62l
|
||||
av7l7R0dAxPI7aM/jVQBJiE6RSN6qUs0ASjpHxy8L/hPw86hieBrYgssngPc9QfgP2e6lxgsf8lN
|
||||
tY2yds7JKgD5oawM55HnTZ+gdT5l8qWD5oMkZEWaggUyWBGx2ZUqxOgpFhTcL02Qq0Ea5pHzlTtp
|
||||
Ni1eePFlzF/wGpr+yxPNNl5L424/0UggnFxoD28cDyPS/UVLogRzpHtVQcRXaLgAKxOKFdQXjc6r
|
||||
gCdvBW76FXDLucDTtwO9S7LRWS6pCsSqkPwkI3U04b7tBbjnxhc87X5gHojX8jb844Q2II0y4mDZ
|
||||
PskCWak0GtW8ED4S05ef+U1rqQPRgGodgabs4ZB20KOHjGiyztJsNjHttv/iV7+5CLfcfg9WrHRf
|
||||
W2vnaS9r+fRa3pgvgOW9IJs9DMwsoewtobwB/GduGVTYSAqK9OaNo9EpSbpkpBwMqeGf3V7ykntn
|
||||
2LSz3KuGXn0im24X+qACUm4gBBf6W2N1AD2LgRlXANP/ATT7/KhM20xiZpGDRmsVTGHeL030QIji
|
||||
4Zac5cuzoMqjRF54LliAO0nVKavQWos5c+fhT5ddgRNOPgNnn3sxZj/1HBr+itpOQDuwqXyyOPOB
|
||||
cEJ686w1WFumf6mSCsp4y/IJ0kbqeHzqqcH7i2Z01v+zANb0utcK3ftn4Oaz3HR71WtA3Y/guRFR
|
||||
QYqOWMXAZ+H6WbPp4rTZB8x9ELj1V8DtvwZefdwzMnvzTc5QpEC7iJeW/biPLGGZJcKiQiMZVHs5
|
||||
UVkXgy4cWSXWWqxc1YMHHp6Js357Ef7vBz/H3y6/FsuWrwBaDWbeuPwzCa5gdl65vhLEF/6yAIEy
|
||||
EktoNImc0z0EPSdKKWeQ14eajncfY7L2ttbdqnrmTuDW37pjzsOAXe16sG1m61dClfbS8qyfPsO6
|
||||
d4O9Pge474/Af34BzPwXsOxl9ogmFZIGDCQ0JTlc/gAFcsJr0racnYmGlAh57LaLpQyI57sdj21a
|
||||
2KbF/AUL8e8bb8OPfno2Tjj5DDzy6Ozws6uFAR2m1f5I6TbEsNai0eDfILIFjmO+Cj5LtFV/wEXx
|
||||
pRZvIw7WVPHyTSDk+aVB32r3+1TTrwRuPB24/zL3yl76NQwanVMmRnqyz3D4Ub5ugL4e4Ikbgf+c
|
||||
CtzzB+DlGUBfr79g+K9khogmsP6i0ltBK/xVvsbYCkLd2sjpEa1/q2yE8FavACaGnpfu62vg6Wdf
|
||||
wB//9E989agTcOav/4hX57/mH/hwPDKgwwMhQWCiMSJSygYHWUc74Jt34UJj4D+5r8p0Ram+GSrw
|
||||
pVhy7vNK5HQpgIHvUzTdXg7MfQj473nAv38KzJ4G2F4XgPzH26Mg9bL4OcHSKAxXxytPALefDdx6
|
||||
BvDsHe4FgzQVSi3/WgI5gx8yX54X11sQyMUFM3A+ClBGKoT0cgpMYK6zysqYLAsA7uksa4Fly1fi
|
||||
gYcfxc/O+D0OPfJ7uOb6W7BmTR9QNDorpAhl+R6q7DZgjEGtxh5GMMZ3MO6XAtjwrwUUyEyK0joj
|
||||
fIFU25XB+l3rpgvY5QvcK3qv/5mbbi+eC3T57z3zgObFOclSADfdq317lwDT/wnccBLw0N+Bhc+7
|
||||
VxmFuxcQesu+iGy5o/qlyOZ2fQIg/vYT1ZzUogKqKpFq5AqwSJQp19uNZhavLXwdN936X3zruB/j
|
||||
hJPPwHMvzEWtVotGuzBKa1UlkWYemB1rJ6der7nYXWdddOy8B8wmW/lfWqj7wE64QqO1jRZ28QHm
|
||||
m3b9YF1ZXrzZdC/QXzwXuP8vwOXfAR6+0t0a6qq5/KYSWNYHHO1GowHMnQHc+DPgjrOBl2YAq5e7
|
||||
lyCEzbRW9NYc00p5DQXlTe4RTakAeaDo8HUYrbxEYFTOK4KKaYFB1Yf8/EFrzDVr+vD8iy/j3Asu
|
||||
xecPOxZ/+dvVWL5iZVg/U1DHu81l9qXhRtKCCVBF1Gq1TM7YcTAf+hjM4T8G3rM/MHy0Z/IBTXYb
|
||||
cbsudb8/gPITfLnvyBYgIaJ1GNHA/pOmW70rXDD++6fAP48Dnp8JdPoHFxs0OvvD+nvRNePW2Hed
|
||||
B1z9feCxa4Elr/pRmF7rmzIgRYcrl5uCs3gZBPS/ZwFsClNgHN8A0Y5WoZUJ1fsTrg7x+/qajSYa
|
||||
jQZWrFyFe+6fjm8f/1McdezJeGj6Y2g2LZp+9C4yKQ+pVJweyOk1QAPGMGCTtwBf+C7w2e8Ckzdh
|
||||
U21vLE33OAr9nsyocBFAQrhlwSjzysDL+PqDCEr74OxZDjx5E/DXw4GbzgRWLXav6LWN7KhZ922r
|
||||
2bcAVxwH3HkusOgZoNHjp+4pHavaPlhI1G8tlG8/kRGSrsHzRfKNv1IyEVHj+/xwcHoLkMU5vQhi
|
||||
JG82m+jra2DBa4twyWVX4qDPfB2nn30Bli1bjq6uTs/FjCmTH/mNmPUNtXYRArnZRKPZhO0DTOcI
|
||||
mN32Bb75S+Ct7wbqdTYyQ7SrpqMC0lcrVgSteZNt3arQorQfbZsWWLkIuPv3wKVfBp68E+ioubd6
|
||||
dtaBZfOBO84D/n0iMOdBNxXna2ovKm90SldGN2IDN+de6cyUzNZQR9faJ7rTIoFSG8Ebsv2J5URZ
|
||||
liDoKbYkfIGoPWXjEo0+jHB6dkpYvHQ57r73Idz7wAy8MOclLHhtUVamyEURMsEujgw23mh9fPAD
|
||||
O2P9KZM9Xam8BNZavL54CW65/V48NuspYNxEYNudgUkbwTQN0LDAOhOBbXcBenuAF2e7F+FVrYvY
|
||||
LLlSm4bLNIOBMqWUkOVlOgXOJ8qQrpEB/lj2KvDkNKB3JTBqEvDsPe6W0mNXAb3LfZegzSwm14R/
|
||||
jODTdBqypQ0ynaIRivI4Ur4Nt59SDAR+9SgI4lx2mYJl9RZB0YOqS1brywTejNFaNzoDwKqe1bjv
|
||||
wUfw2Kyn4lE0arw3Bm5JSHZkj6da49fGPQBGjQcOPQH40onA2AlOacNXUcpU25Md2L3gHHKNzGiS
|
||||
PlBQnK6QADECWuvsXrMKuOd84KJPA1d/F3hpuvcJlM0samStgv7YlyhrIB6hTaEgz212acO/h6HO
|
||||
QpUV2FlQjw5ZQKarQOlAxtODnpxHOkucGxckTf84Z7bRxYyVaob1p8LLMFDTamvd+t2Nlt4eb6u1
|
||||
vr0aAHobwO77wRx1OrDeVEevGcdvoRjikRuBU9D8mU8GpJpARaqTCUTt60dV6Wd35fPT7YXAmp7M
|
||||
dzYIYTrxunn9Um6cDLC+TmKIxPmA5QdHsEciRcvq8Vu0gidCQWbSz6kMZqCKVLlWoMnnRscOiNmy
|
||||
BrVN5F/NoxTJUJgJYOCC2aHADmPcryysbMJsvxPMt34JbLpN9vvAGCBX87pJnmGjvaoeC5xcHlpQ
|
||||
TBGQk8dk0ehs/LmGBDkgxHbB4Afhi1IodkTQ8vK04l3rPH8LCFYr0JQ3Cq0dyHpJJtVZUEdoAEEn
|
||||
BDqTEW1uBKKqQzvrYgl64sxBjEC82pr7YW+7oglssiVw+CnAm98FdPjfCM4r7cH9VOAr7leDbOSz
|
||||
PK+oWaWPiOYRynC+1Dml+SdDUJUvKai8P2iZkkKUJfnIdgpwfruP8RSiLD8Bv49RHMhS38pgjaw6
|
||||
W8CgfUMKwWVKxzJQFlc7BWqoUuSFDdyITHIKbPKwpg67qglsNBX44vHAWyiYC8rlglI7BL9E3nwd
|
||||
4Tl5po86vVfW7NbTtTxAVM711uRLMP4kO9M7yUNykgx5RKxaOeYzr6L+qh8iUWPyRtEcptGAEiVY
|
||||
Mi2AQciipBCXgTkv4vEJ6siG21UyZSIk6yTk7RnI208RND0krVYHeiyw7qbAJ74FbLkDUJfBzBwR
|
||||
3kIqBVUAN7FqcbVfiel36jM61yqUwnMVFSOIFOWizUDWl1QkM3TkVCwvr4/IsjGMV5wS/BsugNIh
|
||||
yit2UxnpZA0UdNKR7FMVQTowvbWsCKkrexXkhA0K4ouBsrnDQb6xcI9v9hlg4zcBB30N2GJ7v2Ym
|
||||
vW3Wxqbm7kMb2bYp2OyBDN4wUboCqF8FeH2iUVuktRFdoiDLQfiU0lE51jdyOtKnP5flWobmN83G
|
||||
zPb8AyGRX3xBKZNgpFEpCJ5KRdioaZEvpNlVhsJ1EO3Oa3KtcEyOIfZXRKzB+Fs/A7VGbgsGLkAb
|
||||
dWDzbYH9DgOmvo3NTAzQ3Q1M3hh4x+7umLwhG7mL6mVrTN5mYYqeCJSAItlFkHJiZPsJ2j4GB89k
|
||||
51Jt8AtNmU/aAVvDG5/O2Uj1xnXnR2RezrA+LGGqBjFBOkgqyJDsrMyJmo3tgldHciMVyBEEf677
|
||||
NEd00+rsPnW/wYMvX3kMnm3gd267gLe8A/jYF4HN3uq+cDFiLLDt+4D9jwAO+TbwuWOB/Q8D3roT
|
||||
MGykL6vVR2lJ5zStsWRaK1+G4jL5Wkl/WTfl8XO2YWW1fIKQVaxSBXh5kVhNXw5tRIYoF9qJ3FLE
|
||||
r1mhlNPYAsT0Nsebaoh+gt82gVYF75QSPi+nawY3WJW9b6sasmro3nACoe04zfjrdzew9buBfb8E
|
||||
7LA78IEDgQOOAN69NzBxY2DCBsB79gE+/g3g/QcAE6b4b1iJWY1FfuoLXmeBfqovSyDrLwLveup0
|
||||
WELQQ7ctKRtMb8MeCZLTiqzs20+MAm3rnGXzzwgaUSiTEKuiMJgHATb8KwF3QsqgvJw1a/qwevWa
|
||||
EMxtB7QBm76GnpYGV5Orag3QMRLY/v3AJ78FfPhzwMZbuXc2N+HW07UuYOo2wH6HAgd/DdhmZ6Cz
|
||||
28utZfdlwXa5Q8fnldF5Sl+Npk+HpeSBB69B1iTTXG/NBo6yfA3Vy9TRNd4/a624SLvyGd8JIgJP
|
||||
W2JiNI/cVaZIjgZZfoAQxDL5haoUZCZc2Lt6NWq1GtadNBFrj1/Lf6fY1Vd13UvPWt96x3147PEn
|
||||
gXETgG3fDUzcyL91hsmh0+Sn93etExgzHhg+Kt7sopHeAhgxClh/KsymWwKjxgGLXgWWL/G8KN5w
|
||||
SzZrTiGW53ySexG/wYC9xFDWlwfXK2UEo2nZEUoZWoCXZcj/IZBlJcRI/3w6TKEEX0CiQQ2UIA4Z
|
||||
ipwUWuFtAZFYbnsVMBtsvuM5l1isXNWL556fi8eeeAorVqzEpEnrYPSokWGErhLMFMi33HkvHpv1
|
||||
JLDWBGCbdwOTWCCTGPlJ5xqdmoamrfS8CKWbgKnVgXHrAJu9CVh/c6BnJTB/rvvWUOIupkoDFKWU
|
||||
c+4PrndaaEX0t3wKfCbLjzYRLo4kQ8qKZ851dI8/0Z17RuOdSOVC/CmCIojRzLDnnVUkM0qgXRAG
|
||||
CGR3ZdUEI/djIGQCV/X0Ys5L8zBj5hOY/dSz6O7uwnpTJqG7u8u9xKDk52CttVj0+hLcfPs9ePzx
|
||||
p4Dx64RvP0UjslRBgqsVgrfAduMvUk0AHV3ApA2Bzd8KrDUReH0+sMx/Q8wY0Q9UYSVpRgsffnRW
|
||||
eYtA/PJzIFGglwn/MliiiXLhlPdvHszGp8kpcXkXyBCNWLobHRhZBTyrSrCxOsKUrqjeKjKHEoqu
|
||||
kYo8P3Nus2mxbNkKPPPci7jvgel4cc48TFpnbawzYW3UaqYwoK21WLhoMabd+l/Mmv20+xrj23YG
|
||||
Jm7IOgiDFCHTHEV5oHx233bkWsDULYGpWwOrVgHz5/iXtrNbNDkbpE/ok3VQme/r9bX+D4M0TGga
|
||||
SEoe0uS8PPKL//R9ro5u+j4yh5RaoCCHgezNaUT345CVy1UhGznH8AYhoYcJ/yTR2eI3uhqNPry+
|
||||
eClmPfE0/nvfw1i9ejXetMWmGD58mHhLZgZrLRb6EdkF8jp+jbxhfo2sJHNpjqK8CCY76p0wa68L
|
||||
bLU1MGF9YNF8YPFrns2IttPakPHk6s8R2oBW5yBAjI6eqKQljUFmGUbMzRS5/xz8iMxQOhoXQF0H
|
||||
p5CoJyK3Iq9dkIe0owgiv7KqMeOaNX2Yv2AB7rl/Bh55dDamrDsRG224Hmq1GhqNRjQ6W+teHDjt
|
||||
1rvxxOxn3Bp5W7FG5uBJk6s6jzKTI5CPasCwUcCmWwJbbg/Uu4AFc4HVq/yuNgmVnwLUWTOCchEv
|
||||
gsZHOkraQCPl3BKbJQyzl9/S48XDeSwzC2RbNrWVfmEjqOavIgSbRSEpe1DBlKbqcjZUMcw4AbkR
|
||||
KIU43+1cG/T09uLpZ1/AtTfcihfnvIyt3jQVa601Blb8CN1rC1/HTbfchdlPPed2rbfeyT2N1UTc
|
||||
fnTKTeBVp0zTaAQqE8qyhO1wF5attgOmbAksW+x2t5sNpWINWiCneMvgy7VbfMjA+zn1HdGHNBsM
|
||||
RH+ztN2ocVMB/iVof9BGVktgSheWHaogzswJPrDs0PhVkL6a3qlysc+bzSaaTYu+viZenb8Q515w
|
||||
KT607+dx0V8ux+rVa8KregG4lwLSjmbRbZ8UCpo7SS+FcaNvsw7TORZ41weBr/8MOPgbbsqt+obg
|
||||
FaK61WlqqyD/cFl03qrsVvmrgvcb7h+hb851rJxBmAXHLxbIFSqBEXrIzwjMISFfMObKteN4gmxE
|
||||
Ttfq8khWqRIFJE+qEkLMTy/JbzYtnnrmBXz1qBPw6UO/jQcemhmm181mE300QtN7m5tgG0xMYBFk
|
||||
/0nROFK+oTLGwBp/u2rcJJgDvwLznV+5h05y37YS8qjuVi5OUbOmFOOdkqc1/hRa4a0CbiPJLqgj
|
||||
conCZ4BaYcOVgnUe7q8yoZEu3LmI1wYBMu0h2yjwybJeLnX2nHqeEFWh1KfSCEV5JWAqWws0Gk0Y
|
||||
U8OaNQ1cftUN+Ngnv4oTf3IW5r2yAJZPhmp18XSVZlsCrapL/FXkWwMLwPT1ASuWAz09XjdRKdc3
|
||||
+KAFxbgurVwAgIqG9BeyDtlA3PiqIBn8iN6iKVC0cWXBKme9MMvM8lI6qnSVmIDUT+pBtAqgohbx
|
||||
tr7xU5tIjLSb327hbOw+umFyI9DbNahsJttawNomasZg+YqVuO+BGZj5+GwYU8NDMx7H3LnzgLXX
|
||||
Bd72XvdcdNEaOQWun4YUXcLAGWGt+zG1Wh/w2jzYG/8O/PUc4IXZPtC0jssVrVrh/wvg/VP2VaJV
|
||||
tbd8KWswanNRS0mhKIg5SEzFQM7Zlup5KYeIetTPFHxZo/jTep0j8X60k2JzNhCPlqHJlnxGKgPA
|
||||
hvg0xqCrqwsWQG/vavdi+k8fA2z1LqDh16kkhj6lzmXQ7JQgtYmPvotsmsDiecC9twB3XAO8MAvo
|
||||
6/P8zhbd6f9/g2xXno46FjtPgfFLdtYO4ksTUgEPaoNEtoPoPfJ2QjStkhoVQfJ6RYJOMp945Lli
|
||||
hOX5BfbxOor8YFCQKUwx8D7y/gpHHvQlC2stentXuyAGgKWvAwtecj8GTj8rKqeYBeqoIBWqlLP0
|
||||
QoGme0f0o/cD558KXPJz4OkZwJrV/mdI/RsuXSEh5P9V8P4kjwRolhua2fO76RcrmpAlyaGtLPvS
|
||||
RAg8cSslp5fe2RxYZ+QdNBdsMk00jS4VEDxRILQIQ+UlsQRGUQsoXo4A3hfZaR4aMbYtfFHeWve8
|
||||
8+LX3Ug8Zqz73jD9LhS/kJK+mvgUpI1R2ne6WtP9SsPcp4Fp/wSu+D3w9PTslhOfToe6uRKtKDRU
|
||||
kG3IdSQnSp4iMF7eHjl4omHnLcBg1FRXU9CPT61LAjniVypv1WZNhirA0yhLBiONSpxsqGMJGsEi
|
||||
UT9B6kGN6pEMYkWmJX6fJ4ta0i0YmJ1LnxoAI8YA274XeN++wObbuLStuQsoPUPdLqQK1roAtgCW
|
||||
zQMe/C8w7XLg2RnZbww3rWOO6uX+kp9vFEgn0ZZRnqRpKJPjwZdokajUBZ4x875rwj9GGrVZtgjj
|
||||
0i1853dsqpKRcGkAT6ccQJBleV28rD+X4qK6E+tZSJpPBPuqQFYsncuRIyjluQ4edB58r10obcYY
|
||||
bDfAupsA794L2HFXYN2p7gsOdBeA+DS1ymDhA9gnViwEnp0F3Plv4L4bgZXLfB00PdQMIkgFZHqw
|
||||
ITtHmX4Ewccv3FGWLE8bmuLClivD+wK1rUjzQqJPGIz2IzJB2pVTjCEEsmU/SVLAXwkkU8gJP+3B
|
||||
ITaPDFzC0vIgLyYDvwpqM48UuM0+WVRHDkxZKqvWTWXpwsQrkgV8EMEAXcOAN+/g3vSxzbuAtSa5
|
||||
xwUssq8bamqlQGu3mgX6eoHnHgfu/g/w4M3AKy86HkOjsCZb0TX6HCqk9EjRIToW51NslWI0UPBb
|
||||
/y+UkY3C67PuMERnCDNPo02tJXiwMAaZHpBA5goIR0eBzEYYUo5f8XggE4uGsHZPMUhw5/JiNiFD
|
||||
0rj/ZF4KvE5ZRjQY98n4ScD2uwI77uZetjdytHvrB/hFlyHneu/Tmv994fkvADPuAe68BnjKT6O5
|
||||
/bmpXwrc55pNgwXhq6yzJOgS3D5RRorQkBvBiwKZw/o2k/msvB7I3EBRmG9eBHgew85VFOURpGxm
|
||||
dJTHd3t9vm3GaZ5vPE0i2Nuqbv48kpuSkaAr6gBQGpyfiIsqMzcDu8jVOoANtwC2fx/wnj2A9bd0
|
||||
+Q0qqOhmvQ50H3z5IuCx+4H/3gA8di+wlH6d0mQXVxOUikHkqJqS5dqggLeR1l4aTYJ45NpfzuYU
|
||||
P8D7FKJNgx/IhxxsmWKQ0I1ma8ZPrbnACMxwa/1tBKLTKS+nyUiBeBOGE/j0HXDloiDmupFTKEPo
|
||||
E6an3KlCVg6iDEgOz0eJDCj5YvPNQPhCqbcQskPRE181oKsbeMsOwE57uhcRjJ/sfoK1yfgssrrq
|
||||
cN9eenYWcP80YPptwLzn3U+0Wu9vwz6DbbKTCwR/88/BRJHveP2cT9KZbQS+aUX5IVu2oRTJ5HHx
|
||||
uXqrgPgKA1kIrhTIHCk6h4mUUQ3gnZ0QAtnnhZkC7dAW1K36q4AfyDdiW/B1RHVyuVEmg0bToMkX
|
||||
S5AJk4G37AjsvCew1TuA7uHAGlamZgDbAF59EXjwNuChW9yaeNVy1gcyV2cJhkJX8osN2V9YoJ9Q
|
||||
5OcuPhA2pPQRfYBE55ZIns/4uuicZaVBPlH0LgHb7JJXdGEoD2TaHMo5RMDQqCM2pVqCLxQcJ7Il
|
||||
jXQimqZezrEaE4EJNyJdCaQgq0NtJ3GFjvQvqjMnSNhFh//o7AQ2mOq+yPDuPYH1psLU3Rcd7NIl
|
||||
wCN3A/fcADz1MLDkNX9PGPoFlRCNUCn4TKv4Y9CQ6xjSsYLG6VpjCJApnNd4X4U8gpSh6dEGfD1Z
|
||||
IFuieqhK8DURoUCR0GbyItEiVMcokG1jJFGiKI+DVdzSiFwknxRkDWoVuiFaGbS2E/VT0tTc5tdm
|
||||
W8NsvwvM1LcAy5ag+dBdwMx73YsB+O8Ic4QRjUGmc5Aj8VChzG9apyL9JD0F1n4qXUK7+wKlfGtg
|
||||
gSwEBT24QkwJQ/8UBSTJAjddfTF2e/9OIqN/sNZi1aoe9K5ejZdefhVPPPksbr7tbvzpsiuxbPlK
|
||||
FhQc1HiSrtEILKi0jqwixZRqYNkGjM+ItApfNmKTbep1p82pWh0YPR5m7HhgzWrY1xcCPSvox6Fz
|
||||
xYMMsLyIRyvE9fL15ngUEGtboDraFpCAJlOmNduIx3+qfShHaAlsjeySAYHGG46tk0z45+HPpT7e
|
||||
pzdddRF2e/+7RebgoKenFzMfexLn/fGvOO+Pf8syaJpPyK3vZaNo0BoBrCxlKrMQ8mER+GYIWN9R
|
||||
6+QQu6Bg5xb5DTpL9vt1dJjy8jbWKtUu5lDsJ7ImAwnDZFqC11Hixxx42XbKQ7GVnwufRmA8/Ybu
|
||||
I//rYjyvoLKorbiwvOCAgqzBwrBh3XjH27fG7846Gc/MnIZvf+NLLNcmbNRoAnRLJolUpqeXVWHp
|
||||
H2PkfSBVPpkn5IQk6eODlvY/tC9dOEYpgAU50ZQObIseD+WGEbhM7SAUtYPkJVpZWtJSqMqnIam0
|
||||
ghSvTv//AAE/eVjcr77lAAAAAElFTkSuQmCC" transform="matrix(4.1322 0 0 4.1322 0 46)">
|
||||
</image>
|
||||
<rect class="st5" width="1000" height="1000"/>
|
||||
<polygon class="st6" points="464,846.5 464,728.4 535,790.5 553,777.5 631,830.5 539,890.5 "/>
|
||||
<polyline class="st7" points="847.97,702.06 541.04,505.96 464.02,557.94 777.96,747.04 "/>
|
||||
<polygon class="st8" points="464,558 464,503.53 500,478 541,506 "/>
|
||||
<polygon class="st7" points="778,278 778,747 848,702 848,321.43 "/>
|
||||
<path class="st9" d="M294.5,366.5l242-148l102,68l-264,161.73l77,49.27l223-133c0,0,48-35,43-82c0,0-2-48-33-62l-150-90
|
||||
l-313,189.73L294.5,366.5z"/>
|
||||
<polygon class="st9" points="221.5,701.5 294.5,746.5 294.5,366.5 221.5,320.23 "/>
|
||||
<polygon class="st9" points="374.5,793.5 451.5,839.5 451.5,497.5 374.5,448.23 "/>
|
||||
<path class="st10" d="M374.5,793.5l77,46l0-342l223-133c0,0,87.22-69.98,15.86-140.74L534.5,130.5l-313,189.73l0,381.27l73,45
|
||||
l0-380l242-148l102,68l-264,161.73L374.5,793.5z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 42 KiB |
@@ -0,0 +1,568 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 1000 1000" style="enable-background:new 0 0 1000 1000;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{display:none;}
|
||||
.st1{stroke:#000000;stroke-miterlimit:10;}
|
||||
.st2{display:inline;fill:#00B5FF;}
|
||||
.st3{display:inline;fill:#008EFF;}
|
||||
.st4{display:inline;fill:#0063EE;}
|
||||
.st5{display:inline;}
|
||||
.st6{fill:#00B5FF;}
|
||||
.st7{fill:#008EFF;}
|
||||
.st8{fill:#0063EE;}
|
||||
.st9{display:none;fill:#FFFFFF;}
|
||||
.st10{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g id="Capa_2" class="st0">
|
||||
<polygon points="374.5,793.5 451.5,839.5 451.5,497.5 374.5,448.23 "/>
|
||||
<polygon class="st1" points="221.5,701.5 294.5,746.5 294.5,366.5 221.5,320.23 "/>
|
||||
<path d="M294.5,366.5l242-148l102,68l-264,161.73l77,49.27l223-133c0,0,48-35,43-82c0,0-2-48-33-62l-150-90l-313,189.73
|
||||
L294.5,366.5z"/>
|
||||
<polygon class="st2" points="464,846.5 464,728.4 535,790.5 553,777.5 631,830.5 539,890.5 "/>
|
||||
<polyline class="st3" points="848,702 541,506 464,558 778,747 "/>
|
||||
<polygon class="st4" points="464,558 464,503.53 500,478 541,506 "/>
|
||||
<polygon class="st3" points="778,278 778,747 848,702 848,321.43 "/>
|
||||
<path class="st5" d="M374.5,793.5l77,46l0-342l223-133c0,0,87.22-69.98,15.86-140.74L534.5,130.5l-313,189.73l0,381.27l73,45l0-380
|
||||
l242-148l102,68l-264,161.73L374.5,793.5z"/>
|
||||
</g>
|
||||
<g id="Capa_1">
|
||||
|
||||
<image style="display:none;overflow:visible;" width="242" height="207" xlink:href="data:image/png;base64,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
|
||||
ghSvTv//AAE/eVjcr77lAAAAAElFTkSuQmCC" transform="matrix(4.1322 0 0 4.1322 0 46)">
|
||||
</image>
|
||||
<rect class="st0" width="1000" height="1000"/>
|
||||
<polygon class="st6" points="464,846.5 464,728.4 535,790.5 553,777.5 631,830.5 539,890.5 "/>
|
||||
<polyline class="st7" points="847.97,702.06 541.04,505.96 464.02,557.94 777.96,747.04 "/>
|
||||
<polygon class="st8" points="464,558 464,503.53 500,478 541,506 "/>
|
||||
<polygon class="st7" points="778,278 778,747 848,702 848,321.43 "/>
|
||||
<path class="st9" d="M294.5,366.5l242-148l102,68l-264,161.73l77,49.27l223-133c0,0,48-35,43-82c0,0-2-48-33-62l-150-90
|
||||
l-313,189.73L294.5,366.5z"/>
|
||||
<polygon class="st9" points="221.5,701.5 294.5,746.5 294.5,366.5 221.5,320.23 "/>
|
||||
<polygon class="st9" points="374.5,793.5 451.5,839.5 451.5,497.5 374.5,448.23 "/>
|
||||
<path class="st10" d="M374.5,793.5l77,46l0-342l223-133c0,0,87.22-69.98,15.86-140.74L534.5,130.5l-313,189.73l0,381.27l73,45
|
||||
l0-380l242-148l102,68l-264,161.73L374.5,793.5z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 42 KiB |
+568
-1
@@ -1 +1,568 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="46" fill="none" viewBox="0 0 48 46"><path fill="#863bff" d="M25.946 44.938c-.664.845-2.021.375-2.021-.698V33.937a2.26 2.26 0 0 0-2.262-2.262H10.287c-.92 0-1.456-1.04-.92-1.788l7.48-10.471c1.07-1.497 0-3.578-1.842-3.578H1.237c-.92 0-1.456-1.04-.92-1.788L10.013.474c.214-.297.556-.474.92-.474h28.894c.92 0 1.456 1.04.92 1.788l-7.48 10.471c-1.07 1.498 0 3.579 1.842 3.579h11.377c.943 0 1.473 1.088.89 1.83L25.947 44.94z" style="fill:#863bff;fill:color(display-p3 .5252 .23 1);fill-opacity:1"/><mask id="a" width="48" height="46" x="0" y="0" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#000" d="M25.842 44.938c-.664.844-2.021.375-2.021-.698V33.937a2.26 2.26 0 0 0-2.262-2.262H10.183c-.92 0-1.456-1.04-.92-1.788l7.48-10.471c1.07-1.498 0-3.579-1.842-3.579H1.133c-.92 0-1.456-1.04-.92-1.787L9.91.473c.214-.297.556-.474.92-.474h28.894c.92 0 1.456 1.04.92 1.788l-7.48 10.471c-1.07 1.498 0 3.578 1.842 3.578h11.377c.943 0 1.473 1.088.89 1.832L25.843 44.94z" style="fill:#000;fill-opacity:1"/></mask><g mask="url(#a)"><g filter="url(#b)"><ellipse cx="5.508" cy="14.704" fill="#ede6ff" rx="5.508" ry="14.704" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -4.47 31.516)"/></g><g filter="url(#c)"><ellipse cx="10.399" cy="29.851" fill="#ede6ff" rx="10.399" ry="29.851" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -39.328 7.883)"/></g><g filter="url(#d)"><ellipse cx="5.508" cy="30.487" fill="#7e14ff" rx="5.508" ry="30.487" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.814 -25.913 -14.639)scale(1 -1)"/></g><g filter="url(#e)"><ellipse cx="5.508" cy="30.599" fill="#7e14ff" rx="5.508" ry="30.599" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.814 -32.644 -3.334)scale(1 -1)"/></g><g filter="url(#f)"><ellipse cx="5.508" cy="30.599" fill="#7e14ff" rx="5.508" ry="30.599" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -34.34 30.47)"/></g><g filter="url(#g)"><ellipse cx="14.072" cy="22.078" fill="#ede6ff" rx="14.072" ry="22.078" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="rotate(93.35 24.506 48.493)scale(-1 1)"/></g><g filter="url(#h)"><ellipse cx="3.47" cy="21.501" fill="#7e14ff" rx="3.47" ry="21.501" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.009 28.708 47.59)scale(-1 1)"/></g><g filter="url(#i)"><ellipse cx="3.47" cy="21.501" fill="#7e14ff" rx="3.47" ry="21.501" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.009 28.708 47.59)scale(-1 1)"/></g><g filter="url(#j)"><ellipse cx=".387" cy="8.972" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(39.51 .387 8.972)"/></g><g filter="url(#k)"><ellipse cx="47.523" cy="-6.092" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 47.523 -6.092)"/></g><g filter="url(#l)"><ellipse cx="41.412" cy="6.333" fill="#47bfff" rx="5.971" ry="9.665" style="fill:#47bfff;fill:color(display-p3 .2799 .748 1);fill-opacity:1" transform="rotate(37.892 41.412 6.333)"/></g><g filter="url(#m)"><ellipse cx="-1.879" cy="38.332" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 -1.88 38.332)"/></g><g filter="url(#n)"><ellipse cx="-1.879" cy="38.332" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 -1.88 38.332)"/></g><g filter="url(#o)"><ellipse cx="35.651" cy="29.907" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 35.651 29.907)"/></g><g filter="url(#p)"><ellipse cx="38.418" cy="32.4" fill="#47bfff" rx="5.971" ry="15.297" style="fill:#47bfff;fill:color(display-p3 .2799 .748 1);fill-opacity:1" transform="rotate(37.892 38.418 32.4)"/></g></g><defs><filter id="b" width="60.045" height="41.654" x="-19.77" y="16.149" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="c" width="90.34" height="51.437" x="-54.613" y="-7.533" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filtLine truncated
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 1000 1000" style="enable-background:new 0 0 1000 1000;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{display:none;}
|
||||
.st1{stroke:#000000;stroke-miterlimit:10;}
|
||||
.st2{display:inline;fill:#00B5FF;}
|
||||
.st3{display:inline;fill:#008EFF;}
|
||||
.st4{display:inline;fill:#0063EE;}
|
||||
.st5{display:inline;}
|
||||
.st6{fill:#00B5FF;}
|
||||
.st7{fill:#008EFF;}
|
||||
.st8{fill:#0063EE;}
|
||||
.st9{display:none;fill:#FFFFFF;}
|
||||
.st10{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g id="Capa_2" class="st0">
|
||||
<polygon points="374.5,793.5 451.5,839.5 451.5,497.5 374.5,448.23 "/>
|
||||
<polygon class="st1" points="221.5,701.5 294.5,746.5 294.5,366.5 221.5,320.23 "/>
|
||||
<path d="M294.5,366.5l242-148l102,68l-264,161.73l77,49.27l223-133c0,0,48-35,43-82c0,0-2-48-33-62l-150-90l-313,189.73
|
||||
L294.5,366.5z"/>
|
||||
<polygon class="st2" points="464,846.5 464,728.4 535,790.5 553,777.5 631,830.5 539,890.5 "/>
|
||||
<polyline class="st3" points="848,702 541,506 464,558 778,747 "/>
|
||||
<polygon class="st4" points="464,558 464,503.53 500,478 541,506 "/>
|
||||
<polygon class="st3" points="778,278 778,747 848,702 848,321.43 "/>
|
||||
<path class="st5" d="M374.5,793.5l77,46l0-342l223-133c0,0,87.22-69.98,15.86-140.74L534.5,130.5l-313,189.73l0,381.27l73,45l0-380
|
||||
l242-148l102,68l-264,161.73L374.5,793.5z"/>
|
||||
</g>
|
||||
<g id="Capa_1">
|
||||
|
||||
<image style="display:none;overflow:visible;" width="242" height="207" xlink:href="data:image/png;base64,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
|
||||
ghSvTv//AAE/eVjcr77lAAAAAElFTkSuQmCC" transform="matrix(4.1322 0 0 4.1322 0 46)">
|
||||
</image>
|
||||
<rect class="st0" width="1000" height="1000"/>
|
||||
<polygon class="st6" points="464,846.5 464,728.4 535,790.5 553,777.5 631,830.5 539,890.5 "/>
|
||||
<polyline class="st7" points="847.97,702.06 541.04,505.96 464.02,557.94 777.96,747.04 "/>
|
||||
<polygon class="st8" points="464,558 464,503.53 500,478 541,506 "/>
|
||||
<polygon class="st7" points="778,278 778,747 848,702 848,321.43 "/>
|
||||
<path class="st9" d="M294.5,366.5l242-148l102,68l-264,161.73l77,49.27l223-133c0,0,48-35,43-82c0,0-2-48-33-62l-150-90
|
||||
l-313,189.73L294.5,366.5z"/>
|
||||
<polygon class="st9" points="221.5,701.5 294.5,746.5 294.5,366.5 221.5,320.23 "/>
|
||||
<polygon class="st9" points="374.5,793.5 451.5,839.5 451.5,497.5 374.5,448.23 "/>
|
||||
<path class="st10" d="M374.5,793.5l77,46l0-342l223-133c0,0,87.22-69.98,15.86-140.74L534.5,130.5l-313,189.73l0,381.27l73,45
|
||||
l0-380l242-148l102,68l-264,161.73L374.5,793.5z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 42 KiB |
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 921 KiB |
-277
@@ -1,277 +0,0 @@
|
||||
import { useState } from 'react';
|
||||
import { Routes, Route, useLocation } from 'react-router';
|
||||
import type { Course, CartItem, User } from './types/course';
|
||||
import { mockCourses } from './data/mockCourses';
|
||||
import { Navbar } from './components/layout/Navbar';
|
||||
import { Footer } from './components/layout/Footer';
|
||||
import { HomeView } from './views/HomeView';
|
||||
import { BuyerView } from './views/BuyerView';
|
||||
import { SellerView } from './views/SellerView';
|
||||
import { TeachersView } from './views/TeachersView';
|
||||
import { CourseDetailView } from './views/CourseDetailView';
|
||||
import { CreateCourseView } from './views/CreateCourseView';
|
||||
import { AdminDashboardView } from './views/AdminDashboardView';
|
||||
import { LoginView } from './views/LoginView';
|
||||
import { RegisterView } from './views/RegisterView';
|
||||
import { TermsView } from './views/TermsView';
|
||||
import { CourseModal } from './components/buyer/CourseModal';
|
||||
import { CartDrawer } from './components/buyer/CartDrawer';
|
||||
import { CreateCourseModal } from './components/seller/CreateCourseModal';
|
||||
import { FiCheckCircle } from 'react-icons/fi';
|
||||
|
||||
export function App() {
|
||||
const location = useLocation();
|
||||
const isAuthPage = location.pathname === '/login' || location.pathname === '/register' || location.pathname === '/terms';
|
||||
const isDashboardPage = location.pathname === '/admin' || location.pathname === '/teachers';
|
||||
const hideNavbarFooter = isAuthPage || isDashboardPage;
|
||||
|
||||
const [courses, setCourses] = useState<Course[]>(mockCourses);
|
||||
const [cartItems, setCartItems] = useState<CartItem[]>([]);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
|
||||
// Modals & Drawers state
|
||||
const [selectedCourseForModal, setSelectedCourseForModal] = useState<Course | null>(null);
|
||||
const [isTeacherPreviewModal, setIsTeacherPreviewModal] = useState(false);
|
||||
const [isCartOpen, setIsCartOpen] = useState(false);
|
||||
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
|
||||
|
||||
// Auth State
|
||||
const [currentUser, setCurrentUser] = useState<User | null>(() => {
|
||||
const savedUser = localStorage.getItem('rycourses_user');
|
||||
return savedUser ? JSON.parse(savedUser) : null;
|
||||
});
|
||||
|
||||
const handleLogin = (user: User) => {
|
||||
setCurrentUser(user);
|
||||
localStorage.setItem('rycourses_user', JSON.stringify(user));
|
||||
};
|
||||
|
||||
const handleLogout = () => {
|
||||
setCurrentUser(null);
|
||||
localStorage.removeItem('rycourses_user');
|
||||
};
|
||||
|
||||
// Toast feedback state
|
||||
const [toastMessage, setToastMessage] = useState<string | null>(null);
|
||||
|
||||
const showToast = (msg: string) => {
|
||||
setToastMessage(msg);
|
||||
setTimeout(() => {
|
||||
setToastMessage(null);
|
||||
}, 3500);
|
||||
};
|
||||
|
||||
// Cart operations
|
||||
const handleAddToCart = (course: Course) => {
|
||||
if (!cartItems.some((item) => item.course.id === course.id)) {
|
||||
setCartItems([...cartItems, { course, addedAt: new Date().toISOString() }]);
|
||||
showToast(`"${course.title.slice(0, 30)}..." añadido al carrito`);
|
||||
} else {
|
||||
setIsCartOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleBuyNow = (course: Course) => {
|
||||
if (!cartItems.some((item) => item.course.id === course.id)) {
|
||||
setCartItems([...cartItems, { course, addedAt: new Date().toISOString() }]);
|
||||
}
|
||||
setIsCartOpen(true);
|
||||
};
|
||||
|
||||
const handleRemoveFromCart = (courseId: string) => {
|
||||
setCartItems(cartItems.filter((item) => item.course.id !== courseId));
|
||||
};
|
||||
|
||||
const handleClearCart = () => {
|
||||
setCartItems([]);
|
||||
};
|
||||
|
||||
// Course Management
|
||||
const handleCreateCourse = (newCourse: Course) => {
|
||||
setCourses([newCourse, ...courses]);
|
||||
showToast('¡Curso creado y publicado exitosamente en el marketplace!');
|
||||
};
|
||||
|
||||
const handleDeleteCourse = (courseId: string) => {
|
||||
setCourses(courses.filter((c) => c.id !== courseId));
|
||||
showToast('Curso eliminado correctamente');
|
||||
};
|
||||
|
||||
const cartCourseIds = cartItems.map((i) => i.course.id);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col transition-colors duration-300 bg-slate-950 text-slate-100 dark:bg-slate-950 dark:text-slate-100 selection:bg-cyan-500">
|
||||
|
||||
{/* Toast notification banner */}
|
||||
{toastMessage && (
|
||||
<div className="fixed bottom-6 right-6 z-50 flex items-center gap-3 px-5 py-3.5 rounded-2xl border shadow-2xl backdrop-blur-md animate-in slide-in-from-bottom-5 duration-300 bg-slate-900/95 border-cyan-500/40 text-white shadow-cyan-500/20">
|
||||
<FiCheckCircle className="text-cyan-500 text-lg shrink-0" />
|
||||
<span className="text-xs font-semibold">{toastMessage}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Main Navigation Bar (hidden on Auth & Dashboard pages) */}
|
||||
{!hideNavbarFooter && (
|
||||
<Navbar
|
||||
currentUser={currentUser}
|
||||
onLogout={handleLogout}
|
||||
cartCount={cartItems.length}
|
||||
onOpenCart={() => setIsCartOpen(true)}
|
||||
onOpenCreateCourseModal={() => setIsCreateModalOpen(true)}
|
||||
searchQuery={searchQuery}
|
||||
onSearchChange={setSearchQuery}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Main Views Container */}
|
||||
<main className="flex-1">
|
||||
<Routes>
|
||||
<Route
|
||||
path="/"
|
||||
element={
|
||||
<HomeView
|
||||
courses={courses}
|
||||
cartCourseIds={cartCourseIds}
|
||||
searchQuery={searchQuery}
|
||||
onAddToCart={handleAddToCart}
|
||||
onOpenPreview={(courseId) => {
|
||||
const target = courses.find((c) => c.id === courseId) || courses[0];
|
||||
setSelectedCourseForModal(target);
|
||||
setIsTeacherPreviewModal(false);
|
||||
}}
|
||||
onOpenDetails={(course) => {
|
||||
setSelectedCourseForModal(course);
|
||||
setIsTeacherPreviewModal(false);
|
||||
}}
|
||||
onClearSearch={() => setSearchQuery('')}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/courses"
|
||||
element={
|
||||
<BuyerView
|
||||
courses={courses}
|
||||
cartCourseIds={cartCourseIds}
|
||||
searchQuery={searchQuery}
|
||||
onAddToCart={handleAddToCart}
|
||||
onOpenDetails={(course) => {
|
||||
setSelectedCourseForModal(course);
|
||||
setIsTeacherPreviewModal(false);
|
||||
}}
|
||||
onClearSearch={() => setSearchQuery('')}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/course/:courseId"
|
||||
element={
|
||||
<CourseDetailView
|
||||
courses={courses}
|
||||
cartCourseIds={cartCourseIds}
|
||||
onAddToCart={handleAddToCart}
|
||||
onBuyNow={handleBuyNow}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/admin"
|
||||
element={
|
||||
<AdminDashboardView
|
||||
courses={courses}
|
||||
onDeleteCourse={handleDeleteCourse}
|
||||
onShowToast={showToast}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/teachers"
|
||||
element={
|
||||
<TeachersView
|
||||
courses={courses}
|
||||
onOpenCreateCourse={() => setIsCreateModalOpen(true)}
|
||||
onPreviewCourse={(course) => {
|
||||
setSelectedCourseForModal(course);
|
||||
setIsTeacherPreviewModal(true);
|
||||
}}
|
||||
onDeleteCourse={handleDeleteCourse}
|
||||
onShowToast={showToast}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/seller"
|
||||
element={
|
||||
<SellerView
|
||||
currentUser={currentUser}
|
||||
courses={courses}
|
||||
onPreviewCourse={(course) => {
|
||||
setSelectedCourseForModal(course);
|
||||
setIsTeacherPreviewModal(true);
|
||||
}}
|
||||
onDeleteCourse={handleDeleteCourse}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/seller/create"
|
||||
element={
|
||||
<CreateCourseView
|
||||
onCreateCourse={handleCreateCourse}
|
||||
onShowToast={showToast}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/terms"
|
||||
element={<TermsView />}
|
||||
/>
|
||||
<Route
|
||||
path="/login"
|
||||
element={<LoginView onShowToast={showToast} onLogin={handleLogin} />}
|
||||
/>
|
||||
<Route
|
||||
path="/register"
|
||||
element={<RegisterView onShowToast={showToast} onLogin={handleLogin} />}
|
||||
/>
|
||||
</Routes>
|
||||
</main>
|
||||
|
||||
{/* Course Detail Modal */}
|
||||
<CourseModal
|
||||
course={selectedCourseForModal}
|
||||
isOpen={!!selectedCourseForModal}
|
||||
isInCart={selectedCourseForModal ? cartCourseIds.includes(selectedCourseForModal.id) : false}
|
||||
isTeacherPreview={isTeacherPreviewModal}
|
||||
onClose={() => {
|
||||
setSelectedCourseForModal(null);
|
||||
setIsTeacherPreviewModal(false);
|
||||
}}
|
||||
onAddToCart={handleAddToCart}
|
||||
onBuyNow={handleBuyNow}
|
||||
/>
|
||||
|
||||
{/* Slide-over Shopping Cart */}
|
||||
<CartDrawer
|
||||
isOpen={isCartOpen}
|
||||
cartItems={cartItems}
|
||||
onClose={() => setIsCartOpen(false)}
|
||||
onRemoveItem={handleRemoveFromCart}
|
||||
onClearCart={handleClearCart}
|
||||
/>
|
||||
|
||||
{/* Create Course Modal */}
|
||||
<CreateCourseModal
|
||||
isOpen={isCreateModalOpen}
|
||||
onClose={() => setIsCreateModalOpen(false)}
|
||||
onCreateCourse={handleCreateCourse}
|
||||
/>
|
||||
|
||||
{/* Global Footer (hidden on Auth & Dashboard pages) */}
|
||||
{!hideNavbarFooter && <Footer />}
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -0,0 +1,36 @@
|
||||
import { FiGrid, FiHome, FiBox, FiTag } from "react-icons/fi";
|
||||
|
||||
export interface INavigation
|
||||
{
|
||||
name: string;
|
||||
description?: string;
|
||||
path: string;
|
||||
icon: React.ReactNode;
|
||||
}
|
||||
|
||||
export const RouteNavigation: INavigation[] = [
|
||||
{
|
||||
name: "Inicio",
|
||||
description: "Inicio",
|
||||
path: "/",
|
||||
icon: <FiHome />,
|
||||
},
|
||||
{
|
||||
name: "Catálogo",
|
||||
description: "Catálogo",
|
||||
path: "/catalog",
|
||||
icon: <FiGrid />,
|
||||
},
|
||||
{
|
||||
name: "Planes",
|
||||
description: "Planes para instructores",
|
||||
path: "/plans",
|
||||
icon: <FiTag />,
|
||||
},
|
||||
{
|
||||
name: "Componentes",
|
||||
description: "Guía de componentes UI",
|
||||
path: "/components",
|
||||
icon: <FiBox />,
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,229 @@
|
||||
"use client";
|
||||
import { useState, useRef } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { FiArrowRight, FiStar, FiUsers, FiSearch } from "react-icons/fi";
|
||||
import { Dropdown } from "@/components/ui/Dropdown";
|
||||
import { HeroSection } from "@/components/buyer/HeroSection";
|
||||
import { CourseCard } from "@/components/buyer/CourseCard";
|
||||
import { FeaturesBanner } from "@/components/buyer/FeaturesBanner";
|
||||
import { useTheme } from "@/context/ThemeContext";
|
||||
import {
|
||||
mockCourses,
|
||||
mockInstructors,
|
||||
mockCategories,
|
||||
} from "@/data/mockCourses";
|
||||
import LayoutBase from "@/components/layout/LayoutBase";
|
||||
|
||||
export default function HomeContent() {
|
||||
const { isDark } = useTheme();
|
||||
const router = useRouter();
|
||||
const catalogRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const [selectedCategories, setSelectedCategories] = useState<string[]>([]);
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
|
||||
const filteredCourses = mockCourses.filter((c) => {
|
||||
const matchesCategory =
|
||||
selectedCategories.length === 0 ||
|
||||
selectedCategories.includes(c.category);
|
||||
const matchesSearch =
|
||||
searchQuery === "" ||
|
||||
c.title.toLowerCase().includes(searchQuery.toLowerCase());
|
||||
return matchesCategory && matchesSearch;
|
||||
});
|
||||
|
||||
const scrollToCatalog = () => {
|
||||
catalogRef.current?.scrollIntoView({ behavior: "smooth" });
|
||||
};
|
||||
|
||||
const instructors = Object.values(mockInstructors);
|
||||
|
||||
return (
|
||||
<>
|
||||
<LayoutBase>
|
||||
{/* Hero — ocupa ancho completo */}
|
||||
<div className="-mx-4 sm:-mx-6 lg:-mx-8 -mt-8">
|
||||
<HeroSection
|
||||
onExploreClick={scrollToCatalog}
|
||||
onOpenPreview={(id) => router.push(`/course/${id}`)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* ── Catálogo ── */}
|
||||
<section ref={catalogRef} className="pt-16 pb-8">
|
||||
<div className="flex flex-col sm:flex-row sm:items-end justify-between gap-4 mb-6">
|
||||
<div>
|
||||
<p
|
||||
className={`text-xs font-bold uppercase tracking-widest mb-1 ${isDark ? "text-violet-400" : "text-violet-600"}`}
|
||||
>
|
||||
Catálogo
|
||||
</p>
|
||||
<h2
|
||||
className={`text-2xl font-bold ${isDark ? "text-white" : "text-slate-900"}`}
|
||||
>
|
||||
Cursos Populares
|
||||
</h2>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => router.push("/catalogo")}
|
||||
className={`flex items-center gap-1.5 text-sm font-semibold transition ${isDark ? "text-violet-400 hover:text-violet-300" : "text-violet-600 hover:text-violet-500"}`}
|
||||
>
|
||||
Ver todo el catálogo <FiArrowRight />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Filter bar */}
|
||||
<div
|
||||
className={`flex flex-col sm:flex-row sm:items-start gap-3 p-1 rounded-2xl border ${isDark ? "bg-slate-900/60 border-slate-800" : "bg-slate-50 border-slate-200"}`}
|
||||
>
|
||||
{/* Search */}
|
||||
<div className="relative flex-1">
|
||||
<FiSearch
|
||||
className={`absolute left-3.5 top-1/2 -translate-y-1/2 text-sm pointer-events-none ${isDark ? "text-slate-500" : "text-slate-400"}`}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Buscar cursos..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className={`w-full pl-9 pr-4 py-2.5 text-sm rounded-xl bg-transparent outline-none ${isDark ? "text-white placeholder:text-slate-500" : "text-slate-800 placeholder:text-slate-400"}`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Divider */}
|
||||
<div
|
||||
className={`hidden sm:block w-px h-10 my-0.5 ${isDark ? "bg-slate-800" : "bg-slate-200"}`}
|
||||
/>
|
||||
|
||||
{/* Category dropdown */}
|
||||
<Dropdown
|
||||
value={selectedCategories}
|
||||
onChange={setSelectedCategories}
|
||||
placeholder="Todas las categorías"
|
||||
className="w-56 shrink-0"
|
||||
options={mockCategories
|
||||
.filter((cat) => cat.id !== "all")
|
||||
.map((cat) => ({
|
||||
value: cat.name,
|
||||
label: cat.name,
|
||||
count: cat.count,
|
||||
}))}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5">
|
||||
{filteredCourses.map((course) => (
|
||||
<CourseCard key={course.id} course={course} />
|
||||
))}
|
||||
{filteredCourses.length === 0 && (
|
||||
<p
|
||||
className={`col-span-3 text-center py-16 text-sm ${isDark ? "text-slate-500" : "text-slate-400"}`}
|
||||
>
|
||||
No hay cursos en esta categoría todavía.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── Features Banner ── */}
|
||||
<div className="-mx-4 sm:-mx-6 lg:-mx-8">
|
||||
<FeaturesBanner />
|
||||
</div>
|
||||
|
||||
{/* ── Instructores ── */}
|
||||
<section className="py-16">
|
||||
<div className="text-center mb-10">
|
||||
<p
|
||||
className={`text-xs font-bold uppercase tracking-widest mb-1 ${isDark ? "text-violet-400" : "text-violet-600"}`}
|
||||
>
|
||||
Aprende de los mejores
|
||||
</p>
|
||||
<h2
|
||||
className={`text-2xl font-bold ${isDark ? "text-white" : "text-slate-900"}`}
|
||||
>
|
||||
Instructores Destacados
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5">
|
||||
{instructors.map((inst) => (
|
||||
<div
|
||||
key={inst.id}
|
||||
className={`flex items-center gap-4 p-4 rounded-2xl border transition-all hover:-translate-y-0.5 ${
|
||||
isDark
|
||||
? "bg-slate-900 border-slate-800 hover:border-slate-700"
|
||||
: "bg-white border-slate-200 hover:border-slate-300 shadow-sm"
|
||||
}`}
|
||||
>
|
||||
<img
|
||||
src={inst.avatar}
|
||||
alt={inst.name}
|
||||
className="w-14 h-14 rounded-xl object-cover shrink-0"
|
||||
/>
|
||||
<div className="min-w-0">
|
||||
<p
|
||||
className={`text-sm font-bold truncate ${isDark ? "text-white" : "text-slate-900"}`}
|
||||
>
|
||||
{inst.name}
|
||||
</p>
|
||||
<p
|
||||
className={`text-xs truncate mb-2 ${isDark ? "text-slate-400" : "text-slate-500"}`}
|
||||
>
|
||||
{inst.role}
|
||||
</p>
|
||||
<div className="flex items-center gap-3 text-xs">
|
||||
<span className="flex items-center gap-1 text-amber-400 font-semibold">
|
||||
<FiStar className="fill-amber-400" /> {inst.rating}
|
||||
</span>
|
||||
<span
|
||||
className={`flex items-center gap-1 ${isDark ? "text-slate-400" : "text-slate-500"}`}
|
||||
>
|
||||
<FiUsers className="text-violet-400" />
|
||||
{inst.totalStudents.toLocaleString()} alumnos
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── CTA Banner ── */}
|
||||
<div className="-mx-4 sm:-mx-6 lg:-mx-8 -mb-8">
|
||||
<div
|
||||
className={`relative px-8 py-16 text-center transition-colors duration-300 `}
|
||||
>
|
||||
<div className="relative z-10 max-w-xl mx-auto">
|
||||
<p
|
||||
className={`text-xs font-bold uppercase tracking-widest mb-3 ${isDark ? "text-violet-400" : "text-violet-600"}`}
|
||||
>
|
||||
Para instructores
|
||||
</p>
|
||||
<h2
|
||||
className={`text-2xl sm:text-3xl font-extrabold mb-3 ${isDark ? "text-white" : "text-slate-900"}`}
|
||||
>
|
||||
¿Tienes conocimiento que compartir?
|
||||
</h2>
|
||||
<p
|
||||
className={`text-sm mb-8 ${isDark ? "text-slate-400" : "text-slate-500"}`}
|
||||
>
|
||||
Únete a nuestra comunidad de instructores y convierte tu
|
||||
experiencia en ingresos recurrentes.
|
||||
</p>
|
||||
<button
|
||||
onClick={() => router.push("/login")}
|
||||
className={`inline-flex items-center gap-2 px-7 py-3 rounded-xl font-bold text-sm transition-all shadow-lg hover:-translate-y-0.5 ${
|
||||
isDark
|
||||
? "bg-violet-600 hover:bg-violet-500 text-white shadow-violet-900/50"
|
||||
: "bg-violet-600 hover:bg-violet-500 text-white shadow-violet-600/25"
|
||||
}`}
|
||||
>
|
||||
Comenzar a enseñar <FiArrowRight />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</LayoutBase>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,116 @@
|
||||
"use client";
|
||||
import { useState } from "react";
|
||||
import { FiSearch, FiX } from "react-icons/fi";
|
||||
import { CourseCard } from "@/components/buyer/CourseCard";
|
||||
import LayoutBase from "@/components/layout/LayoutBase";
|
||||
import { useTheme } from "@/context/ThemeContext";
|
||||
import { useFilters } from "@/hooks/useFilters";
|
||||
import { CatalogSidebar } from "@/components/catalog/CatalogSidebar";
|
||||
import { CatalogToolbar } from "@/components/catalog/CatalogToolbar";
|
||||
import { ListCourseCard } from "@/components/catalog/ListCourseCard";
|
||||
|
||||
function EmptyState({ onClear }: { onClear: () => void }) {
|
||||
const { isDark } = useTheme();
|
||||
return (
|
||||
<div className={`flex flex-col items-center justify-center py-24 text-center rounded-2xl border ${isDark ? "border-slate-800 bg-slate-900/40" : "border-slate-200 bg-white"}`}>
|
||||
<FiSearch className={`text-4xl mb-4 ${isDark ? "text-slate-600" : "text-slate-300"}`} />
|
||||
<p className={`text-base font-semibold mb-1 ${isDark ? "text-slate-400" : "text-slate-500"}`}>No se encontraron cursos</p>
|
||||
<p className={`text-sm mb-4 ${isDark ? "text-slate-600" : "text-slate-400"}`}>Prueba ajustando los filtros o usa otros términos de búsqueda</p>
|
||||
<button onClick={onClear} className="px-4 py-2 bg-violet-600 hover:bg-violet-500 text-white text-sm font-semibold rounded-xl transition-colors">
|
||||
Limpiar filtros
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function MobileSidebarOverlay({ onClose, children }: { onClose: () => void; children: React.ReactNode }) {
|
||||
const { isDark } = useTheme();
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 lg:hidden">
|
||||
<div className="absolute inset-0 bg-black/60 backdrop-blur-sm" onClick={onClose} />
|
||||
<div className={`absolute left-0 top-0 bottom-0 w-72 overflow-y-auto p-5 ${isDark ? "bg-slate-900" : "bg-white"}`}>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className={`font-bold ${isDark ? "text-white" : "text-slate-800"}`}>Filtros</span>
|
||||
<button onClick={onClose} className={`p-1 rounded-lg ${isDark ? "hover:bg-slate-800 text-slate-400" : "hover:bg-slate-100 text-slate-500"}`}>
|
||||
<FiX />
|
||||
</button>
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function CatalogContent() {
|
||||
const { isDark } = useTheme();
|
||||
const [gridView, setGridView] = useState<"grid" | "list">("grid");
|
||||
const [sidebarOpen, setSidebarOpen] = useState(false);
|
||||
const filters = useFilters();
|
||||
|
||||
const sidebarProps = {
|
||||
selectedCategories: filters.selectedCategories,
|
||||
onToggleCategory: filters.toggleCategory,
|
||||
selectedLevels: filters.selectedLevels,
|
||||
onToggleLevel: filters.toggleLevel,
|
||||
minRating: filters.minRating,
|
||||
onToggleRating: filters.toggleRating,
|
||||
maxPrice: filters.maxPrice,
|
||||
onTogglePrice: filters.togglePrice,
|
||||
activeFiltersCount: filters.activeFiltersCount,
|
||||
onClearAll: filters.clearAll,
|
||||
};
|
||||
|
||||
const sidebarCl = isDark ? "bg-slate-900/70 border-slate-800" : "bg-white border-slate-200";
|
||||
|
||||
return (
|
||||
<LayoutBase>
|
||||
<div className={`-mx-4 sm:-mx-6 lg:-mx-8 px-4 sm:px-6 lg:px-8 mb-8 ${isDark ? "" : "bg-white"}`}>
|
||||
<p className={`text-xs font-bold uppercase tracking-widest mb-1 ${isDark ? "text-violet-400" : "text-violet-600"}`}>Explora</p>
|
||||
<h1 className={`text-3xl font-extrabold mb-4 ${isDark ? "text-white" : "text-slate-900"}`}>Catálogo de Cursos</h1>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-6 items-start">
|
||||
<aside className={`hidden lg:block w-60 shrink-0 rounded-2xl border p-5 top-20 ${sidebarCl}`}>
|
||||
<CatalogSidebar {...sidebarProps} />
|
||||
</aside>
|
||||
|
||||
<div className="flex-1 min-w-0">
|
||||
<CatalogToolbar
|
||||
search={filters.search}
|
||||
onSearchChange={filters.setSearch}
|
||||
sort={filters.sort}
|
||||
onSortChange={filters.setSort}
|
||||
gridView={gridView}
|
||||
onGridViewChange={setGridView}
|
||||
activeFiltersCount={filters.activeFiltersCount}
|
||||
onOpenMobileSidebar={() => setSidebarOpen(true)}
|
||||
/>
|
||||
|
||||
{filters.filtered.length === 0 ? (
|
||||
<EmptyState onClear={filters.clearAll} />
|
||||
) : gridView === "grid" ? (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-5">
|
||||
{filters.filtered.map((course) => <CourseCard key={course.id} course={course} />)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col gap-4">
|
||||
{filters.filtered.map((course) => <ListCourseCard key={course.id} course={course} />)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{sidebarOpen && (
|
||||
<MobileSidebarOverlay onClose={() => setSidebarOpen(false)}>
|
||||
<CatalogSidebar {...sidebarProps} />
|
||||
<button
|
||||
onClick={() => setSidebarOpen(false)}
|
||||
className="w-full py-3 bg-violet-600 hover:bg-violet-500 text-white font-bold rounded-xl text-sm transition-colors mt-2"
|
||||
>
|
||||
Ver {filters.filtered.length} resultados
|
||||
</button>
|
||||
</MobileSidebarOverlay>
|
||||
)}
|
||||
</LayoutBase>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import LayoutBase from "@/components/layout/LayoutBase";
|
||||
|
||||
function Skeleton({ className }: { className?: string }) {
|
||||
return (
|
||||
<div className={`animate-pulse rounded-xl bg-slate-800/60 ${className ?? ""}`} />
|
||||
);
|
||||
}
|
||||
|
||||
export default function CatalogLoading() {
|
||||
return (
|
||||
<LayoutBase>
|
||||
{/* Header skeleton */}
|
||||
<div className="-mx-4 sm:-mx-6 lg:-mx-8 px-4 sm:px-6 lg:px-8 mb-8 py-4">
|
||||
<Skeleton className="h-3 w-16 mb-3" />
|
||||
<Skeleton className="h-8 w-56 mb-5" />
|
||||
<Skeleton className="h-9 max-w-xl w-full rounded-xl" />
|
||||
<div className="flex gap-6 mt-4">
|
||||
<Skeleton className="h-3 w-24" />
|
||||
<Skeleton className="h-3 w-28" />
|
||||
<Skeleton className="h-3 w-36" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-6 items-start">
|
||||
{/* Sidebar skeleton */}
|
||||
<aside className="hidden lg:flex flex-col gap-4 w-60 shrink-0 rounded-2xl border border-slate-800 bg-slate-900/70 p-5">
|
||||
<div className="flex justify-between items-center mb-1">
|
||||
<Skeleton className="h-4 w-20" />
|
||||
</div>
|
||||
{["Categoría", "Nivel", "Valoración mínima", "Precio máximo"].map((_, i) => (
|
||||
<div key={i} className="border-b border-slate-800 pb-4 -mx-5 px-5">
|
||||
<Skeleton className="h-4 w-28 mb-3" />
|
||||
<div className="space-y-2">
|
||||
{Array.from({ length: i === 0 ? 4 : 3 }).map((_, j) => (
|
||||
<div key={j} className="flex items-center gap-2">
|
||||
<Skeleton className="h-4 w-4 rounded" />
|
||||
<Skeleton className="h-3 flex-1" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</aside>
|
||||
|
||||
{/* Main skeleton */}
|
||||
<div className="flex-1 min-w-0">
|
||||
{/* Toolbar */}
|
||||
<div className="flex items-center gap-3 mb-5">
|
||||
<Skeleton className="h-9 flex-1 min-w-48 rounded-xl" />
|
||||
<Skeleton className="h-9 w-36 rounded-xl shrink-0" />
|
||||
<Skeleton className="h-9 w-[72px] rounded-xl shrink-0" />
|
||||
</div>
|
||||
|
||||
{/* Grid */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-5">
|
||||
{Array.from({ length: 6 }).map((_, i) => (
|
||||
<div key={i} className="rounded-2xl border border-slate-800 bg-slate-900/90 overflow-hidden">
|
||||
<Skeleton className="aspect-video w-full rounded-none" />
|
||||
<div className="p-5 space-y-3">
|
||||
<div className="flex justify-between">
|
||||
<Skeleton className="h-3 w-24" />
|
||||
<Skeleton className="h-3 w-16" />
|
||||
</div>
|
||||
<Skeleton className="h-4 w-full" />
|
||||
<Skeleton className="h-4 w-4/5" />
|
||||
<Skeleton className="h-3 w-full" />
|
||||
<Skeleton className="h-3 w-3/4" />
|
||||
<div className="flex items-center gap-2 pt-1">
|
||||
<Skeleton className="h-6 w-6 rounded-full" />
|
||||
<Skeleton className="h-3 w-28" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-5 pt-3 border-t border-slate-800 flex justify-between items-center">
|
||||
<div className="space-y-1">
|
||||
<Skeleton className="h-5 w-16" />
|
||||
<Skeleton className="h-3 w-10" />
|
||||
</div>
|
||||
<Skeleton className="h-9 w-24 rounded-xl" />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</LayoutBase>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Suspense } from "react";
|
||||
import { CatalogContent } from "./_content";
|
||||
import Loading from "./loading";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<Suspense fallback={<Loading/>}>
|
||||
<CatalogContent />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,397 @@
|
||||
"use client";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
FiZap,
|
||||
FiMail,
|
||||
FiSearch,
|
||||
FiStar,
|
||||
FiCheck,
|
||||
FiAlertCircle,
|
||||
FiInfo,
|
||||
} from "react-icons/fi";
|
||||
import { useTheme } from "@/context/ThemeContext";
|
||||
import {
|
||||
Button,
|
||||
Input,
|
||||
Textarea,
|
||||
Select,
|
||||
Checkbox,
|
||||
Switch,
|
||||
Badge,
|
||||
Spinner,
|
||||
Card,
|
||||
CardTitle,
|
||||
CardDescription,
|
||||
CardContent,
|
||||
Modal,
|
||||
} from "@/components/ui";
|
||||
import LayoutBase from "@/components/layout/LayoutBase";
|
||||
|
||||
const Section = ({
|
||||
title,
|
||||
children,
|
||||
}: {
|
||||
title: string;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
const { isDark } = useTheme();
|
||||
return (
|
||||
<section className="space-y-4">
|
||||
<div
|
||||
className={`pb-2 border-b ${isDark ? "border-slate-800" : "border-slate-200"}`}
|
||||
>
|
||||
<h2
|
||||
className={`text-lg font-bold ${isDark ? "text-white" : "text-slate-900"}`}
|
||||
>
|
||||
{title}
|
||||
</h2>
|
||||
</div>
|
||||
{children}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
const Row = ({ children }: { children: React.ReactNode }) => (
|
||||
<div className="flex flex-wrap items-center gap-3">{children}</div>
|
||||
);
|
||||
|
||||
const CodeBlock = ({ code }: { code: string }) => {
|
||||
const { isDark } = useTheme();
|
||||
return (
|
||||
<pre
|
||||
className={`text-xs rounded-xl px-4 py-3 overflow-x-auto font-mono ${isDark ? "bg-slate-900 text-slate-300" : "bg-slate-100 text-slate-700"}`}
|
||||
>
|
||||
{code}
|
||||
</pre>
|
||||
);
|
||||
};
|
||||
|
||||
export default function ComponentsContent() {
|
||||
const { isDark } = useTheme();
|
||||
const [inputVal, setInputVal] = useState("");
|
||||
const [textareaVal, setTextareaVal] = useState("");
|
||||
const [checked, setChecked] = useState(false);
|
||||
const [switched, setSwitched] = useState(false);
|
||||
const [modal, setModal] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
{" "}
|
||||
<LayoutBase>
|
||||
<div className="space-y-14 py-4">
|
||||
{/* Header */}
|
||||
<div>
|
||||
<p
|
||||
className={`text-xs font-semibold uppercase tracking-widest mb-1 ${isDark ? "text-violet-400" : "text-violet-600"}`}
|
||||
>
|
||||
Rynext Academy UI
|
||||
</p>
|
||||
<h1
|
||||
className={`text-3xl font-bold ${isDark ? "text-white" : "text-slate-900"}`}
|
||||
>
|
||||
Guía de Componentes
|
||||
</h1>
|
||||
<p
|
||||
className={`mt-2 text-sm ${isDark ? "text-slate-400" : "text-slate-500"}`}
|
||||
>
|
||||
Referencia visual de todos los componentes disponibles y cómo
|
||||
usarlos.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* BUTTON */}
|
||||
<Section title="Button">
|
||||
<p
|
||||
className={`text-xs ${isDark ? "text-slate-400" : "text-slate-500"}`}
|
||||
>
|
||||
Variantes:{" "}
|
||||
<code>
|
||||
primary · secondary · outline · ghost · danger · success ·
|
||||
gradient
|
||||
</code>
|
||||
</p>
|
||||
<Row>
|
||||
<Button variant="primary">Primary</Button>
|
||||
<Button variant="secondary">Secondary</Button>
|
||||
<Button variant="outline">Outline</Button>
|
||||
<Button variant="ghost">Ghost</Button>
|
||||
<Button variant="danger">Danger</Button>
|
||||
<Button variant="success">Success</Button>
|
||||
<Button variant="gradient">Gradient</Button>
|
||||
</Row>
|
||||
<Row>
|
||||
<Button size="xs">xs</Button>
|
||||
<Button size="sm">sm</Button>
|
||||
<Button size="md">md</Button>
|
||||
<Button size="lg">lg</Button>
|
||||
<Button isLoading loadingText="Cargando...">
|
||||
Submit
|
||||
</Button>
|
||||
<Button leftIcon={<FiZap />}>Con ícono</Button>
|
||||
</Row>
|
||||
<CodeBlock
|
||||
code={`<Button variant="gradient" size="lg" leftIcon={<FiZap />}>
|
||||
Acción principal
|
||||
</Button>`}
|
||||
/>
|
||||
</Section>
|
||||
|
||||
{/* BADGE */}
|
||||
<Section title="Badge">
|
||||
<Row>
|
||||
<Badge variant="default">Default</Badge>
|
||||
<Badge variant="primary">Primary</Badge>
|
||||
<Badge variant="secondary">Secondary</Badge>
|
||||
<Badge variant="success" dot dotPulse>
|
||||
En línea
|
||||
</Badge>
|
||||
<Badge variant="warning">Warning</Badge>
|
||||
<Badge variant="danger">Danger</Badge>
|
||||
<Badge variant="info">Info</Badge>
|
||||
<Badge variant="outline">Outline</Badge>
|
||||
</Row>
|
||||
<Row>
|
||||
<Badge size="sm" variant="success">
|
||||
sm
|
||||
</Badge>
|
||||
<Badge size="md" variant="primary">
|
||||
md
|
||||
</Badge>
|
||||
<Badge size="lg" variant="secondary">
|
||||
lg
|
||||
</Badge>
|
||||
<Badge variant="danger" icon={<FiAlertCircle />}>
|
||||
Con ícono
|
||||
</Badge>
|
||||
<Badge variant="primary" removable onRemove={() => {}}>
|
||||
Removable
|
||||
</Badge>
|
||||
</Row>
|
||||
<CodeBlock
|
||||
code={`<Badge variant="success" dot dotPulse>En línea</Badge>
|
||||
<Badge variant="danger" icon={<FiAlertCircle />}>Error</Badge>
|
||||
<Badge variant="primary" removable onRemove={() => {}}>Tag</Badge>`}
|
||||
/>
|
||||
</Section>
|
||||
|
||||
{/* SPINNER */}
|
||||
<Section title="Spinner">
|
||||
<Row>
|
||||
<Spinner size="xs" />
|
||||
<Spinner size="sm" />
|
||||
<Spinner size="md" />
|
||||
<Spinner size="lg" />
|
||||
<Spinner size="xl" />
|
||||
</Row>
|
||||
<CodeBlock
|
||||
code={`<Spinner size="md" />
|
||||
<Spinner size="lg" color="cyan" />`}
|
||||
/>
|
||||
</Section>
|
||||
|
||||
{/* INPUT */}
|
||||
<Section title="Input">
|
||||
<div className="grid sm:grid-cols-2 gap-4">
|
||||
<Input
|
||||
label="Email"
|
||||
type="email"
|
||||
placeholder="[email protected]"
|
||||
leftIcon={<FiMail />}
|
||||
value={inputVal}
|
||||
onChange={(e) => setInputVal(e.target.value)}
|
||||
isClearable
|
||||
onClear={() => setInputVal("")}
|
||||
/>
|
||||
<Input
|
||||
label="Búsqueda"
|
||||
placeholder="Buscar curso..."
|
||||
leftIcon={<FiSearch />}
|
||||
/>
|
||||
<Input
|
||||
label="Con error"
|
||||
placeholder="Campo requerido"
|
||||
errorMessage="Este campo es obligatorio"
|
||||
/>
|
||||
<Input
|
||||
label="Contraseña"
|
||||
type="password"
|
||||
placeholder="••••••••"
|
||||
helperText="Mínimo 8 caracteres"
|
||||
/>
|
||||
</div>
|
||||
<CodeBlock
|
||||
code={`<Input
|
||||
label="Email"
|
||||
type="email"
|
||||
placeholder="[email protected]"
|
||||
leftIcon={<FiMail />}
|
||||
isClearable
|
||||
onClear={() => setEmail("")}
|
||||
/>`}
|
||||
/>
|
||||
</Section>
|
||||
|
||||
{/* TEXTAREA */}
|
||||
<Section title="Textarea">
|
||||
<Textarea
|
||||
label="Descripción"
|
||||
placeholder="Escribe aquí..."
|
||||
value={textareaVal}
|
||||
onChange={(e) => setTextareaVal(e.target.value)}
|
||||
helperText={`${textareaVal.length} / 500 caracteres`}
|
||||
/>
|
||||
<CodeBlock
|
||||
code={`<Textarea
|
||||
label="Descripción"
|
||||
placeholder="Escribe aquí..."
|
||||
helperText="Máximo 500 caracteres"
|
||||
/>`}
|
||||
/>
|
||||
</Section>
|
||||
|
||||
{/* SELECT */}
|
||||
<Section title="Select">
|
||||
<div className="max-w-xs">
|
||||
<Select
|
||||
label="Categoría"
|
||||
leftIcon={<FiStar />}
|
||||
options={[
|
||||
{ value: "dev", label: "Desarrollo Web" },
|
||||
{ value: "design", label: "Diseño UI/UX" },
|
||||
{ value: "data", label: "Ciencia de Datos" },
|
||||
{ value: "mkt", label: "Marketing Digital" },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<CodeBlock
|
||||
code={`<Select
|
||||
label="Categoría"
|
||||
options={[
|
||||
{ value: "dev", label: "Desarrollo Web" },
|
||||
{ value: "design", label: "Diseño UI/UX" },
|
||||
]}
|
||||
/>`}
|
||||
/>
|
||||
</Section>
|
||||
|
||||
{/* CHECKBOX & SWITCH */}
|
||||
<Section title="Checkbox & Switch">
|
||||
<Row>
|
||||
<Checkbox
|
||||
label="Recordar sesión"
|
||||
checked={checked}
|
||||
onChange={(e) => setChecked(e.target.checked)}
|
||||
/>
|
||||
<Checkbox label="Deshabilitado" disabled />
|
||||
<Checkbox
|
||||
label="Marcado fijo"
|
||||
checked
|
||||
disabled
|
||||
onChange={() => {}}
|
||||
/>
|
||||
</Row>
|
||||
<Row>
|
||||
<Switch
|
||||
label="Notificaciones"
|
||||
description="Recibe alertas por correo"
|
||||
checked={switched}
|
||||
onChange={setSwitched}
|
||||
/>
|
||||
<Switch label="Deshabilitado" disabled />
|
||||
</Row>
|
||||
<CodeBlock
|
||||
code={`<Checkbox
|
||||
label="Recordar sesión"
|
||||
checked={checked}
|
||||
onChange={e => setChecked(e.target.checked)}
|
||||
/>
|
||||
|
||||
<Switch
|
||||
label="Notificaciones"
|
||||
description="Recibe alertas por correo"
|
||||
checked={on}
|
||||
onChange={setOn}
|
||||
/>`}
|
||||
/>
|
||||
</Section>
|
||||
|
||||
{/* CARD */}
|
||||
<Section title="Card">
|
||||
<div className="grid sm:grid-cols-3 gap-4">
|
||||
{(["default", "glass", "outline"] as const).map((v) => (
|
||||
<Card key={v} variant={v} padding="md">
|
||||
<CardTitle>Variante {v}</CardTitle>
|
||||
<CardDescription>
|
||||
Descripción de ejemplo para esta card.
|
||||
</CardDescription>
|
||||
<CardContent>
|
||||
<Badge variant="primary">{v}</Badge>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
<CodeBlock
|
||||
code={`<Card variant="glass" padding="md">
|
||||
<CardTitle>Título</CardTitle>
|
||||
<CardDescription>Descripción breve.</CardDescription>
|
||||
<CardContent>
|
||||
<Badge variant="primary">Tag</Badge>
|
||||
</CardContent>
|
||||
</Card>`}
|
||||
/>
|
||||
</Section>
|
||||
|
||||
{/* MODAL */}
|
||||
<Section title="Modal">
|
||||
<Row>
|
||||
<Button
|
||||
variant="outline"
|
||||
leftIcon={<FiInfo />}
|
||||
onClick={() => setModal(true)}
|
||||
>
|
||||
Abrir Modal
|
||||
</Button>
|
||||
</Row>
|
||||
<Modal
|
||||
isOpen={modal}
|
||||
onClose={() => setModal(false)}
|
||||
title="Ejemplo de Modal"
|
||||
description="Los modales se usan para confirmaciones, formularios o información importante."
|
||||
footer={
|
||||
<Row>
|
||||
<Button variant="ghost" onClick={() => setModal(false)}>
|
||||
Cancelar
|
||||
</Button>
|
||||
<Button
|
||||
variant="gradient"
|
||||
leftIcon={<FiCheck />}
|
||||
onClick={() => setModal(false)}
|
||||
>
|
||||
Confirmar
|
||||
</Button>
|
||||
</Row>
|
||||
}
|
||||
>
|
||||
<p
|
||||
className={`text-sm ${isDark ? "text-slate-300" : "text-slate-600"}`}
|
||||
>
|
||||
Contenido del modal. Puede incluir formularios, texto, imágenes
|
||||
o cualquier componente.
|
||||
</p>
|
||||
</Modal>
|
||||
<CodeBlock
|
||||
code={`<Modal
|
||||
isOpen={open}
|
||||
onClose={() => setOpen(false)}
|
||||
title="Confirmar acción"
|
||||
footer={<Button onClick={() => setOpen(false)}>Confirmar</Button>}
|
||||
>
|
||||
<p>¿Estás seguro?</p>
|
||||
</Modal>`}
|
||||
/>
|
||||
</Section>
|
||||
</div>
|
||||
</LayoutBase>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import ComponentsContent from "./_content";
|
||||
|
||||
export default function Page() {
|
||||
return <ComponentsContent />;
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
"use client";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { FiArrowRight, FiCheck } from "react-icons/fi";
|
||||
import LayoutBase from "@/components/layout/LayoutBase";
|
||||
import { useTheme } from "@/context/ThemeContext";
|
||||
import { Button } from "@/components/ui/Button";
|
||||
import { stats, features, perks, sectors } from "@/data/mockEnterprise";
|
||||
|
||||
function HeroSection() {
|
||||
const { isDark } = useTheme();
|
||||
const router = useRouter();
|
||||
return (
|
||||
<div className="text-center max-w-3xl mx-auto pt-12 pb-16 space-y-6">
|
||||
<span className={`inline-block text-xs font-bold uppercase tracking-widest px-3 py-1 rounded-full border ${isDark ? "text-violet-400 bg-violet-500/10 border-violet-500/20" : "text-violet-600 bg-violet-50 border-violet-200"}`}>
|
||||
Rynext Academy para Empresas
|
||||
</span>
|
||||
<h1 className={`text-4xl sm:text-5xl font-black leading-tight ${isDark ? "text-white" : "text-slate-900"}`}>
|
||||
Capacita a tu equipo.<br />
|
||||
<span className={isDark ? "text-violet-400" : "text-violet-600"}>Multiplica tus resultados.</span>
|
||||
</h1>
|
||||
<p className={`text-lg leading-relaxed ${isDark ? "text-slate-400" : "text-slate-500"}`}>
|
||||
La plataforma de aprendizaje corporativo que convierte la capacitación en una ventaja competitiva real. Desde startups hasta corporativos.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row items-center justify-center gap-3 pt-2">
|
||||
<Button variant="violet" size="lg" rightIcon={<FiArrowRight />} onClick={() => router.push("/login")}>
|
||||
Solicitar demo gratuita
|
||||
</Button>
|
||||
<Button variant="outline" size="lg" onClick={() => router.push("/planes")}>
|
||||
Ver precios
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function StatsSection() {
|
||||
const { isDark } = useTheme();
|
||||
return (
|
||||
<div className={`grid grid-cols-1 md:grid-cols-3 gap-6 mb-20 p-8 rounded-2xl border ${isDark ? "bg-slate-900/40 border-slate-800" : "bg-slate-50 border-slate-200"}`}>
|
||||
{stats.map(({ value, label }) => (
|
||||
<div key={value} className="text-center space-y-2">
|
||||
<p className={`text-5xl font-black ${isDark ? "text-violet-400" : "text-violet-600"}`}>{value}</p>
|
||||
<p className={`text-sm leading-relaxed max-w-xs mx-auto ${isDark ? "text-slate-400" : "text-slate-500"}`}>{label}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function FeaturesSection() {
|
||||
const { isDark } = useTheme();
|
||||
return (
|
||||
<div className="mb-20">
|
||||
<div className="text-center mb-12 space-y-2">
|
||||
<h2 className={`text-2xl sm:text-3xl font-extrabold ${isDark ? "text-white" : "text-slate-900"}`}>
|
||||
Todo lo que necesita tu empresa
|
||||
</h2>
|
||||
<p className={`text-sm ${isDark ? "text-slate-400" : "text-slate-500"}`}>
|
||||
Una sola plataforma para capacitar, medir y certificar a tu equipo.
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5">
|
||||
{features.map(({ icon: Icon, color, bg, title, description }) => (
|
||||
<div key={title} className={`p-6 rounded-2xl border transition-all hover:-translate-y-1 ${isDark ? "bg-slate-900/50 border-slate-800 hover:border-slate-700" : "bg-white border-slate-200 hover:border-slate-300 shadow-sm hover:shadow-md"}`}>
|
||||
<div className={`w-11 h-11 rounded-xl flex items-center justify-center mb-4 ${bg}`}>
|
||||
<Icon className={`text-xl ${color}`} />
|
||||
</div>
|
||||
<h3 className={`text-sm font-bold mb-2 ${isDark ? "text-white" : "text-slate-900"}`}>{title}</h3>
|
||||
<p className={`text-xs leading-relaxed ${isDark ? "text-slate-400" : "text-slate-500"}`}>{description}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function EnterpriseCTA() {
|
||||
const { isDark } = useTheme();
|
||||
const router = useRouter();
|
||||
return (
|
||||
<div className={`grid grid-cols-1 lg:grid-cols-2 gap-10 mb-20 p-10 rounded-2xl border ${isDark ? "bg-violet-950/40 border-violet-900/50" : "bg-violet-600 border-violet-500"}`}>
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<p className={`text-xs font-bold uppercase tracking-widest mb-2 ${isDark ? "text-violet-400" : "text-violet-200"}`}>Plan Enterprise</p>
|
||||
<h2 className="text-2xl font-extrabold text-white">Todo incluido.<br />Sin sorpresas.</h2>
|
||||
</div>
|
||||
<ul className="space-y-3">
|
||||
{perks.map((item) => (
|
||||
<li key={item} className="flex items-start gap-3">
|
||||
<FiCheck className={`text-sm mt-0.5 shrink-0 ${isDark ? "text-violet-400" : "text-violet-200"}`} />
|
||||
<span className={`text-sm ${isDark ? "text-slate-300" : "text-violet-50"}`}>{item}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="flex flex-col justify-center space-y-6">
|
||||
<div>
|
||||
<p className="text-4xl font-black text-white">Precio a medida</p>
|
||||
<p className={`text-sm mt-1 ${isDark ? "text-violet-300" : "text-violet-100"}`}>Adaptado al tamaño de tu equipo y necesidades específicas.</p>
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
<Button variant={isDark ? "violet" : "outline"} size="lg" rightIcon={<FiArrowRight />} onClick={() => router.push("/login")} className="w-full justify-center">
|
||||
Hablar con un asesor
|
||||
</Button>
|
||||
<p className={`text-xs text-center ${isDark ? "text-slate-500" : "text-violet-200"}`}>
|
||||
Respuesta en menos de 24 horas · Sin compromisos
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SectorsSection() {
|
||||
const { isDark } = useTheme();
|
||||
return (
|
||||
<div className="text-center mb-16">
|
||||
<p className={`text-xs font-bold uppercase tracking-widest mb-6 ${isDark ? "text-slate-500" : "text-slate-400"}`}>
|
||||
Confiado por empresas en todos los sectores
|
||||
</p>
|
||||
<div className="flex flex-wrap items-center justify-center gap-4">
|
||||
{sectors.map((sector) => (
|
||||
<span key={sector} className={`px-4 py-2 rounded-xl text-sm font-semibold border ${isDark ? "border-slate-800 text-slate-500 bg-slate-900/40" : "border-slate-200 text-slate-400 bg-slate-50"}`}>
|
||||
{sector}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function EnterpriseContent() {
|
||||
return (
|
||||
<LayoutBase>
|
||||
<HeroSection />
|
||||
<StatsSection />
|
||||
<FeaturesSection />
|
||||
<EnterpriseCTA />
|
||||
<SectorsSection />
|
||||
</LayoutBase>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import EmpresasContent from "./_content";
|
||||
|
||||
|
||||
|
||||
export default function Page() {
|
||||
return <EmpresasContent />;
|
||||
}
|
||||
@@ -0,0 +1,568 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 1000 1000" style="enable-background:new 0 0 1000 1000;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{display:none;}
|
||||
.st1{stroke:#000000;stroke-miterlimit:10;}
|
||||
.st2{display:inline;fill:#00B5FF;}
|
||||
.st3{display:inline;fill:#008EFF;}
|
||||
.st4{display:inline;fill:#0063EE;}
|
||||
.st5{display:inline;}
|
||||
.st6{fill:#00B5FF;}
|
||||
.st7{fill:#008EFF;}
|
||||
.st8{fill:#0063EE;}
|
||||
.st9{display:none;fill:#FFFFFF;}
|
||||
.st10{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g id="Capa_2" class="st0">
|
||||
<polygon points="374.5,793.5 451.5,839.5 451.5,497.5 374.5,448.23 "/>
|
||||
<polygon class="st1" points="221.5,701.5 294.5,746.5 294.5,366.5 221.5,320.23 "/>
|
||||
<path d="M294.5,366.5l242-148l102,68l-264,161.73l77,49.27l223-133c0,0,48-35,43-82c0,0-2-48-33-62l-150-90l-313,189.73
|
||||
L294.5,366.5z"/>
|
||||
<polygon class="st2" points="464,846.5 464,728.4 535,790.5 553,777.5 631,830.5 539,890.5 "/>
|
||||
<polyline class="st3" points="848,702 541,506 464,558 778,747 "/>
|
||||
<polygon class="st4" points="464,558 464,503.53 500,478 541,506 "/>
|
||||
<polygon class="st3" points="778,278 778,747 848,702 848,321.43 "/>
|
||||
<path class="st5" d="M374.5,793.5l77,46l0-342l223-133c0,0,87.22-69.98,15.86-140.74L534.5,130.5l-313,189.73l0,381.27l73,45l0-380
|
||||
l242-148l102,68l-264,161.73L374.5,793.5z"/>
|
||||
</g>
|
||||
<g id="Capa_1">
|
||||
|
||||
<image style="display:none;overflow:visible;" width="242" height="207" xlink:href="data:image/png;base64,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
|
||||
ghSvTv//AAE/eVjcr77lAAAAAElFTkSuQmCC" transform="matrix(4.1322 0 0 4.1322 0 46)">
|
||||
</image>
|
||||
<rect class="st0" width="1000" height="1000"/>
|
||||
<polygon class="st6" points="464,846.5 464,728.4 535,790.5 553,777.5 631,830.5 539,890.5 "/>
|
||||
<polyline class="st7" points="847.97,702.06 541.04,505.96 464.02,557.94 777.96,747.04 "/>
|
||||
<polygon class="st8" points="464,558 464,503.53 500,478 541,506 "/>
|
||||
<polygon class="st7" points="778,278 778,747 848,702 848,321.43 "/>
|
||||
<path class="st9" d="M294.5,366.5l242-148l102,68l-264,161.73l77,49.27l223-133c0,0,48-35,43-82c0,0-2-48-33-62l-150-90
|
||||
l-313,189.73L294.5,366.5z"/>
|
||||
<polygon class="st9" points="221.5,701.5 294.5,746.5 294.5,366.5 221.5,320.23 "/>
|
||||
<polygon class="st9" points="374.5,793.5 451.5,839.5 451.5,497.5 374.5,448.23 "/>
|
||||
<path class="st10" d="M374.5,793.5l77,46l0-342l223-133c0,0,87.22-69.98,15.86-140.74L534.5,130.5l-313,189.73l0,381.27l73,45
|
||||
l0-380l242-148l102,68l-264,161.73L374.5,793.5z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 42 KiB |
@@ -0,0 +1,41 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
@custom-variant dark (&:is(.dark, .dark *));
|
||||
|
||||
@font-face {
|
||||
font-family: 'Nunito';
|
||||
src: url('/fonts/Nunito-VariableFont_wght.ttf') format('truetype');
|
||||
font-weight: 100 900;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
font-family: 'Nunito', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
}
|
||||
}
|
||||
|
||||
@theme {
|
||||
--animate-speed-streak-1: speed-streak 0.6s linear infinite;
|
||||
--animate-speed-streak-2: speed-streak 0.5s linear 0.18s infinite;
|
||||
--animate-speed-streak-3: speed-streak 0.7s linear 0.35s infinite;
|
||||
}
|
||||
|
||||
@keyframes speed-streak {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateX(0) scaleX(0.2);
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 0.8;
|
||||
transform: translateX(-8px) scaleX(1);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: translateX(-16px) scaleX(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import type { Metadata } from "next";
|
||||
import { ThemeProvider } from "@/context/ThemeContext";
|
||||
import { CartProvider } from "@/context/CartContext";
|
||||
import { CartDrawer } from "@/components/buyer/CartDrawer";
|
||||
import { CartAnimator } from "@/components/buyer/CartAnimator";
|
||||
import "./globals.css";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: {
|
||||
default: "Rynext Academy — Aprende. Certifícate. Transforma tu carrera.",
|
||||
template: "%s | Rynext Academy",
|
||||
},
|
||||
description:
|
||||
"Plataforma de cursos en línea con instructores expertos. Aprende desarrollo, diseño, negocios y más. Certifícate y avanza en tu carrera.",
|
||||
keywords: ["cursos online", "aprendizaje en línea", "certificaciones", "Rynext Academy", "educación digital"],
|
||||
authors: [{ name: "Rynext Technologies", url: "https://rynext.com.mx" }],
|
||||
creator: "Rynext Technologies",
|
||||
metadataBase: new URL("https://academy.rynext.com.mx"),
|
||||
openGraph: {
|
||||
type: "website",
|
||||
locale: "es_MX",
|
||||
url: "https://academy.rynext.com.mx",
|
||||
siteName: "Rynext Academy",
|
||||
title: "Rynext Academy — Aprende. Certifícate. Transforma tu carrera.",
|
||||
description:
|
||||
"Plataforma de cursos en línea con instructores expertos. Aprende desarrollo, diseño, negocios y más.",
|
||||
images: [{ url: "/branding/og-image.png", width: 1200, height: 630 }],
|
||||
},
|
||||
twitter: {
|
||||
card: "summary_large_image",
|
||||
title: "Rynext Academy",
|
||||
description: "Aprende habilidades de alta demanda con expertos.",
|
||||
images: ["/branding/og-image.png"],
|
||||
},
|
||||
robots: { index: true, follow: true },
|
||||
icons: {
|
||||
icon: "/favicon.svg",
|
||||
shortcut: "/favicon.svg",
|
||||
},
|
||||
};
|
||||
|
||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<html lang="es" suppressHydrationWarning>
|
||||
<body className="bg-slate-950">
|
||||
<ThemeProvider>
|
||||
<CartProvider>
|
||||
{children}
|
||||
<CartDrawer />
|
||||
<CartAnimator />
|
||||
</CartProvider>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
"use client";
|
||||
import { useState } from "react";
|
||||
import { ThemeToggle } from "@/components/ui/ThemeToggle";
|
||||
import { useTheme } from "@/context/ThemeContext";
|
||||
import { BrandLogo } from "@/components/common/BrandLogo";
|
||||
import { LoginForm } from "@/components/auth/LoginForm";
|
||||
import { RegisterForm } from "@/components/auth/RegisterForm";
|
||||
|
||||
function AuthPanel({ flipped, onFlip }: { flipped: boolean; onFlip: (v: boolean) => void }) {
|
||||
const { isDark } = useTheme();
|
||||
return (
|
||||
<div className={`flex flex-col w-full md:w-1/2 overflow-y-auto ${isDark ? "bg-slate-900" : "bg-white"}`}>
|
||||
<div className="flex items-center justify-between px-10 py-5">
|
||||
<BrandLogo size="sm" asLink />
|
||||
<ThemeToggle variant="switch" />
|
||||
</div>
|
||||
<style>{`@keyframes fadeIn{from{opacity:0}to{opacity:1}}`}</style>
|
||||
<div className="flex flex-1 px-12 py-7">
|
||||
<div key={String(flipped)} className="w-full" style={{ animation: "fadeIn 0.6s ease-out" }}>
|
||||
{flipped
|
||||
? <RegisterForm onSwitchToLogin={() => onFlip(false)} />
|
||||
: <LoginForm onSwitchToRegister={() => onFlip(true)} />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function HeroPanel({ flipped }: { flipped: boolean }) {
|
||||
return (
|
||||
<div
|
||||
className="hidden md:flex flex-col justify-between w-[50%] relative overflow-hidden px-14 py-20 text-white"
|
||||
style={{ background: "linear-gradient(160deg, #3b0fa0 0%, #5b21b6 60%, #4338ca 100%)" }}
|
||||
>
|
||||
<svg className="absolute inset-0 w-full h-full opacity-10 pointer-events-none" viewBox="0 0 500 800" fill="none" preserveAspectRatio="xMidYMid slice">
|
||||
{[180, 260, 340, 420, 500].map((r) => (
|
||||
<circle key={r} cx="420" cy="160" r={r} stroke="white" strokeWidth="1" />
|
||||
))}
|
||||
</svg>
|
||||
<div className="relative" />
|
||||
<div className="relative" key={String(flipped)} style={{ animation: "fadeIn 0.6s ease-out" }}>
|
||||
{flipped ? (
|
||||
<>
|
||||
<h2 className="text-7xl font-black leading-none tracking-tight">
|
||||
Crea tu cuenta.<br /><span className="text-violet-300">Empieza</span><br />hoy mismo.<br />Es gratis.
|
||||
</h2>
|
||||
<p className="mt-6 text-base text-white/50 leading-relaxed">
|
||||
Miles de cursos profesionales te esperan. Sin tarjeta de crédito.
|
||||
</p>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<h2 className="text-7xl font-black leading-none tracking-tight">
|
||||
Aprende.<br />Certifícate.<br /><span className="text-violet-300">Transforma</span><br />tu carrera.
|
||||
</h2>
|
||||
<p className="mt-6 text-base text-white/50 leading-relaxed">
|
||||
La plataforma de cursos en línea para llevar tus habilidades al siguiente nivel.
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="relative">
|
||||
<p className="text-xs text-white/70">© 2026 Rynext Technologies</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function LoginContent() {
|
||||
const [flipped, setFlipped] = useState(false);
|
||||
return (
|
||||
<div className="flex min-h-screen">
|
||||
<HeroPanel flipped={flipped} />
|
||||
<AuthPanel flipped={flipped} onFlip={setFlipped} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import LoginContent from "./_content";
|
||||
|
||||
export default function Page() {
|
||||
return <LoginContent />;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
"use client";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { FiArrowLeft, FiHome } from "react-icons/fi";
|
||||
import { useTheme } from "@/context/ThemeContext";
|
||||
import LayoutBase from "@/components/layout/LayoutBase";
|
||||
|
||||
export default function NotFound() {
|
||||
const router = useRouter();
|
||||
const { isDark } = useTheme();
|
||||
|
||||
return (
|
||||
<LayoutBase>
|
||||
<div className="flex flex-col items-center justify-center min-h-[60vh] text-center px-4">
|
||||
<p className={`text-8xl font-black mb-4 ${isDark ? "text-slate-800" : "text-slate-200"}`}>
|
||||
404
|
||||
</p>
|
||||
<h1 className={`text-2xl font-extrabold mb-2 ${isDark ? "text-white" : "text-slate-900"}`}>
|
||||
Página no encontrada
|
||||
</h1>
|
||||
<p className={`text-sm mb-8 max-w-sm ${isDark ? "text-slate-400" : "text-slate-500"}`}>
|
||||
La página que buscas no existe o fue movida.
|
||||
</p>
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
onClick={() => router.back()}
|
||||
className={`flex items-center gap-2 px-4 py-2.5 rounded-xl border text-sm font-semibold transition-all ${
|
||||
isDark
|
||||
? "border-slate-700 text-slate-300 hover:bg-slate-800"
|
||||
: "border-slate-200 text-slate-600 hover:bg-slate-50"
|
||||
}`}
|
||||
>
|
||||
<FiArrowLeft /> Volver
|
||||
</button>
|
||||
<button
|
||||
onClick={() => router.push("/")}
|
||||
className="flex items-center gap-2 px-4 py-2.5 rounded-xl bg-violet-600 hover:bg-violet-500 text-white text-sm font-semibold transition-all"
|
||||
>
|
||||
<FiHome /> Ir al inicio
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</LayoutBase>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { Metadata } from "next";
|
||||
import HomeContent from "./_content";
|
||||
|
||||
|
||||
|
||||
export default function Page() {
|
||||
return <HomeContent />;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
"use client";
|
||||
import LayoutBase from "@/components/layout/LayoutBase";
|
||||
import { PlanesSection } from "@/components/buyer/PlanesSection";
|
||||
|
||||
export default function PlanesContent() {
|
||||
return (
|
||||
<LayoutBase>
|
||||
<PlanesSection />
|
||||
</LayoutBase>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
import PlanesContent from "./_content";
|
||||
|
||||
export default function Page() {
|
||||
return <PlanesContent />;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
"use client";
|
||||
import LayoutBase from "@/components/layout/LayoutBase";
|
||||
import { useTheme } from "@/context/ThemeContext";
|
||||
import { sections } from "@/data/mockPrivacy";
|
||||
|
||||
export default function PrivacyContent() {
|
||||
const { isDark } = useTheme();
|
||||
|
||||
return (
|
||||
<LayoutBase>
|
||||
<div className="max-w-3xl mx-auto py-12">
|
||||
<div className="mb-10">
|
||||
<p className={`text-xs font-bold uppercase tracking-widest mb-2 ${isDark ? "text-violet-400" : "text-violet-600"}`}>
|
||||
Legal
|
||||
</p>
|
||||
<h1 className={`text-3xl font-extrabold mb-3 ${isDark ? "text-white" : "text-slate-900"}`}>
|
||||
Política de Privacidad
|
||||
</h1>
|
||||
<p className={`text-sm ${isDark ? "text-slate-400" : "text-slate-500"}`}>
|
||||
Última actualización: agosto 2026 · Vigente para todos los usuarios de Rynext Academy.
|
||||
</p>
|
||||
</div>
|
||||
<div className="space-y-8">
|
||||
{sections.map(({ title, content }) => (
|
||||
<div key={title}>
|
||||
<h2 className={`text-sm font-bold uppercase tracking-wider mb-3 ${isDark ? "text-slate-300" : "text-slate-700"}`}>
|
||||
{title}
|
||||
</h2>
|
||||
<p className={`text-sm leading-7 ${isDark ? "text-slate-400" : "text-slate-600"}`}>
|
||||
{content}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</LayoutBase>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import PrivacyContent from "./_content";
|
||||
|
||||
export default function Page() {
|
||||
return <PrivacyContent />;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
"use client";
|
||||
import LayoutBase from "@/components/layout/LayoutBase";
|
||||
import { useTheme } from "@/context/ThemeContext";
|
||||
import { sections } from "@/data/mockTerms";
|
||||
|
||||
export default function TermsContent() {
|
||||
const { isDark } = useTheme();
|
||||
|
||||
return (
|
||||
<LayoutBase>
|
||||
<div className="max-w-3xl mx-auto py-12">
|
||||
|
||||
<div className="mb-10">
|
||||
<p className={`text-xs font-bold uppercase tracking-widest mb-2 ${isDark ? "text-violet-400" : "text-violet-600"}`}>
|
||||
Legal
|
||||
</p>
|
||||
<h1 className={`text-3xl font-extrabold mb-3 ${isDark ? "text-white" : "text-slate-900"}`}>
|
||||
Términos de Servicio y Políticas
|
||||
</h1>
|
||||
<p className={`text-sm ${isDark ? "text-slate-400" : "text-slate-500"}`}>
|
||||
Última actualización: agosto 2026 · Vigente para todos los usuarios de Rynext Academy.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-8">
|
||||
{sections.map(({ title, content }) => (
|
||||
<div key={title}>
|
||||
<h2 className={`text-sm font-bold uppercase tracking-wider mb-3 ${isDark ? "text-slate-300" : "text-slate-700"}`}>
|
||||
{title}
|
||||
</h2>
|
||||
<p className={`text-sm leading-7 ${isDark ? "text-slate-400" : "text-slate-600"}`}>
|
||||
{content}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</LayoutBase>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
import TermsContent from "./_content";
|
||||
|
||||
export default function Page() {
|
||||
return <TermsContent />;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
"use client";
|
||||
|
||||
import { FiShoppingCart } from "react-icons/fi";
|
||||
import { useCart } from "../context/CartContext";
|
||||
|
||||
export function ShopCart() {
|
||||
const { cartItems, openDrawer, cartIconRef } = useCart();
|
||||
const count = cartItems.length;
|
||||
|
||||
return (
|
||||
<button
|
||||
ref={cartIconRef}
|
||||
onClick={openDrawer}
|
||||
className="group relative p-2.5 transition-all hover:border-violet-500/50 cursor-pointer"
|
||||
aria-label="Abrir carrito"
|
||||
>
|
||||
<div className="flex items-center justify-center relative">
|
||||
<FiShoppingCart className="text-lg transition-transform duration-300 group-hover:-rotate-12" />
|
||||
|
||||
{count > 0 && (
|
||||
<span className="absolute -top-1.5 -right-1.5 w-4 h-4 rounded-full bg-violet-500 text-white text-[10px] font-black flex items-center justify-center leading-none">
|
||||
{count > 9 ? "9+" : count}
|
||||
</span>
|
||||
)}
|
||||
|
||||
<span className="absolute top-[30%] right-[110%] w-3 h-px rounded-full bg-violet-400 opacity-0 pointer-events-none group-hover:animate-speed-streak-1" />
|
||||
<span className="absolute top-[60%] right-full w-2 h-px rounded-full bg-violet-400 opacity-0 pointer-events-none group-hover:animate-speed-streak-2" />
|
||||
<span className="absolute bottom-[10%] right-[120%] w-3.5 h-px rounded-full bg-indigo-400 opacity-0 pointer-events-none group-hover:animate-speed-streak-3" />
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
"use client";
|
||||
import { useTheme } from "@/context/ThemeContext";
|
||||
|
||||
export function AuthDivider() {
|
||||
const { isDark } = useTheme();
|
||||
return (
|
||||
<div className={`flex items-center gap-3 my-6 text-[11px] ${isDark ? "text-slate-600" : "text-slate-300"}`}>
|
||||
<span className="flex-1 border-t border-current" />
|
||||
<span className={isDark ? "text-slate-500" : "text-slate-400"}>o continúa con</span>
|
||||
<span className="flex-1 border-t border-current" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
"use client";
|
||||
import { useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { FiMail, FiLock, FiArrowRight, FiCheck } from "react-icons/fi";
|
||||
import { Button, Input, Checkbox } from "@/components/ui";
|
||||
import { useTheme } from "@/context/ThemeContext";
|
||||
import { AuthDivider } from "./AuthDivider";
|
||||
import { SocialButtons } from "./SocialButtons";
|
||||
|
||||
export function LoginForm({ onSwitchToRegister }: { onSwitchToRegister: () => void }) {
|
||||
const router = useRouter();
|
||||
const { isDark } = useTheme();
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [remember, setRemember] = useState(true);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const isValidEmail = email.includes("@") && email.includes(".");
|
||||
|
||||
const handleLogin = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!email || !password) return;
|
||||
setIsLoading(true);
|
||||
setTimeout(() => { setIsLoading(false); router.push("/"); }, 800);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mb-10">
|
||||
<h1 className={`text-3xl font-extrabold tracking-tight ${isDark ? "text-white" : "text-slate-900"}`}>
|
||||
Iniciar sesión
|
||||
</h1>
|
||||
<p className={`text-sm mt-1 ${isDark ? "text-slate-400" : "text-slate-500"}`}>
|
||||
Ingresa tu correo y contraseña para continuar.
|
||||
</p>
|
||||
</div>
|
||||
<form onSubmit={handleLogin} className="space-y-6">
|
||||
<div>
|
||||
<Input
|
||||
label="Correo electrónico"
|
||||
type="email"
|
||||
required
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="[email protected]"
|
||||
leftIcon={<FiMail />}
|
||||
/>
|
||||
{isValidEmail && (
|
||||
<p className="flex items-center gap-1 text-[11px] text-emerald-500 mt-1">
|
||||
<FiCheck className="text-xs" /> Formato válido
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<Input
|
||||
label="Contraseña"
|
||||
type="password"
|
||||
required
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
placeholder="••••••••"
|
||||
leftIcon={<FiLock />}
|
||||
/>
|
||||
<div className="flex justify-end mt-1.5">
|
||||
<a href="#" className="text-[11px] text-violet-500 hover:text-violet-400 hover:underline transition">
|
||||
¿Olvidaste tu contraseña?
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<Checkbox
|
||||
checked={remember}
|
||||
onChange={(e) => setRemember(e.target.checked)}
|
||||
label="Recordar sesión"
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="violet"
|
||||
size="lg"
|
||||
fullWidth
|
||||
isLoading={isLoading}
|
||||
loadingText="Verificando..."
|
||||
rightIcon={<FiArrowRight />}
|
||||
>
|
||||
Entrar
|
||||
</Button>
|
||||
</form>
|
||||
<AuthDivider />
|
||||
<SocialButtons />
|
||||
<p className={`text-center text-xs mt-7 pt-7 ${isDark ? "text-slate-500" : "text-slate-400"}`}>
|
||||
¿No tienes cuenta?{" "}
|
||||
<button type="button" onClick={onSwitchToRegister} className="text-violet-500 hover:text-violet-400 font-semibold hover:underline transition">
|
||||
Regístrate gratis
|
||||
</button>
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
"use client";
|
||||
import { useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { FiMail, FiLock, FiArrowRight, FiUser } from "react-icons/fi";
|
||||
import { Button, Input } from "@/components/ui";
|
||||
import { useTheme } from "@/context/ThemeContext";
|
||||
import { AuthDivider } from "./AuthDivider";
|
||||
import { SocialButtons } from "./SocialButtons";
|
||||
|
||||
export function RegisterForm({ onSwitchToLogin }: { onSwitchToLogin: () => void }) {
|
||||
const router = useRouter();
|
||||
const { isDark } = useTheme();
|
||||
const [name, setName] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const handleRegister = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!name || !email || !password) return;
|
||||
setIsLoading(true);
|
||||
setTimeout(() => { setIsLoading(false); router.push("/"); }, 800);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mb-10">
|
||||
<h1 className={`text-3xl font-extrabold tracking-tight ${isDark ? "text-white" : "text-slate-900"}`}>
|
||||
Crear cuenta
|
||||
</h1>
|
||||
<p className={`text-sm mt-1 ${isDark ? "text-slate-400" : "text-slate-500"}`}>
|
||||
Regístrate gratis y empieza a aprender hoy.
|
||||
</p>
|
||||
</div>
|
||||
<form onSubmit={handleRegister} className="space-y-6">
|
||||
<Input
|
||||
label="Nombre completo"
|
||||
type="text"
|
||||
required
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
placeholder="Tu nombre"
|
||||
leftIcon={<FiUser />}
|
||||
/>
|
||||
<Input
|
||||
label="Correo electrónico"
|
||||
type="email"
|
||||
required
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="[email protected]"
|
||||
leftIcon={<FiMail />}
|
||||
/>
|
||||
<Input
|
||||
label="Contraseña"
|
||||
type="password"
|
||||
required
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
placeholder="Mínimo 8 caracteres"
|
||||
leftIcon={<FiLock />}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="violet"
|
||||
size="lg"
|
||||
fullWidth
|
||||
isLoading={isLoading}
|
||||
loadingText="Creando cuenta..."
|
||||
rightIcon={<FiArrowRight />}
|
||||
>
|
||||
Crear cuenta gratis
|
||||
</Button>
|
||||
</form>
|
||||
<AuthDivider />
|
||||
<SocialButtons />
|
||||
<p className={`text-center text-xs mt-7 ${isDark ? "text-slate-500" : "text-slate-400"}`}>
|
||||
¿Ya tienes cuenta?{" "}
|
||||
<button type="button" onClick={onSwitchToLogin} className="text-violet-500 hover:text-violet-400 font-semibold hover:underline transition">
|
||||
Inicia sesión
|
||||
</button>
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
"use client";
|
||||
import { useTheme } from "@/context/ThemeContext";
|
||||
|
||||
const socialProviders = [
|
||||
{
|
||||
label: "Google",
|
||||
svg: (
|
||||
<svg className="w-5 h-5 shrink-0" viewBox="0 0 24 24">
|
||||
<path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" />
|
||||
<path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" />
|
||||
<path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" />
|
||||
<path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
label: "GitHub",
|
||||
svg: (
|
||||
<svg className="w-5 h-5 shrink-0" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
label: "Microsoft",
|
||||
svg: (
|
||||
<svg className="w-5 h-5 shrink-0" viewBox="0 0 24 24">
|
||||
<path fill="#F25022" d="M1 1h10v10H1z" />
|
||||
<path fill="#00A4EF" d="M13 1h10v10H13z" />
|
||||
<path fill="#7FBA00" d="M1 13h10v10H1z" />
|
||||
<path fill="#FFB900" d="M13 13h10v10H13z" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
label: "Apple",
|
||||
svg: (
|
||||
<svg className="w-5 h-5 shrink-0" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.8-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
export function SocialButtons() {
|
||||
const { isDark } = useTheme();
|
||||
return (
|
||||
<div className="flex flex-col gap-2.5">
|
||||
{socialProviders.map(({ label, svg }) => (
|
||||
<button
|
||||
key={label}
|
||||
type="button"
|
||||
className={`w-full flex items-center justify-center gap-2.5 py-2.5 rounded-xl border text-sm font-medium transition-all duration-200 ${
|
||||
isDark
|
||||
? "border-slate-700 text-slate-300 hover:border-slate-600 hover:bg-slate-800"
|
||||
: "border-slate-200 text-slate-600 hover:border-slate-300 hover:bg-slate-50"
|
||||
}`}
|
||||
>
|
||||
{svg}
|
||||
<span>Continuar con {label}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useCart } from "../../context/CartContext";
|
||||
import type { FlyItem } from "../../context/CartContext";
|
||||
|
||||
function FlyingItem({ item }: { item: FlyItem }) {
|
||||
const { cartIconRef } = useCart();
|
||||
const elRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!elRef.current || !cartIconRef.current) return;
|
||||
const el = elRef.current;
|
||||
const target = cartIconRef.current.getBoundingClientRect();
|
||||
const toX = target.left + target.width / 2;
|
||||
const toY = target.top + target.height / 2;
|
||||
const dx = toX - item.fromX;
|
||||
const dy = toY - item.fromY;
|
||||
|
||||
el.style.left = `${item.fromX - 28}px`;
|
||||
el.style.top = `${item.fromY - 28}px`;
|
||||
|
||||
// Start animation on next frame
|
||||
requestAnimationFrame(() => {
|
||||
el.style.transition = "transform 0.75s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.75s ease";
|
||||
el.style.transform = `translate(${dx}px, ${dy}px) scale(0.15)`;
|
||||
el.style.opacity = "0";
|
||||
});
|
||||
}, [item, cartIconRef]);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={elRef}
|
||||
className="fixed z-[9999] w-14 h-14 rounded-xl overflow-hidden shadow-2xl pointer-events-none"
|
||||
style={{ opacity: 1, transform: "scale(1)" }}
|
||||
>
|
||||
<img src={item.thumbnail} alt="" className="w-full h-full object-cover" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function CartAnimator() {
|
||||
const { flyItems } = useCart();
|
||||
|
||||
return (
|
||||
<>
|
||||
{flyItems.map((item) => (
|
||||
<FlyingItem key={item.id} item={item} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
+249
-161
@@ -1,54 +1,79 @@
|
||||
import React, { useState } from 'react';
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import {
|
||||
FiX,
|
||||
FiTrash2,
|
||||
FiShoppingCart,
|
||||
FiTag,
|
||||
FiCheckCircle,
|
||||
FiArrowRight,
|
||||
FiLock,
|
||||
FiShield
|
||||
} from 'react-icons/fi';
|
||||
import type { CartItem } from '../../types/course';
|
||||
FiShield,
|
||||
FiPercent,
|
||||
FiPackage,
|
||||
} from "react-icons/fi";
|
||||
import { useCart } from "../../context/CartContext";
|
||||
import { useTheme } from "../../context/ThemeContext";
|
||||
|
||||
interface CartDrawerProps {
|
||||
isOpen: boolean;
|
||||
cartItems: CartItem[];
|
||||
onClose: () => void;
|
||||
onRemoveItem: (courseId: string) => void;
|
||||
onClearCart: () => void;
|
||||
}
|
||||
export function CartDrawer() {
|
||||
const { cartItems, removeFromCart, clearCart, isDrawerOpen, closeDrawer } =
|
||||
useCart();
|
||||
const { isDark } = useTheme();
|
||||
|
||||
export const CartDrawer: React.FC<CartDrawerProps> = ({
|
||||
isOpen,
|
||||
cartItems,
|
||||
onClose,
|
||||
onRemoveItem,
|
||||
onClearCart,
|
||||
}) => {
|
||||
const [couponCode, setCouponCode] = useState('');
|
||||
const [appliedDiscount, setAppliedDiscount] = useState<number>(0);
|
||||
const panel = isDark
|
||||
? "bg-slate-950 border-white/5"
|
||||
: "bg-white border-slate-200";
|
||||
const header = isDark ? "border-white/5" : "border-slate-100";
|
||||
const item = isDark
|
||||
? "bg-white/[0.03] border-white/5 hover:border-white/10"
|
||||
: "bg-slate-50 shadow-md border-slate-100 border-slate-200 hover:border-slate-300";
|
||||
const title = isDark ? "text-white" : "text-slate-900";
|
||||
const sub = isDark ? "text-slate-500" : "text-slate-400";
|
||||
const footer = isDark
|
||||
? "border-white/5 bg-slate-950"
|
||||
: "border-slate-100 bg-white";
|
||||
const inputCl = isDark
|
||||
? "bg-white/5 border-white/10 text-white placeholder-slate-600 focus:border-violet-500/50"
|
||||
: "bg-slate-100 border-slate-200 text-slate-900 placeholder-slate-400 focus:border-violet-400";
|
||||
const applyBtn = isDark
|
||||
? "bg-white/5 hover:bg-white/10 text-slate-300 border-white/10"
|
||||
: "bg-slate-100 hover:bg-slate-200 text-slate-700 border-slate-200";
|
||||
const [couponCode, setCouponCode] = useState("");
|
||||
const [appliedDiscount, setAppliedDiscount] = useState(0);
|
||||
const [couponMessage, setCouponMessage] = useState<string | null>(null);
|
||||
const [isCheckingOut, setIsCheckingOut] = useState(false);
|
||||
const [checkoutSuccess, setCheckoutSuccess] = useState(false);
|
||||
const [closing, setClosing] = useState(false);
|
||||
|
||||
if (!isOpen) return null;
|
||||
useEffect(() => {
|
||||
if (!isDrawerOpen) setClosing(false);
|
||||
}, [isDrawerOpen]);
|
||||
|
||||
const rawSubtotal = cartItems.reduce((acc, item) => acc + item.course.price, 0);
|
||||
const handleClose = () => {
|
||||
setClosing(true);
|
||||
setTimeout(() => {
|
||||
setClosing(false);
|
||||
closeDrawer();
|
||||
}, 280);
|
||||
};
|
||||
|
||||
if (!isDrawerOpen) return null;
|
||||
|
||||
const rawSubtotal = cartItems.reduce((acc, c) => acc + c.price, 0);
|
||||
const discountAmount = (rawSubtotal * appliedDiscount) / 100;
|
||||
const finalTotal = Math.max(0, rawSubtotal - discountAmount);
|
||||
|
||||
const handleApplyCoupon = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
const code = couponCode.trim().toUpperCase();
|
||||
if (code === 'DESCUENTO20' || code === 'PROMO20') {
|
||||
if (code === "DESCUENTO20" || code === "PROMO20") {
|
||||
setAppliedDiscount(20);
|
||||
setCouponMessage('¡Cupón del 20% aplicado con éxito!');
|
||||
} else if (code === 'PROMO50' || code === 'RYNEXT50') {
|
||||
setCouponMessage("¡20% de descuento aplicado!");
|
||||
} else if (code === "RYNEXT50" || code === "PROMO50") {
|
||||
setAppliedDiscount(50);
|
||||
setCouponMessage('¡Cupón especial del 50% de descuento aplicado!');
|
||||
setCouponMessage("¡50% de descuento aplicado!");
|
||||
} else if (code.length > 0) {
|
||||
setCouponMessage('Cupón no válido. Prueba con "DESCUENTO20" o "RYNEXT50"');
|
||||
setCouponMessage('Cupón inválido. Prueba "DESCUENTO20" o "RYNEXT50"');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -59,183 +84,246 @@ export const CartDrawer: React.FC<CartDrawerProps> = ({
|
||||
setCheckoutSuccess(true);
|
||||
setTimeout(() => {
|
||||
setCheckoutSuccess(false);
|
||||
onClearCart();
|
||||
onClose();
|
||||
clearCart();
|
||||
closeDrawer();
|
||||
}, 3000);
|
||||
}, 1500);
|
||||
};
|
||||
|
||||
const panelAnim = closing
|
||||
? "slideOutRight 0.4s cubic-bezier(0.32, 0.72, 0, 1) forwards"
|
||||
: "slideInRight 0.4s cubic-bezier(0.32, 0.72, 0, 1)";
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 overflow-hidden bg-slate-950/70 backdrop-blur-sm animate-in fade-in duration-200">
|
||||
<div className="absolute inset-y-0 right-0 max-w-full flex pl-10">
|
||||
<div className="fixed inset-0 z-50 flex">
|
||||
<style>{`
|
||||
@keyframes fadeInBg { from { opacity:0 } to { opacity:1 } }
|
||||
@keyframes fadeOutBg { from { opacity:1 } to { opacity:0 } }
|
||||
@keyframes slideInRight { from { transform:translateX(100%) } to { transform:translateX(0) } }
|
||||
@keyframes slideOutRight { from { transform:translateX(0) } to { transform:translateX(100%) } }
|
||||
`}</style>
|
||||
|
||||
{/* Slide panel */}
|
||||
<div className="w-screen max-w-md bg-slate-900 border-l border-slate-800 shadow-2xl flex flex-col justify-between">
|
||||
{/* Backdrop — instantáneo */}
|
||||
<div
|
||||
className="fixed inset-0 bg-black/60 backdrop-blur-sm"
|
||||
style={{
|
||||
animation: closing ? "fadeOutBg 0.25s ease-in-out forwards" : "none",
|
||||
}}
|
||||
onClick={handleClose}
|
||||
/>
|
||||
|
||||
{/* Top Header */}
|
||||
<div className="p-6 border-b border-slate-800 flex items-center justify-between bg-slate-950/50">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<div className="p-2 rounded-lg bg-cyan-500/10 text-cyan-400">
|
||||
<FiShoppingCart className="text-lg" />
|
||||
{/* Panel */}
|
||||
<div
|
||||
className={`relative ml-auto w-100 flex flex-col border-l ${panel}`}
|
||||
style={{
|
||||
animation: panelAnim,
|
||||
boxShadow: "-20px 0 60px rgba(0,0,0,0.4)",
|
||||
}}
|
||||
>
|
||||
{/* Header */}
|
||||
<div
|
||||
className={`flex items-center justify-between px-2 py-2 border-b ${header}`}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<div className="w-8 h-8 rounded-lg flex items-center justify-center">
|
||||
<FiShoppingCart
|
||||
className={`${isDark ? "text-white" : "text-black"} text-sm`}
|
||||
/>
|
||||
</div>
|
||||
<p className={`text-sm font-bold ${title}`}>Carrito</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleClose}
|
||||
className={`w-8 h-8 rounded-lg flex items-center justify-center transition cursor-pointer ${isDark ? "text-slate-500 hover:text-white hover:bg-white/5" : "text-slate-400 hover:text-slate-900 hover:bg-slate-100"}`}
|
||||
>
|
||||
<FiX />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Body */}
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
{checkoutSuccess ? (
|
||||
<div className="h-full flex flex-col items-center justify-center px-8 text-center gap-5">
|
||||
<div className="w-20 h-20 rounded-2xl bg-emerald-500/10 flex items-center justify-center">
|
||||
<FiCheckCircle className="text-emerald-400 text-4xl" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-base font-bold text-white">Carrito de Compras</h3>
|
||||
<p className="text-xs text-slate-400">
|
||||
{cartItems.length} {cartItems.length === 1 ? 'curso seleccionado' : 'cursos seleccionados'}
|
||||
<h4 className={`text-lg font-bold mb-1 ${title}`}>
|
||||
¡Pago completado!
|
||||
</h4>
|
||||
<p className={`text-xs leading-relaxed ${sub}`}>
|
||||
Tus cursos están disponibles en tu biblioteca. Te enviamos los
|
||||
accesos por correo.
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
className={`px-4 py-2 rounded-lg border text-xs text-violet-400 font-mono ${isDark ? "bg-white/5 border-white/10" : "bg-violet-50 border-violet-200"}`}
|
||||
>
|
||||
#{Math.floor(100000 + Math.random() * 900000)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="p-2 rounded-xl text-slate-400 hover:text-white hover:bg-slate-800 transition"
|
||||
aria-label="Cerrar carrito"
|
||||
>
|
||||
<FiX className="text-xl" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Cart Items List */}
|
||||
<div className="flex-1 overflow-y-auto p-6 space-y-4">
|
||||
{checkoutSuccess ? (
|
||||
<div className="text-center py-12 space-y-4 animate-in zoom-in-95 duration-300">
|
||||
<div className="w-16 h-16 rounded-full bg-emerald-500/20 text-emerald-400 flex items-center justify-center mx-auto text-3xl">
|
||||
<FiCheckCircle />
|
||||
</div>
|
||||
<h4 className="text-xl font-bold text-white">¡Compra Realizada con Éxito!</h4>
|
||||
<p className="text-xs text-slate-300 max-w-xs mx-auto">
|
||||
Hemos enviado los accesos a tu correo. Tus cursos ya están disponibles en tu biblioteca de aprendizaje.
|
||||
</p>
|
||||
<div className="p-3 bg-slate-950 rounded-xl border border-slate-800 text-xs text-cyan-400 font-mono">
|
||||
Orden #RY-{Math.floor(100000 + Math.random() * 900000)}
|
||||
</div>
|
||||
) : cartItems.length === 0 ? (
|
||||
<div className="h-full flex flex-col items-center justify-center px-8 text-center gap-4">
|
||||
<div
|
||||
className={`w-16 h-16 rounded-2xl flex items-center justify-center ${isDark ? "bg-white/5" : "bg-slate-100"}`}
|
||||
>
|
||||
<FiPackage className={`text-2xl ${sub}`} />
|
||||
</div>
|
||||
) : cartItems.length > 0 ? (
|
||||
cartItems.map((item) => (
|
||||
<div>
|
||||
<p className={`text-sm font-semibold mb-1 ${title}`}>
|
||||
Tu carrito está vacío
|
||||
</p>
|
||||
<p className={`text-xs ${sub}`}>
|
||||
Explora el catálogo y agrega cursos para comenzar.
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleClose}
|
||||
className="px-4 py-2 bg-violet-600 hover:bg-violet-500 text-white text-xs font-semibold rounded-lg transition cursor-pointer"
|
||||
>
|
||||
Explorar cursos
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="p-4 space-y-2.5">
|
||||
{cartItems.map((course) => (
|
||||
<div
|
||||
key={item.course.id}
|
||||
className="flex gap-3.5 p-3 rounded-xl bg-slate-950/60 border border-slate-800/80 hover:border-slate-700 transition"
|
||||
key={course.id}
|
||||
className={`group flex gap-3.5 p-3 rounded-xl border transition ${item}`}
|
||||
>
|
||||
<img
|
||||
src={item.course.thumbnail}
|
||||
alt={item.course.title}
|
||||
className="w-20 h-14 object-cover rounded-lg bg-slate-800 shrink-0"
|
||||
src={course.thumbnail}
|
||||
alt={course.title}
|
||||
className="w-16 h-16 rounded-lg object-cover bg-slate-200"
|
||||
/>
|
||||
|
||||
<div className="flex-1 min-w-0 flex flex-col justify-between">
|
||||
<div>
|
||||
<h4 className="text-xs font-bold text-white line-clamp-1">
|
||||
{item.course.title}
|
||||
</h4>
|
||||
<p className="text-[11px] text-slate-400 truncate">
|
||||
Por {item.course.instructor.name}
|
||||
<div className="min-w-0">
|
||||
<p
|
||||
className={`text-xs font-semibold truncate leading-snug ${title}`}
|
||||
>
|
||||
{course.title}
|
||||
</p>
|
||||
<p className={`text-[11px] mt-0.5 ${sub}`}>
|
||||
{course.instructor.name}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between pt-1">
|
||||
<span className="text-sm font-bold text-white font-mono">
|
||||
${item.course.price.toFixed(2)}
|
||||
</span>
|
||||
|
||||
<button
|
||||
onClick={() => onRemoveItem(item.course.id)}
|
||||
className="text-slate-500 hover:text-rose-400 text-xs flex items-center gap-1 transition"
|
||||
title="Eliminar curso"
|
||||
<div className="flex items-center justify-between mt-2">
|
||||
<span
|
||||
className={`text-sm font-black tabular-nums ${title}`}
|
||||
>
|
||||
<FiTrash2 />
|
||||
${course.price.toFixed(2)}
|
||||
{course.originalPrice > course.price && (
|
||||
<span
|
||||
className={`ml-1.5 text-[10px] line-through font-normal ${sub}`}
|
||||
>
|
||||
${course.originalPrice.toFixed(2)}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
<button
|
||||
onClick={() => removeFromCart(course.id)}
|
||||
className="opacity-0 group-hover:opacity-100 text-slate-400 hover:text-rose-400 transition cursor-pointer p-1"
|
||||
>
|
||||
<FiTrash2 className="text-xs" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className="text-center py-16 space-y-4">
|
||||
<div className="w-16 h-16 rounded-full bg-slate-800 text-slate-500 flex items-center justify-center mx-auto text-2xl">
|
||||
<FiShoppingCart />
|
||||
</div>
|
||||
<h4 className="text-sm font-bold text-white">Tu carrito está vacío</h4>
|
||||
<p className="text-xs text-slate-400 max-w-xs mx-auto">
|
||||
Explora nuestro catálogo de cursos en desarrollo, IA y diseño para comenzar.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Bottom Summary & Checkout Form */}
|
||||
{cartItems.length > 0 && !checkoutSuccess && (
|
||||
<div className="p-6 border-t border-slate-800 bg-slate-950/90 space-y-4">
|
||||
|
||||
{/* Coupon Form */}
|
||||
<form onSubmit={handleApplyCoupon} className="space-y-1.5">
|
||||
<div className="flex gap-2">
|
||||
<div className="relative flex-1">
|
||||
<FiTag className="absolute left-3 top-3 text-slate-500 text-xs" />
|
||||
<input
|
||||
type="text"
|
||||
value={couponCode}
|
||||
onChange={(e) => setCouponCode(e.target.value)}
|
||||
placeholder="Cupón (ej. DESCUENTO20)"
|
||||
className="w-full pl-8 pr-3 py-2 bg-slate-900 border border-slate-700 rounded-lg text-xs text-white placeholder-slate-500 focus:outline-none focus:border-cyan-500 uppercase"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
className="px-3 py-2 bg-slate-800 hover:bg-slate-700 text-slate-200 font-semibold text-xs rounded-lg transition"
|
||||
>
|
||||
Aplicar
|
||||
</button>
|
||||
{/* Footer — only when items */}
|
||||
{cartItems.length > 0 && !checkoutSuccess && (
|
||||
<div className={`border-t ${footer}`}>
|
||||
{/* Coupon */}
|
||||
<div className="px-4 pt-4 pb-3">
|
||||
<form onSubmit={handleApplyCoupon} className="flex gap-2">
|
||||
<div className="relative flex-1">
|
||||
<FiPercent
|
||||
className={`absolute left-3 top-1/2 -translate-y-1/2 text-[11px] ${sub}`}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
value={couponCode}
|
||||
onChange={(e) => setCouponCode(e.target.value)}
|
||||
placeholder="Código de descuento"
|
||||
className={`w-full pl-8 pr-3 py-2 text-xs rounded-lg border focus:outline-none uppercase tracking-wider ${inputCl}`}
|
||||
/>
|
||||
</div>
|
||||
{couponMessage && (
|
||||
<p className={`text-[11px] ${appliedDiscount > 0 ? 'text-emerald-400' : 'text-amber-400'}`}>
|
||||
{couponMessage}
|
||||
</p>
|
||||
)}
|
||||
<button
|
||||
type="submit"
|
||||
className={`px-3 py-2 text-xs font-semibold rounded-lg border transition cursor-pointer whitespace-nowrap ${applyBtn}`}
|
||||
>
|
||||
Aplicar
|
||||
</button>
|
||||
</form>
|
||||
{couponMessage && (
|
||||
<p
|
||||
className={`text-[11px] mt-1.5 ${appliedDiscount > 0 ? "text-emerald-500" : "text-amber-400"}`}
|
||||
>
|
||||
{couponMessage}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Price Breakdown */}
|
||||
<div className="space-y-1.5 text-xs text-slate-400 pt-2 border-t border-slate-800">
|
||||
<div className="flex justify-between">
|
||||
<span>Subtotal</span>
|
||||
<span className="font-mono text-slate-200">${rawSubtotal.toFixed(2)}</span>
|
||||
</div>
|
||||
{appliedDiscount > 0 && (
|
||||
<div className="flex justify-between text-emerald-400">
|
||||
<span>Descuento ({appliedDiscount}%)</span>
|
||||
<span className="font-mono">-${discountAmount.toFixed(2)}</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex justify-between text-sm font-bold text-white pt-2 border-t border-slate-800">
|
||||
<span>Total a pagar</span>
|
||||
<span className="text-xl text-cyan-400 font-mono">${finalTotal.toFixed(2)}</span>
|
||||
</div>
|
||||
{/* Totals */}
|
||||
<div className={`px-4 py-3 space-y-2 border-t ${header}`}>
|
||||
<div className={`flex justify-between text-xs ${sub}`}>
|
||||
<span>Subtotal</span>
|
||||
<span
|
||||
className={`tabular-nums ${isDark ? "text-slate-300" : "text-slate-600"}`}
|
||||
>
|
||||
${rawSubtotal.toFixed(2)}
|
||||
</span>
|
||||
</div>
|
||||
{appliedDiscount > 0 && (
|
||||
<div className="flex justify-between text-xs text-emerald-500">
|
||||
<span>Descuento ({appliedDiscount}%)</span>
|
||||
<span className="tabular-nums">
|
||||
-${discountAmount.toFixed(2)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={`flex justify-between items-center pt-2 border-t ${header}`}
|
||||
>
|
||||
<span className={`text-sm font-bold ${title}`}>Total</span>
|
||||
<span className={`text-xl font-black tabular-nums ${title}`}>
|
||||
${finalTotal.toFixed(2)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Checkout Button */}
|
||||
{/* CTA */}
|
||||
<div className="px-4 pb-5 space-y-3">
|
||||
<button
|
||||
onClick={handleCheckout}
|
||||
disabled={isCheckingOut}
|
||||
className="w-full py-3.5 bg-gradient-to-r from-blue-600 via-cyan-600 to-teal-500 hover:from-blue-500 hover:to-cyan-400 text-white font-bold rounded-xl text-sm shadow-xl shadow-cyan-500/25 transition-all flex items-center justify-center gap-2 cursor-pointer disabled:opacity-50"
|
||||
className="w-full py-3.5 bg-violet-600 hover:bg-violet-500 active:bg-violet-700 text-white text-sm font-bold rounded-xl flex items-center justify-center gap-2 transition cursor-pointer disabled:opacity-60"
|
||||
>
|
||||
{isCheckingOut ? (
|
||||
<span>Procesando pago seguro...</span>
|
||||
<span className="flex items-center gap-2">
|
||||
<span className="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" />
|
||||
Procesando...
|
||||
</span>
|
||||
) : (
|
||||
<>
|
||||
<FiLock className="text-sm" />
|
||||
<span>Completar Pago Seguro</span>
|
||||
<FiLock className="text-xs opacity-70" />
|
||||
<span>Completar compra</span>
|
||||
<FiArrowRight className="text-sm" />
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
|
||||
<div className="flex items-center justify-center gap-2 text-[11px] text-slate-500">
|
||||
<FiShield className="text-emerald-400" />
|
||||
<span>Garantía total de devolución de 30 días</span>
|
||||
<div className="flex items-center justify-center gap-1.5 text-[11px] text-slate-600">
|
||||
<FiShield className="text-emerald-500 shrink-0" />
|
||||
Garantía de devolución de 30 días
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
"use client";
|
||||
import React from 'react';
|
||||
import {
|
||||
FiGrid,
|
||||
|
||||
@@ -1,214 +1,175 @@
|
||||
"use client";
|
||||
import React from 'react';
|
||||
import { useNavigate } from 'react-router';
|
||||
import {
|
||||
FiStar,
|
||||
FiClock,
|
||||
FiBook,
|
||||
FiShoppingCart,
|
||||
FiCheck,
|
||||
FiEye,
|
||||
FiZap
|
||||
} from 'react-icons/fi';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { FiStar, FiClock, FiBook, FiShoppingCart, FiCheck, FiEye, FiZap, FiTrendingUp, FiPlus, FiTag, FiAward } from 'react-icons/fi';
|
||||
import type { Course } from '../../types/course';
|
||||
import { useTheme } from '../../context/ThemeContext';
|
||||
import { useCart } from '../../context/CartContext';
|
||||
import { useRef } from 'react';
|
||||
|
||||
interface CourseCardProps {
|
||||
course: Course;
|
||||
isInCart: boolean;
|
||||
onAddToCart: (course: Course) => void;
|
||||
onOpenDetails?: (course: Course) => void;
|
||||
}
|
||||
|
||||
export const CourseCard: React.FC<CourseCardProps> = ({
|
||||
course,
|
||||
isInCart,
|
||||
onAddToCart,
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
export const CourseCard: React.FC<CourseCardProps> = ({ course }) => {
|
||||
const router = useRouter();
|
||||
const { isDark } = useTheme();
|
||||
const { addToCart, isInCart: isInCartFn } = useCart();
|
||||
const isInCart = isInCartFn(course.id);
|
||||
const btnRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
const handleCourseClick = () => router.push(`/course/${course.id}`);
|
||||
|
||||
const handleCourseClick = () => {
|
||||
navigate(`/course/${course.id}`);
|
||||
};
|
||||
const discountPercent = Math.round(
|
||||
((course.originalPrice - course.price) / course.originalPrice) * 100
|
||||
);
|
||||
|
||||
const getBadgeStyle = (badge?: string) => {
|
||||
switch (badge) {
|
||||
case 'Bestseller':
|
||||
return 'bg-amber-500 text-slate-950';
|
||||
case 'Nuevo':
|
||||
return 'bg-emerald-500 text-white';
|
||||
case 'Oferta':
|
||||
return 'bg-rose-500 text-white';
|
||||
case 'Destacado':
|
||||
return 'bg-indigo-500 text-white';
|
||||
default:
|
||||
return 'bg-blue-500 text-white';
|
||||
case 'Bestseller': return 'bg-amber-500 text-slate-950';
|
||||
case 'Nuevo': return 'bg-emerald-500 text-white';
|
||||
case 'Oferta': return 'bg-rose-500 text-white';
|
||||
case 'Destacado': return 'bg-indigo-500 text-white';
|
||||
default: return 'bg-blue-500 text-white';
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="group relative flex flex-col justify-between rounded-2xl bg-slate-900/90 border border-slate-800 hover:border-slate-700 transition-all duration-300 hover:shadow-2xl hover:shadow-cyan-500/10 hover:-translate-y-1 overflow-hidden">
|
||||
<div className={`group relative flex flex-col justify-between rounded-2xl border transition-all duration-300 hover:-translate-y-1 overflow-hidden ${
|
||||
isDark
|
||||
? 'bg-slate-900/90 border-slate-800 hover:border-slate-700 hover:shadow-2xl hover:shadow-violet-500/10'
|
||||
: 'bg-white border-slate-200 hover:border-slate-300 hover:shadow-xl hover:shadow-slate-200/60'
|
||||
}`}>
|
||||
|
||||
{/* Top Image Section */}
|
||||
{/* Thumbnail */}
|
||||
<div>
|
||||
<div className="relative aspect-video w-full overflow-hidden bg-slate-800 cursor-pointer" onClick={handleCourseClick}>
|
||||
<img
|
||||
src={course.thumbnail}
|
||||
alt={course.title}
|
||||
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-slate-950/80 via-transparent to-black/30" />
|
||||
<div className={`relative aspect-video w-full overflow-hidden cursor-pointer ${isDark ? 'bg-slate-800' : 'bg-slate-100'}`} onClick={handleCourseClick}>
|
||||
<img src={course.thumbnail} alt={course.title} className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500" />
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-slate-950/80 via-transparent to-black/20" />
|
||||
|
||||
{/* Badge */}
|
||||
{course.badge && (
|
||||
<span
|
||||
className={`absolute top-3 left-3 px-2.5 py-1 rounded-md text-[10px] font-black uppercase tracking-wider shadow-md ${getBadgeStyle(
|
||||
course.badge
|
||||
)}`}
|
||||
>
|
||||
{course.badge === 'Bestseller' && '🔥 '}
|
||||
<span className={`absolute top-3 left-3 flex items-center gap-1 px-2.5 py-1 rounded-md text-[10px] font-black uppercase tracking-wider shadow-md ${getBadgeStyle(course.badge)}`}>
|
||||
{course.badge === 'Bestseller' && <FiTrendingUp className="text-xs" />}
|
||||
{course.badge === 'Nuevo' && <FiPlus className="text-xs" />}
|
||||
{course.badge === 'Oferta' && <FiTag className="text-xs" />}
|
||||
{course.badge === 'Destacado' && <FiAward className="text-xs" />}
|
||||
{course.badge}
|
||||
</span>
|
||||
)}
|
||||
|
||||
{/* Discount Tag */}
|
||||
{discountPercent > 0 && (
|
||||
<span className="absolute top-3 right-3 bg-rose-600/95 text-white px-2 py-0.5 rounded text-[10px] font-black tracking-wide shadow-md">
|
||||
-{discountPercent}% OFF
|
||||
</span>
|
||||
)}
|
||||
|
||||
{/* Quick Preview Hover Overlay */}
|
||||
<div className="absolute inset-0 bg-slate-950/60 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center gap-3 backdrop-blur-[2px]">
|
||||
<div className="absolute inset-0 bg-slate-950/60 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center backdrop-blur-[2px]">
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleCourseClick();
|
||||
}}
|
||||
onClick={(e) => { e.stopPropagation(); handleCourseClick(); }}
|
||||
className="px-3.5 py-2 bg-white/90 hover:bg-white text-slate-950 rounded-xl text-xs font-bold flex items-center gap-1.5 shadow-lg transform translate-y-2 group-hover:translate-y-0 transition-transform"
|
||||
>
|
||||
<FiEye /> Ver Curso Completo
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Duration & Lessons Overlay */}
|
||||
<div className="absolute bottom-2 left-3 right-3 flex items-center justify-between text-[11px] text-slate-300 font-medium">
|
||||
<span className="flex items-center gap-1 bg-slate-950/80 backdrop-blur-md px-2 py-0.5 rounded">
|
||||
<FiClock className="text-cyan-400" /> {course.duration}
|
||||
<FiClock className="text-violet-400" /> {course.duration}
|
||||
</span>
|
||||
<span className="flex items-center gap-1 bg-slate-950/80 backdrop-blur-md px-2 py-0.5 rounded">
|
||||
<FiBook className="text-cyan-400" /> {course.lessonsCount} lecciones
|
||||
<FiBook className="text-violet-400" /> {course.lessonsCount} lecciones
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content Section */}
|
||||
{/* Content */}
|
||||
<div className="p-5 space-y-3">
|
||||
{/* Category & Level */}
|
||||
<div className="flex items-center justify-between text-[11px]">
|
||||
<span className="font-semibold text-cyan-400 uppercase tracking-wider">
|
||||
<span className={`font-semibold uppercase tracking-wider ${isDark ? 'text-violet-400' : 'text-violet-600'}`}>
|
||||
{course.category}
|
||||
</span>
|
||||
<span className="px-2 py-0.5 rounded bg-slate-800 text-slate-400 border border-slate-700/50">
|
||||
<span className={`px-2 py-0.5 rounded border text-[11px] ${isDark ? 'bg-slate-800 text-slate-400 border-slate-700/50' : 'bg-slate-100 text-slate-500 border-slate-200'}`}>
|
||||
{course.level}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Title */}
|
||||
<h3
|
||||
onClick={handleCourseClick}
|
||||
className="text-base font-bold text-white group-hover:text-cyan-300 transition-colors line-clamp-2 cursor-pointer leading-snug"
|
||||
className={`text-base font-bold transition-colors line-clamp-2 cursor-pointer leading-snug ${
|
||||
isDark ? 'text-white group-hover:text-violet-300' : 'text-slate-900 group-hover:text-violet-700'
|
||||
}`}
|
||||
>
|
||||
{course.title}
|
||||
</h3>
|
||||
|
||||
{/* Subtitle */}
|
||||
<p className="text-xs text-slate-400 line-clamp-2 leading-relaxed">
|
||||
<p className={`text-xs line-clamp-2 leading-relaxed ${isDark ? 'text-slate-400' : 'text-slate-500'}`}>
|
||||
{course.subtitle}
|
||||
</p>
|
||||
|
||||
{/* Instructor info */}
|
||||
<div className="flex items-center gap-2 pt-1">
|
||||
<img
|
||||
src={course.instructor.avatar}
|
||||
alt={course.instructor.name}
|
||||
className="w-6 h-6 rounded-full object-cover ring-1 ring-cyan-500/30"
|
||||
/>
|
||||
<span className="text-xs text-slate-300 font-medium truncate">
|
||||
<img src={course.instructor.avatar} alt={course.instructor.name} className="w-6 h-6 rounded-full object-cover ring-1 ring-violet-500/30" />
|
||||
<span className={`text-xs font-medium truncate ${isDark ? 'text-slate-300' : 'text-slate-600'}`}>
|
||||
{course.instructor.name}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Rating and Reviews */}
|
||||
<div className="flex items-center gap-2 pt-1 text-xs">
|
||||
<div className="flex items-center gap-1 font-bold text-amber-400">
|
||||
<span>{course.rating.toFixed(1)}</span>
|
||||
<div className="flex text-amber-400 text-xs">
|
||||
<div className="flex text-xs">
|
||||
{[...Array(5)].map((_, i) => (
|
||||
<FiStar
|
||||
key={i}
|
||||
className={`inline ${
|
||||
i < Math.floor(course.rating)
|
||||
? 'fill-amber-400 text-amber-400'
|
||||
: 'text-slate-600'
|
||||
}`}
|
||||
/>
|
||||
<FiStar key={i} className={`inline ${i < Math.floor(course.rating) ? 'fill-amber-400 text-amber-400' : isDark ? 'text-slate-600' : 'text-slate-300'}`} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<span className="text-slate-500 font-mono text-[11px]">
|
||||
<span className={`font-mono text-[11px] ${isDark ? 'text-slate-500' : 'text-slate-400'}`}>
|
||||
({course.reviewsCount.toLocaleString()})
|
||||
</span>
|
||||
<span className="text-slate-600">•</span>
|
||||
<span className="text-slate-400 text-[11px]">
|
||||
<span className={isDark ? 'text-slate-600' : 'text-slate-300'}>•</span>
|
||||
<span className={`text-[11px] ${isDark ? 'text-slate-400' : 'text-slate-500'}`}>
|
||||
{course.studentsCount.toLocaleString()} alumnos
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Pricing & Cart Action Bottom */}
|
||||
<div className="p-5 pt-3 border-t border-slate-800/80 bg-slate-950/40 flex items-center justify-between gap-3">
|
||||
{/* Pricing & Cart */}
|
||||
<div className={`p-5 pt-3 border-t flex items-center justify-between gap-3 ${
|
||||
isDark ? 'border-slate-800/80 bg-slate-950/40' : 'border-slate-100 bg-slate-50/60'
|
||||
}`}>
|
||||
<div>
|
||||
<div className="flex items-baseline gap-1.5">
|
||||
<span className="text-xl font-black text-white font-mono">
|
||||
<span className={`text-xl font-black font-mono ${isDark ? 'text-white' : 'text-slate-900'}`}>
|
||||
${course.price.toFixed(2)}
|
||||
</span>
|
||||
{course.originalPrice > course.price && (
|
||||
<span className="text-xs text-slate-500 line-through font-mono">
|
||||
<span className={`text-xs line-through font-mono ${isDark ? 'text-slate-500' : 'text-slate-400'}`}>
|
||||
${course.originalPrice.toFixed(2)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<span className="text-[10px] text-emerald-400 flex items-center gap-0.5">
|
||||
<span className="text-[10px] text-emerald-500 flex items-center gap-0.5">
|
||||
<FiZap /> Acceso total + Certificado
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => onAddToCart(course)}
|
||||
ref={btnRef}
|
||||
onClick={() => !isInCart && addToCart(course, btnRef.current)}
|
||||
disabled={isInCart}
|
||||
className={`px-3.5 py-2.5 rounded-xl text-xs font-bold flex items-center gap-1.5 transition-all shadow-md ${
|
||||
isInCart
|
||||
? 'bg-emerald-500/20 text-emerald-300 border border-emerald-500/30 cursor-default'
|
||||
: 'bg-gradient-to-r from-blue-600 to-cyan-500 hover:from-blue-500 hover:to-cyan-400 text-white shadow-cyan-500/20 hover:scale-105 active:scale-95'
|
||||
? 'bg-emerald-500/20 text-emerald-400 border border-emerald-500/30 cursor-default'
|
||||
: 'bg-violet-600 hover:bg-violet-500 text-white shadow-violet-600/20 hover:scale-105 active:scale-95'
|
||||
}`}
|
||||
>
|
||||
{isInCart ? (
|
||||
<>
|
||||
<FiCheck className="text-sm" />
|
||||
<span>En Carrito</span>
|
||||
</>
|
||||
<><FiCheck className="text-sm" /><span>En Carrito</span></>
|
||||
) : (
|
||||
<>
|
||||
<FiShoppingCart className="text-sm" />
|
||||
<span>Añadir</span>
|
||||
</>
|
||||
<><FiShoppingCart className="text-sm" /><span>Añadir</span></>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
"use client";
|
||||
import { useState, useMemo, useEffect } from 'react';
|
||||
import {
|
||||
FiFilter,
|
||||
@@ -446,9 +447,9 @@ export const CourseGrid: React.FC<CourseGridProps> = ({
|
||||
<CourseCard
|
||||
key={course.id}
|
||||
course={course}
|
||||
isInCart={cartCourseIds.includes(course.id)}
|
||||
onAddToCart={onAddToCart}
|
||||
onOpenDetails={onOpenDetails}
|
||||
|
||||
|
||||
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -584,9 +585,9 @@ export const CourseGrid: React.FC<CourseGridProps> = ({
|
||||
<CourseCard
|
||||
key={course.id}
|
||||
course={course}
|
||||
isInCart={cartCourseIds.includes(course.id)}
|
||||
onAddToCart={onAddToCart}
|
||||
onOpenDetails={onOpenDetails}
|
||||
|
||||
|
||||
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"use client";
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
FiX,
|
||||
|
||||
@@ -1,61 +1,102 @@
|
||||
import React from 'react';
|
||||
import { FiAward, FiClock, FiUsers, FiShield } from 'react-icons/fi';
|
||||
"use client";
|
||||
import React from "react";
|
||||
import { FiAward, FiClock, FiUsers, FiShield } from "react-icons/fi";
|
||||
import { useTheme } from "../../context/ThemeContext";
|
||||
|
||||
const features = [
|
||||
{
|
||||
icon: FiAward,
|
||||
color: "text-violet-400",
|
||||
bg: "bg-violet-500/10",
|
||||
title: "Certificados Verificables",
|
||||
description:
|
||||
"Comparte tus logros con enlaces verificables en LinkedIn y tu CV profesional.",
|
||||
},
|
||||
{
|
||||
icon: FiClock,
|
||||
color: "text-indigo-400",
|
||||
bg: "bg-indigo-500/10",
|
||||
title: "Acceso de por Vida",
|
||||
description:
|
||||
"Aprende a tu ritmo. Vuelve al material y sus actualizaciones cuando quieras.",
|
||||
},
|
||||
{
|
||||
icon: FiUsers,
|
||||
color: "text-sky-400",
|
||||
bg: "bg-sky-500/10",
|
||||
title: "Comunidad Exclusiva",
|
||||
description:
|
||||
"Resuelve dudas con profesores y conecta con otros estudiantes en Discord.",
|
||||
},
|
||||
{
|
||||
icon: FiShield,
|
||||
color: "text-emerald-400",
|
||||
bg: "bg-emerald-500/10",
|
||||
title: "Garantía 30 Días",
|
||||
description:
|
||||
"Si el curso no cumple tus expectativas, te devolvemos el dinero sin preguntas.",
|
||||
},
|
||||
];
|
||||
|
||||
export const FeaturesBanner: React.FC = () => {
|
||||
const features = [
|
||||
{
|
||||
icon: <FiAward className="text-2xl text-cyan-400" />,
|
||||
title: 'Certificados Verificables',
|
||||
description: 'Comparte tus logros con enlaces verificables en LinkedIn y tu CV profesional.'
|
||||
},
|
||||
{
|
||||
icon: <FiClock className="text-2xl text-blue-400" />,
|
||||
title: 'Acceso Ilimitado de por Vida',
|
||||
description: 'Aprende a tu ritmo. Vuelve a consultar el material y las actualizaciones cuando quieras.'
|
||||
},
|
||||
{
|
||||
icon: <FiUsers className="text-2xl text-indigo-400" />,
|
||||
title: 'Comunidad Exclusiva en Discord',
|
||||
description: 'Resuelve dudas directamente con profesores y conecta con otros estudiantes.'
|
||||
},
|
||||
{
|
||||
icon: <FiShield className="text-2xl text-emerald-400" />,
|
||||
title: 'Garantía 100% de 30 Días',
|
||||
description: 'Si el curso no cumple con tus expectativas, te devolvemos el dinero sin preguntas.'
|
||||
}
|
||||
];
|
||||
const { isDark } = useTheme();
|
||||
|
||||
return (
|
||||
<section className="py-16 bg-slate-950/80 border-t border-slate-800/80">
|
||||
<section
|
||||
className={`py-16 transition-colors duration-300 ${isDark ? "border-slate-800/60" : "border-slate-200/60"}`}
|
||||
>
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
|
||||
<div className="text-center max-w-3xl mx-auto mb-12 space-y-3">
|
||||
<span className="text-xs font-bold uppercase tracking-wider text-cyan-400 bg-cyan-500/10 px-3 py-1 rounded-full border border-cyan-500/20">
|
||||
¿Por qué elegir RyCourses?
|
||||
<div className="text-center mb-12 space-y-3">
|
||||
<span
|
||||
className={`inline-block text-xs font-bold uppercase tracking-widest px-3 py-1 rounded-full border ${
|
||||
isDark
|
||||
? "text-violet-400 bg-violet-500/10 border-violet-500/20"
|
||||
: "text-violet-600 bg-violet-50 border-violet-200"
|
||||
}`}
|
||||
>
|
||||
¿Por qué elegir Rynext Academy?
|
||||
</span>
|
||||
<h2 className="text-2xl sm:text-4xl font-extrabold text-white">
|
||||
<h2
|
||||
className={`text-2xl sm:text-3xl font-extrabold ${isDark ? "text-white" : "text-slate-900"}`}
|
||||
>
|
||||
Diseñado para acelerar tu crecimiento profesional
|
||||
</h2>
|
||||
<p className="text-sm text-slate-400">
|
||||
No somos solo videos; somos una plataforma integral de educación moderna para la era de la Inteligencia Artificial.
|
||||
<p
|
||||
className={`text-sm max-w-xl mx-auto ${isDark ? "text-slate-400" : "text-slate-500"}`}
|
||||
>
|
||||
No somos solo videos — somos una plataforma integral de educación
|
||||
moderna para la era de la IA.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
{features.map((f, i) => (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-5">
|
||||
{features.map(({ icon: Icon, color, bg, title, description }) => (
|
||||
<div
|
||||
key={i}
|
||||
className="p-6 rounded-2xl bg-slate-900/60 border border-slate-800 hover:border-slate-700 transition-all hover:-translate-y-1 hover:shadow-xl hover:shadow-cyan-500/5 group"
|
||||
key={title}
|
||||
className={`p-6 rounded-2xl border transition-all duration-200 hover:-translate-y-1 group ${
|
||||
isDark
|
||||
? "bg-slate-900/50 border-slate-800 hover:border-slate-700 hover:shadow-xl hover:shadow-violet-500/5"
|
||||
: "bg-white border-slate-200 hover:border-slate-300 hover:shadow-lg hover:shadow-slate-200/60"
|
||||
}`}
|
||||
>
|
||||
<div className="w-12 h-12 rounded-xl bg-slate-800 flex items-center justify-center mb-4 group-hover:scale-110 transition-transform">
|
||||
{f.icon}
|
||||
<div
|
||||
className={`w-11 h-11 rounded-xl flex items-center justify-center mb-4 transition-transform group-hover:scale-110 ${bg}`}
|
||||
>
|
||||
<Icon className={`text-xl ${color}`} />
|
||||
</div>
|
||||
<h3 className="text-base font-bold text-white mb-2">{f.title}</h3>
|
||||
<p className="text-xs text-slate-400 leading-relaxed">{f.description}</p>
|
||||
<h3
|
||||
className={`text-sm font-bold mb-1.5 ${isDark ? "text-white" : "text-slate-900"}`}
|
||||
>
|
||||
{title}
|
||||
</h3>
|
||||
<p
|
||||
className={`text-xs leading-relaxed ${isDark ? "text-slate-400" : "text-slate-500"}`}
|
||||
>
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
@@ -1,198 +1,186 @@
|
||||
import React from 'react';
|
||||
import { FiArrowRight, FiPlay, FiStar, FiUsers, FiAward, FiCheckCircle } from 'react-icons/fi';
|
||||
import { FaGraduationCap } from 'react-icons/fa';
|
||||
"use client";
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { FiArrowRight, FiPlay, FiStar, FiChevronRight } from "react-icons/fi";
|
||||
import { useTheme } from "../../context/ThemeContext";
|
||||
import { mockCourses, mockInstructors } from "../../data/mockCourses";
|
||||
import { Button } from "../ui/Button";
|
||||
|
||||
interface HeroSectionProps {
|
||||
onExploreClick: () => void;
|
||||
onOpenPreview: (courseId: string) => void;
|
||||
}
|
||||
|
||||
const featuredCourse = mockCourses[0];
|
||||
const avatars = Object.values(mockInstructors).slice(0, 4).map((i) => i.avatar);
|
||||
|
||||
const stats = [
|
||||
{ value: "1,200+", label: "Cursos" },
|
||||
{ value: "50K+", label: "Alumnos" },
|
||||
{ value: "4.9★", label: "Valoración" },
|
||||
{ value: "98%", label: "Empleabilidad" },
|
||||
];
|
||||
|
||||
export const HeroSection: React.FC<HeroSectionProps> = ({ onExploreClick, onOpenPreview }) => {
|
||||
const { isDark } = useTheme();
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<div className="relative overflow-hidden bg-slate-950 pt-10 pb-16 lg:pt-16 lg:pb-24 border-b border-slate-800/60">
|
||||
<section className="relative overflow-hidden transition-colors duration-300">
|
||||
|
||||
{/* Background glow effects */}
|
||||
<div className="absolute -top-40 -left-40 w-96 h-96 bg-blue-600/20 rounded-full blur-3xl pointer-events-none" />
|
||||
<div className="absolute top-1/3 -right-40 w-96 h-96 bg-cyan-500/15 rounded-full blur-3xl pointer-events-none" />
|
||||
<div className="absolute -bottom-20 left-1/3 w-80 h-80 bg-indigo-600/15 rounded-full blur-3xl pointer-events-none" />
|
||||
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-12 items-center">
|
||||
<div className="relative z-10 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 lg:gap-24 items-center py-20 lg:py-28">
|
||||
|
||||
{/* Left Column: Copy & CTAs */}
|
||||
<div className="lg:col-span-7 space-y-6 text-center lg:text-left">
|
||||
|
||||
{/* Promo Pill */}
|
||||
<div className="inline-flex items-center gap-2 px-3.5 py-1.5 rounded-full text-xs font-semibold bg-gradient-to-r from-blue-500/15 to-cyan-500/15 border border-cyan-500/30 text-cyan-300">
|
||||
<span className="flex h-2 w-2 rounded-full bg-cyan-400 animate-ping" />
|
||||
<FaGraduationCap className="text-cyan-400 text-sm" />
|
||||
<span>Nuevos cursos actualizados para 2026</span>
|
||||
{/* ── Left col ── */}
|
||||
<div className="space-y-8">
|
||||
{/* Headline */}
|
||||
<div className="space-y-2">
|
||||
<h1 className={`text-4xl sm:text-5xl lg:text-[3.4rem] font-black tracking-tight leading-[1.1] ${isDark ? "text-white" : "text-slate-900"}`}>
|
||||
Domina las habilidades
|
||||
<br />
|
||||
<span className="relative">
|
||||
que el mercado{" "}
|
||||
<span className={`relative inline-block ${isDark ? "text-violet-400" : "text-violet-600"}`}>
|
||||
exige.
|
||||
{/* Underline accent */}
|
||||
<svg className="absolute -bottom-1 left-0 w-full" height="4" viewBox="0 0 100 4" preserveAspectRatio="none">
|
||||
<path d="M0 3 Q50 0 100 3" stroke={isDark ? "#a78bfa" : "#7c3aed"} strokeWidth="2.5" fill="none" strokeLinecap="round" />
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
{/* Main Headline */}
|
||||
<h1 className="text-3xl sm:text-5xl lg:text-6xl font-black text-white tracking-tight leading-[1.15]">
|
||||
Aprende las habilidades{' '}
|
||||
<span className="text-transparent bg-clip-text bg-gradient-to-r from-blue-400 via-cyan-400 to-teal-300">
|
||||
más demandadas
|
||||
</span>{' '}
|
||||
con expertos reales.
|
||||
</h1>
|
||||
|
||||
{/* Subtitle */}
|
||||
<p className="text-base sm:text-lg text-slate-300 max-w-2xl mx-auto lg:mx-0 font-normal leading-relaxed">
|
||||
Cursos prácticos basados en proyectos reales. Desde <span className="text-cyan-300 font-medium">Desarrollo Web y Apps</span> hasta <span className="text-cyan-300 font-medium">Inteligencia Artificial y Ciberseguridad</span>. Aprende a tu propio ritmo y obtén certificaciones verificadas.
|
||||
<p className={`text-base sm:text-lg leading-relaxed max-w-lg ${isDark ? "text-slate-400" : "text-slate-500"}`}>
|
||||
Cursos prácticos con proyectos reales, diseñados con expertos de la industria. Aprende a tu ritmo y obtén certificaciones reconocidas.
|
||||
</p>
|
||||
|
||||
{/* Actions & Buttons */}
|
||||
<div className="flex flex-col sm:flex-row items-center justify-center lg:justify-start gap-4 pt-2">
|
||||
<button
|
||||
{/* CTAs */}
|
||||
<div className="flex flex-col sm:flex-row items-start sm:items-center gap-3">
|
||||
<Button
|
||||
variant="violet"
|
||||
size="lg"
|
||||
onClick={onExploreClick}
|
||||
className="w-full sm:w-auto px-8 py-4 bg-gradient-to-r from-blue-600 via-cyan-600 to-teal-500 hover:from-blue-500 hover:to-cyan-400 text-white font-bold rounded-xl text-base shadow-xl shadow-cyan-500/25 hover:shadow-cyan-500/40 hover:scale-[1.02] active:scale-[0.98] transition-all flex items-center justify-center gap-2"
|
||||
rightIcon={<FiArrowRight />}
|
||||
>
|
||||
<span>Explorar Catálogo</span>
|
||||
<FiArrowRight className="text-lg" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => onOpenPreview('course-1')}
|
||||
className="w-full sm:w-auto px-6 py-4 bg-slate-900/80 hover:bg-slate-800 text-slate-200 hover:text-white font-semibold rounded-xl text-base border border-slate-700 hover:border-slate-600 transition-all flex items-center justify-center gap-2"
|
||||
Explorar Cursos
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="lg"
|
||||
onClick={() => router.push("/login")}
|
||||
>
|
||||
<div className="w-6 h-6 rounded-full bg-cyan-500/20 text-cyan-400 flex items-center justify-center text-xs">
|
||||
<FiPlay className="ml-0.5" />
|
||||
</div>
|
||||
<span>Ver Clase Muestra Gratis</span>
|
||||
</button>
|
||||
Regístrate gratis
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Trust checkmarks */}
|
||||
<div className="pt-3 flex flex-wrap items-center justify-center lg:justify-start gap-x-6 gap-y-2 text-xs text-slate-400">
|
||||
<span className="flex items-center gap-1.5">
|
||||
<FiCheckCircle className="text-emerald-400" /> Acceso de por vida
|
||||
</span>
|
||||
<span className="flex items-center gap-1.5">
|
||||
<FiCheckCircle className="text-emerald-400" /> Garantía de reembolso 30 días
|
||||
</span>
|
||||
<span className="flex items-center gap-1.5">
|
||||
<FiCheckCircle className="text-emerald-400" /> Certificado de finalización
|
||||
</span>
|
||||
{/* Trust bar */}
|
||||
<div className={`flex items-center gap-4 pt-2 ${isDark ? "text-slate-400" : "text-slate-500"}`}>
|
||||
<div className="flex -space-x-2.5 shrink-0">
|
||||
{avatars.map((src, i) => (
|
||||
<img key={i} src={src} alt="" className={`w-8 h-8 rounded-full object-cover ring-2 ${isDark ? "ring-slate-950" : "ring-white"}`} />
|
||||
))}
|
||||
</div>
|
||||
<div className="text-sm leading-tight">
|
||||
<span className={`font-bold ${isDark ? "text-slate-200" : "text-slate-700"}`}>50,000+</span> alumnos activos
|
||||
</div>
|
||||
<div className={`w-px h-4 ${isDark ? "bg-slate-800" : "bg-slate-200"}`} />
|
||||
<div className="flex items-center gap-1 text-sm">
|
||||
<FiStar className="fill-amber-400 text-amber-400 text-xs" />
|
||||
<span className={`font-bold ${isDark ? "text-slate-200" : "text-slate-700"}`}>4.9</span>
|
||||
<span className="text-xs">/5.0</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Column: Hero Visual Card */}
|
||||
<div className="lg:col-span-5 relative">
|
||||
<div className="relative mx-auto max-w-md lg:max-w-none">
|
||||
{/* ── Right col ── */}
|
||||
<div className="relative flex justify-center lg:justify-end">
|
||||
|
||||
{/* Main Course Highlight Card */}
|
||||
<div className="rounded-2xl bg-gradient-to-b from-slate-800/80 to-slate-900/90 border border-slate-700/80 p-5 shadow-2xl backdrop-blur-md relative overflow-hidden group">
|
||||
{/* Card glow */}
|
||||
<div className="absolute inset-x-8 bottom-0 top-8 rounded-3xl blur-3xl bg-violet-600/15 pointer-events-none" />
|
||||
|
||||
{/* Thumbnail image */}
|
||||
<div className="relative rounded-xl overflow-hidden aspect-video mb-4 bg-slate-800">
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1633356122544-f134324a6cee?w=800&auto=format&fit=crop&q=80"
|
||||
alt="React 19 Preview"
|
||||
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-slate-950/90 via-slate-950/20 to-transparent" />
|
||||
{/* Course card */}
|
||||
<div
|
||||
className={`relative w-full max-w-[480px] rounded-2xl overflow-hidden border shadow-2xl cursor-pointer transition-transform duration-300 hover:-translate-y-1 ${
|
||||
isDark ? "bg-slate-900 border-slate-800" : "bg-white border-slate-200"
|
||||
}`}
|
||||
onClick={() => onOpenPreview(featuredCourse.id)}
|
||||
>
|
||||
{/* Thumbnail */}
|
||||
<div className="relative overflow-hidden" style={{ aspectRatio: "16/10" }}>
|
||||
<img
|
||||
src={featuredCourse.thumbnail}
|
||||
alt={featuredCourse.title}
|
||||
className="w-full h-full object-cover transition-transform duration-500 hover:scale-105"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-slate-950/70 via-slate-950/10 to-transparent" />
|
||||
|
||||
{/* Play badge */}
|
||||
<button
|
||||
onClick={() => onOpenPreview('course-1')}
|
||||
className="absolute inset-0 m-auto w-14 h-14 rounded-full bg-gradient-to-r from-blue-600 to-cyan-500 text-white flex items-center justify-center shadow-lg shadow-cyan-500/50 hover:scale-110 active:scale-95 transition-transform"
|
||||
aria-label="Reproducir video"
|
||||
>
|
||||
<FiPlay className="text-xl ml-1" />
|
||||
</button>
|
||||
<span className="absolute top-3 left-3 text-[11px] font-black px-2.5 py-1 rounded-lg bg-amber-400 text-slate-950 uppercase tracking-wide">
|
||||
Más vendido
|
||||
</span>
|
||||
|
||||
<span className="absolute top-3 left-3 bg-amber-500/90 text-slate-950 text-[11px] font-black px-2.5 py-1 rounded-md uppercase tracking-wider shadow">
|
||||
🔥 Curso Más Vendido
|
||||
</span>
|
||||
|
||||
<span className="absolute bottom-3 right-3 bg-slate-950/80 backdrop-blur-md text-white text-xs px-2 py-0.5 rounded font-mono">
|
||||
38h 45m
|
||||
</span>
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<div className="w-11 h-11 rounded-full bg-white/15 backdrop-blur-md border border-white/25 flex items-center justify-center transition-all hover:bg-white/25">
|
||||
<FiPlay className="text-white text-sm ml-0.5" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Course Meta in card */}
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between text-xs text-slate-400">
|
||||
<span className="font-semibold text-cyan-400">Desarrollo Web & Next.js</span>
|
||||
<div className="flex items-center gap-1 text-amber-400">
|
||||
<FiStar className="fill-amber-400" />
|
||||
<span className="font-bold text-white">4.9</span>
|
||||
<span>(1.4k reseñas)</span>
|
||||
</div>
|
||||
</div>
|
||||
<span className={`absolute bottom-3 right-3 text-[11px] font-mono px-2 py-0.5 rounded-md backdrop-blur-sm ${isDark ? "bg-slate-950/80 text-slate-300" : "bg-slate-900/75 text-white"}`}>
|
||||
{featuredCourse.duration}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h3 className="text-lg font-bold text-white leading-snug">
|
||||
Master en React 19, Next.js 15 y TypeScript Profesional
|
||||
{/* Card body */}
|
||||
<div className="p-6 space-y-4">
|
||||
<div>
|
||||
<p className={`text-[11px] font-bold uppercase tracking-widest mb-1.5 ${isDark ? "text-violet-400" : "text-violet-600"}`}>
|
||||
{featuredCourse.category}
|
||||
</p>
|
||||
<h3 className={`text-sm font-bold leading-snug line-clamp-2 ${isDark ? "text-white" : "text-slate-900"}`}>
|
||||
{featuredCourse.title}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between pt-2 border-t border-slate-800">
|
||||
<div className="flex items-center gap-2">
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=100&auto=format&fit=crop&q=80"
|
||||
alt="Instructor"
|
||||
className="w-7 h-7 rounded-full object-cover ring-1 ring-cyan-400/50"
|
||||
/>
|
||||
<span className="text-xs text-slate-300 font-medium">Ing. Carlos Mendoza</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<img src={featuredCourse.instructor.avatar} alt="" className="w-6 h-6 rounded-full object-cover shrink-0" />
|
||||
<span className={`text-xs ${isDark ? "text-slate-400" : "text-slate-500"}`}>
|
||||
{featuredCourse.instructor.name.split(" ").slice(0, 2).join(" ")}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-baseline gap-2">
|
||||
<span className="text-xs text-slate-500 line-through font-mono">$89.99</span>
|
||||
<span className="text-xl font-black text-emerald-400 font-mono">$24.99</span>
|
||||
</div>
|
||||
<div className={`flex items-center justify-between pt-4 border-t ${isDark ? "border-slate-800" : "border-slate-100"}`}>
|
||||
<div className="flex items-baseline gap-2">
|
||||
<span className={`text-xs line-through ${isDark ? "text-slate-600" : "text-slate-400"}`}>
|
||||
${featuredCourse.originalPrice}
|
||||
</span>
|
||||
<span className="text-xl font-black text-emerald-500">
|
||||
${featuredCourse.price}
|
||||
</span>
|
||||
</div>
|
||||
<div className={`flex items-center gap-1 text-xs font-semibold px-3 py-1.5 rounded-lg ${isDark ? "bg-violet-500/15 text-violet-300" : "bg-violet-50 text-violet-700"}`}>
|
||||
Ver curso <FiChevronRight className="text-xs" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Floating Stat Badge: Students Active */}
|
||||
<div className="absolute -bottom-6 -left-6 bg-slate-900/90 border border-slate-700/80 rounded-xl p-3.5 shadow-2xl backdrop-blur-md flex items-center gap-3 hidden sm:flex animate-bounce duration-1000">
|
||||
<div className="w-10 h-10 rounded-lg bg-blue-500/20 text-blue-400 flex items-center justify-center text-lg">
|
||||
<FiUsers />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs font-bold text-white">50,000+ Estudiantes</p>
|
||||
<p className="text-[11px] text-slate-400">Aprendiendo activamente</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Floating Stat Badge: Top Rated */}
|
||||
<div className="absolute -top-6 -right-6 bg-slate-900/90 border border-slate-700/80 rounded-xl p-3.5 shadow-2xl backdrop-blur-md flex items-center gap-3 hidden sm:flex">
|
||||
<div className="w-10 h-10 rounded-lg bg-amber-500/20 text-amber-400 flex items-center justify-center text-lg">
|
||||
<FiAward />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs font-bold text-white">Calidad Top 1%</p>
|
||||
<p className="text-[11px] text-slate-400">Instructores validados</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* Bottom stats banner */}
|
||||
<div className="mt-16 pt-10 border-t border-slate-800/80 grid grid-cols-2 md:grid-cols-4 gap-6 text-center">
|
||||
<div className="p-4 rounded-xl bg-slate-900/40 border border-slate-800/60">
|
||||
<div className="text-2xl sm:text-3xl font-black text-white font-mono">1,200+</div>
|
||||
<div className="text-xs text-slate-400 mt-1">Cursos en Alta Definición</div>
|
||||
</div>
|
||||
<div className="p-4 rounded-xl bg-slate-900/40 border border-slate-800/60">
|
||||
<div className="text-2xl sm:text-3xl font-black text-cyan-400 font-mono">50,000+</div>
|
||||
<div className="text-xs text-slate-400 mt-1">Alumnos Graduados</div>
|
||||
</div>
|
||||
<div className="p-4 rounded-xl bg-slate-900/40 border border-slate-800/60">
|
||||
<div className="text-2xl sm:text-3xl font-black text-amber-400 font-mono">4.9 / 5.0</div>
|
||||
<div className="text-xs text-slate-400 mt-1">Puntuación Media</div>
|
||||
</div>
|
||||
<div className="p-4 rounded-xl bg-slate-900/40 border border-slate-800/60">
|
||||
<div className="text-2xl sm:text-3xl font-black text-emerald-400 font-mono">98%</div>
|
||||
<div className="text-xs text-slate-400 mt-1">Tasa de Contratación</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ── Stats bar — ancho completo ── */}
|
||||
<div className={`${isDark ? "border-slate-800/60" : "border-slate-200/60"}`}>
|
||||
<div className="max-w-7xl mx-auto px-2 sm:px-2 lg:px-2 py-1 grid grid-cols-2 md:grid-cols-4 gap-6 text-center">
|
||||
{stats.map(({ value, label }) => (
|
||||
<div key={label} className="flex flex-col items-center gap-0.5">
|
||||
<span className={`text-2xl font-black tracking-tight ${isDark ? "text-white" : "text-slate-900"}`}>{value}</span>
|
||||
<span className={`text-xs font-medium ${isDark ? "text-slate-500" : "text-slate-400"}`}>{label}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,200 @@
|
||||
"use client";
|
||||
import { FiCheck, FiZap, FiAward, FiBriefcase, FiDollarSign, FiClock, FiUsers, FiShield } from "react-icons/fi";
|
||||
import { useTheme } from "../../context/ThemeContext";
|
||||
import { Button } from "../ui/Button";
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
const beneficios = [
|
||||
{ icon: FiDollarSign, titulo: "0% de comisión", desc: "Tú te quedas el 100% de cada venta. Sin sorpresas." },
|
||||
{ icon: FiClock, titulo: "Pagos semanales", desc: "Recibe tus ganancias directo a tu cuenta cada semana." },
|
||||
{ icon: FiUsers, titulo: "Audiencia activa", desc: "Accede a miles de estudiantes listos para aprender." },
|
||||
{ icon: FiShield, titulo: "Sin contratos", desc: "Cancela cuando quieras. Sin letra pequeña." },
|
||||
];
|
||||
|
||||
const planes = [
|
||||
{
|
||||
id: "starter",
|
||||
icon: FiZap,
|
||||
nombre: "Starter",
|
||||
precio: "Gratis",
|
||||
subprecio: "para siempre",
|
||||
descripcion: "Ideal para comenzar a compartir tu conocimiento.",
|
||||
color: "text-slate-400",
|
||||
highlight: false,
|
||||
cta: "Comenzar gratis",
|
||||
features: [
|
||||
"Hasta 3 cursos publicados",
|
||||
"Herramientas básicas de edición",
|
||||
"Panel de estadísticas simple",
|
||||
"Soporte por email",
|
||||
"Certificados para estudiantes",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "pro",
|
||||
icon: FiAward,
|
||||
nombre: "Pro",
|
||||
precio: "$49",
|
||||
subprecio: "/ mes",
|
||||
descripcion: "Para instructores que quieren crecer y monetizar.",
|
||||
color: "text-violet-400",
|
||||
highlight: true,
|
||||
cta: "Empezar con Pro",
|
||||
features: [
|
||||
"Cursos ilimitados",
|
||||
"Analytics avanzado y reportes",
|
||||
"Cupones y descuentos",
|
||||
"Página de instructor personalizada",
|
||||
"Soporte prioritario 24/7",
|
||||
"Acceso a comunidad privada",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "business",
|
||||
icon: FiBriefcase,
|
||||
nombre: "Business",
|
||||
precio: "$89",
|
||||
subprecio: "/ mes",
|
||||
descripcion: "Para academias y equipos de contenido educativo.",
|
||||
color: "text-sky-400",
|
||||
highlight: false,
|
||||
cta: "Contactar ventas",
|
||||
features: [
|
||||
"Todo lo de Pro",
|
||||
"Múltiples instructores",
|
||||
"White-label y dominio propio",
|
||||
"Integraciones con tu CRM",
|
||||
"Gestor de cuenta dedicado",
|
||||
"Contrato personalizado",
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const PlanesSection: React.FC = () => {
|
||||
const { isDark } = useTheme();
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<section className="py-20">
|
||||
<div className="text-center mb-14 space-y-3">
|
||||
<span className={`inline-block text-xs font-bold uppercase tracking-widest px-3 py-1 rounded-full border ${
|
||||
isDark ? "text-violet-400 bg-violet-500/10 border-violet-500/20" : "text-violet-600 bg-violet-50 border-violet-200"
|
||||
}`}>
|
||||
Para instructores
|
||||
</span>
|
||||
<h2 className={`text-2xl sm:text-3xl font-extrabold ${isDark ? "text-white" : "text-slate-900"}`}>
|
||||
Elige el plan que se adapta a ti
|
||||
</h2>
|
||||
<p className={`text-sm max-w-md mx-auto ${isDark ? "text-slate-400" : "text-slate-500"}`}>
|
||||
Comienza gratis y escala cuando estés listo. Sin contratos forzosos.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Beneficios */}
|
||||
<div className={`grid grid-cols-2 md:grid-cols-4 gap-4 mb-12 p-6 rounded-2xl border ${
|
||||
isDark ? "bg-slate-900/40 border-slate-800" : "bg-slate-50 border-slate-200"
|
||||
}`}>
|
||||
{beneficios.map(({ icon: Icon, titulo, desc }) => (
|
||||
<div key={titulo} className="flex items-start gap-3">
|
||||
<div className={`w-9 h-9 rounded-lg flex items-center justify-center shrink-0 ${
|
||||
isDark ? "bg-violet-500/15" : "bg-violet-100"
|
||||
}`}>
|
||||
<Icon className={`text-sm ${isDark ? "text-violet-400" : "text-violet-600"}`} />
|
||||
</div>
|
||||
<div>
|
||||
<p className={`text-sm font-bold ${isDark ? "text-white" : "text-slate-900"}`}>{titulo}</p>
|
||||
<p className={`text-xs leading-relaxed ${isDark ? "text-slate-400" : "text-slate-500"}`}>{desc}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-5 items-start">
|
||||
{planes.map((plan) => {
|
||||
const Icon = plan.icon;
|
||||
return (
|
||||
<div
|
||||
key={plan.id}
|
||||
className={`relative flex flex-col rounded-2xl border p-7 transition-all duration-200 ${
|
||||
plan.highlight
|
||||
? isDark
|
||||
? "bg-violet-950/60 border-violet-700/60 shadow-xl shadow-violet-900/30"
|
||||
: "bg-violet-600 border-violet-500 shadow-xl shadow-violet-600/25"
|
||||
: isDark
|
||||
? "bg-slate-900/60 border-slate-800 hover:border-slate-700"
|
||||
: "bg-white border-slate-200 hover:border-slate-300 shadow-sm"
|
||||
}`}
|
||||
>
|
||||
{plan.highlight && (
|
||||
<span className={`absolute -top-3 left-1/2 -translate-x-1/2 text-[11px] font-black uppercase tracking-widest px-3 py-1 rounded-full ${
|
||||
isDark ? "bg-violet-500 text-white" : "bg-white text-violet-700"
|
||||
}`}>
|
||||
Más popular
|
||||
</span>
|
||||
)}
|
||||
|
||||
<div className="mb-6">
|
||||
<div className={`w-10 h-10 rounded-xl flex items-center justify-center mb-4 ${
|
||||
plan.highlight
|
||||
? isDark ? "bg-violet-500/20" : "bg-white/20"
|
||||
: isDark ? "bg-slate-800" : "bg-slate-100"
|
||||
}`}>
|
||||
<Icon className={`text-lg ${plan.highlight ? (isDark ? "text-violet-300" : "text-white") : plan.color}`} />
|
||||
</div>
|
||||
<h3 className={`text-lg font-bold mb-1 ${
|
||||
plan.highlight ? (isDark ? "text-white" : "text-white") : isDark ? "text-white" : "text-slate-900"
|
||||
}`}>
|
||||
{plan.nombre}
|
||||
</h3>
|
||||
<p className={`text-xs leading-relaxed ${
|
||||
plan.highlight ? (isDark ? "text-violet-300" : "text-violet-100") : isDark ? "text-slate-400" : "text-slate-500"
|
||||
}`}>
|
||||
{plan.descripcion}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mb-7">
|
||||
<div className="flex items-baseline gap-1">
|
||||
<span className={`text-4xl font-black ${
|
||||
plan.highlight ? (isDark ? "text-white" : "text-white") : isDark ? "text-white" : "text-slate-900"
|
||||
}`}>
|
||||
{plan.precio}
|
||||
</span>
|
||||
<span className={`text-sm ${
|
||||
plan.highlight ? (isDark ? "text-violet-300" : "text-violet-100") : isDark ? "text-slate-500" : "text-slate-400"
|
||||
}`}>
|
||||
{plan.subprecio}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul className="space-y-3 mb-8 flex-1">
|
||||
{plan.features.map((f) => (
|
||||
<li key={f} className="flex items-start gap-2.5">
|
||||
<FiCheck className={`text-sm mt-0.5 shrink-0 ${
|
||||
plan.highlight ? (isDark ? "text-violet-400" : "text-white") : isDark ? "text-violet-400" : "text-violet-600"
|
||||
}`} />
|
||||
<span className={`text-sm ${
|
||||
plan.highlight ? (isDark ? "text-slate-300" : "text-violet-50") : isDark ? "text-slate-300" : "text-slate-600"
|
||||
}`}>
|
||||
{f}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<Button
|
||||
variant={plan.highlight ? (isDark ? "violet" : "outline") : "outline"}
|
||||
size="md"
|
||||
onClick={() => router.push("/login")}
|
||||
className="w-full justify-center"
|
||||
>
|
||||
{plan.cta}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,115 @@
|
||||
"use client";
|
||||
import { FiSliders, FiStar } from "react-icons/fi";
|
||||
import { useTheme } from "@/context/ThemeContext";
|
||||
import { Checkbox } from "@/components/ui/Checkbox";
|
||||
import { FilterSection } from "./FilterSection";
|
||||
import { mockCategories } from "@/data/mockCourses";
|
||||
import type { CourseLevel } from "@/types/course";
|
||||
|
||||
const LEVELS: CourseLevel[] = ["Principiante", "Intermedio", "Avanzado", "Todos los niveles"];
|
||||
const RATINGS = [4.5, 4.0, 3.5, 3.0];
|
||||
const PRICES = [15, 25, 50, 100];
|
||||
|
||||
interface Props {
|
||||
selectedCategories: string[];
|
||||
onToggleCategory: (v: string) => void;
|
||||
selectedLevels: string[];
|
||||
onToggleLevel: (v: string) => void;
|
||||
minRating: number | null;
|
||||
onToggleRating: (r: number) => void;
|
||||
maxPrice: number | null;
|
||||
onTogglePrice: (p: number) => void;
|
||||
activeFiltersCount: number;
|
||||
onClearAll: () => void;
|
||||
}
|
||||
|
||||
export function CatalogSidebar({
|
||||
selectedCategories, onToggleCategory,
|
||||
selectedLevels, onToggleLevel,
|
||||
minRating, onToggleRating,
|
||||
maxPrice, onTogglePrice,
|
||||
activeFiltersCount, onClearAll,
|
||||
}: Props) {
|
||||
const { isDark } = useTheme();
|
||||
const hoverRow = isDark ? "hover:bg-slate-800" : "hover:bg-slate-50";
|
||||
const ratingActive = isDark ? "bg-violet-600/20 text-violet-300" : "bg-violet-50 text-violet-700";
|
||||
const ratingIdle = isDark ? `text-slate-400 hover:text-slate-200 ${hoverRow}` : `text-slate-600 ${hoverRow}`;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center justify-between mb-5">
|
||||
<span className={`text-sm font-bold flex items-center gap-2 ${isDark ? "text-white" : "text-slate-800"}`}>
|
||||
<FiSliders /> Filtros
|
||||
{activeFiltersCount > 0 && (
|
||||
<span className="bg-violet-600 text-white text-[10px] font-black px-1.5 py-0.5 rounded-full">
|
||||
{activeFiltersCount}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
{activeFiltersCount > 0 && (
|
||||
<button onClick={onClearAll} className="text-xs text-violet-400 hover:text-violet-300 font-semibold">
|
||||
Limpiar todo
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<FilterSection title="Categoría">
|
||||
<div className="space-y-0.5">
|
||||
{mockCategories.filter((c) => c.id !== "all").map((cat) => (
|
||||
<div key={cat.id} className={`flex items-center justify-between px-2 py-1 rounded-lg transition-colors ${hoverRow}`}>
|
||||
<Checkbox
|
||||
label={cat.name}
|
||||
checked={selectedCategories.includes(cat.name)}
|
||||
onChange={() => onToggleCategory(cat.name)}
|
||||
/>
|
||||
<span className="text-[10px] px-1.5 py-0.5 rounded">{cat.count}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</FilterSection>
|
||||
|
||||
<FilterSection title="Nivel">
|
||||
<div className="space-y-0.5">
|
||||
{LEVELS.map((lvl) => (
|
||||
<div key={lvl} className={`px-2 py-1 rounded-lg transition-colors ${hoverRow}`}>
|
||||
<Checkbox
|
||||
label={lvl}
|
||||
checked={selectedLevels.includes(lvl)}
|
||||
onChange={() => onToggleLevel(lvl)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</FilterSection>
|
||||
|
||||
<FilterSection title="Valoración mínima">
|
||||
<div className="space-y-0.5">
|
||||
{RATINGS.map((r) => (
|
||||
<button
|
||||
key={r}
|
||||
onClick={() => onToggleRating(r)}
|
||||
className={`w-full flex items-center gap-2 px-2 py-1.5 rounded-lg text-xs transition-colors ${minRating === r ? ratingActive : ratingIdle}`}
|
||||
>
|
||||
<FiStar className={minRating === r ? "fill-amber-400 text-amber-400" : "text-amber-400"} />
|
||||
<span className="font-medium">{r}+ estrellas</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</FilterSection>
|
||||
|
||||
<FilterSection title="Precio máximo" defaultOpen={false}>
|
||||
<div className="space-y-0.5">
|
||||
{PRICES.map((p) => (
|
||||
<button
|
||||
key={p}
|
||||
onClick={() => onTogglePrice(p)}
|
||||
className={`w-full flex items-center justify-between px-2 py-1.5 rounded-lg text-xs transition-colors ${maxPrice === p ? ratingActive : ratingIdle}`}
|
||||
>
|
||||
<span className="font-medium">Hasta ${p} USD</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</FilterSection>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
"use client";
|
||||
import { FiSearch, FiX, FiFilter, FiGrid, FiList } from "react-icons/fi";
|
||||
import { useTheme } from "@/context/ThemeContext";
|
||||
import { SortDropdown } from "./SortDropdown";
|
||||
|
||||
interface Props {
|
||||
search: string;
|
||||
onSearchChange: (v: string) => void;
|
||||
sort: string;
|
||||
onSortChange: (v: string) => void;
|
||||
gridView: "grid" | "list";
|
||||
onGridViewChange: (v: "grid" | "list") => void;
|
||||
activeFiltersCount: number;
|
||||
onOpenMobileSidebar: () => void;
|
||||
}
|
||||
|
||||
export function CatalogToolbar({
|
||||
search, onSearchChange,
|
||||
sort, onSortChange,
|
||||
gridView, onGridViewChange,
|
||||
activeFiltersCount, onOpenMobileSidebar,
|
||||
}: Props) {
|
||||
const { isDark } = useTheme();
|
||||
const inputCl = isDark
|
||||
? "bg-slate-900 border-slate-700 text-white placeholder:text-slate-500 focus:border-violet-500"
|
||||
: "bg-white border-slate-300 text-slate-800 placeholder:text-slate-400 focus:border-violet-500";
|
||||
const toggleBorder = isDark ? "border-slate-700" : "border-slate-200";
|
||||
const toggleActive = "bg-violet-600 text-white";
|
||||
const toggleIdle = isDark ? "bg-slate-900 text-slate-400 hover:text-slate-200" : "bg-white text-slate-400 hover:text-slate-700";
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-3 mb-5 flex-wrap">
|
||||
<button
|
||||
onClick={onOpenMobileSidebar}
|
||||
className={`lg:hidden flex items-center gap-1.5 px-3 py-2 rounded-xl border text-xs font-semibold shrink-0 ${isDark ? "border-slate-700 text-slate-300 bg-slate-900" : "border-slate-200 text-slate-600 bg-white"}`}
|
||||
>
|
||||
<FiFilter /> Filtros
|
||||
{activeFiltersCount > 0 && ` (${activeFiltersCount})`}
|
||||
</button>
|
||||
|
||||
<div className="relative flex-1 min-w-48">
|
||||
<FiSearch className={`absolute left-3.5 top-1/2 -translate-y-1/2 text-sm pointer-events-none ${isDark ? "text-slate-500" : "text-slate-400"}`} />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Buscar cursos, instructores..."
|
||||
value={search}
|
||||
onChange={(e) => onSearchChange(e.target.value)}
|
||||
className={`w-full h-9 pl-9 pr-9 rounded-xl border text-sm outline-none transition-colors ${inputCl}`}
|
||||
/>
|
||||
{search && (
|
||||
<button onClick={() => onSearchChange("")} className="absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 hover:text-slate-300">
|
||||
<FiX className="text-sm" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 shrink-0">
|
||||
<SortDropdown value={sort} onChange={onSortChange} />
|
||||
<div className={`flex rounded-xl border overflow-hidden ${toggleBorder}`}>
|
||||
{(["grid", "list"] as const).map((v) => (
|
||||
<button
|
||||
key={v}
|
||||
onClick={() => onGridViewChange(v)}
|
||||
className={`w-9 h-9 flex items-center justify-center transition-colors ${gridView === v ? toggleActive : toggleIdle}`}
|
||||
>
|
||||
{v === "grid" ? <FiGrid className="text-sm" /> : <FiList className="text-sm" />}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
"use client";
|
||||
import { useState } from "react";
|
||||
import { FiChevronDown, FiChevronUp } from "react-icons/fi";
|
||||
import { useTheme } from "@/context/ThemeContext";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
children: React.ReactNode;
|
||||
defaultOpen?: boolean;
|
||||
}
|
||||
|
||||
export function FilterSection({ title, children, defaultOpen = true }: Props) {
|
||||
const [open, setOpen] = useState(defaultOpen);
|
||||
const { isDark } = useTheme();
|
||||
|
||||
return (
|
||||
<div className={`border-b pb-4 mb-4 -mx-5 px-5 ${isDark ? "border-slate-800" : "border-slate-200"}`}>
|
||||
<button
|
||||
onClick={() => setOpen(!open)}
|
||||
className={`flex items-center justify-between w-full text-sm font-semibold mb-3 ${isDark ? "text-white" : "text-slate-800"}`}
|
||||
>
|
||||
{title}
|
||||
{open ? <FiChevronUp className="text-slate-400" /> : <FiChevronDown className="text-slate-400" />}
|
||||
</button>
|
||||
{open && children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
"use client";
|
||||
import { useRef } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { FiStar, FiShoppingCart, FiCheck } from "react-icons/fi";
|
||||
import { useTheme } from "@/context/ThemeContext";
|
||||
import { useCart } from "@/context/CartContext";
|
||||
import type { Course } from "@/types/course";
|
||||
|
||||
export function ListCourseCard({ course }: { course: Course }) {
|
||||
const { isDark } = useTheme();
|
||||
const { addToCart, isInCart: isInCartFn } = useCart();
|
||||
const isInCart = isInCartFn(course.id);
|
||||
const router = useRouter();
|
||||
const btnRef = useRef<HTMLButtonElement>(null);
|
||||
const discountPercent = Math.round(
|
||||
((course.originalPrice - course.price) / course.originalPrice) * 100,
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`flex gap-4 rounded-2xl border p-4 transition-all hover:-translate-y-0.5 ${
|
||||
isDark
|
||||
? "bg-slate-900/90 border-slate-800 hover:border-slate-700"
|
||||
: "bg-white border-slate-200 hover:border-slate-300 hover:shadow-md"
|
||||
}`}
|
||||
>
|
||||
<img
|
||||
src={course.thumbnail}
|
||||
alt={course.title}
|
||||
onClick={() => router.push(`/course/${course.id}`)}
|
||||
className="w-40 h-24 rounded-xl object-cover shrink-0 cursor-pointer"
|
||||
/>
|
||||
<div className="flex-1 min-w-0 flex flex-col justify-between">
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<span className={`text-[10px] font-bold uppercase tracking-wider ${isDark ? "text-violet-400" : "text-violet-600"}`}>
|
||||
{course.category}
|
||||
</span>
|
||||
{course.badge && (
|
||||
<span className="text-[10px] font-black px-2 py-0.5 rounded bg-amber-500 text-slate-950">
|
||||
{course.badge}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<h3
|
||||
onClick={() => router.push(`/course/${course.id}`)}
|
||||
className={`text-sm font-bold line-clamp-1 cursor-pointer hover:text-violet-400 transition-colors ${isDark ? "text-white" : "text-slate-900"}`}
|
||||
>
|
||||
{course.title}
|
||||
</h3>
|
||||
<p className={`text-xs line-clamp-1 mt-0.5 ${isDark ? "text-slate-400" : "text-slate-500"}`}>
|
||||
{course.subtitle}
|
||||
</p>
|
||||
<div className="flex items-center gap-3 mt-2 text-xs">
|
||||
<span className="flex items-center gap-1 font-bold text-amber-400">
|
||||
<FiStar className="fill-amber-400" /> {course.rating.toFixed(1)}
|
||||
</span>
|
||||
<span className={isDark ? "text-slate-500" : "text-slate-400"}>
|
||||
({course.reviewsCount.toLocaleString()})
|
||||
</span>
|
||||
<span className={isDark ? "text-slate-500" : "text-slate-400"}>
|
||||
{course.studentsCount.toLocaleString()} alumnos
|
||||
</span>
|
||||
<span className={isDark ? "text-slate-500" : "text-slate-400"}>
|
||||
{course.duration}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col items-end justify-between shrink-0">
|
||||
<div className="text-right">
|
||||
<div className={`text-lg font-black font-mono ${isDark ? "text-white" : "text-slate-900"}`}>
|
||||
${course.price.toFixed(2)}
|
||||
</div>
|
||||
{discountPercent > 0 && (
|
||||
<div className={`text-xs line-through font-mono ${isDark ? "text-slate-500" : "text-slate-400"}`}>
|
||||
${course.originalPrice.toFixed(2)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
ref={btnRef}
|
||||
onClick={() => !isInCart && addToCart(course, btnRef.current)}
|
||||
disabled={isInCart}
|
||||
className={`px-3 py-2 rounded-xl text-xs font-bold flex items-center gap-1.5 transition-all ${
|
||||
isInCart
|
||||
? "bg-emerald-500/20 text-emerald-400 border border-emerald-500/30 cursor-default"
|
||||
: "bg-violet-600 hover:bg-violet-500 text-white hover:scale-105 active:scale-95"
|
||||
}`}
|
||||
>
|
||||
{isInCart ? (
|
||||
<><FiCheck /><span>En carrito</span></>
|
||||
) : (
|
||||
<><FiShoppingCart /><span>Añadir</span></>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
"use client";
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import { FiChevronDown, FiCheck } from "react-icons/fi";
|
||||
import { useTheme } from "@/context/ThemeContext";
|
||||
import { SORT_OPTIONS } from "./sortOptions";
|
||||
|
||||
interface Props { value: string; onChange: (v: string) => void; }
|
||||
|
||||
export function SortDropdown({ value, onChange }: Props) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const { isDark } = useTheme();
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const active = SORT_OPTIONS.find((o) => o.value === value) ?? SORT_OPTIONS[0];
|
||||
|
||||
useEffect(() => {
|
||||
const handler = (e: MouseEvent) => {
|
||||
if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false);
|
||||
};
|
||||
document.addEventListener("mousedown", handler);
|
||||
return () => document.removeEventListener("mousedown", handler);
|
||||
}, []);
|
||||
|
||||
const triggerCl = isDark
|
||||
? "bg-slate-900 border-slate-700 text-slate-200 hover:border-violet-500/50"
|
||||
: "bg-white border-slate-200 text-slate-700 hover:border-violet-400";
|
||||
const menuCl = isDark
|
||||
? "bg-slate-900 border-slate-700 shadow-xl shadow-black/40"
|
||||
: "bg-white border-slate-200 shadow-xl shadow-slate-200/60";
|
||||
|
||||
return (
|
||||
<div ref={ref} className="relative">
|
||||
<button
|
||||
onClick={() => setOpen((p) => !p)}
|
||||
className={`flex items-center gap-2 h-9 pl-3 pr-2.5 rounded-xl border text-xs font-medium transition-all ${triggerCl} ${open ? "border-violet-500/60" : ""}`}
|
||||
>
|
||||
<active.icon className={`text-sm shrink-0 ${isDark ? "text-violet-400" : "text-violet-500"}`} />
|
||||
<span className="whitespace-nowrap">{active.label}</span>
|
||||
<FiChevronDown className={`text-slate-400 text-xs shrink-0 transition-transform duration-200 ${open ? "rotate-180" : ""}`} />
|
||||
</button>
|
||||
|
||||
{open && (
|
||||
<div className={`absolute right-0 top-full mt-1.5 z-50 min-w-[188px] rounded-2xl border overflow-hidden ${menuCl}`}>
|
||||
<div className="py-1.5">
|
||||
{SORT_OPTIONS.map((opt) => {
|
||||
const isActive = opt.value === value;
|
||||
return (
|
||||
<button
|
||||
key={opt.value}
|
||||
onClick={() => { onChange(opt.value); setOpen(false); }}
|
||||
className={`w-full flex items-center gap-3 px-3.5 py-2.5 text-xs font-medium transition-colors text-left ${
|
||||
isActive
|
||||
? isDark ? "bg-violet-600/20 text-violet-300" : "bg-violet-50 text-violet-700"
|
||||
: isDark ? "text-slate-300 hover:bg-slate-800" : "text-slate-600 hover:bg-slate-50"
|
||||
}`}
|
||||
>
|
||||
<opt.icon className={`text-sm shrink-0 ${isActive ? (isDark ? "text-violet-400" : "text-violet-500") : "text-slate-400"}`} />
|
||||
<span className="flex-1">{opt.label}</span>
|
||||
{isActive && <FiCheck className={`text-xs shrink-0 ${isDark ? "text-violet-400" : "text-violet-500"}`} />}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { FiTrendingUp, FiAward, FiClock, FiArrowUp, FiArrowDown } from "react-icons/fi";
|
||||
|
||||
export const SORT_OPTIONS = [
|
||||
{ value: "popular", label: "Más populares", icon: FiTrendingUp },
|
||||
{ value: "rating", label: "Mejor valorados", icon: FiAward },
|
||||
{ value: "newest", label: "Más recientes", icon: FiClock },
|
||||
{ value: "price_lo", label: "Precio: menor a mayor", icon: FiArrowUp },
|
||||
{ value: "price_hi", label: "Precio: mayor a menor", icon: FiArrowDown },
|
||||
];
|
||||
@@ -1,45 +1,48 @@
|
||||
import React from 'react';
|
||||
"use client";
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useTheme } from "../../context/ThemeContext";
|
||||
|
||||
interface BrandLogoProps {
|
||||
size?: 'sm' | 'md' | 'lg';
|
||||
showSubtitle?: boolean;
|
||||
size?: "sm" | "md" | "lg";
|
||||
asLink?: boolean;
|
||||
}
|
||||
|
||||
export const BrandLogo: React.FC<BrandLogoProps> = ({ size = 'md', showSubtitle = true }) => {
|
||||
const iconSizes = {
|
||||
sm: 'w-8 h-8',
|
||||
md: 'w-10 h-10',
|
||||
lg: 'w-12 h-12',
|
||||
};
|
||||
const sizes = {
|
||||
sm: { img: "h-7", title: "text-sm", sub: "text-[10px]" },
|
||||
md: { img: "h-9", title: "text-base", sub: "text-xs" },
|
||||
lg: { img: "h-11", title: "text-lg", sub: "text-xs" },
|
||||
};
|
||||
|
||||
const textSizes = {
|
||||
sm: 'text-lg',
|
||||
md: 'text-xl',
|
||||
lg: 'text-2xl',
|
||||
};
|
||||
export const BrandLogo: React.FC<BrandLogoProps> = ({ size = "md", asLink = false }) => {
|
||||
const { isDark } = useTheme();
|
||||
const router = useRouter();
|
||||
const s = sizes[size];
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-3 group select-none cursor-pointer">
|
||||
{/* Official PNG Logo Image with sleek container styling */}
|
||||
<div className={`relative ${iconSizes[size]} p-0.5 rounded-xl bg-slate-100 dark:bg-slate-900 border border-slate-200 dark:border-slate-700/80 shadow-md group-hover:border-cyan-500/60 group-hover:scale-105 transition-all duration-300 overflow-hidden`}>
|
||||
<img
|
||||
src="/logo.png"
|
||||
alt="RyCourses Emblem"
|
||||
className="w-full h-full object-cover rounded-[10px]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Typography */}
|
||||
<div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className={`${textSizes[size]} font-extrabold tracking-tight text-slate-900 dark:text-white group-hover:text-cyan-600 dark:group-hover:text-cyan-300 transition-colors`}>
|
||||
Ry<span className="text-transparent bg-clip-text bg-gradient-to-r from-cyan-500 via-sky-500 to-blue-500 dark:from-cyan-400 dark:to-blue-400">Courses</span>
|
||||
</span>
|
||||
</div>
|
||||
{showSubtitle && (
|
||||
<p className="text-[11px] text-slate-500 dark:text-slate-400 font-medium tracking-wide">Plataforma Educativa</p>
|
||||
)}
|
||||
const content = (
|
||||
<div className="flex items-center gap-2.5">
|
||||
<img
|
||||
src={isDark ? "/branding/logo_white.svg" : "/branding/logo_black.svg"}
|
||||
alt="Rynext Academy logo"
|
||||
className={`${s.img} w-auto`}
|
||||
/>
|
||||
<div style={{ fontFamily: "'Quicksand', sans-serif" }} className="flex flex-col leading-tight">
|
||||
<span className={`font-bold tracking-wide ${s.title} ${isDark ? "text-white" : "text-slate-900"}`}>
|
||||
Rynext Academy
|
||||
</span>
|
||||
<span className={`font-medium tracking-widest ${s.sub} ${isDark ? "text-slate-400" : "text-slate-500"}`}>
|
||||
by Rynext Technologies
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
if (asLink) {
|
||||
return (
|
||||
<button onClick={() => router.push("/")} className="shrink-0 text-left cursor-pointer">
|
||||
{content}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
return <div className="shrink-0">{content}</div>;
|
||||
};
|
||||
@@ -1,104 +1,130 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import {
|
||||
FiGlobe,
|
||||
FiShield,
|
||||
FiHeart
|
||||
} from 'react-icons/fi';
|
||||
import { FaTwitter, FaYoutube, FaGithub, FaLinkedin, FaDiscord } from 'react-icons/fa';
|
||||
"use client";
|
||||
import Link from 'next/link';
|
||||
import { FiGlobe, FiShield } from "react-icons/fi";
|
||||
import { FaTwitter, FaYoutube, FaGithub, FaLinkedin, FaDiscord } from "react-icons/fa";
|
||||
import { BrandLogo } from "../common/BrandLogo";
|
||||
import { useTheme } from "../../context/ThemeContext";
|
||||
|
||||
import { BrandLogo } from '../common/BrandLogo';
|
||||
const links = {
|
||||
estudiantes: [
|
||||
{ label: "Explorar Cursos", to: "/" },
|
||||
{ label: "Rutas de Especialización", to: "/" },
|
||||
{ label: "Certificados Oficiales", to: "/" },
|
||||
{ label: "Becas y Descuentos", to: "/" },
|
||||
{ label: "Preguntas Frecuentes", to: "/" },
|
||||
],
|
||||
instructores: [
|
||||
{ label: "Enseñar en Rynext Academy", to: "/" },
|
||||
{ label: "Calculadora de Ganancias", to: "/" },
|
||||
{ label: "Guía del Creador", to: "/" },
|
||||
{ label: "Políticas de Contenido", to: "/" },
|
||||
{ label: "Comunidad de Profesores", to: "/" },
|
||||
],
|
||||
empresa: [
|
||||
{ label: "Sobre Nosotros", to: "https://rynext.com.mx" },
|
||||
{ label: "Rynext Academy para Empresas", to: "/empresas" },
|
||||
{ label: "Términos de Servicio", to: "/terms" },
|
||||
{ label: "Política de Privacidad", to: "/privacy" },
|
||||
{ label: "Centro de Ayuda", to: "/" },
|
||||
],
|
||||
};
|
||||
|
||||
const socials = [
|
||||
{ icon: <FaTwitter />, hover: "hover:text-cyan-400 hover:border-cyan-500/40" },
|
||||
{ icon: <FaYoutube />, hover: "hover:text-red-400 hover:border-red-500/40" },
|
||||
{ icon: <FaGithub />, hover: "hover:text-white hover:border-slate-500" },
|
||||
{ icon: <FaLinkedin />, hover: "hover:text-blue-400 hover:border-blue-500/40" },
|
||||
{ icon: <FaDiscord />, hover: "hover:text-indigo-400 hover:border-indigo-500/40"},
|
||||
];
|
||||
|
||||
export const Footer: React.FC = () => {
|
||||
const { isDark } = useTheme();
|
||||
|
||||
const border = isDark ? "border-slate-800" : "border-slate-200";
|
||||
const text = isDark ? "text-slate-400" : "text-slate-500";
|
||||
const heading = isDark ? "text-slate-200" : "text-slate-700";
|
||||
const linkHov = isDark ? "hover:text-violet-300" : "hover:text-violet-600";
|
||||
const iconBg = isDark ? "bg-slate-900 border-slate-800" : "bg-slate-100 border-slate-200";
|
||||
|
||||
return (
|
||||
<footer className="bg-slate-950 border-t border-slate-800 text-slate-400 text-sm">
|
||||
|
||||
{/* Main Footer Links */}
|
||||
<footer className={`${text} text-sm transition-colors duration-300 ${isDark ? "bg-slate-950" : "bg-white border-t border-slate-200"}`}>
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-14">
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-5 gap-8 lg:gap-12">
|
||||
|
||||
{/* Brand col */}
|
||||
<div className="col-span-2 lg:col-span-2">
|
||||
{/* Brand */}
|
||||
<div className="col-span-2">
|
||||
<div className="mb-4">
|
||||
<BrandLogo size="md" />
|
||||
<BrandLogo size="md" asLink />
|
||||
</div>
|
||||
<p className="text-xs text-slate-400 leading-relaxed mb-6 max-w-sm">
|
||||
La plataforma líder para aprender habilidades de alta demanda y convertir tu conocimiento profesional en un negocio digital rentable.
|
||||
<p className="text-xs leading-relaxed mb-6 max-w-sm">
|
||||
La plataforma para aprender habilidades de alta demanda y convertir tu conocimiento en un negocio digital rentable.
|
||||
</p>
|
||||
<div className="flex items-center gap-3">
|
||||
<a href="#" className="w-9 h-9 rounded-lg bg-slate-900 border border-slate-800 flex items-center justify-center text-slate-400 hover:text-cyan-400 hover:border-cyan-500/40 transition">
|
||||
<FaTwitter />
|
||||
</a>
|
||||
<a href="#" className="w-9 h-9 rounded-lg bg-slate-900 border border-slate-800 flex items-center justify-center text-slate-400 hover:text-red-400 hover:border-red-500/40 transition">
|
||||
<FaYoutube />
|
||||
</a>
|
||||
<a href="#" className="w-9 h-9 rounded-lg bg-slate-900 border border-slate-800 flex items-center justify-center text-slate-400 hover:text-white hover:border-slate-600 transition">
|
||||
<FaGithub />
|
||||
</a>
|
||||
<a href="#" className="w-9 h-9 rounded-lg bg-slate-900 border border-slate-800 flex items-center justify-center text-slate-400 hover:text-blue-400 hover:border-blue-500/40 transition">
|
||||
<FaLinkedin />
|
||||
</a>
|
||||
<a href="#" className="w-9 h-9 rounded-lg bg-slate-900 border border-slate-800 flex items-center justify-center text-slate-400 hover:text-indigo-400 hover:border-indigo-500/40 transition">
|
||||
<FaDiscord />
|
||||
</a>
|
||||
<div className="flex items-center gap-2">
|
||||
{socials.map(({ icon, hover }, i) => (
|
||||
<a key={i} href="#"
|
||||
className={`w-9 h-9 rounded-lg border flex items-center justify-center transition-all duration-200 ${iconBg} ${hover}`}
|
||||
>
|
||||
{icon}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Column 1: Estudiantes */}
|
||||
{/* Estudiantes */}
|
||||
<div>
|
||||
<h4 className="text-xs font-bold uppercase tracking-wider text-slate-200 mb-4">Para Estudiantes</h4>
|
||||
<h4 className={`text-xs font-bold uppercase tracking-wider mb-4 ${heading}`}>Para Estudiantes</h4>
|
||||
<ul className="space-y-2.5 text-xs">
|
||||
<li><a href="#" className="hover:text-cyan-300 transition">Explorar Cursos</a></li>
|
||||
<li><a href="#" className="hover:text-cyan-300 transition">Rutas de Especialización</a></li>
|
||||
<li><a href="#" className="hover:text-cyan-300 transition">Certificados Oficiales</a></li>
|
||||
<li><a href="#" className="hover:text-cyan-300 transition">Becas y Descuentos</a></li>
|
||||
<li><a href="#" className="hover:text-cyan-300 transition">Preguntas Frecuentes</a></li>
|
||||
{links.estudiantes.map(({ label, to }) => (
|
||||
<li key={label}><Link href={to} className={`transition ${linkHov}`}>{label}</Link></li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Column 2: Instructores */}
|
||||
{/* Instructores */}
|
||||
<div>
|
||||
<h4 className="text-xs font-bold uppercase tracking-wider text-slate-200 mb-4">Para Instructores</h4>
|
||||
<h4 className={`text-xs font-bold uppercase tracking-wider mb-4 ${heading}`}>Para Instructores</h4>
|
||||
<ul className="space-y-2.5 text-xs">
|
||||
<li><a href="#" className="hover:text-cyan-300 transition">Enseñar en RyCourses</a></li>
|
||||
<li><a href="#" className="hover:text-cyan-300 transition">Calculadora de Ganancias</a></li>
|
||||
<li><a href="#" className="hover:text-cyan-300 transition">Guía del Creador</a></li>
|
||||
<li><a href="#" className="hover:text-cyan-300 transition">Políticas de Contenido</a></li>
|
||||
<li><a href="#" className="hover:text-cyan-300 transition">Comunidad de Profesores</a></li>
|
||||
{links.instructores.map(({ label, to }) => (
|
||||
<li key={label}><Link href={to} className={`transition ${linkHov}`}>{label}</Link></li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Column 3: Empresa & Legal */}
|
||||
{/* Empresa */}
|
||||
<div>
|
||||
<h4 className="text-xs font-bold uppercase tracking-wider text-slate-200 mb-4">Empresa & Soporte</h4>
|
||||
<h4 className={`text-xs font-bold uppercase tracking-wider mb-4 ${heading}`}>Empresa & Soporte</h4>
|
||||
<ul className="space-y-2.5 text-xs">
|
||||
<li><a href="#" className="hover:text-cyan-300 transition">Sobre Nosotros</a></li>
|
||||
<li><a href="#" className="hover:text-cyan-300 transition">RyCourses para Empresas</a></li>
|
||||
<li><Link to="/terms" className="hover:text-cyan-300 transition">Términos de Servicio</Link></li>
|
||||
<li><Link to="/terms" className="hover:text-cyan-300 transition">Política de Privacidad</Link></li>
|
||||
<li><a href="#" className="hover:text-cyan-300 transition">Centro de Ayuda</a></li>
|
||||
{links.empresa.map(({ label, to }) => (
|
||||
<li key={label}>
|
||||
{to.startsWith("http") ? (
|
||||
<a href={to} target="_blank" rel="noopener noreferrer" className={`transition ${linkHov}`}>{label}</a>
|
||||
) : (
|
||||
<Link href={to} className={`transition ${linkHov}`}>{label}</Link>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* Bottom bar */}
|
||||
<div className="mt-12 pt-8 border-t border-slate-800/80 flex flex-col md:flex-row items-center justify-between gap-4 text-xs">
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="flex items-center gap-1.5 text-slate-400">
|
||||
<FiGlobe className="text-cyan-400" /> Español (América Latina)
|
||||
</span>
|
||||
<span className="flex items-center gap-1.5 text-slate-400">
|
||||
<FiShield className="text-emerald-400" /> Pagos cifrados SSL 256-bit
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-1 text-slate-500">
|
||||
<span>© 2026 RyCourses Inc. Hecho con</span>
|
||||
<FiHeart className="text-rose-500 inline text-xs" />
|
||||
<span>para creadores y estudiantes.</span>
|
||||
</div>
|
||||
{/* Bottom bar — ancho completo */}
|
||||
<div className={`border-t ${border} transition-colors duration-300`}>
|
||||
<div className={`max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-5 flex flex-col md:flex-row items-center justify-between gap-4 text-xs ${text}`}>
|
||||
<span>© 2026 Rynext Academy. Todos los derechos reservados.</span>
|
||||
<span className={isDark ? "text-slate-500" : "text-slate-400"}>
|
||||
Powered by{" "}
|
||||
<a
|
||||
href="https://rynext.com.mx"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={`font-semibold transition hover:underline ${isDark ? "text-slate-300 hover:text-white" : "text-slate-600 hover:text-slate-900"}`}
|
||||
>
|
||||
Rynext Technologies
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
"use client";
|
||||
import { Navbar } from "./Navbar";
|
||||
import { Footer } from "./Footer";
|
||||
import { useTheme } from "../../context/ThemeContext";
|
||||
|
||||
interface IProps {
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export default function LayoutBase({ ...props }: IProps) {
|
||||
const { isDark } = useTheme();
|
||||
|
||||
return (
|
||||
<div className={`relative flex flex-col min-h-screen min-w-screen transition-colors duration-300 ${isDark ? "text-slate-100" : "text-slate-900"}`}>
|
||||
<div className={`fixed inset-0 -z-10 transition-all duration-300 ${isDark ? "bg-linear-to-b from-slate-950 via-slate-950 to-[#1a0a4a]" : "bg-white"}`} />
|
||||
|
||||
<Navbar />
|
||||
<main className="flex-1 mx-auto w-full max-w-7xl px-4 sm:px-6 lg:px-8 py-8">
|
||||
{props.children}
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,326 +1,124 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useNavigate, useLocation } from 'react-router';
|
||||
import {
|
||||
FiShoppingCart,
|
||||
FiMenu,
|
||||
FiX,
|
||||
FiHome,
|
||||
FiGrid,
|
||||
FiUser
|
||||
} from 'react-icons/fi';
|
||||
import { FaChalkboardTeacher, FaSignOutAlt } from 'react-icons/fa';
|
||||
import { BrandLogo } from '../common/BrandLogo';
|
||||
import { ThemeToggle, Button } from '../ui';
|
||||
import type { User } from '../../types/course';
|
||||
"use client";
|
||||
|
||||
interface NavbarProps {
|
||||
currentUser: User | null;
|
||||
onLogout: () => void;
|
||||
cartCount: number;
|
||||
onOpenCart: () => void;
|
||||
onOpenCreateCourseModal?: () => void;
|
||||
searchQuery: string;
|
||||
onSearchChange: (query: string) => void;
|
||||
}
|
||||
import { useState } from "react";
|
||||
import { useRouter, usePathname } from "next/navigation";
|
||||
import { FiMenu, FiX } from "react-icons/fi";
|
||||
import { RouteNavigation, type INavigation } from "../../Routes";
|
||||
import { ShopCart } from "../ShopCart";
|
||||
import { ThemeToggle } from "../ui/ThemeToggle";
|
||||
import { useTheme } from "../../context/ThemeContext";
|
||||
import { BrandLogo } from "../common/BrandLogo";
|
||||
|
||||
export const Navbar: React.FC<NavbarProps> = ({
|
||||
currentUser,
|
||||
onLogout,
|
||||
cartCount,
|
||||
onOpenCart,
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||
const [cartHovered, setCartHovered] = useState(false);
|
||||
export const Navbar = () => {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const { isDark } = useTheme();
|
||||
const [mobileOpen, setMobileOpen] = useState(false);
|
||||
|
||||
// Inject keyframe animation styles inline
|
||||
useEffect(() => {
|
||||
const styleId = 'cart-run-keyframes';
|
||||
if (!document.getElementById(styleId)) {
|
||||
const style = document.createElement('style');
|
||||
style.id = styleId;
|
||||
style.textContent = `
|
||||
@keyframes cart-run {
|
||||
0% { transform: rotate(0deg) translateY(0px); }
|
||||
25% { transform: rotate(-8deg) translateY(-1px); }
|
||||
50% { transform: rotate(0deg) translateY(0px); }
|
||||
75% { transform: rotate(-5deg) translateY(-1px); }
|
||||
100% { transform: rotate(0deg) translateY(0px); }
|
||||
}
|
||||
@keyframes speed-streak {
|
||||
0% { opacity: 0; transform: translateX(2px) scaleX(0.1); }
|
||||
40% { opacity: 0.6; transform: translateX(-3px) scaleX(1); }
|
||||
100% { opacity: 0; transform: translateX(-6px) scaleX(0); }
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
}, []);
|
||||
const navigate = (path: string) => {
|
||||
router.push(path);
|
||||
setMobileOpen(false);
|
||||
};
|
||||
|
||||
const currentPath = location.pathname;
|
||||
const border = isDark ? "border-slate-800/50" : "border-slate-200";
|
||||
const bg = isDark ? "bg-slate-950/80" : "bg-white/90";
|
||||
const mobileBg = isDark ? "bg-slate-950" : "bg-white";
|
||||
|
||||
return (
|
||||
<header className="sticky top-0 z-40 w-full border-b backdrop-blur-xl transition-all border-slate-200 dark:border-slate-800/80 bg-white/90 dark:bg-slate-950/90 text-slate-900 dark:text-slate-100">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex items-center justify-between h-20 gap-4">
|
||||
<>
|
||||
<header className={`sticky top-0 z-40 w-full backdrop-blur-xl transition-colors duration-300 border-b ${bg} ${border}`}>
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex items-center h-14 gap-4">
|
||||
|
||||
{/* 1. Left Brand & Main Links */}
|
||||
<div className="flex items-center gap-6 shrink-0">
|
||||
<div onClick={() => navigate('/')}>
|
||||
<BrandLogo size="md" />
|
||||
{/* Logo */}
|
||||
<div className="shrink-0">
|
||||
<BrandLogo size="sm" asLink />
|
||||
</div>
|
||||
|
||||
{/* Main Nav Links */}
|
||||
<nav className="hidden lg:flex items-center gap-1">
|
||||
<button
|
||||
onClick={() => navigate('/')}
|
||||
className={`flex items-center gap-1.5 px-3.5 py-2 rounded-xl text-xs font-semibold transition-all ${
|
||||
currentPath === '/'
|
||||
? 'bg-slate-100 dark:bg-slate-800 text-cyan-600 dark:text-cyan-400 border border-slate-200 dark:border-cyan-500/30'
|
||||
: 'text-slate-600 dark:text-slate-300 hover:text-slate-900 dark:hover:text-white hover:bg-slate-100 dark:hover:bg-slate-900'
|
||||
}`}
|
||||
>
|
||||
<FiHome />
|
||||
<span>Inicio</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => navigate('/courses')}
|
||||
className={`flex items-center gap-1.5 px-3.5 py-2 rounded-xl text-xs font-semibold transition-all ${
|
||||
currentPath === '/courses'
|
||||
? 'bg-slate-100 dark:bg-slate-800 text-cyan-600 dark:text-cyan-400 border border-slate-200 dark:border-cyan-500/30'
|
||||
: 'text-slate-600 dark:text-slate-300 hover:text-slate-900 dark:hover:text-white hover:bg-slate-100 dark:hover:bg-slate-900'
|
||||
}`}
|
||||
>
|
||||
<FiGrid />
|
||||
<span>Catálogo</span>
|
||||
</button>
|
||||
|
||||
{/* Landing link for instructor sales */}
|
||||
<button
|
||||
onClick={() => navigate('/seller')}
|
||||
className={`flex items-center gap-1.5 px-3.5 py-2 rounded-xl text-xs font-semibold transition-all ${
|
||||
currentPath === '/seller'
|
||||
? 'bg-slate-100 dark:bg-slate-800 text-violet-600 dark:text-violet-400 border border-slate-200 dark:border-violet-500/30'
|
||||
: 'text-slate-600 dark:text-slate-300 hover:text-slate-900 dark:hover:text-white hover:bg-slate-100 dark:hover:bg-slate-900'
|
||||
}`}
|
||||
>
|
||||
<FaChalkboardTeacher />
|
||||
<span>Vender Cursos</span>
|
||||
</button>
|
||||
{/* Links desktop */}
|
||||
<nav className="hidden lg:flex items-center gap-0.5 flex-1 justify-center">
|
||||
{RouteNavigation.map((route: INavigation, i) => {
|
||||
const isActive = pathname === route.path;
|
||||
return (
|
||||
<button
|
||||
key={i}
|
||||
onClick={() => navigate(route.path)}
|
||||
className={`flex items-center gap-1.5 px-3.5 py-2 rounded-lg text-sm font-medium transition-colors duration-200 cursor-pointer ${
|
||||
isActive
|
||||
? isDark ? "text-white" : "text-violet-700"
|
||||
: isDark ? "text-slate-400 hover:text-white" : "text-slate-500 hover:text-slate-900"
|
||||
}`}
|
||||
>
|
||||
{route.icon}
|
||||
<span>{route.name}</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
{/* 2. Right Actions & ThemeToggle Beside Cart */}
|
||||
<div className="flex items-center gap-3 shrink-0">
|
||||
|
||||
{/* Login & Register Buttons OR User Profile */}
|
||||
{!currentUser ? (
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => navigate('/login')}
|
||||
>
|
||||
Iniciar Sesión
|
||||
</Button>
|
||||
<Button
|
||||
variant="gradient"
|
||||
size="sm"
|
||||
onClick={() => navigate('/register')}
|
||||
>
|
||||
Registrarse
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-2 pl-2 border-l border-slate-200 dark:border-slate-800 relative group">
|
||||
<div className="relative cursor-pointer flex items-center gap-2" title="Mi Cuenta">
|
||||
<div className="hidden sm:block text-right">
|
||||
<p className="text-[11px] font-bold leading-none text-slate-800 dark:text-slate-200">
|
||||
{currentUser.name}
|
||||
</p>
|
||||
<p className="text-[9px] text-cyan-600 dark:text-cyan-400 font-medium capitalize mt-0.5">
|
||||
{currentUser.role === 'buyer' ? 'Estudiante' : 'Instructor'}
|
||||
</p>
|
||||
</div>
|
||||
<div className="relative">
|
||||
{currentUser.avatar ? (
|
||||
<img
|
||||
src={currentUser.avatar}
|
||||
alt={currentUser.name}
|
||||
className="w-9 h-9 rounded-full object-cover ring-2 ring-indigo-500/50 group-hover:ring-cyan-400 transition"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-9 h-9 rounded-full flex items-center justify-center text-sm font-bold ring-2 ring-indigo-500/50 group-hover:ring-cyan-400 transition bg-slate-200 dark:bg-slate-800 text-slate-800 dark:text-slate-300">
|
||||
{currentUser.name.charAt(0).toUpperCase()}
|
||||
</div>
|
||||
)}
|
||||
<span className="absolute bottom-0 right-0 w-2.5 h-2.5 bg-emerald-500 ring-2 ring-white dark:ring-slate-950 rounded-full"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Dropdown Menu */}
|
||||
<div className="absolute top-full right-0 mt-2 w-48 border rounded-xl shadow-2xl opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 transform origin-top-right scale-95 group-hover:scale-100 z-50 bg-white dark:bg-slate-900 border-slate-200 dark:border-slate-800">
|
||||
<div className="p-2 space-y-1">
|
||||
<button onClick={onLogout} className="w-full flex items-center gap-2 px-3 py-2 text-xs font-semibold text-rose-500 hover:text-rose-600 hover:bg-rose-500/10 rounded-lg transition">
|
||||
<FaSignOutAlt />
|
||||
<span>Cerrar Sesión</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* ThemeToggle and Cart Button Grouped Together */}
|
||||
<div className="flex items-center gap-2.5 pl-2 border-l border-slate-200 dark:border-slate-800 relative">
|
||||
<ThemeToggle variant="icon" size="md" />
|
||||
|
||||
{/* Derecha */}
|
||||
<div className="flex items-center gap-2 shrink-0 ml-auto">
|
||||
{/* Login — oculto en mobile muy pequeño */}
|
||||
<button
|
||||
onClick={onOpenCart}
|
||||
onMouseEnter={() => setCartHovered(true)}
|
||||
onMouseLeave={() => setCartHovered(false)}
|
||||
className="group relative p-2.5 rounded-xl transition-all shadow-md backdrop-blur-sm text-slate-700 dark:text-slate-300 hover:text-slate-900 dark:hover:text-white bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-800 hover:border-cyan-500/50"
|
||||
aria-label="Carrito de compras"
|
||||
onClick={() => navigate("/login")}
|
||||
className={`hidden sm:inline-flex text-sm font-medium px-4 py-1.5 rounded-full border transition-colors cursor-pointer ${
|
||||
isDark
|
||||
? "border-slate-700 text-slate-200 hover:border-violet-400 hover:text-violet-300"
|
||||
: "border-slate-300 text-slate-700 hover:border-violet-500 hover:text-violet-600"
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
position: 'relative',
|
||||
animation: cartHovered ? 'cart-run 0.45s ease-in-out infinite' : 'none',
|
||||
}}
|
||||
>
|
||||
<FiShoppingCart className="text-lg" />
|
||||
Iniciar sesión
|
||||
</button>
|
||||
<ThemeToggle variant="switch" />
|
||||
<ShopCart />
|
||||
|
||||
{/* Speed streaks */}
|
||||
<span style={{
|
||||
position: 'absolute',
|
||||
top: '25%',
|
||||
left: '-4px',
|
||||
width: '5px',
|
||||
height: '1.5px',
|
||||
borderRadius: '9999px',
|
||||
background: 'rgb(34 211 238)',
|
||||
animation: cartHovered ? 'speed-streak 0.5s linear infinite' : 'none',
|
||||
opacity: 0,
|
||||
}} />
|
||||
<span style={{
|
||||
position: 'absolute',
|
||||
top: '55%',
|
||||
left: '-2px',
|
||||
width: '4px',
|
||||
height: '1.5px',
|
||||
borderRadius: '9999px',
|
||||
background: 'rgb(34 211 238)',
|
||||
animation: cartHovered ? 'speed-streak 0.5s linear 0.18s infinite' : 'none',
|
||||
opacity: 0,
|
||||
}} />
|
||||
<span style={{
|
||||
position: 'absolute',
|
||||
bottom: '15%',
|
||||
left: '-5px',
|
||||
width: '6px',
|
||||
height: '1.5px',
|
||||
borderRadius: '9999px',
|
||||
background: 'rgb(99 102 241)',
|
||||
animation: cartHovered ? 'speed-streak 0.5s linear 0.35s infinite' : 'none',
|
||||
opacity: 0,
|
||||
}} />
|
||||
</div>
|
||||
|
||||
{/* Badge counter: Perfectly placed floating badge */}
|
||||
{cartCount > 0 && (
|
||||
<span className="absolute -top-1.5 -right-1.5 min-w-[20px] h-[20px] px-1 bg-gradient-to-r from-rose-500 to-pink-500 text-white text-[11px] font-bold rounded-full flex items-center justify-center shadow-lg shadow-rose-500/40 ring-2 ring-white dark:ring-slate-950 z-30 transition-transform duration-200 group-hover:scale-110">
|
||||
{cartCount}
|
||||
</span>
|
||||
)}
|
||||
{/* Hamburger */}
|
||||
<button
|
||||
onClick={() => setMobileOpen((v) => !v)}
|
||||
className={`lg:hidden p-2 rounded-lg transition cursor-pointer ${
|
||||
isDark ? "text-slate-400 hover:text-white hover:bg-white/5" : "text-slate-500 hover:text-slate-900 hover:bg-slate-100"
|
||||
}`}
|
||||
aria-label="Menú"
|
||||
>
|
||||
{mobileOpen ? <FiX className="text-xl" /> : <FiMenu className="text-xl" />}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Mobile menu trigger */}
|
||||
<button
|
||||
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
||||
className="p-2 md:hidden text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white"
|
||||
>
|
||||
{mobileMenuOpen ? <FiX className="text-2xl" /> : <FiMenu className="text-2xl" />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile menu expanded */}
|
||||
{mobileMenuOpen && (
|
||||
<div className="md:hidden py-4 border-t space-y-4 border-slate-200 dark:border-slate-800">
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
<button
|
||||
onClick={() => { navigate('/'); setMobileMenuOpen(false); }}
|
||||
className={`flex items-center justify-center gap-1.5 p-2 rounded-lg text-xs font-semibold ${
|
||||
currentPath === '/' ? 'bg-blue-600 text-white' : 'bg-slate-100 dark:bg-slate-800 text-slate-700 dark:text-slate-300'
|
||||
}`}
|
||||
>
|
||||
<FiHome /> Inicio
|
||||
</button>
|
||||
<button
|
||||
onClick={() => { navigate('/courses'); setMobileMenuOpen(false); }}
|
||||
className={`flex items-center justify-center gap-1.5 p-2 rounded-lg text-xs font-semibold ${
|
||||
currentPath === '/courses' ? 'bg-cyan-600 text-white' : 'bg-slate-100 dark:bg-slate-800 text-slate-700 dark:text-slate-300'
|
||||
}`}
|
||||
>
|
||||
<FiGrid /> Catálogo
|
||||
</button>
|
||||
<button
|
||||
onClick={() => { navigate('/seller'); setMobileMenuOpen(false); }}
|
||||
className={`flex items-center justify-center gap-1.5 p-2 rounded-lg text-xs font-semibold ${
|
||||
currentPath === '/seller' ? 'bg-violet-600 text-white' : 'bg-slate-100 dark:bg-slate-800 text-slate-700 dark:text-slate-300'
|
||||
}`}
|
||||
>
|
||||
<FaChalkboardTeacher /> Vender
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-2 pt-2 border-t border-slate-200 dark:border-slate-800">
|
||||
{!currentUser ? (
|
||||
<>
|
||||
{/* Mobile menu */}
|
||||
{mobileOpen && (
|
||||
<div className={`lg:hidden border-t ${border} ${mobileBg}`}>
|
||||
<div className="max-w-7xl mx-auto px-4 py-3 flex flex-col gap-1">
|
||||
{RouteNavigation.map((route: INavigation, i) => {
|
||||
const isActive = pathname === route.path;
|
||||
return (
|
||||
<button
|
||||
onClick={() => { navigate('/login'); setMobileMenuOpen(false); }}
|
||||
className="w-full py-2.5 font-semibold rounded-lg text-xs flex items-center justify-center gap-2 bg-slate-100 dark:bg-slate-800 text-slate-900 dark:text-white"
|
||||
key={i}
|
||||
onClick={() => navigate(route.path)}
|
||||
className={`flex items-center gap-2.5 px-4 py-3 rounded-xl text-sm font-medium text-left transition cursor-pointer ${
|
||||
isActive
|
||||
? isDark ? "bg-white/5 text-white" : "bg-violet-50 text-violet-700"
|
||||
: isDark ? "text-slate-400 hover:text-white hover:bg-white/5" : "text-slate-600 hover:text-slate-900 hover:bg-slate-50"
|
||||
}`}
|
||||
>
|
||||
<FiUser /> Iniciar Sesión
|
||||
{route.icon}
|
||||
{route.name}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => { navigate('/register'); setMobileMenuOpen(false); }}
|
||||
className="w-full py-2.5 bg-cyan-600 text-white font-semibold rounded-lg text-xs flex items-center justify-center gap-2"
|
||||
>
|
||||
Registrarse
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="w-full py-2.5 px-3 rounded-lg flex items-center gap-3 bg-slate-100 dark:bg-slate-800">
|
||||
{currentUser.avatar ? (
|
||||
<img src={currentUser.avatar} alt="Avatar" className="w-6 h-6 rounded-full object-cover" />
|
||||
) : (
|
||||
<div className="w-6 h-6 rounded-full bg-slate-200 dark:bg-slate-700 flex items-center justify-center text-[10px] font-bold text-slate-800 dark:text-white">
|
||||
{currentUser.name.charAt(0).toUpperCase()}
|
||||
</div>
|
||||
)}
|
||||
<div className="text-left">
|
||||
<p className="text-[10px] font-bold truncate max-w-[100px] text-slate-900 dark:text-white">{currentUser.name}</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => { onLogout(); setMobileMenuOpen(false); }}
|
||||
className="w-full py-2.5 bg-rose-500/10 text-rose-500 font-semibold rounded-lg text-xs flex items-center justify-center gap-2 border border-rose-500/20"
|
||||
>
|
||||
<FaSignOutAlt /> Cerrar Sesión
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
);
|
||||
})}
|
||||
<div className={`mt-2 pt-3 border-t ${border}`}>
|
||||
<button
|
||||
onClick={() => navigate("/login")}
|
||||
className="w-full py-3 bg-violet-600 hover:bg-violet-500 text-white text-sm font-semibold rounded-xl transition cursor-pointer"
|
||||
>
|
||||
Iniciar sesión
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</header>
|
||||
</header>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
"use client";
|
||||
import React, { useState, useRef } from 'react';
|
||||
import {
|
||||
FiX,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"use client";
|
||||
import { useState } from 'react';
|
||||
import { FiDollarSign, FiUsers, FiCheckCircle } from 'react-icons/fi';
|
||||
|
||||
@@ -126,7 +127,7 @@ export const SellerCalculator: React.FC<SellerCalculatorProps> = ({ onStartCours
|
||||
</div>
|
||||
<div className="h-8 w-[1px] bg-slate-800" />
|
||||
<div>
|
||||
<span className="text-[11px] text-slate-400">Comisión RyCourses</span>
|
||||
<span className="text-[11px] text-slate-400">Comisión Rynext Academy</span>
|
||||
<p className="text-xl font-bold text-emerald-400 font-mono">Solo 10%</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"use client";
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
FiPlusCircle,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"use client";
|
||||
import React from 'react';
|
||||
import {
|
||||
FiDollarSign,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"use client";
|
||||
import React from 'react';
|
||||
import {
|
||||
FiPlusCircle,
|
||||
@@ -30,7 +31,7 @@ export const SellerHero: React.FC<SellerHeroProps> = ({
|
||||
|
||||
<div className="inline-flex items-center gap-2 px-3.5 py-1.5 rounded-full text-xs font-semibold bg-violet-500/15 border border-violet-500/30 text-violet-300">
|
||||
<FaChalkboardTeacher className="text-violet-400" />
|
||||
<span>RyCourses Creator Studio</span>
|
||||
<span>Rynext Academy Creator Studio</span>
|
||||
</div>
|
||||
|
||||
<h1 className="text-3xl sm:text-5xl lg:text-6xl font-black text-white tracking-tight leading-[1.15]">
|
||||
@@ -127,7 +128,7 @@ export const SellerHero: React.FC<SellerHeroProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xs text-slate-300 italic">
|
||||
"Publicar mi curso en RyCourses me permitió alcanzar a más de 30,000 alumnos en toda Latinoamérica y generar ingresos pasivos recurrentes."
|
||||
"Publicar mi curso en Rynext Academy me permitió alcanzar a más de 30,000 alumnos en toda Latinoamérica y generar ingresos pasivos recurrentes."
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"use client";
|
||||
import React from 'react';
|
||||
import { FiX } from 'react-icons/fi';
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
"use client";
|
||||
import React from 'react';
|
||||
import { Spinner } from './Spinner';
|
||||
|
||||
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
variant?: 'primary' | 'secondary' | 'outline' | 'ghost' | 'danger' | 'success' | 'gradient';
|
||||
variant?: 'primary' | 'secondary' | 'outline' | 'ghost' | 'danger' | 'success' | 'gradient' | 'violet';
|
||||
size?: 'xs' | 'sm' | 'md' | 'lg' | 'icon';
|
||||
isLoading?: boolean;
|
||||
loadingText?: string;
|
||||
@@ -26,6 +27,8 @@ const variantStyles: Record<NonNullable<ButtonProps['variant']>, string> = {
|
||||
'bg-emerald-600 hover:bg-emerald-500 text-white font-medium shadow-md shadow-emerald-600/25 focus-visible:ring-emerald-500 dark:bg-emerald-500 dark:hover:bg-emerald-400 dark:text-slate-950',
|
||||
gradient:
|
||||
'bg-gradient-to-r from-cyan-500 via-sky-500 to-blue-600 hover:from-cyan-400 hover:via-sky-400 hover:to-blue-500 text-white font-semibold shadow-lg shadow-cyan-500/25 hover:shadow-cyan-500/40 focus-visible:ring-cyan-500 dark:from-cyan-500 dark:to-blue-600',
|
||||
violet:
|
||||
'bg-violet-600 hover:bg-violet-500 active:bg-violet-700 text-white font-bold shadow-lg shadow-violet-600/25 hover:shadow-violet-500/30 hover:-translate-y-0.5 focus-visible:ring-violet-500',
|
||||
};
|
||||
|
||||
const sizeStyles: Record<NonNullable<ButtonProps['size']>, string> = {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"use client";
|
||||
import React from 'react';
|
||||
|
||||
export interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"use client";
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { FiCheck, FiMinus } from 'react-icons/fi';
|
||||
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
"use client";
|
||||
import React, { useState, useRef, useEffect } from "react";
|
||||
import { FiCheck, FiChevronDown, FiSearch, FiX } from "react-icons/fi";
|
||||
import { useTheme } from "../../context/ThemeContext";
|
||||
|
||||
export interface DropdownOption {
|
||||
value: string;
|
||||
label: string;
|
||||
count?: number;
|
||||
}
|
||||
|
||||
interface DropdownProps {
|
||||
value: string[];
|
||||
onChange: (values: string[]) => void;
|
||||
options: DropdownOption[];
|
||||
placeholder?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const Dropdown: React.FC<DropdownProps> = ({
|
||||
value,
|
||||
onChange,
|
||||
options,
|
||||
placeholder = "Seleccionar",
|
||||
className = "",
|
||||
}) => {
|
||||
const { isDark } = useTheme();
|
||||
const [open, setOpen] = useState(false);
|
||||
const [search, setSearch] = useState("");
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const searchRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const filtered = options.filter((o) =>
|
||||
o.label.toLowerCase().includes(search.toLowerCase())
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const handler = (e: MouseEvent) => {
|
||||
if (ref.current && !ref.current.contains(e.target as Node)) {
|
||||
setOpen(false);
|
||||
setSearch("");
|
||||
}
|
||||
};
|
||||
document.addEventListener("mousedown", handler);
|
||||
return () => document.removeEventListener("mousedown", handler);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (open) setTimeout(() => searchRef.current?.focus(), 50);
|
||||
}, [open]);
|
||||
|
||||
const toggle = (val: string) =>
|
||||
onChange(value.includes(val) ? value.filter((v) => v !== val) : [...value, val]);
|
||||
|
||||
const removeTag = (val: string, e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
onChange(value.filter((v) => v !== val));
|
||||
};
|
||||
|
||||
const labelOf = (val: string) => options.find((o) => o.value === val)?.label ?? val;
|
||||
|
||||
const borderColor = open
|
||||
? isDark ? "border-violet-500/50" : "border-violet-400"
|
||||
: isDark ? "border-slate-700" : "border-slate-200";
|
||||
|
||||
const hoverBorder = !open
|
||||
? isDark ? "hover:border-violet-500/40" : "hover:border-violet-300"
|
||||
: "";
|
||||
|
||||
const bg = isDark ? "bg-slate-800" : "bg-white";
|
||||
const divider = isDark ? "bg-slate-700" : "bg-slate-100";
|
||||
|
||||
return (
|
||||
<div ref={ref} className={`relative ${className}`}>
|
||||
|
||||
{/* Trigger */}
|
||||
<div
|
||||
onClick={() => setOpen((p) => !p)}
|
||||
className={`flex items-center gap-2 min-h-[46px] w-full px-4 py-2 cursor-pointer border transition-colors duration-150 ${bg} ${borderColor} ${hoverBorder} ${
|
||||
open ? "rounded-t-xl border-b-0" : "rounded-xl"
|
||||
}`}
|
||||
>
|
||||
<div className="flex-1 flex items-center gap-1.5 min-w-0 overflow-hidden">
|
||||
{value.length === 0 ? (
|
||||
<span className={`text-sm font-medium ${isDark ? "text-slate-500" : "text-slate-400"}`}>
|
||||
{placeholder}
|
||||
</span>
|
||||
) : (
|
||||
<>
|
||||
{value.slice(0, 1).map((v) => (
|
||||
<span key={v} className={`inline-flex items-center gap-1 px-2 py-0.5 rounded-lg text-xs font-semibold shrink-0 ${
|
||||
isDark ? "bg-violet-600/25 text-violet-300" : "bg-violet-100 text-violet-700"
|
||||
}`}>
|
||||
{labelOf(v)}
|
||||
<button type="button" onClick={(e) => removeTag(v, e)} className="hover:opacity-70">
|
||||
<FiX className="text-[10px]" />
|
||||
</button>
|
||||
</span>
|
||||
))}
|
||||
{value.length > 1 && (
|
||||
<span className={`text-xs font-semibold px-2 py-0.5 rounded-lg shrink-0 ${
|
||||
isDark ? "bg-slate-700 text-slate-400" : "bg-slate-100 text-slate-500"
|
||||
}`}>
|
||||
+{value.length - 2} más
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<FiChevronDown className={`shrink-0 text-sm transition-transform duration-200 ${open ? "rotate-180" : ""} ${isDark ? "text-slate-500" : "text-slate-400"}`} />
|
||||
</div>
|
||||
|
||||
{/* Menu — absolute, sin border-top para no doblar la línea */}
|
||||
{open && (
|
||||
<div className={`absolute left-0 top-full z-50 w-full rounded-b-xl border border-t-0 shadow-lg overflow-hidden ${bg} ${borderColor}`}>
|
||||
|
||||
{/* Search */}
|
||||
<div className={`h-px ${divider}`} />
|
||||
<div className={`flex items-center gap-2 px-3 py-2 mx-3 my-2 rounded-lg border ${isDark ? "border-slate-700 bg-slate-900/50" : "border-slate-200 bg-slate-50"}`}>
|
||||
<FiSearch className={`shrink-0 text-sm ${isDark ? "text-slate-500" : "text-slate-400"}`} />
|
||||
<input
|
||||
ref={searchRef}
|
||||
type="text"
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
placeholder="Buscar..."
|
||||
className={`flex-1 text-sm bg-transparent outline-none ${
|
||||
isDark ? "text-slate-200 placeholder:text-slate-600" : "text-slate-700 placeholder:text-slate-400"
|
||||
}`}
|
||||
/>
|
||||
{search && (
|
||||
<button onClick={() => setSearch("")} className={isDark ? "text-slate-500 hover:text-slate-300" : "text-slate-400 hover:text-slate-600"}>
|
||||
<FiX className="text-xs" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={`h-px ${divider}`} />
|
||||
|
||||
{/* Options */}
|
||||
<div className="max-h-52 overflow-y-auto py-1">
|
||||
{filtered.length === 0 ? (
|
||||
<p className={`px-4 py-3 text-xs text-center ${isDark ? "text-slate-600" : "text-slate-400"}`}>
|
||||
Sin resultados
|
||||
</p>
|
||||
) : (
|
||||
filtered.map((opt) => {
|
||||
const isActive = value.includes(opt.value);
|
||||
return (
|
||||
<button
|
||||
key={opt.value}
|
||||
type="button"
|
||||
onClick={() => toggle(opt.value)}
|
||||
className={`w-full flex items-center gap-3 px-4 py-3 text-sm transition-colors text-left ${
|
||||
isActive
|
||||
? isDark ? "bg-violet-600/15 text-violet-300" : "bg-violet-50 text-violet-700"
|
||||
: isDark ? "text-slate-300 hover:bg-slate-700/50" : "text-slate-700 hover:bg-slate-50"
|
||||
}`}
|
||||
>
|
||||
<span className={`w-4 h-4 rounded flex items-center justify-center border shrink-0 transition-colors ${
|
||||
isActive ? "bg-violet-600 border-violet-600" : isDark ? "border-slate-600" : "border-slate-300"
|
||||
}`}>
|
||||
{isActive && <FiCheck className="text-white text-[10px]" />}
|
||||
</span>
|
||||
<span className="flex-1 font-medium">{opt.label}</span>
|
||||
{opt.count !== undefined && (
|
||||
<span className={`text-[11px] px-1.5 py-0.5 rounded font-mono shrink-0 ${
|
||||
isActive
|
||||
? isDark ? "bg-violet-500/30 text-violet-300" : "bg-violet-100 text-violet-600"
|
||||
: isDark ? "bg-slate-700 text-slate-500" : "bg-slate-100 text-slate-400"
|
||||
}`}>
|
||||
{opt.count}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
|
||||
{value.length > 0 && (
|
||||
<>
|
||||
<div className={`h-px ${divider}`} />
|
||||
<div className="px-4 py-2.5 flex justify-end">
|
||||
<button
|
||||
onClick={() => onChange([])}
|
||||
className={`text-xs font-medium transition-colors ${isDark ? "text-slate-500 hover:text-slate-300" : "text-slate-400 hover:text-slate-600"}`}
|
||||
>
|
||||
Limpiar selección
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
"use client";
|
||||
import React, { useState } from 'react';
|
||||
import { FiEye, FiEyeOff, FiX, FiAlertCircle } from 'react-icons/fi';
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"use client";
|
||||
import React, { useEffect } from 'react';
|
||||
import { FiX } from 'react-icons/fi';
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"use client";
|
||||
import React from 'react';
|
||||
import { FiChevronDown, FiAlertCircle } from 'react-icons/fi';
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"use client";
|
||||
import React from 'react';
|
||||
|
||||
export interface SpinnerProps {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"use client";
|
||||
import React from 'react';
|
||||
|
||||
export interface SwitchProps {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"use client";
|
||||
import React from 'react';
|
||||
import { FiAlertCircle } from 'react-icons/fi';
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
"use client";
|
||||
import React from 'react';
|
||||
import { FiSun, FiMoon } from 'react-icons/fi';
|
||||
import { useTheme } from '../../context/ThemeContext';
|
||||
|
||||
export interface ThemeToggleProps {
|
||||
variant?: 'icon' | 'pill' | 'switch';
|
||||
@@ -12,30 +14,7 @@ export const ThemeToggle: React.FC<ThemeToggleProps> = ({
|
||||
size = 'md',
|
||||
className = '',
|
||||
}) => {
|
||||
const [isDark, setIsDark] = useState<boolean>(() => {
|
||||
if (typeof window === 'undefined') return true;
|
||||
const stored = localStorage.getItem('theme');
|
||||
if (stored) return stored === 'dark';
|
||||
return (
|
||||
document.documentElement.classList.contains('dark') ||
|
||||
window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
);
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const root = document.documentElement;
|
||||
if (isDark) {
|
||||
root.classList.add('dark');
|
||||
localStorage.setItem('theme', 'dark');
|
||||
} else {
|
||||
root.classList.remove('dark');
|
||||
localStorage.setItem('theme', 'light');
|
||||
}
|
||||
}, [isDark]);
|
||||
|
||||
const toggleTheme = () => {
|
||||
setIsDark((prev) => !prev);
|
||||
};
|
||||
const { isDark, toggle: toggleTheme } = useTheme();
|
||||
|
||||
const iconSizes = {
|
||||
sm: 'text-sm',
|
||||
@@ -88,17 +67,21 @@ export const ThemeToggle: React.FC<ThemeToggleProps> = ({
|
||||
aria-label="Alternar tema de color"
|
||||
className={`
|
||||
relative inline-flex items-center w-14 h-8 rounded-full p-1 transition-colors duration-300
|
||||
${isDark ? 'bg-slate-800 border border-slate-700' : 'bg-slate-200 border border-slate-300'}
|
||||
${isDark ? "bg-slate-800 border border-slate-700" : "bg-slate-100 border border-slate-200"}
|
||||
${className}
|
||||
`}
|
||||
>
|
||||
<span
|
||||
className={`
|
||||
flex items-center justify-center w-6 h-6 rounded-full bg-white dark:bg-slate-900 shadow-md transform transition-transform duration-300
|
||||
${isDark ? 'translate-x-6 text-cyan-400' : 'translate-x-0 text-amber-500'}
|
||||
flex items-center justify-center w-6 h-6 rounded-full shadow-md transform transition-transform duration-300
|
||||
${isDark ? "bg-slate-900 translate-x-6 text-white" : "bg-white/85 translate-x-0 text-yellow-400"}
|
||||
`}
|
||||
>
|
||||
{isDark ? <FiMoon className="text-xs" /> : <FiSun className="text-xs" />}
|
||||
{isDark ? (
|
||||
<FiMoon className="text-xs" />
|
||||
) : (
|
||||
<FiSun className="text-xs" />
|
||||
)}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
|
||||
@@ -46,3 +46,4 @@ export type { ModalProps } from './Modal';
|
||||
// Theme Control
|
||||
export { ThemeToggle } from './ThemeToggle';
|
||||
export type { ThemeToggleProps } from './ThemeToggle';
|
||||
export * from './Dropdown';
|
||||
@@ -0,0 +1,79 @@
|
||||
"use client";
|
||||
|
||||
import { createContext, useContext, useState, useCallback, useRef } from "react";
|
||||
import type { Course } from "../types/course";
|
||||
|
||||
export interface FlyItem {
|
||||
id: string;
|
||||
thumbnail: string;
|
||||
fromX: number;
|
||||
fromY: number;
|
||||
}
|
||||
|
||||
interface CartContextType {
|
||||
cartItems: Course[];
|
||||
addToCart: (course: Course, triggerEl?: HTMLElement | null) => void;
|
||||
removeFromCart: (id: string) => void;
|
||||
clearCart: () => void;
|
||||
isInCart: (id: string) => boolean;
|
||||
isDrawerOpen: boolean;
|
||||
openDrawer: () => void;
|
||||
closeDrawer: () => void;
|
||||
flyItems: FlyItem[];
|
||||
cartIconRef: React.RefObject<HTMLButtonElement | null>;
|
||||
}
|
||||
|
||||
const CartContext = createContext<CartContextType>(null!);
|
||||
|
||||
export function CartProvider({ children }: { children: React.ReactNode }) {
|
||||
const [cartItems, setCartItems] = useState<Course[]>([]);
|
||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
||||
const [flyItems, setFlyItems] = useState<FlyItem[]>([]);
|
||||
const cartIconRef = useRef<HTMLButtonElement | null>(null);
|
||||
|
||||
const addToCart = useCallback((course: Course, triggerEl?: HTMLElement | null) => {
|
||||
// Trigger flying animation
|
||||
if (triggerEl && cartIconRef.current) {
|
||||
const from = triggerEl.getBoundingClientRect();
|
||||
const flyId = `${course.id}-${Date.now()}`;
|
||||
const newFly: FlyItem = {
|
||||
id: flyId,
|
||||
thumbnail: course.thumbnail,
|
||||
fromX: from.left + from.width / 2,
|
||||
fromY: from.top + from.height / 2,
|
||||
};
|
||||
setFlyItems((p) => [...p, newFly]);
|
||||
setTimeout(() => setFlyItems((p) => p.filter((f) => f.id !== flyId)), 900);
|
||||
}
|
||||
|
||||
setCartItems((prev) =>
|
||||
prev.some((c) => c.id === course.id) ? prev : [...prev, course]
|
||||
);
|
||||
}, []);
|
||||
|
||||
const removeFromCart = useCallback((id: string) => {
|
||||
setCartItems((prev) => prev.filter((c) => c.id !== id));
|
||||
}, []);
|
||||
|
||||
const clearCart = useCallback(() => setCartItems([]), []);
|
||||
const isInCart = useCallback((id: string) => cartItems.some((c) => c.id === id), [cartItems]);
|
||||
|
||||
return (
|
||||
<CartContext.Provider value={{
|
||||
cartItems,
|
||||
addToCart,
|
||||
removeFromCart,
|
||||
clearCart,
|
||||
isInCart,
|
||||
isDrawerOpen,
|
||||
openDrawer: () => setIsDrawerOpen(true),
|
||||
closeDrawer: () => setIsDrawerOpen(false),
|
||||
flyItems,
|
||||
cartIconRef,
|
||||
}}>
|
||||
{children}
|
||||
</CartContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export const useCart = () => useContext(CartContext);
|
||||
@@ -0,0 +1,45 @@
|
||||
"use client";
|
||||
|
||||
import { createContext, useContext, useEffect, useState } from "react";
|
||||
|
||||
interface ThemeContextType {
|
||||
isDark: boolean;
|
||||
toggle: () => void;
|
||||
}
|
||||
|
||||
const ThemeContext = createContext<ThemeContextType>({
|
||||
isDark: true,
|
||||
toggle: () => {},
|
||||
});
|
||||
|
||||
export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
||||
const [isDark, setIsDark] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const stored = localStorage.getItem("theme");
|
||||
if (stored) {
|
||||
setIsDark(stored === "dark");
|
||||
} else {
|
||||
setIsDark(window.matchMedia("(prefers-color-scheme: dark)").matches);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const root = document.documentElement;
|
||||
if (isDark) {
|
||||
root.classList.add("dark");
|
||||
localStorage.setItem("theme", "dark");
|
||||
} else {
|
||||
root.classList.remove("dark");
|
||||
localStorage.setItem("theme", "light");
|
||||
}
|
||||
}, [isDark]);
|
||||
|
||||
return (
|
||||
<ThemeContext.Provider value={{ isDark, toggle: () => setIsDark((p) => !p) }}>
|
||||
{children}
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export const useTheme = () => useContext(ThemeContext);
|
||||
@@ -0,0 +1,73 @@
|
||||
import { FiUsers, FiBarChart2, FiShield, FiGlobe, FiBook, FiAward } from "react-icons/fi";
|
||||
import type { IStat, IFeature } from "@/types/enterprise";
|
||||
|
||||
export const stats: IStat[] = [
|
||||
{ value: "3x", label: "más retención de talento en empresas que invierten en capacitación" },
|
||||
{ value: "94%", label: "de empleados permanecen más tiempo cuando su empresa invierte en su desarrollo" },
|
||||
{ value: "42%", label: "mayor productividad en equipos con programas de aprendizaje continuo" },
|
||||
];
|
||||
|
||||
export const features: IFeature[] = [
|
||||
{
|
||||
icon: FiUsers,
|
||||
color: "text-violet-400",
|
||||
bg: "bg-violet-500/10",
|
||||
title: "Gestión de equipos",
|
||||
description: "Asigna cursos por departamento, rol o nivel. Controla quién aprende qué y cuándo desde un panel centralizado.",
|
||||
},
|
||||
{
|
||||
icon: FiBarChart2,
|
||||
color: "text-sky-400",
|
||||
bg: "bg-sky-500/10",
|
||||
title: "Analytics en tiempo real",
|
||||
description: "Dashboards con progreso individual y grupal, tiempo invertido, tasas de completitud y rankings internos.",
|
||||
},
|
||||
{
|
||||
icon: FiBook,
|
||||
color: "text-emerald-400",
|
||||
bg: "bg-emerald-500/10",
|
||||
title: "Catálogo personalizado",
|
||||
description: "Accede a todo el catálogo de Rynext Academy o sube contenido interno exclusivo para tu empresa.",
|
||||
},
|
||||
{
|
||||
icon: FiAward,
|
||||
color: "text-amber-400",
|
||||
bg: "bg-amber-500/10",
|
||||
title: "Certificaciones corporativas",
|
||||
description: "Emite certificados con el logo de tu empresa al completar rutas de aprendizaje personalizadas.",
|
||||
},
|
||||
{
|
||||
icon: FiShield,
|
||||
color: "text-rose-400",
|
||||
bg: "bg-rose-500/10",
|
||||
title: "SSO & seguridad",
|
||||
description: "Integración con tu directorio corporativo (Google Workspace, Azure AD). Acceso seguro para todos los empleados.",
|
||||
},
|
||||
{
|
||||
icon: FiGlobe,
|
||||
color: "text-indigo-400",
|
||||
bg: "bg-indigo-500/10",
|
||||
title: "White-label disponible",
|
||||
description: "Tu marca, tu dominio. La plataforma se adapta a tu identidad visual sin que los empleados salgan de tu ecosistema.",
|
||||
},
|
||||
];
|
||||
|
||||
export const perks: string[] = [
|
||||
"Licencias ilimitadas para tu equipo",
|
||||
"Onboarding guiado con gestor de cuenta dedicado",
|
||||
"Facturación a empresa con CFDI",
|
||||
"Contrato personalizado y SLA garantizado",
|
||||
"Soporte prioritario 24/7 en español",
|
||||
"Integraciones con Slack, Teams y tu HRMS",
|
||||
"Reportes ejecutivos mensuales en PDF",
|
||||
"Acceso anticipado a nuevas funciones",
|
||||
];
|
||||
|
||||
export const sectors: string[] = [
|
||||
"Fintech",
|
||||
"SaaS",
|
||||
"Manufactura",
|
||||
"Retail",
|
||||
"Salud",
|
||||
"Educación",
|
||||
];
|
||||
@@ -0,0 +1,44 @@
|
||||
import type { IPrivacySection } from "@/types/privacy";
|
||||
|
||||
export const sections: IPrivacySection[] = [
|
||||
{
|
||||
title: "1. Responsable del Tratamiento",
|
||||
content: `Rynext Technologies, con domicilio en Ciudad de México, México, es la empresa responsable del tratamiento de tus datos personales recabados a través de Rynext Academy. Para cualquier consulta relacionada con esta política puedes contactarnos en [email protected].`,
|
||||
},
|
||||
{
|
||||
title: "2. Datos que Recopilamos",
|
||||
content: `Recopilamos los siguientes datos según el tipo de usuario: (a) Estudiantes — nombre, correo electrónico, historial de cursos adquiridos y progreso de aprendizaje. (b) Instructores — nombre, correo electrónico, información de perfil público, datos fiscales opcionales para facturación y datos de cuenta Stripe Connect para procesamiento de pagos. (c) Todos los usuarios — dirección IP, tipo de dispositivo, navegador, páginas visitadas y tiempo de sesión con fines analíticos.`,
|
||||
},
|
||||
{
|
||||
title: "3. Finalidad del Tratamiento",
|
||||
content: `Utilizamos tus datos para: (a) crear y gestionar tu cuenta; (b) procesar pagos y suscripciones; (c) enviarte notificaciones relacionadas con tu actividad en la plataforma; (d) mejorar nuestros servicios mediante análisis de uso; (e) cumplir con obligaciones legales. No utilizamos tus datos para publicidad de terceros sin tu consentimiento explícito.`,
|
||||
},
|
||||
{
|
||||
title: "4. Base Legal del Tratamiento",
|
||||
content: `El tratamiento de tus datos se basa en: (a) la ejecución del contrato de servicio que aceptas al registrarte; (b) tu consentimiento explícito cuando así se solicita; (c) el cumplimiento de obligaciones legales aplicables en México bajo la Ley Federal de Protección de Datos Personales en Posesión de los Particulares (LFPDPPP).`,
|
||||
},
|
||||
{
|
||||
title: "5. Compartición de Datos",
|
||||
content: `No vendemos tus datos personales a terceros. Podemos compartir información con: (a) proveedores de servicios tecnológicos que operan bajo acuerdos de confidencialidad (hosting, analíticas, soporte); (b) Stripe para procesamiento de pagos; (c) autoridades competentes cuando la ley lo requiera. Los instructores tienen acceso únicamente a los datos mínimos necesarios de sus estudiantes (nombre y correo) para gestionar su curso.`,
|
||||
},
|
||||
{
|
||||
title: "6. Cookies y Tecnologías de Seguimiento",
|
||||
content: `Utilizamos cookies estrictamente necesarias para el funcionamiento de la plataforma (sesión, autenticación) y cookies analíticas propias para entender cómo se usa el sitio. No utilizamos cookies de terceros con fines publicitarios. Puedes desactivar las cookies analíticas desde la configuración de tu cuenta sin afectar el funcionamiento de la plataforma.`,
|
||||
},
|
||||
{
|
||||
title: "7. Retención de Datos",
|
||||
content: `Conservamos tus datos mientras tu cuenta esté activa. Al eliminar tu cuenta tus datos personales son eliminados en un plazo máximo de 30 días, excepto aquellos que debamos conservar por obligación legal (ej. registros de transacciones por 5 años conforme a la legislación fiscal mexicana).`,
|
||||
},
|
||||
{
|
||||
title: "8. Tus Derechos (ARCO)",
|
||||
content: `Conforme a la LFPDPPP tienes derecho a: Acceder a tus datos personales, Rectificarlos si son inexactos, Cancelarlos cuando proceda y Oponerte a su tratamiento. Para ejercer estos derechos envía una solicitud a [email protected] con tu nombre completo, descripción del derecho que deseas ejercer y copia de identificación oficial. Responderemos en un plazo máximo de 20 días hábiles.`,
|
||||
},
|
||||
{
|
||||
title: "9. Seguridad",
|
||||
content: `Implementamos medidas técnicas y organizativas para proteger tus datos: conexiones cifradas (HTTPS/TLS), almacenamiento con cifrado en reposo, acceso restringido por roles y monitoreo continuo. En caso de incidente de seguridad que afecte tus datos te notificaremos en un plazo máximo de 72 horas conforme a la ley aplicable.`,
|
||||
},
|
||||
{
|
||||
title: "10. Cambios a esta Política",
|
||||
content: `Podemos actualizar esta política ocasionalmente. Cualquier cambio relevante será notificado por correo electrónico con al menos 15 días de anticipación. El uso continuado de la plataforma tras la notificación implica la aceptación de la nueva versión. La versión vigente siempre estará disponible en esta página con la fecha de última actualización.`,
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,44 @@
|
||||
import { ISection } from "@/types/terms";
|
||||
|
||||
export const sections: ISection[] = [
|
||||
{
|
||||
title: "1. Términos Generales",
|
||||
content: `Rynext Academy es una plataforma tecnológica operada por Rynext Technologies que conecta instructores con estudiantes. Al usar la plataforma aceptas estos términos en su totalidad. Nos reservamos el derecho de modificarlos con previo aviso de 15 días.`,
|
||||
},
|
||||
{
|
||||
title: "2. Modelo de Pagos",
|
||||
content: `Rynext Academy no actúa como intermediario de pagos entre estudiantes e instructores. Cada instructor procesa sus propios cobros mediante Stripe Connect u otras pasarelas habilitadas. Rynext Technologies únicamente cobra la suscripción mensual del plan contratado por el instructor (Starter, Pro o Business). Los ingresos por ventas de cursos pertenecen íntegramente al instructor.`,
|
||||
},
|
||||
{
|
||||
title: "3. Política de Reembolsos — Cursos",
|
||||
content: `Los reembolsos de cursos son responsabilidad exclusiva del instructor. Cada instructor debe ofrecer una garantía mínima de 7 días naturales desde la compra si el estudiante ha completado menos del 30% del content. Rynext Academy no procesa ni financia estos reembolsos. En caso de disputa no resuelta por el instructor en un plazo de 10 días hábiles, Rynext Academy podrá suspender la cuenta del instructor.`,
|
||||
},
|
||||
{
|
||||
title: "4. Política de Reembolsos — Suscripciones de Plan",
|
||||
content: `Las suscripciones de plan (Pro, Business) son reembolsables dentro de los primeros 7 días naturales si el instructor no ha publicado ningún curso ni utilizado funciones de pago. Pasado ese plazo, no se emiten reembolsos por periodos parciales. Para solicitar un reembolso de suscripción escribe a [email protected].`,
|
||||
},
|
||||
{
|
||||
title: "5. Obligaciones del Instructor",
|
||||
content: `El instructor es responsable de: (a) cumplir con sus obligaciones fiscales en su país de residencia; (b) emitir los comprobantes fiscales que correspondan a sus ventas; (c) garantizar que el content publicado es original o cuenta con los derechos necesarios; (d) mantener actualizada la información de su perfil y métodos de pago; (e) cumplir la política de reembolsos mínima establecida en estos términos.`,
|
||||
},
|
||||
{
|
||||
title: "6. Propiedad Intelectual",
|
||||
content: `El instructor conserva todos los derechos sobre el content que publica. Al subir material a Rynext Academy otorga una licencia no exclusiva para distribuirlo dentro de la plataforma. Rynext Academy no puede revender, redistribuir ni modificar el content sin autorización expresa del instructor.`,
|
||||
},
|
||||
{
|
||||
title: "7. Conducta Prohibida",
|
||||
content: `Está prohibido publicar content engañoso, ofensivo, plagiado o que infrinja derechos de terceros. También está prohibido manipular reseñas, incitar a los estudiantes a salir de la plataforma para comprar directamente, o usar la plataforma para actividades ilegales. El incumplimiento resulta en suspensión inmediata de la cuenta sin reembolso de suscripción.`,
|
||||
},
|
||||
{
|
||||
title: "8. Limitación de Responsabilidad",
|
||||
content: `Rynext Academy no garantiza resultados de aprendizaje ni ingresos para los instructores. La plataforma se ofrece "tal cual" sin garantías de disponibilidad continua. En ningún caso la responsabilidad de Rynext Technologies excederá el monto pagado por el instructor en los últimos 3 meses de suscripción.`,
|
||||
},
|
||||
{
|
||||
title: "9. Legislación Aplicable",
|
||||
content: `Estos términos se rigen por las leyes de los Estados Unidos Mexicanos. Cualquier controversia se someterá a la jurisdicción de los tribunales competentes de la Ciudad de México, renunciando las partes a cualquier otro fuero que pudiera corresponderles.`,
|
||||
},
|
||||
{
|
||||
title: "10. Contacto",
|
||||
content: `Para dudas sobre estos términos o para ejercer tus derechos: [email protected] — Rynext Technologies, Ciudad de México, México.`,
|
||||
},
|
||||
];
|
||||
Binary file not shown.
@@ -0,0 +1,70 @@
|
||||
"use client";
|
||||
import { useState, useMemo } from "react";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { mockCourses } from "@/data/mockCourses";
|
||||
|
||||
function toggleItem(arr: string[], val: string): string[] {
|
||||
return arr.includes(val) ? arr.filter((x) => x !== val) : [...arr, val];
|
||||
}
|
||||
|
||||
export function useFilters() {
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const [search, setSearch] = useState(searchParams.get("q") ?? "");
|
||||
const [selectedCategories, setSelectedCategories] = useState<string[]>(() => {
|
||||
const cat = searchParams.get("cat");
|
||||
return cat ? [cat] : [];
|
||||
});
|
||||
const [selectedLevels, setSelectedLevels] = useState<string[]>([]);
|
||||
const [minRating, setMinRating] = useState<number | null>(null);
|
||||
const [maxPrice, setMaxPrice] = useState<number | null>(null);
|
||||
const [sort, setSort] = useState("popular");
|
||||
|
||||
const toggleCategory = (val: string) => setSelectedCategories((prev) => toggleItem(prev, val));
|
||||
const toggleLevel = (val: string) => setSelectedLevels((prev) => toggleItem(prev, val));
|
||||
const toggleRating = (r: number) => setMinRating((prev) => (prev === r ? null : r));
|
||||
const togglePrice = (p: number) => setMaxPrice((prev) => (prev === p ? null : p));
|
||||
|
||||
const activeFiltersCount =
|
||||
selectedCategories.length + selectedLevels.length + (minRating ? 1 : 0) + (maxPrice ? 1 : 0);
|
||||
|
||||
const clearAll = () => {
|
||||
setSearch("");
|
||||
setSelectedCategories([]);
|
||||
setSelectedLevels([]);
|
||||
setMinRating(null);
|
||||
setMaxPrice(null);
|
||||
};
|
||||
|
||||
const filtered = useMemo(() => {
|
||||
let list = [...mockCourses];
|
||||
if (search)
|
||||
list = list.filter(
|
||||
(c) =>
|
||||
c.title.toLowerCase().includes(search.toLowerCase()) ||
|
||||
c.subtitle.toLowerCase().includes(search.toLowerCase()),
|
||||
);
|
||||
if (selectedCategories.length) list = list.filter((c) => selectedCategories.includes(c.category));
|
||||
if (selectedLevels.length) list = list.filter((c) => selectedLevels.includes(c.level));
|
||||
if (minRating) list = list.filter((c) => c.rating >= minRating);
|
||||
if (maxPrice) list = list.filter((c) => c.price <= maxPrice);
|
||||
switch (sort) {
|
||||
case "rating": list.sort((a, b) => b.rating - a.rating); break;
|
||||
case "price_lo": list.sort((a, b) => a.price - b.price); break;
|
||||
case "price_hi": list.sort((a, b) => b.price - a.price); break;
|
||||
case "newest": list.sort((a, b) => (b.badge === "Nuevo" ? 1 : 0) - (a.badge === "Nuevo" ? 1 : 0)); break;
|
||||
default: list.sort((a, b) => b.studentsCount - a.studentsCount);
|
||||
}
|
||||
return list;
|
||||
}, [search, selectedCategories, selectedLevels, minRating, maxPrice, sort]);
|
||||
|
||||
return {
|
||||
search, setSearch,
|
||||
selectedCategories, toggleCategory,
|
||||
selectedLevels, toggleLevel,
|
||||
minRating, toggleRating,
|
||||
maxPrice, togglePrice,
|
||||
sort, setSort,
|
||||
activeFiltersCount, clearAll, filtered,
|
||||
};
|
||||
}
|
||||
+17
-146
@@ -1,159 +1,30 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
@custom-variant dark (&:where(.dark, .dark *));
|
||||
@custom-variant dark (&:is(.dark, .dark *));
|
||||
|
||||
@font-face {
|
||||
font-family: 'Nunito';
|
||||
src: url('./fonts/Nunito-VariableFont_wght.ttf') format('truetype');
|
||||
font-weight: 100 900;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
font-family: 'Nunito', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
@theme {
|
||||
--animate-speed-streak-1: speed-streak 0.6s linear infinite;
|
||||
--animate-speed-streak-2: speed-streak 0.5s linear 0.18s infinite;
|
||||
--animate-speed-streak-3: speed-streak 0.7s linear 0.35s infinite;
|
||||
}
|
||||
|
||||
@keyframes scaleUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.95) translateY(8px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1) translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fadeIn {
|
||||
animation: fadeIn 0.2s ease-out forwards;
|
||||
}
|
||||
|
||||
.animate-scaleUp {
|
||||
animation: scaleUp 0.25s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||||
}
|
||||
|
||||
/* Custom modern scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: #0f172a;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #334155;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #475569;
|
||||
}
|
||||
|
||||
/* Full-Screen Crossing Flying Spaceship Animation */
|
||||
@keyframes float-spaceship {
|
||||
0% {
|
||||
transform: translate(-10vw, 68vh) rotate(23deg) scale(0.65);
|
||||
opacity: 0.1;
|
||||
}
|
||||
|
||||
15% {
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
35% {
|
||||
transform: translate(30vw, 45vh) rotate(25deg) scale(0.85);
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
65% {
|
||||
transform: translate(65vw, 25vh) rotate(22deg) scale(1);
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
85% {
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(110vw, 5vh) rotate(24deg) scale(0.7);
|
||||
opacity: 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
.animate-spaceship {
|
||||
animation: float-spaceship 20s cubic-bezier(0.35, 0, 0.25, 1) infinite;
|
||||
}
|
||||
|
||||
/* Speed Streaks for Cart */
|
||||
@keyframes speed-streak {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateX(0) scaleX(0.2);
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 0.8;
|
||||
transform: translateX(-8px) scaleX(1);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: translateX(-16px) scaleX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.speed-line {
|
||||
position: absolute;
|
||||
height: 2px;
|
||||
background: theme('colors.cyan.400');
|
||||
border-radius: 9999px;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.group:hover .speed-line-1 {
|
||||
animation: speed-streak 0.6s infinite linear;
|
||||
top: 30%;
|
||||
right: 110%;
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
.group:hover .speed-line-2 {
|
||||
animation: speed-streak 0.5s infinite linear 0.2s;
|
||||
top: 60%;
|
||||
right: 100%;
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.group:hover .speed-line-3 {
|
||||
animation: speed-streak 0.7s infinite linear 0.4s;
|
||||
bottom: 10%;
|
||||
right: 120%;
|
||||
width: 14px;
|
||||
}
|
||||
|
||||
/* Twinkling Star Particles Animation */
|
||||
@keyframes star-twinkle {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
opacity: 0.15;
|
||||
transform: scale(0.65);
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 0.95;
|
||||
transform: scale(1.3);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-twinkle {
|
||||
animation: star-twinkle 3s ease-in-out infinite alternate;
|
||||
0% { opacity: 0; transform: translateX(0) scaleX(0.2); }
|
||||
50% { opacity: 0.8; transform: translateX(-8px) scaleX(1); }
|
||||
100% { opacity: 0; transform: translateX(-16px) scaleX(0); }
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import { BrowserRouter } from 'react-router'
|
||||
import './index.css'
|
||||
import App from './App.tsx'
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
</StrictMode>,
|
||||
)
|
||||
@@ -0,0 +1,35 @@
|
||||
import { create } from "zustand";
|
||||
import { persist, createJSONStorage } from "zustand/middleware";
|
||||
import type { AuthState, AuthActions, AuthUser } from "@/types/auth";
|
||||
|
||||
type AuthStore = AuthState & AuthActions;
|
||||
|
||||
export const useAuthStore = create<AuthStore>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
user: null,
|
||||
token: null,
|
||||
isAuthenticated: false,
|
||||
isLoading: false,
|
||||
|
||||
login: (user: AuthUser, token: string) =>
|
||||
set({ user, token, isAuthenticated: true, isLoading: false }),
|
||||
|
||||
logout: () =>
|
||||
set({ user: null, token: null, isAuthenticated: false }),
|
||||
|
||||
setLoading: (isLoading: boolean) =>
|
||||
set({ isLoading }),
|
||||
|
||||
updateUser: (partial: Partial<AuthUser>) =>
|
||||
set((state) => ({
|
||||
user: state.user ? { ...state.user, ...partial } : null,
|
||||
})),
|
||||
}),
|
||||
{
|
||||
name: "auth-storage",
|
||||
storage: createJSONStorage(() => localStorage),
|
||||
partialize: (state) => ({ user: state.user, token: state.token, isAuthenticated: state.isAuthenticated }),
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -0,0 +1,23 @@
|
||||
export type UserRole = "student" | "instructor" | "admin";
|
||||
|
||||
export interface AuthUser {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
avatar?: string;
|
||||
role: UserRole;
|
||||
}
|
||||
|
||||
export interface AuthState {
|
||||
user: AuthUser | null;
|
||||
token: string | null;
|
||||
isAuthenticated: boolean;
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
export interface AuthActions {
|
||||
login: (user: AuthUser, token: string) => void;
|
||||
logout: () => void;
|
||||
setLoading: (loading: boolean) => void;
|
||||
updateUser: (partial: Partial<AuthUser>) => void;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import type { IconType } from "react-icons";
|
||||
|
||||
export interface IStat {
|
||||
value: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface IFeature {
|
||||
icon: IconType;
|
||||
color: string;
|
||||
bg: string;
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface IPrivacySection {
|
||||
title: string;
|
||||
content: string;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface ISection {
|
||||
title: string;
|
||||
content: string;
|
||||
}
|
||||
@@ -1,632 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import type { Course } from '../types/course';
|
||||
import { BrandLogo } from '../components/common/BrandLogo';
|
||||
import {
|
||||
Card,
|
||||
Button,
|
||||
Badge,
|
||||
Input,
|
||||
Select,
|
||||
ThemeToggle
|
||||
} from '../components/ui';
|
||||
import {
|
||||
FiDollarSign,
|
||||
FiUsers,
|
||||
FiBookOpen,
|
||||
FiTrendingUp,
|
||||
FiCheckCircle,
|
||||
FiTrash2,
|
||||
FiShield,
|
||||
FiSearch,
|
||||
FiActivity,
|
||||
FiPieChart,
|
||||
FiSettings,
|
||||
FiMenu,
|
||||
FiX,
|
||||
FiArrowLeft
|
||||
} from 'react-icons/fi';
|
||||
|
||||
interface AdminDashboardViewProps {
|
||||
courses: Course[];
|
||||
onDeleteCourse: (courseId: string) => void;
|
||||
onShowToast: (msg: string) => void;
|
||||
}
|
||||
|
||||
export const AdminDashboardView: React.FC<AdminDashboardViewProps> = ({
|
||||
courses,
|
||||
onDeleteCourse,
|
||||
onShowToast,
|
||||
}) => {
|
||||
const [activeTab, setActiveTab] = useState<'overview' | 'courses' | 'users' | 'finance' | 'settings'>('overview');
|
||||
const [sidebarOpen, setSidebarOpen] = useState(false);
|
||||
const [statusFilter, setStatusFilter] = useState<'all' | 'published' | 'pending'>('all');
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [userSearchTerm, setUserSearchTerm] = useState('');
|
||||
|
||||
// Course status state management for demonstration
|
||||
const [courseStatuses, setCourseStatuses] = useState<Record<string, 'published' | 'pending' | 'rejected'>>({});
|
||||
|
||||
// Mock platform users
|
||||
const [users, setUsers] = useState([
|
||||
{ id: 'u-1', name: 'Ing. Carlos Mendoza', email: '[email protected]', role: 'Instructor', courses: 4, joined: '12 Ene 2026', status: 'Activo' },
|
||||
{ id: 'u-2', name: 'Dra. Elena Torres', email: '[email protected]', role: 'Instructor', courses: 2, joined: '04 Feb 2026', status: 'Activo' },
|
||||
{ id: 'u-3', name: 'Marcos Rodriguez', email: '[email protected]', role: 'Estudiante', courses: 5, joined: '18 Feb 2026', status: 'Activo' },
|
||||
{ id: 'u-4', name: 'Sofía Martinez', email: '[email protected]', role: 'Estudiante', courses: 2, joined: '20 Feb 2026', status: 'Activo' },
|
||||
{ id: 'u-5', name: 'Admin Principal', email: '[email protected]', role: 'Administrador', courses: 0, joined: '01 Ene 2026', status: 'Activo' },
|
||||
]);
|
||||
|
||||
// Platform Commission Setting State
|
||||
const [commissionRate, setCommissionRate] = useState(20);
|
||||
const [siteName, setSiteName] = useState('RyCourses');
|
||||
|
||||
const getStatus = (courseId: string) => {
|
||||
return courseStatuses[courseId] || 'published';
|
||||
};
|
||||
|
||||
const toggleCourseStatus = (courseId: string, title: string) => {
|
||||
const current = getStatus(courseId);
|
||||
const nextStatus = current === 'published' ? 'pending' : 'published';
|
||||
setCourseStatuses({ ...courseStatuses, [courseId]: nextStatus });
|
||||
onShowToast(`Estado del curso "${title.slice(0, 22)}..." cambiado a ${nextStatus === 'published' ? 'Publicado' : 'Pendiente'}`);
|
||||
};
|
||||
|
||||
const toggleUserStatus = (userId: string, userName: string) => {
|
||||
setUsers(users.map((u) => {
|
||||
if (u.id === userId) {
|
||||
const next = u.status === 'Activo' ? 'Suspendido' : 'Activo';
|
||||
onShowToast(`Estado de ${userName} cambiado a ${next}`);
|
||||
return { ...u, status: next };
|
||||
}
|
||||
return u;
|
||||
}));
|
||||
};
|
||||
|
||||
const filteredCourses = courses.filter((c) => {
|
||||
const matchesSearch = c.title.toLowerCase().includes(searchTerm.toLowerCase()) || c.instructor.name.toLowerCase().includes(searchTerm.toLowerCase());
|
||||
const currentStatus = getStatus(c.id);
|
||||
const matchesStatus = statusFilter === 'all' || currentStatus === statusFilter;
|
||||
return matchesSearch && matchesStatus;
|
||||
});
|
||||
|
||||
const filteredUsers = users.filter((u) =>
|
||||
u.name.toLowerCase().includes(userSearchTerm.toLowerCase()) || u.email.toLowerCase().includes(userSearchTerm.toLowerCase()) || u.role.toLowerCase().includes(userSearchTerm.toLowerCase())
|
||||
);
|
||||
|
||||
const totalGrossRevenue = 184920.00;
|
||||
const netCommission = totalGrossRevenue * (commissionRate / 100);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen transition-colors duration-300 bg-slate-50 dark:bg-slate-950 text-slate-900 dark:text-slate-100 flex flex-col md:flex-row font-sans selection:bg-cyan-500">
|
||||
|
||||
{/* Mobile Sidebar Overlay */}
|
||||
{sidebarOpen && (
|
||||
<div
|
||||
className="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-40 md:hidden"
|
||||
onClick={() => setSidebarOpen(false)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 1. Left Sidebar Navigation */}
|
||||
<aside className={`
|
||||
fixed md:sticky top-0 left-0 h-screen w-72 bg-white dark:bg-slate-900 border-r border-slate-200 dark:border-slate-800 z-50 flex flex-col justify-between p-5 transition-transform duration-300 shadow-xl
|
||||
${sidebarOpen ? 'translate-x-0' : '-translate-x-full md:translate-x-0'}
|
||||
`}>
|
||||
<div className="space-y-6">
|
||||
{/* Sidebar Brand Logo & Close button */}
|
||||
<div className="flex items-center justify-between pb-3 border-b border-slate-200 dark:border-slate-800">
|
||||
<Link to="/" className="hover:opacity-90 transition">
|
||||
<BrandLogo size="sm" showSubtitle={false} />
|
||||
</Link>
|
||||
|
||||
<button
|
||||
onClick={() => setSidebarOpen(false)}
|
||||
className="md:hidden p-1.5 rounded-lg text-slate-500 hover:text-slate-900 dark:text-slate-400 dark:hover:text-white"
|
||||
>
|
||||
<FiX className="text-xl" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Admin Pill Badge */}
|
||||
<div className="px-3 py-2 rounded-xl bg-violet-500/10 border border-violet-500/25 flex items-center gap-2.5">
|
||||
<div className="w-7 h-7 rounded-lg bg-violet-500/20 text-violet-600 dark:text-violet-400 flex items-center justify-center text-sm font-bold shrink-0">
|
||||
<FiShield />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs font-bold text-slate-900 dark:text-white leading-none">Admin Suite</p>
|
||||
<p className="text-[10px] text-violet-600 dark:text-violet-400 font-medium mt-0.5">Control Central</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Navigation Links */}
|
||||
<nav className="space-y-1.5">
|
||||
<button
|
||||
onClick={() => { setActiveTab('overview'); setSidebarOpen(false); }}
|
||||
className={`w-full flex items-center gap-3 px-3.5 py-3 rounded-xl text-xs font-semibold transition-all ${
|
||||
activeTab === 'overview'
|
||||
? 'bg-slate-100 dark:bg-slate-800 text-cyan-600 dark:text-cyan-400 border border-slate-200 dark:border-cyan-500/30 shadow-sm'
|
||||
: 'text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white hover:bg-slate-100 dark:hover:bg-slate-800/60'
|
||||
}`}
|
||||
>
|
||||
<FiActivity className="text-base" />
|
||||
<span>Dashboard & Métricas</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => { setActiveTab('courses'); setSidebarOpen(false); }}
|
||||
className={`w-full flex items-center justify-between px-3.5 py-3 rounded-xl text-xs font-semibold transition-all ${
|
||||
activeTab === 'courses'
|
||||
? 'bg-slate-100 dark:bg-slate-800 text-cyan-600 dark:text-cyan-400 border border-slate-200 dark:border-cyan-500/30 shadow-sm'
|
||||
: 'text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white hover:bg-slate-100 dark:hover:bg-slate-800/60'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<FiBookOpen className="text-base" />
|
||||
<span>Gestor de Cursos</span>
|
||||
</div>
|
||||
<Badge variant="primary" size="sm">{courses.length}</Badge>
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => { setActiveTab('users'); setSidebarOpen(false); }}
|
||||
className={`w-full flex items-center justify-between px-3.5 py-3 rounded-xl text-xs font-semibold transition-all ${
|
||||
activeTab === 'users'
|
||||
? 'bg-slate-100 dark:bg-slate-800 text-cyan-600 dark:text-cyan-400 border border-slate-200 dark:border-cyan-500/30 shadow-sm'
|
||||
: 'text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white hover:bg-slate-100 dark:hover:bg-slate-800/60'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<FiUsers className="text-base" />
|
||||
<span>Usuarios & Roles</span>
|
||||
</div>
|
||||
<Badge variant="secondary" size="sm">{users.length}</Badge>
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => { setActiveTab('finance'); setSidebarOpen(false); }}
|
||||
className={`w-full flex items-center gap-3 px-3.5 py-3 rounded-xl text-xs font-semibold transition-all ${
|
||||
activeTab === 'finance'
|
||||
? 'bg-slate-100 dark:bg-slate-800 text-cyan-600 dark:text-cyan-400 border border-slate-200 dark:border-cyan-500/30 shadow-sm'
|
||||
: 'text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white hover:bg-slate-100 dark:hover:bg-slate-800/60'
|
||||
}`}
|
||||
>
|
||||
<FiDollarSign className="text-base" />
|
||||
<span>Finanzas & Ventas</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => { setActiveTab('settings'); setSidebarOpen(false); }}
|
||||
className={`w-full flex items-center gap-3 px-3.5 py-3 rounded-xl text-xs font-semibold transition-all ${
|
||||
activeTab === 'settings'
|
||||
? 'bg-slate-100 dark:bg-slate-800 text-cyan-600 dark:text-cyan-400 border border-slate-200 dark:border-cyan-500/30 shadow-sm'
|
||||
: 'text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white hover:bg-slate-100 dark:hover:bg-slate-800/60'
|
||||
}`}
|
||||
>
|
||||
<FiSettings className="text-base" />
|
||||
<span>Configuración Global</span>
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
{/* Sidebar Footer */}
|
||||
<div className="pt-4 border-t border-slate-200 dark:border-slate-800 space-y-3">
|
||||
<div className="flex items-center justify-between px-2">
|
||||
<span className="text-xs text-slate-600 dark:text-slate-400 font-medium">Modo</span>
|
||||
<ThemeToggle variant="pill" size="sm" />
|
||||
</div>
|
||||
|
||||
<Link to="/">
|
||||
<Button variant="outline" size="sm" fullWidth leftIcon={<FiArrowLeft />}>
|
||||
Volver al inicio
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
{/* 2. Main Content Area */}
|
||||
<div className="flex-1 flex flex-col min-w-0">
|
||||
|
||||
{/* Top Header Bar */}
|
||||
<header className="sticky top-0 z-30 h-16 bg-white/90 dark:bg-slate-900/90 border-b border-slate-200 dark:border-slate-800 backdrop-blur-xl px-4 sm:px-8 flex items-center justify-between gap-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
onClick={() => setSidebarOpen(true)}
|
||||
className="md:hidden text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white p-2"
|
||||
>
|
||||
<FiMenu className="text-xl" />
|
||||
</button>
|
||||
<h1 className="text-base sm:text-lg font-bold text-slate-900 dark:text-white tracking-tight">
|
||||
{activeTab === 'overview' && 'Panel General de Administración'}
|
||||
{activeTab === 'courses' && 'Catálogo & Moderación de Cursos'}
|
||||
{activeTab === 'users' && 'Directorio de Usuarios & Permisos'}
|
||||
{activeTab === 'finance' && 'Finanzas, Comisiones & Repartición'}
|
||||
{activeTab === 'settings' && 'Ajustes del Sistema RyCourses'}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<Badge variant="success" size="md">
|
||||
<span className="w-2 h-2 rounded-full bg-emerald-400 animate-pulse mr-1" />
|
||||
Servidor Producción
|
||||
</Badge>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Dynamic Content Views */}
|
||||
<main className="p-4 sm:p-8 space-y-8 flex-1">
|
||||
|
||||
{/* TAB 0: OVERVIEW */}
|
||||
{activeTab === 'overview' && (
|
||||
<div className="space-y-8">
|
||||
|
||||
{/* KPI Cards Grid */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-5">
|
||||
<Card variant="elevated" padding="md" className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs text-slate-500 dark:text-slate-400 font-medium">Ventas Brutas Totales</span>
|
||||
<div className="w-9 h-9 rounded-xl bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 flex items-center justify-center text-lg">
|
||||
<FiDollarSign />
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-2xl font-black text-slate-900 dark:text-white font-mono">${totalGrossRevenue.toLocaleString()}</div>
|
||||
<div className="text-[11px] text-emerald-600 dark:text-emerald-400 font-semibold flex items-center gap-1">
|
||||
<FiTrendingUp /> +24.8% en crecimiento
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card variant="elevated" padding="md" className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs text-slate-500 dark:text-slate-400 font-medium">Usuarios Registrados</span>
|
||||
<div className="w-9 h-9 rounded-xl bg-cyan-500/10 text-cyan-600 dark:text-cyan-400 flex items-center justify-center text-lg">
|
||||
<FiUsers />
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-2xl font-black text-slate-900 dark:text-white font-mono">54,320</div>
|
||||
<div className="text-[11px] text-slate-500 dark:text-slate-400">
|
||||
<strong className="text-cyan-600 dark:text-cyan-400">1,240</strong> Instructores activos
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card variant="elevated" padding="md" className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs text-slate-500 dark:text-slate-400 font-medium">Cursos Publicados</span>
|
||||
<div className="w-9 h-9 rounded-xl bg-violet-500/10 text-violet-600 dark:text-violet-400 flex items-center justify-center text-lg">
|
||||
<FiBookOpen />
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-2xl font-black text-slate-900 dark:text-white font-mono">{courses.length} Cursos</div>
|
||||
<div className="text-[11px] text-amber-600 dark:text-amber-400 font-medium">2 pendientes de revisión</div>
|
||||
</Card>
|
||||
|
||||
<Card variant="elevated" padding="md" className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs text-slate-500 dark:text-slate-400 font-medium">Comisión Neta Plataforma ({commissionRate}%)</span>
|
||||
<div className="w-9 h-9 rounded-xl bg-amber-500/10 text-amber-600 dark:text-amber-400 flex items-center justify-center text-lg">
|
||||
<FiPieChart />
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-2xl font-black text-slate-900 dark:text-white font-mono">${netCommission.toLocaleString()}</div>
|
||||
<div className="text-[11px] text-slate-500 dark:text-slate-400">Ingreso neto RyCourses</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Quick Summary Grid */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
|
||||
{/* Recent Courses Moderation Preview */}
|
||||
<Card variant="elevated" padding="md" className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="font-bold text-sm text-slate-900 dark:text-white">Últimos Cursos Registrados</h3>
|
||||
<Button variant="ghost" size="xs" onClick={() => setActiveTab('courses')}>
|
||||
Ver catálogo completo
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
{courses.slice(0, 4).map((c) => (
|
||||
<div key={c.id} className="flex items-center justify-between p-3 rounded-xl bg-slate-100/80 dark:bg-slate-950/60 border border-slate-200 dark:border-slate-800">
|
||||
<div className="flex items-center gap-3 min-w-0">
|
||||
<img src={c.thumbnail} alt={c.title} className="w-10 h-8 rounded object-cover border border-slate-300 dark:border-slate-700 shrink-0" />
|
||||
<div className="min-w-0">
|
||||
<h4 className="font-bold text-xs text-slate-900 dark:text-white truncate">{c.title}</h4>
|
||||
<span className="text-[10px] text-slate-500 dark:text-slate-400">{c.instructor.name}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Badge variant="primary" size="sm">${c.price}</Badge>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* System Status & Server Logs */}
|
||||
<Card variant="elevated" padding="md" className="space-y-4">
|
||||
<h3 className="font-bold text-sm text-slate-900 dark:text-white">Estado de Infraestructura & Logs</h3>
|
||||
<div className="space-y-3 text-xs">
|
||||
<div className="flex justify-between p-3 bg-slate-100/80 dark:bg-slate-950 rounded-xl border border-slate-200 dark:border-slate-800">
|
||||
<span className="text-slate-600 dark:text-slate-400">Base de Datos PostgreSQL (Supabase):</span>
|
||||
<span className="font-mono text-emerald-600 dark:text-emerald-400 font-bold">Conectado (4ms)</span>
|
||||
</div>
|
||||
<div className="flex justify-between p-3 bg-slate-100/80 dark:bg-slate-950 rounded-xl border border-slate-200 dark:border-slate-800">
|
||||
<span className="text-slate-600 dark:text-slate-400">Pasarela Stripe / PayPal Webhooks:</span>
|
||||
<span className="font-mono text-emerald-600 dark:text-emerald-400 font-bold">Operativo 100%</span>
|
||||
</div>
|
||||
<div className="flex justify-between p-3 bg-slate-100/80 dark:bg-slate-950 rounded-xl border border-slate-200 dark:border-slate-800">
|
||||
<span className="text-slate-600 dark:text-slate-400">Almacenamiento de Video CDN:</span>
|
||||
<span className="font-mono text-cyan-600 dark:text-cyan-400 font-bold">Cloudflare Stream Active</span>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* TAB 1: COURSES MANAGEMENT */}
|
||||
{activeTab === 'courses' && (
|
||||
<div className="space-y-6">
|
||||
|
||||
{/* Filter and Search Bar */}
|
||||
<div className="flex flex-col sm:flex-row items-center justify-between gap-4 bg-white dark:bg-slate-900 p-4 rounded-2xl border border-slate-200 dark:border-slate-800 shadow-sm">
|
||||
<div className="w-full sm:max-w-md">
|
||||
<Input
|
||||
placeholder="Buscar por título de curso o nombre del instructor..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
leftIcon={<FiSearch className="text-slate-400" />}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3 w-full sm:w-auto">
|
||||
<Select
|
||||
value={statusFilter}
|
||||
onChange={(e) => setStatusFilter(e.target.value as any)}
|
||||
options={[
|
||||
{ value: 'all', label: 'Todos los estados' },
|
||||
{ value: 'published', label: 'Publicados' },
|
||||
{ value: 'pending', label: 'En Revisión' },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Courses Management Table */}
|
||||
<Card variant="elevated" padding="none" className="overflow-hidden">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-left text-xs text-slate-700 dark:text-slate-300">
|
||||
<thead className="bg-slate-100 dark:bg-slate-950 text-slate-600 dark:text-slate-400 uppercase tracking-wider font-semibold border-b border-slate-200 dark:border-slate-800">
|
||||
<tr>
|
||||
<th className="p-4">Curso</th>
|
||||
<th className="p-4">Categoría</th>
|
||||
<th className="p-4">Instructor</th>
|
||||
<th className="p-4">Precio</th>
|
||||
<th className="p-4">Alumnos</th>
|
||||
<th className="p-4">Estado</th>
|
||||
<th className="p-4 text-right">Acciones Admin</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-slate-200 dark:divide-slate-800/80">
|
||||
{filteredCourses.map((c) => {
|
||||
const status = getStatus(c.id);
|
||||
return (
|
||||
<tr key={c.id} className="hover:bg-slate-100/60 dark:hover:bg-slate-800/40 transition">
|
||||
<td className="p-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<img
|
||||
src={c.thumbnail}
|
||||
alt={c.title}
|
||||
className="w-12 h-8 rounded object-cover border border-slate-300 dark:border-slate-700 shrink-0"
|
||||
/>
|
||||
<div>
|
||||
<h4 className="font-bold text-slate-900 dark:text-white max-w-xs line-clamp-1">{c.title}</h4>
|
||||
<span className="text-[10px] text-slate-500 dark:text-slate-500">ID: {c.id}</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td className="p-4">
|
||||
<Badge variant="primary" size="sm">{c.category}</Badge>
|
||||
</td>
|
||||
|
||||
<td className="p-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<img
|
||||
src={c.instructor.avatar}
|
||||
alt={c.instructor.name}
|
||||
className="w-6 h-6 rounded-full object-cover"
|
||||
/>
|
||||
<span className="font-medium text-slate-800 dark:text-slate-200">{c.instructor.name}</span>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td className="p-4 font-mono font-bold text-slate-900 dark:text-white">
|
||||
${c.price.toFixed(2)}
|
||||
</td>
|
||||
|
||||
<td className="p-4 font-mono text-slate-700 dark:text-slate-300">
|
||||
{c.studentsCount.toLocaleString()}
|
||||
</td>
|
||||
|
||||
<td className="p-4">
|
||||
{status === 'published' ? (
|
||||
<Badge variant="success" size="sm"><FiCheckCircle /> Publicado</Badge>
|
||||
) : (
|
||||
<Badge variant="warning" size="sm"><FiActivity /> En Revisión</Badge>
|
||||
)}
|
||||
</td>
|
||||
|
||||
<td className="p-4 text-right">
|
||||
<div className="flex items-center justify-end gap-2">
|
||||
<Button
|
||||
variant={status === 'published' ? 'outline' : 'success'}
|
||||
size="xs"
|
||||
onClick={() => toggleCourseStatus(c.id, c.title)}
|
||||
>
|
||||
{status === 'published' ? 'Pausar' : 'Aprobar'}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="danger"
|
||||
size="xs"
|
||||
onClick={() => onDeleteCourse(c.id)}
|
||||
>
|
||||
<FiTrash2 />
|
||||
</Button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* TAB 2: USERS MANAGEMENT */}
|
||||
{activeTab === 'users' && (
|
||||
<div className="space-y-6">
|
||||
|
||||
<div className="flex flex-col sm:flex-row items-center justify-between gap-4 bg-white dark:bg-slate-900 p-4 rounded-2xl border border-slate-200 dark:border-slate-800 shadow-sm">
|
||||
<div className="w-full sm:max-w-md">
|
||||
<Input
|
||||
placeholder="Buscar usuario por nombre, email o rol..."
|
||||
value={userSearchTerm}
|
||||
onChange={(e) => setUserSearchTerm(e.target.value)}
|
||||
leftIcon={<FiSearch className="text-slate-400" />}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Badge variant="secondary" size="md">
|
||||
Total Registrados: {users.length}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<Card variant="elevated" padding="none" className="overflow-hidden">
|
||||
<table className="w-full text-left text-xs text-slate-700 dark:text-slate-300">
|
||||
<thead className="bg-slate-100 dark:bg-slate-950 text-slate-600 dark:text-slate-400 uppercase tracking-wider font-semibold border-b border-slate-200 dark:border-slate-800">
|
||||
<tr>
|
||||
<th className="p-4">Usuario</th>
|
||||
<th className="p-4">Correo</th>
|
||||
<th className="p-4">Rol</th>
|
||||
<th className="p-4">Cursos</th>
|
||||
<th className="p-4">Registro</th>
|
||||
<th className="p-4">Estado</th>
|
||||
<th className="p-4 text-right">Acción</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-slate-200 dark:divide-slate-800/80">
|
||||
{filteredUsers.map((u) => (
|
||||
<tr key={u.id} className="hover:bg-slate-100/60 dark:hover:bg-slate-800/40 transition">
|
||||
<td className="p-4 font-bold text-slate-900 dark:text-white">{u.name}</td>
|
||||
<td className="p-4 text-slate-500 dark:text-slate-400 font-mono">{u.email}</td>
|
||||
<td className="p-4">
|
||||
<Badge
|
||||
variant={u.role === 'Administrador' ? 'danger' : u.role === 'Instructor' ? 'secondary' : 'primary'}
|
||||
size="sm"
|
||||
>
|
||||
{u.role}
|
||||
</Badge>
|
||||
</td>
|
||||
<td className="p-4 font-mono">{u.courses}</td>
|
||||
<td className="p-4 text-slate-500 dark:text-slate-400">{u.joined}</td>
|
||||
<td className="p-4">
|
||||
<Badge variant={u.status === 'Activo' ? 'success' : 'danger'} size="sm">
|
||||
{u.status}
|
||||
</Badge>
|
||||
</td>
|
||||
<td className="p-4 text-right">
|
||||
<Button
|
||||
variant={u.status === 'Activo' ? 'outline' : 'success'}
|
||||
size="xs"
|
||||
onClick={() => toggleUserStatus(u.id, u.name)}
|
||||
>
|
||||
{u.status === 'Activo' ? 'Suspender' : 'Activar'}
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</Card>
|
||||
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* TAB 3: FINANCIALS */}
|
||||
{activeTab === 'finance' && (
|
||||
<div className="space-y-6">
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<Card variant="elevated" padding="lg" className="space-y-4">
|
||||
<h3 className="font-bold text-slate-900 dark:text-white text-base">Desglose de Repartición Financiera</h3>
|
||||
<div className="space-y-3 text-xs">
|
||||
<div className="flex justify-between p-3.5 bg-slate-100/80 dark:bg-slate-950 rounded-xl border border-slate-200 dark:border-slate-800">
|
||||
<span className="text-slate-600 dark:text-slate-400">Ventas Brutas Acumuladas:</span>
|
||||
<span className="font-mono font-bold text-slate-900 dark:text-white">${totalGrossRevenue.toLocaleString()}</span>
|
||||
</div>
|
||||
<div className="flex justify-between p-3.5 bg-slate-100/80 dark:bg-slate-950 rounded-xl border border-slate-200 dark:border-slate-800">
|
||||
<span className="text-slate-600 dark:text-slate-400">Pago a Instructores ({100 - commissionRate}%):</span>
|
||||
<span className="font-mono font-bold text-emerald-600 dark:text-emerald-400">${(totalGrossRevenue * ((100 - commissionRate) / 100)).toLocaleString()}</span>
|
||||
</div>
|
||||
<div className="flex justify-between p-3.5 bg-slate-100/80 dark:bg-slate-950 rounded-xl border border-slate-200 dark:border-slate-800">
|
||||
<span className="text-slate-600 dark:text-slate-400">Comisión Neta RyCourses ({commissionRate}%):</span>
|
||||
<span className="font-mono font-bold text-cyan-600 dark:text-cyan-400">${netCommission.toLocaleString()}</span>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card variant="elevated" padding="lg" className="space-y-4">
|
||||
<h3 className="font-bold text-slate-900 dark:text-white text-base">Métodos de Pago & Webhooks</h3>
|
||||
<p className="text-xs text-slate-600 dark:text-slate-400 leading-relaxed">
|
||||
Integración automatizada con Stripe Connect y PayPal Payouts API para la liquidación semanal a instructores.
|
||||
</p>
|
||||
<div className="p-4 rounded-xl bg-emerald-500/10 border border-emerald-500/20 text-emerald-600 dark:text-emerald-400 text-xs font-semibold flex items-center gap-2">
|
||||
<FiCheckCircle /> Transacciones de pago en vivo cifradas a 256-bit SSL.
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* TAB 4: GLOBAL SETTINGS */}
|
||||
{activeTab === 'settings' && (
|
||||
<Card variant="elevated" padding="lg" className="max-w-xl space-y-6">
|
||||
<h3 className="font-bold text-slate-900 dark:text-white text-base">Configuración de Plataforma</h3>
|
||||
|
||||
<div className="space-y-4">
|
||||
<Input
|
||||
label="Nombre de la Plataforma"
|
||||
value={siteName}
|
||||
onChange={(e) => setSiteName(e.target.value)}
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Porcentaje de Comisión Base Plataforma (%)"
|
||||
type="number"
|
||||
value={commissionRate}
|
||||
onChange={(e) => setCommissionRate(Number(e.target.value))}
|
||||
/>
|
||||
|
||||
<Button
|
||||
variant="gradient"
|
||||
fullWidth
|
||||
size="md"
|
||||
onClick={() => onShowToast('Ajustes globales guardados con éxito')}
|
||||
>
|
||||
Guardar Cambios
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
</main>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,40 +0,0 @@
|
||||
import React from 'react';
|
||||
import type { Course } from '../types/course';
|
||||
import { CourseGrid } from '../components/buyer/CourseGrid';
|
||||
import { FeaturesBanner } from '../components/buyer/FeaturesBanner';
|
||||
|
||||
interface BuyerViewProps {
|
||||
courses: Course[];
|
||||
cartCourseIds: string[];
|
||||
searchQuery: string;
|
||||
onAddToCart: (course: Course) => void;
|
||||
onOpenDetails: (course: Course) => void;
|
||||
onClearSearch: () => void;
|
||||
}
|
||||
|
||||
export const BuyerView: React.FC<BuyerViewProps> = ({
|
||||
courses,
|
||||
cartCourseIds,
|
||||
searchQuery,
|
||||
onAddToCart,
|
||||
onOpenDetails,
|
||||
onClearSearch,
|
||||
}) => {
|
||||
return (
|
||||
<div className="py-6 space-y-12">
|
||||
{/* Course Grid & Filtering with integrated header, search, and banners layout */}
|
||||
<CourseGrid
|
||||
courses={courses}
|
||||
cartCourseIds={cartCourseIds}
|
||||
searchQuery={searchQuery}
|
||||
onAddToCart={onAddToCart}
|
||||
onOpenDetails={onOpenDetails}
|
||||
onClearSearch={onClearSearch}
|
||||
showBanners={true}
|
||||
/>
|
||||
|
||||
{/* Features Banner */}
|
||||
<FeaturesBanner />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,346 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router';
|
||||
import type { Course } from '../types/course';
|
||||
import {
|
||||
FiStar,
|
||||
FiClock,
|
||||
FiBook,
|
||||
FiGlobe,
|
||||
FiCheck,
|
||||
FiShoppingCart,
|
||||
FiPlay,
|
||||
FiChevronDown,
|
||||
FiChevronUp,
|
||||
FiAward,
|
||||
FiShield,
|
||||
FiArrowLeft
|
||||
} from 'react-icons/fi';
|
||||
|
||||
interface CourseDetailViewProps {
|
||||
courses: Course[];
|
||||
cartCourseIds: string[];
|
||||
onAddToCart: (course: Course) => void;
|
||||
onBuyNow: (course: Course) => void;
|
||||
}
|
||||
|
||||
export const CourseDetailView: React.FC<CourseDetailViewProps> = ({
|
||||
courses,
|
||||
cartCourseIds,
|
||||
onAddToCart,
|
||||
onBuyNow,
|
||||
}) => {
|
||||
const { courseId } = useParams<{ courseId: string }>();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const course = courses.find((c) => c.id === courseId) || courses[0];
|
||||
const isInCart = cartCourseIds.includes(course.id);
|
||||
|
||||
const [activeTab, setActiveTab] = useState<'overview' | 'curriculum' | 'instructor' | 'reviews'>('overview');
|
||||
const [expandedModules, setExpandedModules] = useState<string[]>([course.modules[0]?.id || 'm-1']);
|
||||
|
||||
const toggleModule = (id: string) => {
|
||||
if (expandedModules.includes(id)) {
|
||||
setExpandedModules(expandedModules.filter((mId) => mId !== id));
|
||||
} else {
|
||||
setExpandedModules([...expandedModules, id]);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-950 pb-20">
|
||||
|
||||
{/* Top Breadcrumb & Back Navigation */}
|
||||
<div className="bg-slate-900/60 border-b border-slate-800 py-3">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<button
|
||||
onClick={() => navigate('/courses')}
|
||||
className="inline-flex items-center gap-2 text-xs font-semibold text-slate-400 hover:text-cyan-400 transition"
|
||||
>
|
||||
<FiArrowLeft /> Regresar al Catálogo
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main Course Hero & Details */}
|
||||
<div className="bg-gradient-to-b from-slate-900 via-slate-950 to-slate-950 border-b border-slate-800 py-10 lg:py-14">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-10">
|
||||
|
||||
{/* Left Info Column */}
|
||||
<div className="lg:col-span-8 space-y-6">
|
||||
|
||||
<div className="flex flex-wrap items-center gap-2 text-xs">
|
||||
<span className="px-3 py-1 rounded-full font-bold bg-cyan-500/10 text-cyan-400 border border-cyan-500/20">
|
||||
{course.category}
|
||||
</span>
|
||||
<span className="px-2.5 py-1 rounded bg-slate-800 text-slate-300 font-medium">
|
||||
Nivel: {course.level}
|
||||
</span>
|
||||
{course.badge && (
|
||||
<span className="px-2.5 py-1 rounded bg-amber-500 text-slate-950 font-black">
|
||||
🔥 {course.badge}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<h1 className="text-3xl sm:text-4xl font-extrabold text-white leading-tight">
|
||||
{course.title}
|
||||
</h1>
|
||||
|
||||
<p className="text-slate-300 text-base sm:text-lg leading-relaxed">
|
||||
{course.subtitle}
|
||||
</p>
|
||||
|
||||
{/* Rating & Instructor summary */}
|
||||
<div className="flex flex-wrap items-center gap-4 text-xs text-slate-300 pt-1">
|
||||
<div className="flex items-center gap-1.5 font-bold text-amber-400">
|
||||
<span>{course.rating.toFixed(1)}</span>
|
||||
<div className="flex">
|
||||
{[...Array(5)].map((_, i) => (
|
||||
<FiStar key={i} className="fill-amber-400 text-amber-400 text-sm" />
|
||||
))}
|
||||
</div>
|
||||
<span className="text-slate-400 font-normal">({course.reviewsCount} reseñas)</span>
|
||||
</div>
|
||||
|
||||
<span className="text-slate-600">•</span>
|
||||
<span className="text-slate-300">{course.studentsCount.toLocaleString()} alumnos inscritos</span>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap items-center gap-6 text-xs text-slate-400 pt-2 border-t border-slate-800/80">
|
||||
<div className="flex items-center gap-2">
|
||||
<img
|
||||
src={course.instructor.avatar}
|
||||
alt={course.instructor.name}
|
||||
className="w-8 h-8 rounded-full object-cover ring-1 ring-cyan-400"
|
||||
/>
|
||||
<span>Creado por <strong className="text-white font-medium">{course.instructor.name}</strong></span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-1.5">
|
||||
<FiGlobe className="text-cyan-400" />
|
||||
<span>{course.language}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-1.5">
|
||||
<FiClock className="text-cyan-400" />
|
||||
<span>Última actualización: {course.updatedAt}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* Right Sticky Purchase Card */}
|
||||
<div className="lg:col-span-4">
|
||||
<div className="bg-slate-900 border border-slate-700/80 rounded-3xl p-6 shadow-2xl space-y-6 sticky top-24">
|
||||
|
||||
{/* Thumbnail Preview */}
|
||||
<div className="relative rounded-2xl overflow-hidden aspect-video bg-slate-800 group">
|
||||
<img
|
||||
src={course.thumbnail}
|
||||
alt={course.title}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-slate-950/40 flex items-center justify-center">
|
||||
<div className="w-14 h-14 rounded-full bg-cyan-500 text-slate-950 flex items-center justify-center text-xl shadow-xl shadow-cyan-500/40 group-hover:scale-110 transition">
|
||||
<FiPlay className="ml-1" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Price section */}
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-baseline gap-3">
|
||||
<span className="text-3xl font-black text-white font-mono">
|
||||
${course.price.toFixed(2)}
|
||||
</span>
|
||||
<span className="text-sm text-slate-500 line-through font-mono">
|
||||
${course.originalPrice.toFixed(2)}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs text-emerald-400 font-semibold">
|
||||
¡Ahorra un {Math.round(((course.originalPrice - course.price) / course.originalPrice) * 100)}%!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Buttons */}
|
||||
<div className="space-y-3">
|
||||
<button
|
||||
onClick={() => onBuyNow(course)}
|
||||
className="w-full py-3.5 bg-gradient-to-r from-blue-600 to-cyan-500 hover:from-blue-500 hover:to-cyan-400 text-white font-bold rounded-xl text-sm shadow-xl shadow-cyan-500/25 transition-all text-center"
|
||||
>
|
||||
Comprar Ahora
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => onAddToCart(course)}
|
||||
className={`w-full py-3.5 border rounded-xl text-sm font-bold transition-all flex items-center justify-center gap-2 ${
|
||||
isInCart
|
||||
? 'bg-emerald-500/20 border-emerald-500/40 text-emerald-300'
|
||||
: 'bg-slate-800 hover:bg-slate-700 border-slate-700 text-white'
|
||||
}`}
|
||||
>
|
||||
{isInCart ? <FiCheck /> : <FiShoppingCart />}
|
||||
<span>{isInCart ? 'En el Carrito' : 'Añadir al Carrito'}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Benefits checklist */}
|
||||
<div className="space-y-2.5 pt-4 border-t border-slate-800 text-xs text-slate-300">
|
||||
<h4 className="font-bold text-white uppercase tracking-wider text-[11px]">Este curso incluye:</h4>
|
||||
<div className="flex items-center gap-2">
|
||||
<FiClock className="text-cyan-400 shrink-0" />
|
||||
<span>{course.duration} de video bajo demanda</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<FiBook className="text-cyan-400 shrink-0" />
|
||||
<span>{course.lessonsCount} lecciones descargables</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<FiAward className="text-cyan-400 shrink-0" />
|
||||
<span>Certificado de finalización</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<FiShield className="text-emerald-400 shrink-0" />
|
||||
<span>Garantía de reembolso de 30 días</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main Content Tabs & Curriculum */}
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
||||
<div className="lg:w-8/12 space-y-10">
|
||||
|
||||
{/* Navigation Tabs */}
|
||||
<div className="flex border-b border-slate-800 space-x-6 text-sm font-semibold">
|
||||
<button
|
||||
onClick={() => setActiveTab('overview')}
|
||||
className={`pb-3 transition-colors ${
|
||||
activeTab === 'overview'
|
||||
? 'text-cyan-400 border-b-2 border-cyan-400'
|
||||
: 'text-slate-400 hover:text-white'
|
||||
}`}
|
||||
>
|
||||
Descripción
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('curriculum')}
|
||||
className={`pb-3 transition-colors ${
|
||||
activeTab === 'curriculum'
|
||||
? 'text-cyan-400 border-b-2 border-cyan-400'
|
||||
: 'text-slate-400 hover:text-white'
|
||||
}`}
|
||||
>
|
||||
Temario del Curso ({course.modules.length} módulos)
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('instructor')}
|
||||
className={`pb-3 transition-colors ${
|
||||
activeTab === 'instructor'
|
||||
? 'text-cyan-400 border-b-2 border-cyan-400'
|
||||
: 'text-slate-400 hover:text-white'
|
||||
}`}
|
||||
>
|
||||
Instructor
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Tab 1: What you will learn */}
|
||||
{activeTab === 'overview' && (
|
||||
<div className="space-y-8">
|
||||
<div className="bg-slate-900/80 border border-slate-800 rounded-2xl p-6 space-y-4">
|
||||
<h3 className="text-lg font-bold text-white">Lo que aprenderás</h3>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 text-xs text-slate-300">
|
||||
{course.whatYouWillLearn.map((item, idx) => (
|
||||
<div key={idx} className="flex items-start gap-2">
|
||||
<FiCheck className="text-cyan-400 shrink-0 mt-0.5" />
|
||||
<span>{item}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<h3 className="text-lg font-bold text-white">Requisitos previas</h3>
|
||||
<ul className="list-disc list-inside space-y-1.5 text-xs text-slate-300">
|
||||
{course.requirements.map((req, idx) => (
|
||||
<li key={idx}>{req}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Tab 2: Curriculum Accordion */}
|
||||
{activeTab === 'curriculum' && (
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-lg font-bold text-white">Contenido del Programa</h3>
|
||||
<div className="divide-y divide-slate-800 border border-slate-800 rounded-2xl bg-slate-900/60 overflow-hidden">
|
||||
{course.modules.map((mod) => (
|
||||
<div key={mod.id} className="bg-slate-900/90">
|
||||
<button
|
||||
onClick={() => toggleModule(mod.id)}
|
||||
className="w-full px-5 py-4 flex items-center justify-between text-left hover:bg-slate-800/60 transition"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
{expandedModules.includes(mod.id) ? (
|
||||
<FiChevronUp className="text-cyan-400 text-lg" />
|
||||
) : (
|
||||
<FiChevronDown className="text-slate-500 text-lg" />
|
||||
)}
|
||||
<span className="text-sm font-bold text-white">{mod.title}</span>
|
||||
</div>
|
||||
<span className="text-xs text-slate-400 font-mono">{mod.lessons.length} lecciones</span>
|
||||
</button>
|
||||
|
||||
{expandedModules.includes(mod.id) && (
|
||||
<div className="px-5 pb-4 space-y-2 bg-slate-950/40 divide-y divide-slate-800/40">
|
||||
{mod.lessons.map((les) => (
|
||||
<div key={les.id} className="pt-2 flex items-center justify-between text-xs text-slate-300">
|
||||
<div className="flex items-center gap-2">
|
||||
<FiPlay className="text-cyan-400 text-xs" />
|
||||
<span>{les.title}</span>
|
||||
</div>
|
||||
<span className="text-slate-500 font-mono text-[11px]">{les.duration}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Tab 3: Instructor */}
|
||||
{activeTab === 'instructor' && (
|
||||
<div className="bg-slate-900/80 border border-slate-800 rounded-2xl p-6 space-y-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<img
|
||||
src={course.instructor.avatar}
|
||||
alt={course.instructor.name}
|
||||
className="w-16 h-16 rounded-full object-cover ring-2 ring-cyan-400"
|
||||
/>
|
||||
<div>
|
||||
<h3 className="text-lg font-bold text-white">{course.instructor.name}</h3>
|
||||
<p className="text-xs text-cyan-400 font-medium">{course.instructor.role}</p>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xs text-slate-300 leading-relaxed">
|
||||
{course.instructor.bio}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,554 +0,0 @@
|
||||
import React, { useState, useRef } from 'react';
|
||||
import { useNavigate } from 'react-router';
|
||||
import type { Course } from '../types/course';
|
||||
import {
|
||||
FiArrowLeft,
|
||||
FiPlus,
|
||||
FiTrash2,
|
||||
FiUploadCloud,
|
||||
FiVideo,
|
||||
FiCheckCircle,
|
||||
FiPlay,
|
||||
FiX,
|
||||
FiFilm,
|
||||
FiLink,
|
||||
FiImage
|
||||
} from 'react-icons/fi';
|
||||
|
||||
interface CreateCourseViewProps {
|
||||
onCreateCourse: (course: Course) => void;
|
||||
onShowToast: (msg: string) => void;
|
||||
}
|
||||
|
||||
export const CreateCourseView: React.FC<CreateCourseViewProps> = ({
|
||||
onCreateCourse,
|
||||
onShowToast,
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const imageInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const [title, setTitle] = useState('');
|
||||
const [subtitle, setSubtitle] = useState('');
|
||||
const [category, setCategory] = useState('Desarrollo Web');
|
||||
const [level, setLevel] = useState<'Principiante' | 'Intermedio' | 'Avanzado' | 'Todos los niveles'>('Principiante');
|
||||
const [price, setPrice] = useState(29.99);
|
||||
const [originalPrice, setOriginalPrice] = useState(89.99);
|
||||
|
||||
// Thumbnail Image Drag & Drop State
|
||||
const [thumbnailFile, setThumbnailFile] = useState<File | null>(null);
|
||||
const [thumbnail, setThumbnail] = useState('https://images.unsplash.com/photo-1517694712202-14dd9538aa97?w=800&auto=format&fit=crop&q=80');
|
||||
|
||||
// Video Upload States
|
||||
const [videoFile, setVideoFile] = useState<File | null>(null);
|
||||
const [videoPreviewUrl, setVideoPreviewUrl] = useState<string>('');
|
||||
const [videoEmbedUrl, setVideoEmbedUrl] = useState<string>('');
|
||||
const [isUploading, setIsUploading] = useState(false);
|
||||
const [uploadProgress, setUploadProgress] = useState(0);
|
||||
|
||||
const [modules, setModules] = useState<{ title: string; lessonsCount: number; videoName?: string }[]>([
|
||||
{ title: 'Módulo 1: Introducción y Fundamentos', lessonsCount: 4, videoName: '01_introduccion.mp4' },
|
||||
{ title: 'Módulo 2: Proyecto Práctico Principal', lessonsCount: 6, videoName: '02_proyecto_demo.mp4' },
|
||||
]);
|
||||
|
||||
const [newModuleTitle, setNewModuleTitle] = useState('');
|
||||
|
||||
const handleImageFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (!file) return;
|
||||
|
||||
setThumbnailFile(file);
|
||||
const objectUrl = URL.createObjectURL(file);
|
||||
setThumbnail(objectUrl);
|
||||
onShowToast(`Imagen de portada "${file.name}" cargada correctamente`);
|
||||
};
|
||||
|
||||
const handleRemoveThumbnail = () => {
|
||||
setThumbnailFile(null);
|
||||
setThumbnail('https://images.unsplash.com/photo-1517694712202-14dd9538aa97?w=800&auto=format&fit=crop&q=80');
|
||||
if (imageInputRef.current) imageInputRef.current.value = '';
|
||||
};
|
||||
|
||||
// Handle Video File Upload Simulation
|
||||
const handleVideoFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (!file) return;
|
||||
|
||||
setVideoFile(file);
|
||||
setIsUploading(true);
|
||||
setUploadProgress(15);
|
||||
|
||||
// Simulate progress bar upload to 100%
|
||||
const interval = setInterval(() => {
|
||||
setUploadProgress((prev) => {
|
||||
if (prev >= 100) {
|
||||
clearInterval(interval);
|
||||
setIsUploading(false);
|
||||
const previewUrl = URL.createObjectURL(file);
|
||||
setVideoPreviewUrl(previewUrl);
|
||||
onShowToast(`¡Video "${file.name}" cargado exitosamente!`);
|
||||
return 100;
|
||||
}
|
||||
return prev + 25;
|
||||
});
|
||||
}, 200);
|
||||
};
|
||||
|
||||
const handleRemoveVideo = () => {
|
||||
setVideoFile(null);
|
||||
setVideoPreviewUrl('');
|
||||
setUploadProgress(0);
|
||||
if (fileInputRef.current) fileInputRef.current.value = '';
|
||||
};
|
||||
|
||||
const handleAddModule = () => {
|
||||
if (newModuleTitle.trim()) {
|
||||
setModules([...modules, { title: newModuleTitle, lessonsCount: 3, videoName: 'clase_muestra.mp4' }]);
|
||||
setNewModuleTitle('');
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveModule = (idx: number) => {
|
||||
setModules(modules.filter((_, i) => i !== idx));
|
||||
};
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!title.trim()) return;
|
||||
|
||||
const newCourse: Course = {
|
||||
id: `course-${Date.now()}`,
|
||||
title,
|
||||
subtitle: subtitle || 'Aprende paso a paso con proyectos prácticos.',
|
||||
category,
|
||||
level,
|
||||
price: Number(price),
|
||||
originalPrice: Number(originalPrice),
|
||||
rating: 5.0,
|
||||
reviewsCount: 1,
|
||||
studentsCount: 1,
|
||||
thumbnail,
|
||||
duration: '12h 30m',
|
||||
lessonsCount: modules.reduce((sum, m) => sum + m.lessonsCount, 0),
|
||||
badge: 'Nuevo',
|
||||
instructor: {
|
||||
id: 'inst-1',
|
||||
name: 'Tu Nombre (Instructor)',
|
||||
role: 'Instructor Experto',
|
||||
avatar: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=800&auto=format&fit=crop&q=80',
|
||||
rating: 5.0,
|
||||
totalStudents: 1,
|
||||
totalCourses: 1,
|
||||
bio: 'Creador de contenido y profesional en tecnología.',
|
||||
},
|
||||
updatedAt: 'Reciente',
|
||||
language: 'Español',
|
||||
whatYouWillLearn: [
|
||||
'Dominio completo de la tecnología aprendida.',
|
||||
'Construcción de proyectos prácticos listos para producción.',
|
||||
'Código fuente completo e instructivos.',
|
||||
],
|
||||
requirements: ['Computadora con conexión a internet.'],
|
||||
modules: modules.map((m, idx) => ({
|
||||
id: `mod-${idx}`,
|
||||
title: m.title,
|
||||
lessons: [
|
||||
{ id: `l-${idx}-1`, title: 'Lección 1: Conceptos Clave', duration: '15:00', isFreePreview: true },
|
||||
{ id: `l-${idx}-2`, title: 'Lección 2: Implementación', duration: '22:30' },
|
||||
],
|
||||
})),
|
||||
reviews: [],
|
||||
isPublished: true,
|
||||
salesCount: 0,
|
||||
totalRevenue: 0,
|
||||
};
|
||||
|
||||
onCreateCourse(newCourse);
|
||||
onShowToast('¡Tu curso, portada y videos han sido publicados exitosamente!');
|
||||
navigate('/teachers');
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen transition-colors duration-300 bg-slate-950 text-slate-100 py-10 selection:bg-cyan-500 font-sans">
|
||||
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 space-y-8">
|
||||
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between border-b border-slate-800 pb-5">
|
||||
<div className="space-y-1">
|
||||
<button
|
||||
onClick={() => navigate('/teachers')}
|
||||
className="inline-flex items-center gap-2 text-xs font-semibold text-slate-400 hover:text-cyan-400 mb-2 transition"
|
||||
>
|
||||
<FiArrowLeft /> Regresar a Panel de Instructor
|
||||
</button>
|
||||
<h1 className="text-2xl sm:text-3xl font-extrabold text-white">
|
||||
Publicar Nuevo Curso & Cargar Contenido
|
||||
</h1>
|
||||
<p className="text-xs text-slate-400">
|
||||
Sube la portada y los videos de tus clases para lanzar tu contenido a la plataforma.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Form Container */}
|
||||
<form onSubmit={handleSubmit} className="space-y-8">
|
||||
|
||||
{/* Card 1: Informacion Basica */}
|
||||
<div className="bg-slate-900 border border-slate-800 rounded-3xl p-6 sm:p-8 space-y-6 shadow-xl">
|
||||
<h3 className="text-sm font-bold text-cyan-400 uppercase tracking-wider">
|
||||
1. Información Básica
|
||||
</h3>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-xs font-semibold text-slate-300 mb-1">
|
||||
Título del Curso *
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
required
|
||||
value={title}
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
placeholder="Ej: Master en React 19 y Next.js 15 desde Cero"
|
||||
className="w-full px-4 py-3 bg-slate-950 border border-slate-700/80 rounded-xl text-xs text-white placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-cyan-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-xs font-semibold text-slate-300 mb-1">
|
||||
Subtítulo Resumen
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={subtitle}
|
||||
onChange={(e) => setSubtitle(e.target.value)}
|
||||
placeholder="Aprende a construir aplicaciones fullstack de alto rendimiento..."
|
||||
className="w-full px-4 py-3 bg-slate-950 border border-slate-700/80 rounded-xl text-xs text-white placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-cyan-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-xs font-semibold text-slate-300 mb-1">
|
||||
Categoría
|
||||
</label>
|
||||
<select
|
||||
value={category}
|
||||
onChange={(e) => setCategory(e.target.value)}
|
||||
className="w-full px-4 py-3 bg-slate-950 border border-slate-700/80 rounded-xl text-xs text-white focus:outline-none focus:ring-2 focus:ring-cyan-500"
|
||||
>
|
||||
<option value="Desarrollo Web">Desarrollo Web</option>
|
||||
<option value="Inteligencia Artificial">Inteligencia Artificial</option>
|
||||
<option value="Diseño UI/UX">Diseño UI/UX</option>
|
||||
<option value="Ciberseguridad">Ciberseguridad</option>
|
||||
<option value="Marketing Digital">Marketing Digital</option>
|
||||
<option value="Negocios">Negocios</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-xs font-semibold text-slate-300 mb-1">
|
||||
Nivel del Curso
|
||||
</label>
|
||||
<select
|
||||
value={level}
|
||||
onChange={(e) => setLevel(e.target.value as any)}
|
||||
className="w-full px-4 py-3 bg-slate-950 border border-slate-700/80 rounded-xl text-xs text-white focus:outline-none focus:ring-2 focus:ring-cyan-500"
|
||||
>
|
||||
<option value="Principiante">Principiante</option>
|
||||
<option value="Intermedio">Intermedio</option>
|
||||
<option value="Avanzado">Avanzado</option>
|
||||
<option value="Todos los niveles">Todos los niveles</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Card 2: Subir Video de Muestra / Lección Promocional */}
|
||||
<div className="bg-slate-900 border border-slate-800 rounded-3xl p-6 sm:p-8 space-y-6 shadow-xl">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h3 className="text-sm font-bold text-cyan-400 uppercase tracking-wider flex items-center gap-2">
|
||||
<FiVideo /> 2. Subir Video Promocional / Clase de Muestra
|
||||
</h3>
|
||||
<p className="text-xs text-slate-400 mt-1">
|
||||
Sube el archivo de video (.mp4, .mov, .webm) o pega la URL directa de tu lección para la vista previa del curso.
|
||||
</p>
|
||||
</div>
|
||||
<span className="px-2.5 py-1 rounded bg-violet-500/10 text-violet-300 border border-violet-500/20 text-[11px] font-bold">
|
||||
HD 1080p
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Video Upload Dropzone */}
|
||||
<div className="space-y-4">
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
accept="video/*"
|
||||
onChange={handleVideoFileChange}
|
||||
className="hidden"
|
||||
id="video-upload-input"
|
||||
/>
|
||||
|
||||
{!videoFile && !videoPreviewUrl ? (
|
||||
<label
|
||||
htmlFor="video-upload-input"
|
||||
className="flex flex-col items-center justify-center p-8 border-2 border-dashed border-slate-700 hover:border-cyan-500/60 rounded-2xl bg-slate-950/60 hover:bg-slate-950 transition cursor-pointer group text-center space-y-3"
|
||||
>
|
||||
<div className="w-14 h-14 rounded-2xl bg-cyan-500/10 text-cyan-400 flex items-center justify-center text-2xl group-hover:scale-110 transition-transform">
|
||||
<FiUploadCloud />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-bold text-white">Haz clic o arrastra tu archivo de video aquí</p>
|
||||
<p className="text-xs text-slate-400 mt-0.5">Soporta MP4, MOV, WEBM hasta 2GB por clase</p>
|
||||
</div>
|
||||
<span className="px-4 py-2 bg-slate-800 hover:bg-slate-700 text-cyan-400 font-semibold rounded-xl text-xs transition border border-slate-700">
|
||||
Seleccionar Archivo de Video
|
||||
</span>
|
||||
</label>
|
||||
) : (
|
||||
<div className="p-4 rounded-2xl bg-slate-950 border border-slate-800 space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 rounded-xl bg-emerald-500/10 text-emerald-400 flex items-center justify-center text-xl">
|
||||
<FiFilm />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs font-bold text-white truncate max-w-xs">{videoFile?.name || 'video_promocional.mp4'}</p>
|
||||
<p className="text-[11px] text-emerald-400 font-semibold flex items-center gap-1">
|
||||
<FiCheckCircle /> Video procesado correctamente ({videoFile ? `${(videoFile.size / (1024 * 1024)).toFixed(1)} MB` : '142 MB'})
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleRemoveVideo}
|
||||
className="p-2 rounded-lg bg-slate-800 hover:bg-rose-950 text-slate-400 hover:text-rose-400 transition"
|
||||
title="Eliminar video"
|
||||
>
|
||||
<FiX />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{isUploading && (
|
||||
<div className="space-y-1">
|
||||
<div className="flex justify-between text-[11px] text-slate-400 font-mono">
|
||||
<span>Cargando video al servidor de la plataforma...</span>
|
||||
<span>{uploadProgress}%</span>
|
||||
</div>
|
||||
<div className="w-full bg-slate-800 h-2 rounded-full overflow-hidden">
|
||||
<div className="bg-cyan-500 h-full transition-all duration-300" style={{ width: `${uploadProgress}%` }} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{videoPreviewUrl && (
|
||||
<div className="aspect-video w-full rounded-xl overflow-hidden bg-black border border-slate-800 relative">
|
||||
<video
|
||||
src={videoPreviewUrl}
|
||||
controls
|
||||
className="w-full h-full object-contain"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="pt-2">
|
||||
<label className="block text-xs font-semibold text-slate-300 mb-1 flex items-center gap-1.5">
|
||||
<FiLink className="text-slate-400" /> O pega el enlace directo de tu video (Vimeo, YouTube, Cloudflare)
|
||||
</label>
|
||||
<input
|
||||
type="url"
|
||||
value={videoEmbedUrl}
|
||||
onChange={(e) => setVideoEmbedUrl(e.target.value)}
|
||||
placeholder="https://vimeo.com/123456789 o https://youtube.com/watch?v=..."
|
||||
className="w-full px-4 py-2.5 bg-slate-950 border border-slate-700/80 rounded-xl text-xs text-white placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-cyan-500 font-mono"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Card 3: Precio e Imagen de Portada (Drag & Drop + URL) */}
|
||||
<div className="bg-slate-900 border border-slate-800 rounded-3xl p-6 sm:p-8 space-y-6 shadow-xl">
|
||||
<h3 className="text-sm font-bold text-cyan-400 uppercase tracking-wider flex items-center gap-2">
|
||||
<FiImage /> 3. Precio e Imagen de Portada
|
||||
</h3>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-xs font-semibold text-slate-300 mb-1">
|
||||
Precio de Venta ($ USD)
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
step="0.01"
|
||||
required
|
||||
value={price}
|
||||
onChange={(e) => setPrice(Number(e.target.value))}
|
||||
className="w-full px-4 py-3 bg-slate-950 border border-slate-700/80 rounded-xl text-xs text-white font-mono focus:outline-none focus:ring-2 focus:ring-cyan-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-xs font-semibold text-slate-300 mb-1">
|
||||
Precio Original Tachado ($ USD)
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
step="0.01"
|
||||
required
|
||||
value={originalPrice}
|
||||
onChange={(e) => setOriginalPrice(Number(e.target.value))}
|
||||
className="w-full px-4 py-3 bg-slate-950 border border-slate-700/80 rounded-xl text-xs text-white font-mono focus:outline-none focus:ring-2 focus:ring-cyan-500"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Thumbnail Drag and Drop Section */}
|
||||
<div className="space-y-4 pt-2">
|
||||
<label className="block text-xs font-semibold text-slate-300">
|
||||
Imagen de Portada (Thumbnail del Curso)
|
||||
</label>
|
||||
|
||||
<input
|
||||
ref={imageInputRef}
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={handleImageFileChange}
|
||||
className="hidden"
|
||||
id="create-view-image-upload"
|
||||
/>
|
||||
|
||||
{!thumbnailFile ? (
|
||||
<label
|
||||
htmlFor="create-view-image-upload"
|
||||
className="flex flex-col items-center justify-center p-8 border-2 border-dashed border-slate-700 hover:border-cyan-500/60 rounded-2xl bg-slate-950/60 hover:bg-slate-950 transition cursor-pointer group text-center space-y-3"
|
||||
>
|
||||
<div className="w-12 h-12 rounded-2xl bg-cyan-500/10 text-cyan-400 flex items-center justify-center text-xl group-hover:scale-110 transition-transform">
|
||||
<FiUploadCloud />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs font-bold text-white">Haz clic o arrastra tu imagen de portada aquí</p>
|
||||
<p className="text-[11px] text-slate-400 mt-0.5">Soporta PNG, JPG, WEBP o SVG (Recomendado: 1280x720)</p>
|
||||
</div>
|
||||
<span className="px-3.5 py-1.5 bg-slate-800 hover:bg-slate-700 text-cyan-400 font-semibold rounded-xl text-xs transition border border-slate-700">
|
||||
Seleccionar Imagen
|
||||
</span>
|
||||
</label>
|
||||
) : (
|
||||
<div className="p-3 rounded-xl bg-slate-950 border border-slate-800 flex items-center justify-between">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<FiCheckCircle className="text-emerald-400 text-lg" />
|
||||
<span className="text-xs font-bold text-white truncate max-w-xs">{thumbnailFile.name}</span>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleRemoveThumbnail}
|
||||
className="p-1.5 rounded-lg bg-slate-800 text-slate-400 hover:text-rose-400 transition"
|
||||
>
|
||||
<FiX />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Live Thumbnail Preview */}
|
||||
{thumbnail && (
|
||||
<div className="aspect-video max-w-sm rounded-xl overflow-hidden bg-slate-950 border border-slate-800 shadow-md">
|
||||
<img src={thumbnail} alt="Vista previa de portada" className="w-full h-full object-cover" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<label className="block text-xs font-semibold text-slate-400 mb-1 flex items-center gap-1.5">
|
||||
<FiLink /> O pega la URL directa de la imagen de portada
|
||||
</label>
|
||||
<input
|
||||
type="url"
|
||||
value={thumbnail}
|
||||
onChange={(e) => {
|
||||
setThumbnailFile(null);
|
||||
setThumbnail(e.target.value);
|
||||
}}
|
||||
placeholder="https://images.unsplash.com/..."
|
||||
className="w-full px-4 py-3 bg-slate-950 border border-slate-700/80 rounded-xl text-xs text-white placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-cyan-500 font-mono"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Card 4: Estructura del Temario & Asignación de Videos por Módulo */}
|
||||
<div className="bg-slate-900 border border-slate-800 rounded-3xl p-6 sm:p-8 space-y-6 shadow-xl">
|
||||
<h3 className="text-sm font-bold text-cyan-400 uppercase tracking-wider">
|
||||
4. Módulos y Videos de Clases
|
||||
</h3>
|
||||
|
||||
<div className="space-y-3">
|
||||
{modules.map((m, idx) => (
|
||||
<div key={idx} className="p-4 bg-slate-950 border border-slate-800 rounded-xl text-xs flex items-center justify-between gap-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-8 h-8 rounded-lg bg-cyan-500/10 text-cyan-400 flex items-center justify-center text-sm">
|
||||
<FiPlay />
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-semibold text-white block">{m.title}</span>
|
||||
<span className="text-[10px] text-slate-400">Video adjunto: {m.videoName || 'leccion.mp4'}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleRemoveModule(idx)}
|
||||
className="p-2 text-slate-500 hover:text-rose-400 transition"
|
||||
>
|
||||
<FiTrash2 />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
|
||||
<div className="flex gap-2 pt-2">
|
||||
<input
|
||||
type="text"
|
||||
value={newModuleTitle}
|
||||
onChange={(e) => setNewModuleTitle(e.target.value)}
|
||||
placeholder="Agregar nuevo título de módulo..."
|
||||
className="flex-1 px-4 py-2.5 bg-slate-950 border border-slate-700/80 rounded-xl text-xs text-white focus:outline-none focus:ring-2 focus:ring-cyan-500"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleAddModule}
|
||||
className="px-4 py-2.5 bg-slate-800 hover:bg-slate-700 text-cyan-400 font-semibold rounded-xl text-xs flex items-center gap-1.5 transition"
|
||||
>
|
||||
<FiPlus /> Módulo
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Submit Actions */}
|
||||
<div className="flex items-center justify-end gap-4 pt-4">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => navigate('/teachers')}
|
||||
className="px-6 py-3.5 bg-slate-800 hover:bg-slate-700 text-slate-300 font-semibold rounded-xl text-xs transition"
|
||||
>
|
||||
Cancelar
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="px-8 py-3.5 bg-gradient-to-r from-blue-600 to-cyan-500 hover:from-blue-500 hover:to-cyan-400 text-white font-bold rounded-xl text-sm shadow-xl shadow-cyan-500/25 transition-all flex items-center gap-2"
|
||||
>
|
||||
<FiUploadCloud />
|
||||
<span>Publicar Curso y Videos</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,186 +0,0 @@
|
||||
import React from 'react';
|
||||
import type { Course } from '../types/course';
|
||||
import { CourseGrid } from '../components/buyer/CourseGrid';
|
||||
import {
|
||||
FiPlay,
|
||||
FiStar,
|
||||
FiCheckCircle,
|
||||
FiAward
|
||||
} from 'react-icons/fi';
|
||||
|
||||
interface HomeViewProps {
|
||||
courses: Course[];
|
||||
cartCourseIds: string[];
|
||||
searchQuery: string;
|
||||
onAddToCart: (course: Course) => void;
|
||||
onOpenPreview: (courseId: string) => void;
|
||||
onOpenDetails: (course: Course) => void;
|
||||
onClearSearch: () => void;
|
||||
}
|
||||
|
||||
export const HomeView: React.FC<HomeViewProps> = ({
|
||||
courses,
|
||||
cartCourseIds,
|
||||
searchQuery,
|
||||
onAddToCart,
|
||||
onOpenDetails,
|
||||
onClearSearch,
|
||||
}) => {
|
||||
// Most watched course spotlight (top featured course)
|
||||
const topCourse = courses[0];
|
||||
|
||||
return (
|
||||
<div className="space-y-8 pb-16">
|
||||
|
||||
{/* 1. Welcome & Top Trending Video Hero Section */}
|
||||
<section className="relative overflow-hidden bg-gradient-to-b from-slate-900 via-slate-950 to-slate-950 border-b border-slate-800/80 pt-8 pb-12">
|
||||
|
||||
{/* Glow background accents */}
|
||||
<div className="absolute top-0 left-1/4 w-96 h-96 bg-cyan-500/10 rounded-full blur-3xl pointer-events-none" />
|
||||
<div className="absolute bottom-0 right-1/4 w-96 h-96 bg-blue-600/10 rounded-full blur-3xl pointer-events-none" />
|
||||
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-8 items-center">
|
||||
|
||||
{/* Left Column: Welcome & Quick Metrics */}
|
||||
<div className="lg:col-span-7 space-y-5">
|
||||
|
||||
<div className="inline-flex items-center gap-2 px-3.5 py-1.5 rounded-full text-xs font-semibold bg-cyan-500/10 text-cyan-400 border border-cyan-500/20">
|
||||
<FiAward />
|
||||
<span>¡Bienvenido a RyCourses Pro!</span>
|
||||
</div>
|
||||
|
||||
<h1 className="text-3xl sm:text-4xl lg:text-5xl font-black text-white tracking-tight leading-tight">
|
||||
Bienvenido a{' '}
|
||||
<span className="text-transparent bg-clip-text bg-gradient-to-r from-cyan-400 via-blue-400 to-teal-300">
|
||||
RyCourses Pro!
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
<p className="text-sm sm:text-base text-slate-300 max-w-xl leading-relaxed font-normal">
|
||||
Explora cursos prácticos, proyectos guiados y contenido creado por instructores expertos en Desarrollo Web, IA, Diseño y Tecnología.
|
||||
</p>
|
||||
|
||||
{/* Trust Checkmarks */}
|
||||
<div className="flex flex-wrap items-center gap-x-5 gap-y-2 text-xs text-slate-300 pt-1">
|
||||
<span className="flex items-center gap-1.5">
|
||||
<FiCheckCircle className="text-emerald-400 text-sm" /> Acceso de por vida
|
||||
</span>
|
||||
<span className="flex items-center gap-1.5">
|
||||
<FiCheckCircle className="text-emerald-400 text-sm" /> Garantía de 30 días
|
||||
</span>
|
||||
<span className="flex items-center gap-1.5">
|
||||
<FiCheckCircle className="text-emerald-400 text-sm" /> Certificados al finalizar
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Stats summary */}
|
||||
<div className="pt-3 grid grid-cols-3 gap-4 max-w-md border-t border-slate-800/80">
|
||||
<div>
|
||||
<div className="text-lg font-black text-white font-mono">{courses.length}+</div>
|
||||
<div className="text-[11px] text-slate-400">Cursos disponibles</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-lg font-black text-cyan-400 font-mono">50k+</div>
|
||||
<div className="text-[11px] text-slate-400">Estudiantes</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-lg font-black text-amber-400 font-mono">4.9 ★</div>
|
||||
<div className="text-[11px] text-slate-400">Valoración media</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* Right Column: Top Trending Video Spotlight Card */}
|
||||
{topCourse && (
|
||||
<div className="lg:col-span-5">
|
||||
<div className="bg-slate-900/90 border border-slate-800 rounded-3xl p-5 shadow-2xl backdrop-blur-md space-y-4 hover:border-cyan-500/40 transition">
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="inline-flex items-center gap-1.5 px-3 py-1 rounded-md text-[11px] font-black bg-amber-500 text-slate-950 uppercase tracking-wider shadow">
|
||||
<FiAward /> Curso Más Visto de la Semana
|
||||
</span>
|
||||
<span className="text-[11px] text-slate-400 font-mono">38h 45m</span>
|
||||
</div>
|
||||
|
||||
{/* Video Thumbnail with Play Button */}
|
||||
<div
|
||||
onClick={() => onOpenDetails(topCourse)}
|
||||
className="relative aspect-video rounded-2xl overflow-hidden bg-slate-800 cursor-pointer group"
|
||||
>
|
||||
<img
|
||||
src={topCourse.thumbnail}
|
||||
alt={topCourse.title}
|
||||
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-slate-950/80 via-slate-950/20 to-transparent" />
|
||||
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<div className="w-14 h-14 rounded-full bg-cyan-500 text-slate-950 flex items-center justify-center text-xl shadow-xl shadow-cyan-500/40 group-hover:scale-110 active:scale-95 transition-all">
|
||||
<FiPlay className="ml-1" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="absolute bottom-3 left-3 right-3 flex items-center justify-between text-xs text-white">
|
||||
<span className="font-semibold px-2 py-0.5 rounded bg-slate-950/80 backdrop-blur-md">
|
||||
Ver Clase Muestra
|
||||
</span>
|
||||
<span className="flex items-center gap-1 font-bold text-amber-400 bg-slate-950/80 backdrop-blur-md px-2 py-0.5 rounded">
|
||||
<FiStar className="fill-amber-400" /> {topCourse.rating}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Title & Price */}
|
||||
<div className="space-y-2">
|
||||
<h3
|
||||
onClick={() => onOpenDetails(topCourse)}
|
||||
className="text-base font-bold text-white hover:text-cyan-300 cursor-pointer line-clamp-1 transition-colors"
|
||||
>
|
||||
{topCourse.title}
|
||||
</h3>
|
||||
|
||||
<div className="flex items-center justify-between pt-1 border-t border-slate-800">
|
||||
<div className="flex items-center gap-2">
|
||||
<img
|
||||
src={topCourse.instructor.avatar}
|
||||
alt={topCourse.instructor.name}
|
||||
className="w-6 h-6 rounded-full object-cover"
|
||||
/>
|
||||
<span className="text-xs text-slate-300 font-medium">{topCourse.instructor.name}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-baseline gap-2 font-mono">
|
||||
<span className="text-xs text-slate-500 line-through">${topCourse.originalPrice}</span>
|
||||
<span className="text-lg font-black text-emerald-400">${topCourse.price}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* 2. Direct Catalog Header & Full Interactive Course Grid */}
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 space-y-4">
|
||||
|
||||
{/* Full Interactive Grid (Categories, Search & Cards) */}
|
||||
<CourseGrid
|
||||
courses={courses}
|
||||
cartCourseIds={cartCourseIds}
|
||||
searchQuery={searchQuery}
|
||||
onAddToCart={onAddToCart}
|
||||
onOpenDetails={onOpenDetails}
|
||||
onClearSearch={onClearSearch}
|
||||
showBanners={false}
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,235 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useNavigate, Link } from 'react-router';
|
||||
import { BrandLogo } from '../components/common/BrandLogo';
|
||||
import {
|
||||
Button,
|
||||
Input,
|
||||
Checkbox,
|
||||
ThemeToggle,
|
||||
Card
|
||||
} from '../components/ui';
|
||||
import {
|
||||
FiMail,
|
||||
FiLock,
|
||||
FiArrowRight,
|
||||
FiArrowLeft,
|
||||
FiCheckCircle
|
||||
} from 'react-icons/fi';
|
||||
import type { User } from '../types/course';
|
||||
|
||||
interface LoginViewProps {
|
||||
onShowToast: (msg: string) => void;
|
||||
onLogin: (user: User) => void;
|
||||
}
|
||||
|
||||
export const LoginView: React.FC<LoginViewProps> = ({ onShowToast, onLogin }) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [rememberMe, setRememberMe] = useState(true);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const isValidEmail = email.includes('@') && email.includes('.');
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!email || !password) return;
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
setTimeout(() => {
|
||||
let derivedName = 'Usuario RyCourses';
|
||||
if (email.includes('@')) {
|
||||
const rawName = email.split('@')[0];
|
||||
derivedName = rawName
|
||||
.replace(/[._-]/g, ' ')
|
||||
.replace(/\b\w/g, (char) => char.toUpperCase());
|
||||
}
|
||||
|
||||
const isSeller = email.toLowerCase().includes('instructor') || email.toLowerCase().includes('seller');
|
||||
const role: 'buyer' | 'seller' = isSeller ? 'seller' : 'buyer';
|
||||
|
||||
const userObj: User = {
|
||||
id: Math.random().toString(36).substring(2, 11),
|
||||
name: derivedName,
|
||||
email,
|
||||
role,
|
||||
avatar: role === 'seller'
|
||||
? 'https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?w=100&auto=format&fit=crop&q=80'
|
||||
: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=100&auto=format&fit=crop&q=80',
|
||||
};
|
||||
|
||||
onLogin(userObj);
|
||||
onShowToast(`¡Bienvenido de nuevo, ${derivedName}!`);
|
||||
setIsLoading(false);
|
||||
navigate(role === 'seller' ? '/seller' : '/courses');
|
||||
}, 450);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col justify-between font-sans selection:bg-cyan-500 relative overflow-hidden p-6 sm:p-10 lg:p-12 transition-colors duration-300 bg-slate-50 dark:bg-slate-950 text-slate-900 dark:text-slate-100">
|
||||
|
||||
{/* Top-Right Background Radial Gradient Glow */}
|
||||
<div
|
||||
className="absolute -top-32 -right-32 w-[600px] sm:w-[850px] h-[600px] sm:h-[850px] rounded-full pointer-events-none opacity-30 dark:opacity-35 blur-[150px]"
|
||||
style={{
|
||||
background: 'radial-gradient(circle at center, rgba(6, 182, 212, 0.45) 0%, rgba(37, 99, 235, 0.3) 45%, transparent 70%)'
|
||||
}}
|
||||
/>
|
||||
<div className="absolute bottom-0 left-0 w-[450px] h-[450px] rounded-full pointer-events-none opacity-15 dark:opacity-20 blur-[160px] bg-blue-900/30" />
|
||||
<div className="absolute inset-0 bg-[radial-gradient(#94a3b8_1px,transparent_1px)] dark:bg-[radial-gradient(#334155_1px,transparent_1px)] [background-size:24px_24px] opacity-20 dark:opacity-10 pointer-events-none" />
|
||||
|
||||
{/* Interactive Background Atmosphere: Twinkling Star Particles & Flying Space Shuttle */}
|
||||
<div className="absolute inset-0 pointer-events-none overflow-hidden z-0">
|
||||
<div className="absolute top-12 left-1/6 w-1.5 h-1.5 rounded-full bg-cyan-400 animate-twinkle opacity-75" style={{ animationDelay: '0s' }} />
|
||||
<div className="absolute top-1/4 right-1/5 w-2 h-2 rounded-full bg-blue-400 animate-twinkle opacity-85" style={{ animationDelay: '1.4s' }} />
|
||||
<div className="absolute top-1/3 left-1/4 w-1 h-1 rounded-full bg-teal-400 animate-twinkle opacity-70" style={{ animationDelay: '2.1s' }} />
|
||||
<div className="absolute top-1/2 right-1/3 w-1.5 h-1.5 rounded-full bg-cyan-400 animate-twinkle opacity-80" style={{ animationDelay: '0.6s' }} />
|
||||
<div className="absolute top-2/3 left-1/5 w-2 h-2 rounded-full bg-cyan-500 animate-twinkle opacity-75" style={{ animationDelay: '1.8s' }} />
|
||||
|
||||
{/* Crossing Space Shuttle Rocket */}
|
||||
<div className="absolute top-0 left-0 animate-spaceship">
|
||||
<svg className="w-16 h-16 sm:w-20 sm:h-20 filter drop-shadow-[0_0_25px_rgba(6,182,212,0.8)]" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M22 78L10 90L26 84L14 98L30 86L22 78Z" fill="url(#plasmaFlame)" className="animate-pulse" />
|
||||
<path d="M78 22C62 26 42 42 34 58L26 62L38 74L42 66C58 58 74 38 78 22Z" fill="url(#rocketBodyGradient)" stroke="#38bdf8" strokeWidth="2" strokeLinejoin="round" />
|
||||
<path d="M78 22C74 23 68 28 64 34L66 36C72 32 77 26 78 22Z" fill="#22d3ee" />
|
||||
<path d="M34 58L16 64L24 50L30 52Z" fill="#0284c7" stroke="#38bdf8" strokeWidth="1.5" strokeLinejoin="round" />
|
||||
<path d="M42 66L48 84L50 76L46 70Z" fill="#0284c7" stroke="#38bdf8" strokeWidth="1.5" strokeLinejoin="round" />
|
||||
<circle cx="58" cy="42" r="5" fill="#0f172a" stroke="#38bdf8" strokeWidth="1.5" />
|
||||
<circle cx="58" cy="42" r="3.5" fill="#22d3ee" />
|
||||
<circle cx="59.5" cy="40.5" r="1" fill="#ffffff" />
|
||||
<circle cx="48" cy="52" r="4" fill="#0f172a" stroke="#38bdf8" strokeWidth="1.5" />
|
||||
<circle cx="48" cy="52" r="2.5" fill="#22d3ee" />
|
||||
|
||||
<defs>
|
||||
<linearGradient id="plasmaFlame" x1="30" y1="78" x2="10" y2="98" gradientUnits="userSpaceOnUse">
|
||||
<stop stopColor="#38bdf8" />
|
||||
<stop offset="0.5" stopColor="#06b6d4" />
|
||||
<stop offset="1" stopColor="transparent" />
|
||||
</linearGradient>
|
||||
<linearGradient id="rocketBodyGradient" x1="78" y1="22" x2="26" y2="74" gradientUnits="userSpaceOnUse">
|
||||
<stop stopColor="#e0f2fe" />
|
||||
<stop offset="0.4" stopColor="#38bdf8" />
|
||||
<stop offset="0.8" stopColor="#0284c7" />
|
||||
<stop offset="1" stopColor="#0f172a" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Top Navigation Bar */}
|
||||
<div className="w-full max-w-6xl mx-auto flex items-center justify-between z-10 pt-2 pb-4">
|
||||
{/* Reusable UI ThemeToggle Component */}
|
||||
<ThemeToggle variant="pill" />
|
||||
|
||||
{/* Back Link Button */}
|
||||
<Link to="/">
|
||||
<Button variant="outline" size="sm" leftIcon={<FiArrowLeft />}>
|
||||
Volver al inicio
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Main Login Card Container */}
|
||||
<div className="my-auto w-full max-w-md mx-auto z-10">
|
||||
<Card variant="glass" padding="lg" className="relative space-y-6 shadow-2xl">
|
||||
|
||||
{/* Subtle Top Border Gradient Accent */}
|
||||
<div className="absolute top-0 left-6 right-6 h-[1.5px] bg-gradient-to-r from-transparent via-cyan-500/60 to-transparent pointer-events-none" />
|
||||
|
||||
{/* Centered Brand Logo & Titles inside Form Card */}
|
||||
<div className="text-center space-y-2 pt-1">
|
||||
<div className="flex justify-center pb-1">
|
||||
<BrandLogo size="lg" showSubtitle={true} />
|
||||
</div>
|
||||
<h1 className="text-2xl sm:text-3xl font-bold tracking-tight pt-2 text-slate-900 dark:text-white">
|
||||
Iniciar Sesión
|
||||
</h1>
|
||||
<p className="text-xs sm:text-sm text-slate-600 dark:text-slate-400">
|
||||
Ingresa tus credenciales para acceder a tu cuenta.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Form */}
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
|
||||
{/* Email field using UI Input */}
|
||||
<div>
|
||||
<Input
|
||||
label="Correo electrónico"
|
||||
type="email"
|
||||
required
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="[email protected]"
|
||||
leftIcon={<FiMail className="text-slate-400" />}
|
||||
/>
|
||||
{isValidEmail && (
|
||||
<div className="flex items-center gap-1 text-[11px] text-emerald-500 font-medium mt-1 animate-fadeIn">
|
||||
<FiCheckCircle className="text-xs" /> Formato válido
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Password field using UI Input (built-in password eye toggle) */}
|
||||
<div>
|
||||
<Input
|
||||
label="Contraseña"
|
||||
type="password"
|
||||
required
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
placeholder="••••••••"
|
||||
leftIcon={<FiLock className="text-slate-400" />}
|
||||
/>
|
||||
<div className="flex justify-end mt-1">
|
||||
<a href="#" className="text-[11px] font-medium text-cyan-600 dark:text-cyan-400 hover:underline transition">
|
||||
¿Olvidaste tu contraseña?
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Remember Me using UI Checkbox */}
|
||||
<div className="pt-0.5">
|
||||
<Checkbox
|
||||
checked={rememberMe}
|
||||
onChange={(e) => setRememberMe(e.target.checked)}
|
||||
label="Recordar sesión"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Submit Button using UI Button */}
|
||||
<Button
|
||||
type="submit"
|
||||
variant="gradient"
|
||||
size="lg"
|
||||
fullWidth
|
||||
isLoading={isLoading}
|
||||
loadingText="Verificando credenciales..."
|
||||
rightIcon={<FiArrowRight className="text-base" />}
|
||||
>
|
||||
Acceder a la cuenta
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
{/* Register Callout */}
|
||||
<div className="text-center text-xs pt-3 border-t border-slate-200 dark:border-slate-800 text-slate-600 dark:text-slate-400">
|
||||
¿Aún no tienes una cuenta?{' '}
|
||||
<Link to="/register" className="text-cyan-600 dark:text-cyan-400 font-semibold hover:underline transition">
|
||||
Regístrate aquí
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Footer info */}
|
||||
<div className="w-full max-w-6xl mx-auto text-center text-[11px] z-10 pt-4 pb-2 text-slate-500 dark:text-slate-500">
|
||||
© 2026 RyCourses. Todos los derechos reservados.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,261 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useNavigate, Link } from 'react-router';
|
||||
import { BrandLogo } from '../components/common/BrandLogo';
|
||||
import {
|
||||
Button,
|
||||
Input,
|
||||
Checkbox,
|
||||
ThemeToggle,
|
||||
Card
|
||||
} from '../components/ui';
|
||||
import {
|
||||
FiMail,
|
||||
FiLock,
|
||||
FiUser,
|
||||
FiArrowRight,
|
||||
FiArrowLeft,
|
||||
FiCheckCircle
|
||||
} from 'react-icons/fi';
|
||||
import { FaGraduationCap, FaChalkboardTeacher } from 'react-icons/fa';
|
||||
import type { User } from '../types/course';
|
||||
|
||||
interface RegisterViewProps {
|
||||
onShowToast: (msg: string) => void;
|
||||
onLogin: (user: User) => void;
|
||||
}
|
||||
|
||||
export const RegisterView: React.FC<RegisterViewProps> = ({ onShowToast, onLogin }) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [name, setName] = useState('');
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [accountType, setAccountType] = useState<'buyer' | 'seller'>('buyer');
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const isValidEmail = email.includes('@') && email.includes('.');
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!name || !email || !password) return;
|
||||
|
||||
setIsLoading(true);
|
||||
setTimeout(() => {
|
||||
const newUser: User = {
|
||||
id: Math.random().toString(36).substring(2, 11),
|
||||
name,
|
||||
email,
|
||||
role: accountType,
|
||||
avatar: accountType === 'seller'
|
||||
? 'https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?w=100&auto=format&fit=crop&q=80'
|
||||
: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=100&auto=format&fit=crop&q=80',
|
||||
};
|
||||
onLogin(newUser);
|
||||
onShowToast(`¡Cuenta de ${accountType === 'buyer' ? 'Estudiante' : 'Instructor'} creada con éxito!`);
|
||||
setIsLoading(false);
|
||||
navigate(accountType === 'seller' ? '/seller' : '/courses');
|
||||
}, 450);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col justify-between font-sans selection:bg-cyan-500 relative overflow-hidden p-6 sm:p-10 lg:p-12 transition-colors duration-300 bg-slate-50 dark:bg-slate-950 text-slate-900 dark:text-slate-100">
|
||||
|
||||
{/* Top-Right Background Radial Gradient Glow */}
|
||||
<div
|
||||
className="absolute -top-32 -right-32 w-[600px] sm:w-[850px] h-[600px] sm:h-[850px] rounded-full pointer-events-none opacity-30 dark:opacity-35 blur-[150px]"
|
||||
style={{
|
||||
background: 'radial-gradient(circle at center, rgba(6, 182, 212, 0.45) 0%, rgba(37, 99, 235, 0.3) 45%, transparent 70%)'
|
||||
}}
|
||||
/>
|
||||
<div className="absolute bottom-0 left-0 w-[450px] h-[450px] rounded-full pointer-events-none opacity-15 dark:opacity-20 blur-[160px] bg-blue-900/30" />
|
||||
<div className="absolute inset-0 bg-[radial-gradient(#94a3b8_1px,transparent_1px)] dark:bg-[radial-gradient(#334155_1px,transparent_1px)] [background-size:24px_24px] opacity-20 dark:opacity-10 pointer-events-none" />
|
||||
|
||||
{/* Interactive Background Atmosphere: Twinkling Star Particles & Flying Space Shuttle */}
|
||||
<div className="absolute inset-0 pointer-events-none overflow-hidden z-0">
|
||||
<div className="absolute top-12 left-1/6 w-1.5 h-1.5 rounded-full bg-cyan-400 animate-twinkle opacity-75" style={{ animationDelay: '0s' }} />
|
||||
<div className="absolute top-1/4 right-1/5 w-2 h-2 rounded-full bg-blue-400 animate-twinkle opacity-85" style={{ animationDelay: '1.4s' }} />
|
||||
<div className="absolute top-1/3 left-1/4 w-1 h-1 rounded-full bg-teal-400 animate-twinkle opacity-70" style={{ animationDelay: '2.1s' }} />
|
||||
<div className="absolute top-1/2 right-1/3 w-1.5 h-1.5 rounded-full bg-cyan-400 animate-twinkle opacity-80" style={{ animationDelay: '0.6s' }} />
|
||||
<div className="absolute top-2/3 left-1/5 w-2 h-2 rounded-full bg-cyan-500 animate-twinkle opacity-75" style={{ animationDelay: '1.8s' }} />
|
||||
|
||||
{/* Crossing Space Shuttle Rocket */}
|
||||
<div className="absolute top-0 left-0 animate-spaceship">
|
||||
<svg className="w-16 h-16 sm:w-20 sm:h-20 filter drop-shadow-[0_0_25px_rgba(6,182,212,0.8)]" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M22 78L10 90L26 84L14 98L30 86L22 78Z" fill="url(#plasmaFlameReg)" className="animate-pulse" />
|
||||
<path d="M78 22C62 26 42 42 34 58L26 62L38 74L42 66C58 58 74 38 78 22Z" fill="url(#rocketBodyGradientReg)" stroke="#38bdf8" strokeWidth="2" strokeLinejoin="round" />
|
||||
<path d="M78 22C74 23 68 28 64 34L66 36C72 32 77 26 78 22Z" fill="#22d3ee" />
|
||||
<path d="M34 58L16 64L24 50L30 52Z" fill="#0284c7" stroke="#38bdf8" strokeWidth="1.5" strokeLinejoin="round" />
|
||||
<path d="M42 66L48 84L50 76L46 70Z" fill="#0284c7" stroke="#38bdf8" strokeWidth="1.5" strokeLinejoin="round" />
|
||||
<circle cx="58" cy="42" r="5" fill="#0f172a" stroke="#38bdf8" strokeWidth="1.5" />
|
||||
<circle cx="58" cy="42" r="3.5" fill="#22d3ee" />
|
||||
<circle cx="59.5" cy="40.5" r="1" fill="#ffffff" />
|
||||
<circle cx="48" cy="52" r="4" fill="#0f172a" stroke="#38bdf8" strokeWidth="1.5" />
|
||||
<circle cx="48" cy="52" r="2.5" fill="#22d3ee" />
|
||||
|
||||
<defs>
|
||||
<linearGradient id="plasmaFlameReg" x1="30" y1="78" x2="10" y2="98" gradientUnits="userSpaceOnUse">
|
||||
<stop stopColor="#38bdf8" />
|
||||
<stop offset="0.5" stopColor="#06b6d4" />
|
||||
<stop offset="1" stopColor="transparent" />
|
||||
</linearGradient>
|
||||
<linearGradient id="rocketBodyGradientReg" x1="78" y1="22" x2="26" y2="74" gradientUnits="userSpaceOnUse">
|
||||
<stop stopColor="#e0f2fe" />
|
||||
<stop offset="0.4" stopColor="#38bdf8" />
|
||||
<stop offset="0.8" stopColor="#0284c7" />
|
||||
<stop offset="1" stopColor="#0f172a" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Top Navigation Bar */}
|
||||
<div className="w-full max-w-6xl mx-auto flex items-center justify-between z-10 pt-2 pb-4">
|
||||
{/* Reusable UI ThemeToggle Component */}
|
||||
<ThemeToggle variant="pill" />
|
||||
|
||||
{/* Back Link Button */}
|
||||
<Link to="/">
|
||||
<Button variant="outline" size="sm" leftIcon={<FiArrowLeft />}>
|
||||
Volver al inicio
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Main Centered Register Card Container */}
|
||||
<div className="my-auto w-full max-w-md mx-auto z-10">
|
||||
<Card variant="glass" padding="lg" className="relative space-y-5 shadow-2xl">
|
||||
|
||||
{/* Subtle Top Border Gradient Accent */}
|
||||
<div className="absolute top-0 left-6 right-6 h-[1.5px] bg-gradient-to-r from-transparent via-cyan-500/60 to-transparent pointer-events-none" />
|
||||
|
||||
{/* Centered Brand Logo & Header Titles inside Form Card */}
|
||||
<div className="text-center space-y-2 pt-1">
|
||||
<div className="flex justify-center pb-1">
|
||||
<BrandLogo size="lg" showSubtitle={true} />
|
||||
</div>
|
||||
<h1 className="text-2xl sm:text-3xl font-bold tracking-tight pt-2 text-slate-900 dark:text-white">
|
||||
Crear Cuenta
|
||||
</h1>
|
||||
<p className="text-xs sm:text-sm text-slate-600 dark:text-slate-400">
|
||||
Crea tu perfil en RyCourses para comenzar tu experiencia.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Account type switcher */}
|
||||
<div className="grid grid-cols-2 gap-2 p-1 rounded-xl border bg-slate-100 dark:bg-slate-950/80 border-slate-200 dark:border-slate-800">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setAccountType('buyer')}
|
||||
className={`flex items-center justify-center gap-2 py-2 rounded-lg text-xs font-medium transition-all cursor-pointer ${
|
||||
accountType === 'buyer'
|
||||
? 'bg-white dark:bg-slate-800 text-cyan-600 dark:text-cyan-400 border border-slate-200 dark:border-slate-700 font-semibold shadow-sm'
|
||||
: 'text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white'
|
||||
}`}
|
||||
>
|
||||
<FaGraduationCap className="text-sm" /> Estudiante
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setAccountType('seller')}
|
||||
className={`flex items-center justify-center gap-2 py-2 rounded-lg text-xs font-medium transition-all cursor-pointer ${
|
||||
accountType === 'seller'
|
||||
? 'bg-white dark:bg-slate-800 text-violet-600 dark:text-violet-400 border border-slate-200 dark:border-slate-700 font-semibold shadow-sm'
|
||||
: 'text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white'
|
||||
}`}
|
||||
>
|
||||
<FaChalkboardTeacher className="text-sm" /> Instructor
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Form */}
|
||||
<form onSubmit={handleSubmit} className="space-y-3.5">
|
||||
<div>
|
||||
<Input
|
||||
label="Nombre Completo"
|
||||
type="text"
|
||||
required
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
placeholder="Ej. Sofía Rodríguez"
|
||||
leftIcon={<FiUser className="text-slate-400" />}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Input
|
||||
label="Correo Electrónico"
|
||||
type="email"
|
||||
required
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="[email protected]"
|
||||
leftIcon={<FiMail className="text-slate-400" />}
|
||||
/>
|
||||
{isValidEmail && (
|
||||
<div className="flex items-center gap-1 text-[11px] text-emerald-500 font-medium mt-1 animate-fadeIn">
|
||||
<FiCheckCircle className="text-xs" /> Formato válido
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Input
|
||||
label="Contraseña"
|
||||
type="password"
|
||||
required
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
placeholder="Mínimo 8 caracteres"
|
||||
leftIcon={<FiLock className="text-slate-400" />}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="pt-0.5">
|
||||
<Checkbox
|
||||
checked={true}
|
||||
readOnly
|
||||
label={
|
||||
<span>
|
||||
Acepto los{' '}
|
||||
<Link to="/terms" target="_blank" className="text-cyan-600 dark:text-cyan-400 font-semibold hover:underline">
|
||||
Términos de Servicio y Políticas de Privacidad
|
||||
</Link>.
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
variant="gradient"
|
||||
size="lg"
|
||||
fullWidth
|
||||
isLoading={isLoading}
|
||||
loadingText="Creando perfil..."
|
||||
rightIcon={<FiArrowRight className="text-base" />}
|
||||
>
|
||||
Crear cuenta como {accountType === 'buyer' ? 'Estudiante' : 'Instructor'}
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
{/* Login Link */}
|
||||
<div className="text-center text-xs pt-3 border-t border-slate-200 dark:border-slate-800 text-slate-600 dark:text-slate-400">
|
||||
¿Ya tienes una cuenta registrada?{' '}
|
||||
<Link to="/login" className="text-cyan-600 dark:text-cyan-400 font-semibold hover:underline transition">
|
||||
Inicia sesión aquí
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Footer info */}
|
||||
<div className="w-full max-w-6xl mx-auto text-center text-[11px] z-10 pt-4 pb-2 text-slate-500 dark:text-slate-500">
|
||||
© 2026 RyCourses. Todos los derechos reservados.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,159 +0,0 @@
|
||||
import React from 'react';
|
||||
import { useNavigate, Link } from 'react-router';
|
||||
import type { Course, User } from '../types/course';
|
||||
import { SellerHero } from '../components/seller/SellerHero';
|
||||
import { SellerCalculator } from '../components/seller/SellerCalculator';
|
||||
import { Card, Button, Badge } from '../components/ui';
|
||||
import {
|
||||
FiDollarSign,
|
||||
FiGlobe,
|
||||
FiVideo,
|
||||
FiArrowRight,
|
||||
FiHelpCircle
|
||||
} from 'react-icons/fi';
|
||||
|
||||
interface SellerViewProps {
|
||||
currentUser: User | null;
|
||||
courses: Course[];
|
||||
onPreviewCourse: (course: Course) => void;
|
||||
onDeleteCourse: (courseId: string) => void;
|
||||
}
|
||||
|
||||
export const SellerView: React.FC<SellerViewProps> = ({
|
||||
currentUser,
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleCreateCourseClick = () => {
|
||||
if (!currentUser) {
|
||||
navigate('/login');
|
||||
} else {
|
||||
navigate('/seller/create');
|
||||
}
|
||||
};
|
||||
|
||||
const handleScrollToCalculator = () => {
|
||||
const el = document.getElementById('seller-calculator');
|
||||
if (el) {
|
||||
el.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-16 pb-20 selection:bg-cyan-500">
|
||||
|
||||
{/* 1. Hero Landing Section */}
|
||||
<SellerHero
|
||||
onOpenCreateCourse={handleCreateCourseClick}
|
||||
onScrollToCalculator={handleScrollToCalculator}
|
||||
/>
|
||||
|
||||
{/* 2. Direct Navigation CTA to Teacher Portal */}
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="p-8 rounded-3xl bg-gradient-to-r from-slate-900 via-indigo-950 to-slate-900 border border-slate-800 flex flex-col md:flex-row items-center justify-between gap-6 shadow-2xl">
|
||||
<div className="space-y-2 text-center md:text-left">
|
||||
<Badge variant="primary" size="md">¿Ya eres instructor registrado?</Badge>
|
||||
<h2 className="text-xl sm:text-2xl font-bold text-white">Accede directamente a tu Panel de Docente</h2>
|
||||
<p className="text-xs sm:text-sm text-slate-400">Gestiona tus cursos publicados, ingresos acumulados y responde a tus alumnos.</p>
|
||||
</div>
|
||||
|
||||
<Link to={currentUser ? "/teachers" : "/login"}>
|
||||
<Button variant="gradient" size="lg" rightIcon={<FiArrowRight />}>
|
||||
{currentUser ? "Ir a mi Panel Docente" : "Iniciar Sesión como Instructor"}
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 3. Why Teach on RyCourses Benefits Grid */}
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 space-y-10">
|
||||
<div className="text-center space-y-3 max-w-2xl mx-auto">
|
||||
<Badge variant="primary" size="md">Beneficios Únicos</Badge>
|
||||
<h2 className="text-3xl sm:text-4xl font-extrabold text-white tracking-tight">
|
||||
¿Por qué enseñar en RyCourses?
|
||||
</h2>
|
||||
<p className="text-xs sm:text-sm text-slate-400">
|
||||
Diseñamos las herramientas necesarias para que te enfoques en crear contenido extraordinario mientras nosotros nos encargamos de todo lo demás.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<Card variant="elevated" padding="lg" className="space-y-4">
|
||||
<div className="w-12 h-12 rounded-2xl bg-emerald-500/10 text-emerald-400 flex items-center justify-center text-2xl">
|
||||
<FiDollarSign />
|
||||
</div>
|
||||
<h3 className="text-lg font-bold text-white">80% de Comisión Directa</h3>
|
||||
<p className="text-xs text-slate-400 leading-relaxed">
|
||||
Conserva hasta el 80% de cada venta generada. Pagos automáticos semanales procesados a tu cuenta bancaria o PayPal.
|
||||
</p>
|
||||
</Card>
|
||||
|
||||
<Card variant="elevated" padding="lg" className="space-y-4">
|
||||
<div className="w-12 h-12 rounded-2xl bg-cyan-500/10 text-cyan-400 flex items-center justify-center text-2xl">
|
||||
<FiGlobe />
|
||||
</div>
|
||||
<h3 className="text-lg font-bold text-white">Audiencia Global</h3>
|
||||
<p className="text-xs text-slate-400 leading-relaxed">
|
||||
Llega a más de 50,000 estudiantes activos en Latinoamérica y España buscando aprender nuevas tecnologías y habilidades.
|
||||
</p>
|
||||
</Card>
|
||||
|
||||
<Card variant="elevated" padding="lg" className="space-y-4">
|
||||
<div className="w-12 h-12 rounded-2xl bg-violet-500/10 text-violet-400 flex items-center justify-center text-2xl">
|
||||
<FiVideo />
|
||||
</div>
|
||||
<h3 className="text-lg font-bold text-white">Reproductor HD & CDN Ultra Rápido</h3>
|
||||
<p className="text-xs text-slate-400 leading-relaxed">
|
||||
Tus videos se transmiten sin interrupciones con encriptación anti-descarga y calidad adaptable en cualquier dispositivo.
|
||||
</p>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 4. Earnings Calculator Section */}
|
||||
<div id="seller-calculator">
|
||||
<SellerCalculator
|
||||
onStartCourse={handleCreateCourseClick}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 5. Frequently Asked Questions */}
|
||||
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 space-y-8">
|
||||
<div className="text-center space-y-3">
|
||||
<Badge variant="secondary" size="md">Preguntas Frecuentes</Badge>
|
||||
<h2 className="text-2xl sm:text-3xl font-extrabold text-white">Todo lo que necesitas saber</h2>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<Card variant="elevated" padding="md" className="space-y-2">
|
||||
<h4 className="font-bold text-sm text-white flex items-center gap-2">
|
||||
<FiHelpCircle className="text-cyan-400" /> ¿Tengo que pagar alguna tarifa para subir mi curso?
|
||||
</h4>
|
||||
<p className="text-xs text-slate-400 pl-6">
|
||||
No. Publicar tu curso en RyCourses es 100% gratuito. Solo cobramos una comisión cuando realizas una venta efectiva.
|
||||
</p>
|
||||
</Card>
|
||||
|
||||
<Card variant="elevated" padding="md" className="space-y-2">
|
||||
<h4 className="font-bold text-sm text-white flex items-center gap-2">
|
||||
<FiHelpCircle className="text-cyan-400" /> ¿Quién conserva los derechos de propiedad intelectual de mi contenido?
|
||||
</h4>
|
||||
<p className="text-xs text-slate-400 pl-6">
|
||||
Tú mantienes el 100% de la propiedad intelectual y derechos de autor de todos los videos y materiales que crees.
|
||||
</p>
|
||||
</Card>
|
||||
|
||||
<Card variant="elevated" padding="md" className="space-y-2">
|
||||
<h4 className="font-bold text-sm text-white flex items-center gap-2">
|
||||
<FiHelpCircle className="text-cyan-400" /> ¿Cómo recibo mis pagos?
|
||||
</h4>
|
||||
<p className="text-xs text-slate-400 pl-6">
|
||||
Puedes solicitar retiros en cualquier momento desde tu panel de instructor. Depositamos vía Stripe Connect o PayPal.
|
||||
</p>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,489 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import type { Course } from '../types/course';
|
||||
import { BrandLogo } from '../components/common/BrandLogo';
|
||||
import {
|
||||
Card,
|
||||
Button,
|
||||
Badge,
|
||||
Input,
|
||||
ThemeToggle
|
||||
} from '../components/ui';
|
||||
import {
|
||||
FiGrid,
|
||||
FiBookOpen,
|
||||
FiDollarSign,
|
||||
FiUsers,
|
||||
FiStar,
|
||||
FiPlusCircle,
|
||||
FiTrendingUp,
|
||||
FiSearch,
|
||||
FiEye,
|
||||
FiTrash2,
|
||||
FiMessageSquare,
|
||||
FiAward,
|
||||
FiMenu,
|
||||
FiX,
|
||||
FiArrowLeft,
|
||||
FiDownload
|
||||
} from 'react-icons/fi';
|
||||
|
||||
interface TeachersViewProps {
|
||||
courses: Course[];
|
||||
onOpenCreateCourse: () => void;
|
||||
onPreviewCourse: (course: Course) => void;
|
||||
onDeleteCourse: (courseId: string) => void;
|
||||
onShowToast: (msg: string) => void;
|
||||
}
|
||||
|
||||
export const TeachersView: React.FC<TeachersViewProps> = ({
|
||||
courses,
|
||||
onOpenCreateCourse,
|
||||
onPreviewCourse,
|
||||
onDeleteCourse,
|
||||
onShowToast,
|
||||
}) => {
|
||||
const [activeTab, setActiveTab] = useState<'overview' | 'my-courses' | 'payouts' | 'reviews'>('overview');
|
||||
const [sidebarOpen, setSidebarOpen] = useState(false);
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [payoutRequested, setPayoutRequested] = useState(false);
|
||||
|
||||
// Filter instructor courses
|
||||
const instructorCourses = courses.filter((c) =>
|
||||
c.title.toLowerCase().includes(searchTerm.toLowerCase()) || c.category.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
);
|
||||
|
||||
const totalEarnings = 14280.50;
|
||||
const pendingPayout = 2450.00;
|
||||
const totalStudents = courses.reduce((acc, curr) => acc + curr.studentsCount, 0);
|
||||
const avgRating = (courses.reduce((acc, curr) => acc + curr.rating, 0) / (courses.length || 1)).toFixed(1);
|
||||
|
||||
const handleRequestPayout = () => {
|
||||
setPayoutRequested(true);
|
||||
onShowToast('¡Solicitud de retiro de $2,450.00 recibida y en proceso!');
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen transition-colors duration-300 bg-slate-50 dark:bg-slate-950 text-slate-900 dark:text-slate-100 flex flex-col md:flex-row font-sans selection:bg-cyan-500">
|
||||
|
||||
{/* Mobile Sidebar Overlay */}
|
||||
{sidebarOpen && (
|
||||
<div
|
||||
className="fixed inset-0 bg-slate-950/80 backdrop-blur-sm z-40 md:hidden"
|
||||
onClick={() => setSidebarOpen(false)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 1. Left Sidebar Navigation */}
|
||||
<aside className={`
|
||||
fixed md:sticky top-0 left-0 h-screen w-72 bg-white dark:bg-slate-900 border-r border-slate-200 dark:border-slate-800 z-50 flex flex-col justify-between p-5 transition-transform duration-300 shadow-xl
|
||||
${sidebarOpen ? 'translate-x-0' : '-translate-x-full md:translate-x-0'}
|
||||
`}>
|
||||
<div className="space-y-6">
|
||||
{/* Sidebar Brand Header */}
|
||||
<div className="flex items-center justify-between pb-3 border-b border-slate-200 dark:border-slate-800">
|
||||
<Link to="/" className="hover:opacity-90 transition">
|
||||
<BrandLogo size="sm" showSubtitle={false} />
|
||||
</Link>
|
||||
|
||||
<button
|
||||
onClick={() => setSidebarOpen(false)}
|
||||
className="md:hidden p-1.5 rounded-lg text-slate-500 hover:text-slate-900 dark:text-slate-400 dark:hover:text-white"
|
||||
>
|
||||
<FiX className="text-xl" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Teacher Badge Pill */}
|
||||
<div className="px-3 py-2 rounded-xl bg-cyan-500/10 border border-cyan-500/25 flex items-center gap-2.5">
|
||||
<div className="w-7 h-7 rounded-lg bg-cyan-500/20 text-cyan-600 dark:text-cyan-400 flex items-center justify-center text-sm font-bold shrink-0">
|
||||
T
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs font-bold text-slate-900 dark:text-white leading-none">Panel Docente</p>
|
||||
<p className="text-[10px] text-cyan-600 dark:text-cyan-400 font-medium mt-0.5">Instructores & Creadores</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Quick Create Course CTA */}
|
||||
<Button
|
||||
variant="gradient"
|
||||
size="md"
|
||||
fullWidth
|
||||
leftIcon={<FiPlusCircle />}
|
||||
onClick={onOpenCreateCourse}
|
||||
className="shadow-lg shadow-cyan-500/20"
|
||||
>
|
||||
Crear Nuevo Curso
|
||||
</Button>
|
||||
|
||||
{/* Navigation Links */}
|
||||
<nav className="space-y-1.5">
|
||||
<button
|
||||
onClick={() => { setActiveTab('overview'); setSidebarOpen(false); }}
|
||||
className={`w-full flex items-center gap-3 px-3.5 py-3 rounded-xl text-xs font-semibold transition-all ${
|
||||
activeTab === 'overview'
|
||||
? 'bg-slate-100 dark:bg-slate-800 text-cyan-600 dark:text-cyan-400 border border-slate-200 dark:border-cyan-500/30 shadow-sm'
|
||||
: 'text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white hover:bg-slate-100 dark:hover:bg-slate-800/60'
|
||||
}`}
|
||||
>
|
||||
<FiGrid className="text-base" />
|
||||
<span>Resumen & Métricas</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => { setActiveTab('my-courses'); setSidebarOpen(false); }}
|
||||
className={`w-full flex items-center justify-between px-3.5 py-3 rounded-xl text-xs font-semibold transition-all ${
|
||||
activeTab === 'my-courses'
|
||||
? 'bg-slate-100 dark:bg-slate-800 text-cyan-600 dark:text-cyan-400 border border-slate-200 dark:border-cyan-500/30 shadow-sm'
|
||||
: 'text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white hover:bg-slate-100 dark:hover:bg-slate-800/60'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<FiBookOpen className="text-base" />
|
||||
<span>Mis Cursos</span>
|
||||
</div>
|
||||
<Badge variant="primary" size="sm">{courses.length}</Badge>
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => { setActiveTab('payouts'); setSidebarOpen(false); }}
|
||||
className={`w-full flex items-center justify-between px-3.5 py-3 rounded-xl text-xs font-semibold transition-all ${
|
||||
activeTab === 'payouts'
|
||||
? 'bg-slate-100 dark:bg-slate-800 text-cyan-600 dark:text-cyan-400 border border-slate-200 dark:border-cyan-500/30 shadow-sm'
|
||||
: 'text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white hover:bg-slate-100 dark:hover:bg-slate-800/60'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<FiDollarSign className="text-base" />
|
||||
<span>Ganancias & Retiros</span>
|
||||
</div>
|
||||
<Badge variant="success" size="sm">${pendingPayout}</Badge>
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => { setActiveTab('reviews'); setSidebarOpen(false); }}
|
||||
className={`w-full flex items-center gap-3 px-3.5 py-3 rounded-xl text-xs font-semibold transition-all ${
|
||||
activeTab === 'reviews'
|
||||
? 'bg-slate-100 dark:bg-slate-800 text-cyan-600 dark:text-cyan-400 border border-slate-200 dark:border-cyan-500/30 shadow-sm'
|
||||
: 'text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white hover:bg-slate-100 dark:hover:bg-slate-800/60'
|
||||
}`}
|
||||
>
|
||||
<FiMessageSquare className="text-base" />
|
||||
<span>Reseñas de Alumnos</span>
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
{/* Sidebar Footer */}
|
||||
<div className="pt-4 border-t border-slate-200 dark:border-slate-800 space-y-3">
|
||||
<div className="flex items-center justify-between px-2">
|
||||
<span className="text-xs text-slate-600 dark:text-slate-400 font-medium">Modo</span>
|
||||
<ThemeToggle variant="pill" size="sm" />
|
||||
</div>
|
||||
|
||||
<Link to="/">
|
||||
<Button variant="outline" size="sm" fullWidth leftIcon={<FiArrowLeft />}>
|
||||
Volver al inicio
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
{/* 2. Main Content Area */}
|
||||
<div className="flex-1 flex flex-col min-w-0">
|
||||
|
||||
{/* Top Header Bar */}
|
||||
<header className="sticky top-0 z-30 h-16 bg-white/90 dark:bg-slate-900/90 border-b border-slate-200 dark:border-slate-800 backdrop-blur-xl px-4 sm:px-8 flex items-center justify-between gap-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
onClick={() => setSidebarOpen(true)}
|
||||
className="md:hidden text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white p-2"
|
||||
>
|
||||
<FiMenu className="text-xl" />
|
||||
</button>
|
||||
<h1 className="text-base sm:text-lg font-bold text-slate-900 dark:text-white tracking-tight">
|
||||
{activeTab === 'overview' && 'Resumen del Instructor'}
|
||||
{activeTab === 'my-courses' && 'Catálogo de Mis Cursos'}
|
||||
{activeTab === 'payouts' && 'Finanzas & Solicitud de Retiros'}
|
||||
{activeTab === 'reviews' && 'Calificaciones & Feedback de Estudiantes'}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<Badge variant="primary" size="md">
|
||||
<span className="w-2 h-2 rounded-full bg-cyan-400 animate-pulse mr-1" />
|
||||
Docente Verificado
|
||||
</Badge>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Dynamic Content Views */}
|
||||
<main className="p-4 sm:p-8 space-y-8 flex-1">
|
||||
|
||||
{/* TAB 1: OVERVIEW */}
|
||||
{activeTab === 'overview' && (
|
||||
<div className="space-y-8">
|
||||
|
||||
{/* Stat Cards Grid */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-5">
|
||||
<Card variant="elevated" padding="md" className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs text-slate-500 dark:text-slate-400 font-medium">Ingresos Totales</span>
|
||||
<div className="w-9 h-9 rounded-xl bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 flex items-center justify-center text-lg">
|
||||
<FiDollarSign />
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-2xl font-black text-slate-900 dark:text-white font-mono">${totalEarnings.toLocaleString()}</div>
|
||||
<div className="text-[11px] text-emerald-600 dark:text-emerald-400 font-semibold flex items-center gap-1">
|
||||
<FiTrendingUp /> +18.4% este mes
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card variant="elevated" padding="md" className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs text-slate-500 dark:text-slate-400 font-medium">Estudiantes Inscritos</span>
|
||||
<div className="w-9 h-9 rounded-xl bg-cyan-500/10 text-cyan-600 dark:text-cyan-400 flex items-center justify-center text-lg">
|
||||
<FiUsers />
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-2xl font-black text-slate-900 dark:text-white font-mono">{totalStudents.toLocaleString()}</div>
|
||||
<div className="text-[11px] text-slate-500 dark:text-slate-400">En {courses.length} cursos activos</div>
|
||||
</Card>
|
||||
|
||||
<Card variant="elevated" padding="md" className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs text-slate-500 dark:text-slate-400 font-medium">Calificación Promedio</span>
|
||||
<div className="w-9 h-9 rounded-xl bg-amber-500/10 text-amber-600 dark:text-amber-400 flex items-center justify-center text-lg">
|
||||
<FiStar />
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-2xl font-black text-slate-900 dark:text-white font-mono">{avgRating} / 5.0</div>
|
||||
<div className="text-[11px] text-amber-600 dark:text-amber-400 font-medium">Basado en 340 reseñas</div>
|
||||
</Card>
|
||||
|
||||
<Card variant="elevated" padding="md" className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs text-slate-500 dark:text-slate-400 font-medium">Saldo Disponible</span>
|
||||
<div className="w-9 h-9 rounded-xl bg-violet-500/10 text-violet-600 dark:text-violet-400 flex items-center justify-center text-lg">
|
||||
<FiAward />
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-2xl font-black text-slate-900 dark:text-white font-mono">${pendingPayout.toLocaleString()}</div>
|
||||
<Button
|
||||
variant="primary"
|
||||
size="xs"
|
||||
onClick={handleRequestPayout}
|
||||
disabled={payoutRequested}
|
||||
className="mt-1"
|
||||
>
|
||||
{payoutRequested ? 'Retiro solicitado' : 'Solicitar Retiro'}
|
||||
</Button>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Course Performance Summary */}
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="text-sm font-extrabold text-slate-900 dark:text-white">Rendimiento por Curso</h3>
|
||||
<Button variant="ghost" size="xs" onClick={() => setActiveTab('my-courses')}>
|
||||
Ver todos los cursos
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5">
|
||||
{courses.slice(0, 3).map((c) => (
|
||||
<Card key={c.id} variant="interactive" padding="md" className="space-y-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<img src={c.thumbnail} alt={c.title} className="w-14 h-10 rounded-lg object-cover border border-slate-300 dark:border-slate-700" />
|
||||
<div className="min-w-0 flex-1">
|
||||
<h4 className="font-bold text-xs text-slate-900 dark:text-white truncate">{c.title}</h4>
|
||||
<span className="text-[10px] text-cyan-600 dark:text-cyan-400">{c.category}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-2 pt-2 border-t border-slate-200 dark:border-slate-800 text-xs">
|
||||
<div>
|
||||
<span className="text-[10px] text-slate-500 dark:text-slate-400 block">Estudiantes</span>
|
||||
<span className="font-bold font-mono text-slate-800 dark:text-slate-200">{c.studentsCount}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-[10px] text-slate-500 dark:text-slate-400 block">Ingresos</span>
|
||||
<span className="font-bold font-mono text-emerald-600 dark:text-emerald-400">${(c.price * c.studentsCount * 0.8).toFixed(2)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* TAB 2: MY COURSES */}
|
||||
{activeTab === 'my-courses' && (
|
||||
<div className="space-y-6">
|
||||
|
||||
{/* Search & Filter Bar */}
|
||||
<div className="flex flex-col sm:flex-row items-center justify-between gap-4 bg-white dark:bg-slate-900 p-4 rounded-2xl border border-slate-200 dark:border-slate-800 shadow-sm">
|
||||
<div className="w-full sm:max-w-md">
|
||||
<Input
|
||||
placeholder="Buscar entre mis cursos..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
leftIcon={<FiSearch className="text-slate-400" />}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button variant="gradient" size="md" leftIcon={<FiPlusCircle />} onClick={onOpenCreateCourse}>
|
||||
Crear Nuevo Curso
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Courses Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{instructorCourses.map((c) => (
|
||||
<Card key={c.id} variant="elevated" padding="none" className="overflow-hidden flex flex-col justify-between">
|
||||
<div>
|
||||
<div className="relative h-44">
|
||||
<img src={c.thumbnail} alt={c.title} className="w-full h-full object-cover" />
|
||||
<div className="absolute top-3 right-3">
|
||||
<Badge variant="success" size="sm">Publicado</Badge>
|
||||
</div>
|
||||
<div className="absolute bottom-3 left-3">
|
||||
<Badge variant="secondary" size="sm">{c.category}</Badge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-5 space-y-3">
|
||||
<h3 className="font-bold text-sm text-slate-900 dark:text-white line-clamp-2">{c.title}</h3>
|
||||
<div className="flex items-center justify-between text-xs text-slate-500 dark:text-slate-400 pt-1">
|
||||
<span className="flex items-center gap-1"><FiUsers className="text-cyan-600 dark:text-cyan-400" /> {c.studentsCount} alumnos</span>
|
||||
<span className="flex items-center gap-1"><FiStar className="text-amber-500 dark:text-amber-400" /> {c.rating}</span>
|
||||
<span className="font-mono font-bold text-slate-900 dark:text-white">${c.price.toFixed(2)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-4 bg-slate-100/80 dark:bg-slate-950/60 border-t border-slate-200 dark:border-slate-800 flex items-center justify-between gap-2">
|
||||
<Button variant="outline" size="sm" leftIcon={<FiEye />} onClick={() => onPreviewCourse(c)}>
|
||||
Ver Vista Previa
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm" className="text-rose-600 dark:text-rose-400 hover:text-rose-700" onClick={() => onDeleteCourse(c.id)}>
|
||||
<FiTrash2 />
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* TAB 3: PAYOUTS */}
|
||||
{activeTab === 'payouts' && (
|
||||
<div className="space-y-6">
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<Card variant="elevated" padding="lg" className="space-y-4 md:col-span-1">
|
||||
<h3 className="font-extrabold text-base text-slate-900 dark:text-white">Solicitud de Retiro</h3>
|
||||
<div className="space-y-2">
|
||||
<span className="text-xs text-slate-500 dark:text-slate-400">Saldo Disponible para Transferencia</span>
|
||||
<div className="text-3xl font-black text-emerald-600 dark:text-emerald-400 font-mono">${pendingPayout.toFixed(2)}</div>
|
||||
</div>
|
||||
|
||||
<p className="text-xs text-slate-500 dark:text-slate-400">
|
||||
Las transferencias se procesan automáticamente a tu cuenta bancaria registrada o cuenta PayPal asociada.
|
||||
</p>
|
||||
|
||||
<Button
|
||||
variant="gradient"
|
||||
fullWidth
|
||||
size="md"
|
||||
onClick={handleRequestPayout}
|
||||
disabled={payoutRequested}
|
||||
>
|
||||
{payoutRequested ? 'Solicitud Enviada' : 'Transferir a mi Cuenta ($2,450.00)'}
|
||||
</Button>
|
||||
</Card>
|
||||
|
||||
{/* History Table */}
|
||||
<Card variant="elevated" padding="none" className="md:col-span-2 overflow-hidden">
|
||||
<div className="p-5 border-b border-slate-200 dark:border-slate-800 flex items-center justify-between">
|
||||
<h3 className="font-bold text-sm text-slate-900 dark:text-white">Historial Reciente de Retiros</h3>
|
||||
<Button variant="outline" size="xs" leftIcon={<FiDownload />}>
|
||||
Exportar CSV
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<table className="w-full text-left text-xs text-slate-700 dark:text-slate-300">
|
||||
<thead className="bg-slate-100 dark:bg-slate-950 text-slate-600 dark:text-slate-400 uppercase tracking-wider font-semibold border-b border-slate-200 dark:border-slate-800">
|
||||
<tr>
|
||||
<th className="p-4">Fecha</th>
|
||||
<th className="p-4">Monto</th>
|
||||
<th className="p-4">Método</th>
|
||||
<th className="p-4">Estado</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-slate-200 dark:divide-slate-800/80">
|
||||
<tr>
|
||||
<td className="p-4 font-mono">15 Feb 2026</td>
|
||||
<td className="p-4 font-mono font-bold text-emerald-600 dark:text-emerald-400">$3,420.00</td>
|
||||
<td className="p-4">Cuenta IBAN ****4921</td>
|
||||
<td className="p-4"><Badge variant="success" size="sm">Completado</Badge></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="p-4 font-mono">01 Feb 2026</td>
|
||||
<td className="p-4 font-mono font-bold text-emerald-600 dark:text-emerald-400">$4,180.00</td>
|
||||
<td className="p-4">PayPal (carlos@rycourses.pro)</td>
|
||||
<td className="p-4"><Badge variant="success" size="sm">Completado</Badge></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* TAB 4: REVIEWS */}
|
||||
{activeTab === 'reviews' && (
|
||||
<div className="space-y-4 max-w-3xl">
|
||||
<Card variant="elevated" padding="md" className="space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-bold text-sm text-slate-900 dark:text-white">Marcos Rodríguez</span>
|
||||
<span className="text-xs text-slate-500 dark:text-slate-400">• Curso: React & TypeScript Pro</span>
|
||||
</div>
|
||||
<div className="flex items-center text-amber-500 dark:text-amber-400 text-xs">
|
||||
<FiStar /><FiStar /><FiStar /><FiStar /><FiStar />
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xs text-slate-700 dark:text-slate-300 leading-relaxed">
|
||||
"Explicación súper clara de la arquitectura de componentes y estado global. El proyecto final me ayudó a conseguir trabajo."
|
||||
</p>
|
||||
</Card>
|
||||
|
||||
<Card variant="elevated" padding="md" className="space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-bold text-sm text-slate-900 dark:text-white">Sofía Martinez</span>
|
||||
<span className="text-xs text-slate-500 dark:text-slate-400">• Curso: UX/UI Masterclass</span>
|
||||
</div>
|
||||
<div className="flex items-center text-amber-500 dark:text-amber-400 text-xs">
|
||||
<FiStar /><FiStar /><FiStar /><FiStar /><FiStar />
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xs text-slate-700 dark:text-slate-300 leading-relaxed">
|
||||
"Excelente contenido. Muy práctico y los recursos descargables son una maravilla."
|
||||
</p>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</main>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Loaded 100 of 104 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user