restructuring layout
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
@keyframes slideInLeft {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
100% {
|
||||
opacity: 0.9;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideOutRight {
|
||||
0% {
|
||||
opacity: 0.9;
|
||||
transform: translateX(0);
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
|
||||
.messageBox {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
padding: 0.5rem 1.25rem;
|
||||
border-radius: 0 4px 4px 0;
|
||||
font-weight: bold;
|
||||
font-size: 2rem;
|
||||
text-align: center;
|
||||
z-index: 100;
|
||||
top: 0;
|
||||
left: 0;
|
||||
max-width: 500px;
|
||||
max-height: min-content;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.messageBox:not(.hidden) {
|
||||
animation: slideInLeft 0.5s ease forwards;
|
||||
}
|
||||
|
||||
.messageBox.hidden {
|
||||
animation: slideOutRight 0.5s ease forwards;
|
||||
}
|
||||
|
||||
.success {
|
||||
background-color: #d1f7c4;
|
||||
color: #000000;
|
||||
border: 1px solid #a5d6a7;
|
||||
}
|
||||
|
||||
.error {
|
||||
background-color: #ffcdd2;
|
||||
color: #000000;
|
||||
border: 1px solid #ef9a9a;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import "./AlertBox.css";
|
||||
|
||||
interface AlertBoxProps {
|
||||
message: string | null;
|
||||
type: "error" | "success";
|
||||
duration?: number;
|
||||
}
|
||||
|
||||
export default function AlertBox({
|
||||
message,
|
||||
type,
|
||||
duration = 6000,
|
||||
}: AlertBoxProps) {
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [text, setText] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
if (message) {
|
||||
setText(message);
|
||||
setVisible(true);
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
setVisible(false);
|
||||
setTimeout(() => setText(""), 500);
|
||||
}, duration);
|
||||
|
||||
return () => clearTimeout(timeout);
|
||||
}
|
||||
}, [message, duration]);
|
||||
|
||||
if (!text) return null;
|
||||
|
||||
return (
|
||||
<div className={`messageBox ${type} ${!visible ? "hidden" : ""}`}>
|
||||
{text}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
|
||||
interface ClearParamsProps {
|
||||
paramsToClear: string[];
|
||||
}
|
||||
|
||||
export default function ClearParams({ paramsToClear }: ClearParamsProps) {
|
||||
useEffect(() => {
|
||||
const url = new URL(window.location.href);
|
||||
let changed = false;
|
||||
|
||||
paramsToClear.forEach((param) => {
|
||||
if (url.searchParams.has(param)) {
|
||||
url.searchParams.delete(param);
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (changed) {
|
||||
window.history.replaceState({}, "", url.toString());
|
||||
}
|
||||
}, [paramsToClear]);
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
'use client';
|
||||
|
||||
interface InformationProps {
|
||||
[key: string]: string | number | undefined;
|
||||
}
|
||||
|
||||
export default function Information(props: InformationProps) {
|
||||
// Obtenemos las entradas (key + value) y filtramos los que sean undefined
|
||||
const entries = Object.entries(props).filter(([_, value]) => value !== undefined);
|
||||
|
||||
if (entries.length === 0) return null; // no mostrar nada si no hay datos
|
||||
|
||||
return (
|
||||
<div className="information">
|
||||
<ul>
|
||||
{entries.map(([key, value]) => (
|
||||
<li key={key}>
|
||||
<b>{key}: </b>{value}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
//IO
|
||||
@@ -0,0 +1,57 @@
|
||||
'use client'
|
||||
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
interface urlProp{
|
||||
urlBase:string
|
||||
value:string|null
|
||||
}
|
||||
|
||||
function SearchUser(props:urlProp) {
|
||||
const [numAcount, setnumAcount] = useState("");
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
if (props.value) {
|
||||
setnumAcount(props.value);
|
||||
}
|
||||
}, [props.value]);
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (numAcount) {
|
||||
router.push(`/${props.urlBase}?numAcount=${numAcount}`);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<form className="containerForm"
|
||||
onSubmit={handleSubmit}>
|
||||
<label className="label">No.Cuenta</label>
|
||||
<div className="groupInput">
|
||||
<input
|
||||
type="text"
|
||||
value={numAcount}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value;
|
||||
if (/^\d*$/.test(value) && value.length <= 9) {
|
||||
setnumAcount(value);
|
||||
}
|
||||
}}
|
||||
placeholder="Coloca un número de cuenta..."
|
||||
inputMode="numeric"
|
||||
pattern="[0-9]*"
|
||||
/>
|
||||
<button className="button buttonSearch" type="submit">
|
||||
Buscar
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default SearchUser;
|
||||
@@ -0,0 +1,12 @@
|
||||
.stepNavigator {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
margin-top: 1rem;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 4px;
|
||||
background-color: #f9fafb;
|
||||
padding: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
'use client'
|
||||
|
||||
import { useState, ReactNode } from "react";
|
||||
import "./StepNavigator.css"
|
||||
|
||||
interface StepNavigatorProps {
|
||||
totalSteps: number;
|
||||
children: ReactNode[];
|
||||
onFinish?: () => void;
|
||||
}
|
||||
|
||||
export default function StepNavigator({ totalSteps, children, onFinish }: StepNavigatorProps) {
|
||||
const [step, setStep] = useState(1);
|
||||
|
||||
const handleNext = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.preventDefault();
|
||||
if (step < totalSteps) setStep(step + 1);
|
||||
else if (onFinish) onFinish();
|
||||
};
|
||||
|
||||
const handlePrev = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.preventDefault();
|
||||
if (step > 1) setStep(step - 1);
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="stepNavigator">
|
||||
{children[step - 1]}
|
||||
|
||||
<div className="absoluteButton">
|
||||
{step > 1 && (
|
||||
<button
|
||||
onClick={handlePrev}
|
||||
className="button buttonSearch">
|
||||
Atrás
|
||||
</button>
|
||||
)}
|
||||
|
||||
{step < totalSteps &&
|
||||
<button
|
||||
onClick={handleNext}
|
||||
className="button buttonSearch"
|
||||
>
|
||||
Siguiente
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
"use client";
|
||||
|
||||
import { useState, ReactNode } from "react";
|
||||
|
||||
interface ToggleOption {
|
||||
key: string;
|
||||
label: string;
|
||||
content: ReactNode;
|
||||
}
|
||||
|
||||
interface ToggleProps {
|
||||
options: ToggleOption[];
|
||||
defaultView?: string;
|
||||
}
|
||||
|
||||
export default function Toggle({ options, defaultView }: ToggleProps) {
|
||||
const [view, setView] = useState(defaultView || options[0].key);
|
||||
|
||||
return (
|
||||
<section className="toggleSection">
|
||||
<div className="toggleGroup">
|
||||
{options.map((opt) => (
|
||||
<button
|
||||
key={opt.key}
|
||||
className={`toggleButton ${view === opt.key ? "active" : ""}`}
|
||||
onClick={() => setView(opt.key)}
|
||||
>
|
||||
{opt.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="padding">
|
||||
{options.find((opt) => opt.key === view)?.content}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import "./style.css"
|
||||
export default function FallingSquares() {
|
||||
return (
|
||||
<div className="falling-container">
|
||||
{Array.from({ length: 10 }).map((_, i) => (
|
||||
<div key={i} className="square"></div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
//Chat
|
||||
//IO
|
||||
@@ -0,0 +1,119 @@
|
||||
.falling-container {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.square {
|
||||
position: absolute;
|
||||
top: -100px;
|
||||
opacity: 0.1;
|
||||
animation: fall linear infinite;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
@keyframes fall {
|
||||
0% {
|
||||
transform: translateY(0) rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(110vh) rotate(1080deg);
|
||||
}
|
||||
}
|
||||
|
||||
.square:nth-child(1) {
|
||||
left: 10%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: linear-gradient(45deg, #f6ff7e, #e6ff75);
|
||||
animation-duration: 11s;
|
||||
animation-delay: 0s;
|
||||
}
|
||||
|
||||
.square:nth-child(2) {
|
||||
left: 25%;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
background: linear-gradient(45deg, #438bce, #185a9d);
|
||||
animation-duration: 12s;
|
||||
animation-delay: 2s;
|
||||
}
|
||||
|
||||
.square:nth-child(3) {
|
||||
left: 40%;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background: linear-gradient(45deg, #f7d71e, #ffd200);
|
||||
animation-duration: 15s;
|
||||
animation-delay: 1s;
|
||||
}
|
||||
.square:nth-child(4) {
|
||||
left: 55%;
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
background: linear-gradient(45deg, #ffe600, #ffd000);
|
||||
animation-duration: 17s;
|
||||
animation-delay: 3s;
|
||||
}
|
||||
.square:nth-child(5) {
|
||||
left: 70%;
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
background: linear-gradient(45deg, #00c6ff, #0072ff);
|
||||
animation-duration: 14.5s;
|
||||
animation-delay: 1.5s;
|
||||
}
|
||||
.square:nth-child(6) {
|
||||
left: 85%;
|
||||
width: 55px;
|
||||
height: 55px;
|
||||
background: linear-gradient(45deg, #535ef9, #201db9);
|
||||
animation-duration: 16.5s;
|
||||
animation-delay: 2.5s;
|
||||
}
|
||||
.square:nth-child(7) {
|
||||
left: 15%;
|
||||
width: 65px;
|
||||
height: 65px;
|
||||
background: linear-gradient(45deg, #115799, #384aef);
|
||||
animation-duration: 15.5s;
|
||||
animation-delay: 1.2s;
|
||||
}
|
||||
.square:nth-child(8) {
|
||||
left: 35%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: linear-gradient(45deg, #ffdf4e, #f9d423);
|
||||
animation-duration: 17.5s;
|
||||
animation-delay: 0.5s;
|
||||
}
|
||||
.square:nth-child(9) {
|
||||
left: 60%;
|
||||
width: 75px;
|
||||
height: 75px;
|
||||
background: linear-gradient(45deg, #b09000, #98c93d);
|
||||
animation-duration: 14.2s;
|
||||
animation-delay: 2.8s;
|
||||
}
|
||||
.square:nth-child(10) {
|
||||
left: 80%;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
background: linear-gradient(45deg, #ffdb66, #f4ff5e);
|
||||
animation-duration: 16.8s;
|
||||
animation-delay: 1.8s;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.square {
|
||||
animation: fall linear infinite;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.square {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import AlertBox from "@/app/Components/Global/AlertBox/AlertBox";
|
||||
import ClearParams from "@/app/Components/Global/ClearParams/ClearParams";
|
||||
|
||||
export default function GlobalAlert() {
|
||||
const [showSuccess, setShowSuccess] = useState<string | null>(null);
|
||||
const [showError, setShowError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const url = new URL(window.location.href);
|
||||
const success = url.searchParams.get("success");
|
||||
const error = url.searchParams.get("error");
|
||||
|
||||
if (success) setShowSuccess(success);
|
||||
if (error) setShowError(error);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<section className="containerAlerts">
|
||||
{showError && (
|
||||
<>
|
||||
<AlertBox key={Date.now()} message={showError} type="error" />
|
||||
</>
|
||||
)}
|
||||
|
||||
{showSuccess && (
|
||||
<>
|
||||
<AlertBox key={Date.now()} message={showSuccess} type="success" />
|
||||
</>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
.ocean {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 30vh;
|
||||
overflow: hidden;
|
||||
opacity: 0.4;
|
||||
background: #4fc3f7; /* color del mar */
|
||||
z-index: -1; /* para que quede atrás del contenido */
|
||||
}
|
||||
|
||||
.wave {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 160%;
|
||||
height: 130%;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border-radius: 50% 50%;
|
||||
animation: waveMove 6s linear infinite;
|
||||
}
|
||||
|
||||
.wave:nth-child(2) {
|
||||
top: -10;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
animation-duration: 8s;
|
||||
animation-delay: -2s;
|
||||
}
|
||||
|
||||
.wave:nth-child(3) {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
animation-duration: 10s;
|
||||
animation-delay: -4s;
|
||||
}
|
||||
|
||||
@keyframes waveMove {
|
||||
0% {
|
||||
transform: translateX(0) translateY(0);
|
||||
}
|
||||
50% {
|
||||
transform: translateX(-25%) translateY(5%);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(0) translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
.ocean {
|
||||
background: transparent;
|
||||
}
|
||||
.wave {
|
||||
display: none;
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
'use client';
|
||||
import styles from './wave.module.css';
|
||||
|
||||
export default function WavesBackground() {
|
||||
return (
|
||||
<div className={styles.ocean}>
|
||||
<div className={styles.wave}></div>
|
||||
<div className={styles.wave}></div>
|
||||
<div className={styles.wave}></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
//Chat
|
||||
//IO
|
||||
Reference in New Issue
Block a user