landing page y login listos en estilos
This commit is contained in:
Generated
+43
@@ -11,6 +11,7 @@
|
||||
"axios": "^1.9.0",
|
||||
"bootstrap": "^5.3.6",
|
||||
"bootstrap-icons": "^1.13.1",
|
||||
"framer-motion": "^12.18.1",
|
||||
"next": "15.3.3",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
@@ -987,6 +988,33 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/framer-motion": {
|
||||
"version": "12.18.1",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.18.1.tgz",
|
||||
"integrity": "sha512-6o4EDuRPLk4LSZ1kRnnEOurbQ86MklVk+Y1rFBUKiF+d2pCdvMjWVu0ZkyMVCTwl5UyTH2n/zJEJx+jvTYuxow==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"motion-dom": "^12.18.1",
|
||||
"motion-utils": "^12.18.1",
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/is-prop-valid": "*",
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/is-prop-valid": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
@@ -1195,6 +1223,21 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/motion-dom": {
|
||||
"version": "12.18.1",
|
||||
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.18.1.tgz",
|
||||
"integrity": "sha512-dR/4EYT23Snd+eUSLrde63Ws3oXQtJNw/krgautvTfwrN/2cHfCZMdu6CeTxVfRRWREW3Fy1f5vobRDiBb/q+w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"motion-utils": "^12.18.1"
|
||||
}
|
||||
},
|
||||
"node_modules/motion-utils": {
|
||||
"version": "12.18.1",
|
||||
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.18.1.tgz",
|
||||
"integrity": "sha512-az26YDU4WoDP0ueAkUtABLk2BIxe28d8NH1qWT8jPGhPyf44XTdDUh8pDk9OPphaSrR9McgpcJlgwSOIw/sfkA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.11",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"axios": "^1.9.0",
|
||||
"bootstrap": "^5.3.6",
|
||||
"bootstrap-icons": "^1.13.1",
|
||||
"framer-motion": "^12.18.1",
|
||||
"next": "15.3.3",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
|
||||
+16
-2
@@ -3,9 +3,22 @@ import SimpleInput from "@/components/input";
|
||||
import PasswordInput from "@/components/password";
|
||||
import React from "react";
|
||||
|
||||
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<div className="flex-grow-1 d-flex flex-column justify-content-center align-items-center">
|
||||
|
||||
|
||||
<motion.main
|
||||
className="flex-fill flex-grow-1 d-flex flex-column justify-content-center align-items-center"
|
||||
initial={{ y: 50, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
transition={{ delay: 0, duration: .5 }}
|
||||
>
|
||||
|
||||
|
||||
<h2 className="text-dorado">Bienvenido al sistema de</h2>
|
||||
<h1 className="text-azul">Carga Masiva</h1>
|
||||
<SimpleInput
|
||||
@@ -21,6 +34,7 @@ export default function Page() {
|
||||
}}
|
||||
/>
|
||||
<Button>Iniciar Sesión</Button>
|
||||
</div>
|
||||
|
||||
</motion.main>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
* {
|
||||
padding: none;
|
||||
margin: none;
|
||||
}
|
||||
|
||||
.icon-circle {
|
||||
position: relative;
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
margin: 2rem auto;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.central-icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: 2rem;
|
||||
background: white;
|
||||
padding: 1rem;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.floating-icon {
|
||||
position: absolute;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: white;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: rotate(calc(45deg * var(--i))) translate(130px) rotate(calc(-45deg * var(--i)));
|
||||
box-shadow: 0 0 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.floating-icon i {
|
||||
font-size: 1.25rem;
|
||||
color: #7c3aed; /* Morado como el diseño */
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// IconCircle.tsx
|
||||
'use client';
|
||||
import React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import './IconCircle.css';
|
||||
|
||||
const iconClasses = [
|
||||
'bi bi-graph-up',
|
||||
'bi bi-lightning-fill',
|
||||
'bi bi-currency-exchange',
|
||||
'bi bi-globe2',
|
||||
'bi bi-bar-chart-line-fill',
|
||||
'bi bi-arrow-left-right',
|
||||
'bi bi-bank',
|
||||
'bi bi-boxes',
|
||||
];
|
||||
|
||||
interface IconCircleProps {
|
||||
delay?: number;
|
||||
}
|
||||
|
||||
const IconCircle: React.FC<IconCircleProps> = ({ delay = 0 }) => {
|
||||
return (
|
||||
<div className="icon-circle">
|
||||
<motion.div
|
||||
className="central-icon"
|
||||
initial={{ scale: 0 }}
|
||||
animate={{ scale: 1, rotate: 1080 }}
|
||||
transition={{ duration: 1, delay }}
|
||||
>
|
||||
<i className="bi bi-stars"></i>
|
||||
</motion.div>
|
||||
|
||||
{iconClasses.map((iconClass, i) => (
|
||||
<motion.div
|
||||
key={i}
|
||||
className="floating-icon"
|
||||
initial={{
|
||||
transform: 'translate(0, 0) scale(0)',
|
||||
opacity: 0,
|
||||
}}
|
||||
animate={{
|
||||
transform: `rotate(${i * 45}deg) translate(130px) rotate(-${i * 45}deg)`,
|
||||
scale: 1,
|
||||
opacity: 1,
|
||||
}}
|
||||
transition={{
|
||||
delay: delay + 0.5 + i * 0.1, // empieza después del centro
|
||||
duration: 0.5,
|
||||
type: 'spring',
|
||||
}}
|
||||
>
|
||||
<i className={iconClass}></i>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default IconCircle;
|
||||
@@ -0,0 +1,152 @@
|
||||
* {
|
||||
padding: none;
|
||||
margin: none;
|
||||
}
|
||||
|
||||
/* Fondo general y tipografía */
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Segoe UI', sans-serif;
|
||||
background: radial-gradient(circle at center, #f8f4ff, hsl(218, 45%, 37%));
|
||||
|
||||
/* background: radial-gradient(circle at center, #f8f4ff, hsl(217, 100%, 89%)); */
|
||||
|
||||
color: #1a1a1a;
|
||||
text-align: center;
|
||||
|
||||
}
|
||||
|
||||
/* Header */
|
||||
header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1.5rem 2rem;
|
||||
background: transparent;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
header div:first-child {
|
||||
font-size: 1.5rem;
|
||||
color: #5b2edd;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
background-color: white;
|
||||
padding: 10px;
|
||||
border-radius: 50px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); /* sombra suave */
|
||||
display: flex;
|
||||
gap: 1rem; /* espacio entre los <a> */
|
||||
}
|
||||
|
||||
header a {
|
||||
margin: 0 0.75rem;
|
||||
text-decoration: none;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
header button {
|
||||
margin-left: 0.5rem;
|
||||
padding: 0.4rem 1rem;
|
||||
border-radius: 20px;
|
||||
border: none;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
header button:first-of-type {
|
||||
background-color: white;
|
||||
border: 1px solid #ccc;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
header button:last-of-type {
|
||||
background-color: #0033ff;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Texto principal */
|
||||
strong {
|
||||
display: block;
|
||||
font-size: 2.5rem;
|
||||
margin: 2rem 0 1rem;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 1rem;
|
||||
color: #555;
|
||||
/* max-width: 600px; */
|
||||
margin: 0 auto 1rem;
|
||||
}
|
||||
|
||||
/* Botón principal */
|
||||
div > button {
|
||||
margin-top: 1rem;
|
||||
background-color: #5b2edd;
|
||||
color: white;
|
||||
padding: 0.7rem 2rem;
|
||||
border: none;
|
||||
border-radius: 25px;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Texto de "years of reliability" */
|
||||
div > div {
|
||||
/* display: flex; */
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
div > div p {
|
||||
margin: 0;
|
||||
font-size: 0.9rem;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Scroll hint */
|
||||
body > p:last-of-type {
|
||||
margin-top: 2rem;
|
||||
font-size: 0.85rem;
|
||||
color: #aaa;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.circle-background {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
position: fixed;
|
||||
top: none;
|
||||
left: none;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
z-index: -1; /* para que esté detrás de todo */
|
||||
pointer-events: none; /* no interfiere con el contenido */
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.circle {
|
||||
position: absolute;
|
||||
background: radial-gradient(circle, rgb(255, 255, 255), transparent);
|
||||
border-radius: 50%;
|
||||
pointer-events: none;
|
||||
backdrop-filter: blur(2px);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.invert {
|
||||
filter: invert(1);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// components/LandingBody.tsx
|
||||
"use client";
|
||||
|
||||
import { motion } from "framer-motion";
|
||||
import IconCircle from "./IconCircle";
|
||||
|
||||
export default function LandingBody() {
|
||||
return (
|
||||
<motion.main
|
||||
className="flex-fill"
|
||||
initial={{ y: 50, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
transition={{ delay: 1, duration: 1 }}
|
||||
>
|
||||
<IconCircle delay={2} />
|
||||
|
||||
<strong>Explore markets ready for trading</strong>
|
||||
<p>
|
||||
Explore 300+ trading instruments across 10+ asset classes with Alchemy
|
||||
Markets. Trade Forex, Stocks, Indices, and more!
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<button>Start Trading</button>
|
||||
<div>
|
||||
<i></i>
|
||||
</div>
|
||||
</div>
|
||||
</motion.main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
"use client";
|
||||
|
||||
import { motion } from "framer-motion";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
|
||||
|
||||
|
||||
interface HeaderProps {
|
||||
onNavClick: (page: string) => void;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export default function Header({ onNavClick }: HeaderProps) {
|
||||
return (
|
||||
<motion.header
|
||||
initial={{ x: -100, opacity: 0 }}
|
||||
animate={{ x: 0, opacity: 1 }}
|
||||
transition={{ duration: 1.5, ease: "easeOut" }}
|
||||
>
|
||||
<div className="first-child">
|
||||
<Link
|
||||
href={"https://www.unam.mx/"}
|
||||
target="_blank"
|
||||
className="d-none d-sm-block"
|
||||
>
|
||||
<Image
|
||||
src="/logo-unam.png"
|
||||
alt="Logo de la UNAM"
|
||||
width={280}
|
||||
height={84}
|
||||
className="invert"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* <div className="nav-links">
|
||||
<a href="">Trading</a>
|
||||
<a href="">Platforms</a>
|
||||
<a href="">Tools & Education</a>
|
||||
<a href="">About Us</a>
|
||||
<a href="">Partners</a>
|
||||
<a href="">Login</a>
|
||||
</div> */}
|
||||
|
||||
|
||||
|
||||
<nav className="nav-links">
|
||||
{[
|
||||
{ label: "Landing", page: "landing" },
|
||||
{ label: "Login", page: "login" },
|
||||
{ label: "Admin", page: "admin" },
|
||||
{ label: "Worker", page: "worker" },
|
||||
{ label: "Público", page: "publico" },
|
||||
].map((item) => (
|
||||
<a
|
||||
key={item.page}
|
||||
|
||||
onClick={() => onNavClick(item.page)}
|
||||
>
|
||||
{item.label}
|
||||
</a>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
<div className="first-child">
|
||||
<Link href={"https://acatlan.unam.mx/"} target="_blank">
|
||||
<Image
|
||||
src="/logo-fes.png"
|
||||
alt="Logo de la FES Acatlán"
|
||||
width={250}
|
||||
height={60}
|
||||
className="invert"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
</motion.header>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
"use client";
|
||||
|
||||
import "./globals.css";
|
||||
import "bootstrap-icons/font/bootstrap-icons.css";
|
||||
import IconCircle from "./IconCircle";
|
||||
import { motion } from "framer-motion";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
|
||||
|
||||
import Header from "./new_header"; // ajusta la ruta si estás en otra carpeta
|
||||
import Footer from "@/components/layout/footer";
|
||||
import LandingBody from "./landing_body";
|
||||
import { useState } from "react";
|
||||
import Page from "../(auth)/page";
|
||||
|
||||
|
||||
type PageKey = "landing" | "login" | "admin" | "worker" | "publico";
|
||||
|
||||
|
||||
const Home = () => {
|
||||
|
||||
|
||||
const [page, setPage] = useState<PageKey>("landing");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 1 }}
|
||||
className="page-wrapper d-flex flex-column min-vh-100"
|
||||
>
|
||||
<div className="circle-background">
|
||||
{[150, 300, 450, 600, 900].map((size, i) => (
|
||||
<motion.div
|
||||
key={i}
|
||||
className="circle"
|
||||
initial={{ scale: 0 }}
|
||||
animate={{ scale: 3 }}
|
||||
transition={{
|
||||
delay: 1 + i * 0.3, // empieza después de la animación del texto
|
||||
duration: 2,
|
||||
ease: "easeOut",
|
||||
}}
|
||||
style={{
|
||||
width: size,
|
||||
height: size,
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* ⬇️ Tu contenido principal */}
|
||||
<Header onNavClick={setPage} />
|
||||
|
||||
{/* <LandingBody />*/}
|
||||
{/* renderiza el body según el estado `page` */}
|
||||
{page === "landing" && <LandingBody />}
|
||||
{page === "login" && <Page />}
|
||||
{/* {page === "admin" && <ContenidoAdmin1 />}
|
||||
{page === "worker" && <ContenidoWorker />}
|
||||
{page === "publico" && <ContenidoPublico />}
|
||||
*/}
|
||||
|
||||
|
||||
{/* <Login />
|
||||
|
||||
|
||||
<contenidoAdmin1 />
|
||||
<contenidoWorker />
|
||||
<contenidoPublico />
|
||||
*/}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<Footer></Footer>
|
||||
</motion.div>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
export default Home;
|
||||
+3
-7
@@ -4,8 +4,7 @@ import { Roboto } from "next/font/google";
|
||||
import 'bootstrap-icons/font/bootstrap-icons.css';
|
||||
import '@/styles/sass/bootstrap.scss';
|
||||
import BootstrapClient from "@/components/bootstrap-client";
|
||||
import Footer from "@/components/layout/footer";
|
||||
import Header from "@/components/layout/header";
|
||||
|
||||
|
||||
const roboto = Roboto({
|
||||
weight: ["100", "300", "400", "500", "700", "900"],
|
||||
@@ -20,11 +19,8 @@ export default function RootLayout({
|
||||
return (
|
||||
<html lang="en">
|
||||
<body >
|
||||
<div className="d-flex flex-column min-vh-100">
|
||||
<Header />
|
||||
{children}
|
||||
<Footer />
|
||||
</div>
|
||||
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
@@ -1,20 +1,33 @@
|
||||
import React from "react";
|
||||
|
||||
|
||||
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
|
||||
export default function Footer() {
|
||||
const date = new Date();
|
||||
const year = date.getFullYear();
|
||||
|
||||
return (
|
||||
<footer className="bg-azul p-3 text-center text-white small">
|
||||
<p className="m-0">
|
||||
Hecho en México. Todos los derechos reservados {year}.
|
||||
return (
|
||||
<motion.footer
|
||||
// Empieza 50px abajo y totalmente transparente
|
||||
initial={{ y: 50, opacity: 0 }}
|
||||
// Termina en su posición natural y opacidad 1
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
// Retraso para que aparezca justo después del main
|
||||
transition={{ delay: 2, duration: 1, ease: "easeOut" }}
|
||||
className="p-3 text-center small text-muted opacity-50"
|
||||
>
|
||||
<p className="mb-1">
|
||||
Hecho en México. Todos los derechos reservados 2025.
|
||||
</p>
|
||||
<p className="m-0">
|
||||
<p className="mb-0">
|
||||
Esta página puede ser reproducida con fines no lucrativos, siempre y
|
||||
cuando no se mutile, se cite la fuente completa y su dirección
|
||||
electrónica. De otra forma, requiere permiso previo por escrito de la
|
||||
institución.
|
||||
</p>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
</motion.footer>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user