fixes and architecture

This commit is contained in:
Emmanuel Rojas committed 2026-08-28 16:25:37 -05:00
1 parent ce98f270d5
commit dd43fa04c1
104 files changed
+7023 -5123

No files matched your search

+2
View File
@@ -11,6 +11,8 @@ node_modules
dist
dist-ssr
*.local
.next
.claude
# Editor directories and files
.vscode/*
+7 -1
View File
@@ -1,13 +1,19 @@
<!doctype html>
<html lang="en">
<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>courses-rynext</title>
<title>RyCourses</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
+7
View File
@@ -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.
+7
View File
@@ -0,0 +1,7 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
reactStrictMode: true,
};
export default nextConfig;
+936 -763
View File
File diff suppressed because it is too large. Load diff
+10 -10
View File
@@ -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"
}
}
+7
View File
@@ -0,0 +1,7 @@
const config = {
plugins: {
"@tailwindcss/postcss": {},
},
};
export default config;
+568
View File
@@ -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,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
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

+568
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+36
View File
@@ -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 />,
},
];
+229
View File
@@ -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>
</>
);
};
+116
View File
@@ -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>
);
}
+87
View File
@@ -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>
);
}
+11
View File
@@ -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>
);
}
+397
View File
@@ -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>
</>
);
}
+5
View File
@@ -0,0 +1,5 @@
import ComponentsContent from "./_content";
export default function Page() {
return <ComponentsContent />;
}
+142
View File
@@ -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>
);
}
+7
View File
@@ -0,0 +1,7 @@
import EmpresasContent from "./_content";
export default function Page() {
return <EmpresasContent />;
}
+568
View File
@@ -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

+41
View File
@@ -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);
}
}
+56
View File
@@ -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>
);
}
+78
View File
@@ -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>
);
}
+5
View File
@@ -0,0 +1,5 @@
import LoginContent from "./_content";
export default function Page() {
return <LoginContent />;
}
+44
View File
@@ -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>
);
}
+8
View File
@@ -0,0 +1,8 @@
import type { Metadata } from "next";
import HomeContent from "./_content";
export default function Page() {
return <HomeContent />;
}
+11
View File
@@ -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>
);
};
+5
View File
@@ -0,0 +1,5 @@
import PlanesContent from "./_content";
export default function Page() {
return <PlanesContent />;
}
+38
View File
@@ -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>
);
}
+5
View File
@@ -0,0 +1,5 @@
import PrivacyContent from "./_content";
export default function Page() {
return <PrivacyContent />;
}
+40
View File
@@ -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>
);
};
+5
View File
@@ -0,0 +1,5 @@
import TermsContent from "./_content";
export default function Page() {
return <TermsContent />;
}
+32
View File
@@ -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>
);
}
+13
View File
@@ -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>
);
}
+96
View File
@@ -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>
</>
);
}
+85
View File
@@ -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>
</>
);
}
+65
View File
@@ -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>
);
}
+52
View File
@@ -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} />
))}
</>
);
}
+232 -144
View File
@@ -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">
{/* 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" />
</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'}
</p>
</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>
</div>
) : cartItems.length > 0 ? (
cartItems.map((item) => (
{/* Backdrop — instantáneo */}
<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"
>
<img
src={item.course.thumbnail}
alt={item.course.title}
className="w-20 h-14 object-cover rounded-lg bg-slate-800 shrink-0"
className="fixed inset-0 bg-black/60 backdrop-blur-sm"
style={{
animation: closing ? "fadeOutBg 0.25s ease-in-out forwards" : "none",
}}
onClick={handleClose}
/>
<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}
</p>
{/* 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>
<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"
{/* 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>
<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"}`}
>
<FiTrash2 />
#{Math.floor(100000 + Math.random() * 900000)}
</div>
</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>
<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={course.id}
className={`group flex gap-3.5 p-3 rounded-xl border transition ${item}`}
>
<img
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 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 mt-2">
<span
className={`text-sm font-black tabular-nums ${title}`}
>
${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>
{/* Bottom Summary & Checkout Form */}
{/* Footer — only when items */}
{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={`border-t ${footer}`}>
{/* Coupon */}
<div className="px-4 pt-4 pb-3">
<form onSubmit={handleApplyCoupon} className="flex gap-2">
<div className="relative flex-1">
<FiTag className="absolute left-3 top-3 text-slate-500 text-xs" />
<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="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"
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>
<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"
className={`px-3 py-2 text-xs font-semibold rounded-lg border transition cursor-pointer whitespace-nowrap ${applyBtn}`}
>
Aplicar
</button>
</div>
</form>
{couponMessage && (
<p className={`text-[11px] ${appliedDiscount > 0 ? 'text-emerald-400' : 'text-amber-400'}`}>
<p
className={`text-[11px] mt-1.5 ${appliedDiscount > 0 ? "text-emerald-500" : "text-amber-400"}`}
>
{couponMessage}
</p>
)}
</form>
</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">
{/* 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="font-mono text-slate-200">${rawSubtotal.toFixed(2)}</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-emerald-400">
<div className="flex justify-between text-xs text-emerald-500">
<span>Descuento ({appliedDiscount}%)</span>
<span className="font-mono">-${discountAmount.toFixed(2)}</span>
<span className="tabular-nums">
-${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
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
View File
@@ -1,3 +1,4 @@
"use client";
import React from 'react';
import {
FiGrid,
+64 -103
View File
@@ -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>
);
};
+7 -6
View File
@@ -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
View File
@@ -1,3 +1,4 @@
"use client";
import { useState } from 'react';
import {
FiX,
+75 -34
View File
@@ -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";
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: 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 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: 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 className="text-2xl text-indigo-400" />,
title: 'Comunidad Exclusiva en Discord',
description: 'Resuelve dudas directamente con profesores y conecta con otros estudiantes.'
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 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.'
}
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.",
},
];
return (
<section className="py-16 bg-slate-950/80 border-t border-slate-800/80">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
export const FeaturesBanner: React.FC = () => {
const { isDark } = useTheme();
<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?
return (
<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 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>
);
+132 -144
View File
@@ -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>
</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.
{/* ── 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>
{/* 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">
{/* 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="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"
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/90 via-slate-950/20 to-transparent" />
<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 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 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 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
<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>
<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>
{/* 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>
<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>
<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-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 text-slate-500 line-through font-mono">$89.99</span>
<span className="text-xl font-black text-emerald-400 font-mono">$24.99</span>
<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>
</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>
{/* ── 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 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>
</section>
);
};
+200
View File
@@ -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>
);
};
+115
View File
@@ -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>
</>
);
}
+73
View File
@@ -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>
);
}
+28
View File
@@ -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>
);
}
+100
View File
@@ -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>
);
}
+67
View File
@@ -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>
);
}
+9
View File
@@ -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 },
];
+34 -31
View File
@@ -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`}>
const content = (
<div className="flex items-center gap-2.5">
<img
src="/logo.png"
alt="RyCourses Emblem"
className="w-full h-full object-cover rounded-[10px]"
src={isDark ? "/branding/logo_white.svg" : "/branding/logo_black.svg"}
alt="Rynext Academy logo"
className={`${s.img} w-auto`}
/>
</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>
<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>
{showSubtitle && (
<p className="text-[11px] text-slate-500 dark:text-slate-400 font-medium tracking-wide">Plataforma Educativa</p>
)}
</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>;
};
+94 -68
View File
@@ -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 />
<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)
</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>
<span className="flex items-center gap-1.5 text-slate-400">
<FiShield className="text-emerald-400" /> Pagos cifrados SSL 256-bit
</span>
</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>
</div>
</div>
</footer>
+24
View File
@@ -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>
);
}
+85 -287
View File
@@ -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">
<>
<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 justify-between h-20 gap-4">
<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">
{/* 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
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'
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"
}`}
>
<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>
{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"
>
<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" />
{/* 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>
)}
</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'
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"
}`}
>
<FiHome /> Inicio
Iniciar sesión
</button>
<ThemeToggle variant="switch" />
<ShopCart />
{/* Hamburger */}
<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'
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ú"
>
<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
{mobileOpen ? <FiX className="text-xl" /> : <FiMenu className="text-xl" />}
</button>
</div>
</div>
</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>
);
})}
<div className={`mt-2 pt-3 border-t ${border}`}>
<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"
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"
>
Registrarse
Iniciar sesión
</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>
</div>
)}
</div>
</header>
</>
);
};
@@ -1,3 +1,4 @@
"use client";
import React, { useState, useRef } from 'react';
import {
FiX,
+2 -1
View File
@@ -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,
+3 -2
View File
@@ -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
View File
@@ -1,3 +1,4 @@
"use client";
import React from 'react';
import { FiX } from 'react-icons/fi';
+4 -1
View File
@@ -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
View File
@@ -1,3 +1,4 @@
"use client";
import React from 'react';
export interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
+1
View File
@@ -1,3 +1,4 @@
"use client";
import React, { useEffect, useRef } from 'react';
import { FiCheck, FiMinus } from 'react-icons/fi';
+198
View File
@@ -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
View File
@@ -1,3 +1,4 @@
"use client";
import React, { useState } from 'react';
import { FiEye, FiEyeOff, FiX, FiAlertCircle } from 'react-icons/fi';
+1
View File
@@ -1,3 +1,4 @@
"use client";
import React, { useEffect } from 'react';
import { FiX } from 'react-icons/fi';
+1
View File
@@ -1,3 +1,4 @@
"use client";
import React from 'react';
import { FiChevronDown, FiAlertCircle } from 'react-icons/fi';
+1
View File
@@ -1,3 +1,4 @@
"use client";
import React from 'react';
export interface SpinnerProps {
+1
View File
@@ -1,3 +1,4 @@
"use client";
import React from 'react';
export interface SwitchProps {
+1
View File
@@ -1,3 +1,4 @@
"use client";
import React from 'react';
import { FiAlertCircle } from 'react-icons/fi';
+12 -29
View File
@@ -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>
);
+1
View File
@@ -46,3 +46,4 @@ export type { ModalProps } from './Modal';
// Theme Control
export { ThemeToggle } from './ThemeToggle';
export type { ThemeToggleProps } from './ThemeToggle';
export * from './Dropdown';
+79
View File
@@ -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);
+45
View File
@@ -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);
+73
View File
@@ -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",
];
+44
View File
@@ -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.`,
},
];
+44
View File
@@ -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.
+70
View File
@@ -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
View File
@@ -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); }
}
-13
View File
@@ -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>,
)
+35
View File
@@ -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 }),
},
),
);
+23
View File
@@ -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;
}
+14
View File
@@ -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;
}
+4
View File
@@ -0,0 +1,4 @@
export interface IPrivacySection {
title: string;
content: string;
}
+4
View File
@@ -0,0 +1,4 @@
export interface ISection {
title: string;
content: string;
}
-632
View File
@@ -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>
);
};
-40
View File
@@ -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>
);
};
-346
View File
@@ -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>
);
};
-554
View File
@@ -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>
);
};
-186
View File
@@ -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>
);
};
-235
View File
@@ -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>
);
};
-261
View File
@@ -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>
);
};
-159
View File
@@ -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>
);
};
-489
View File
@@ -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