fix campos CVV validade e melhoria carrosel aulas
Build and Deploy (Gitea) / build-and-deploy (push) Successful in 1m1s
Details
Build and Deploy (Gitea) / build-and-deploy (push) Successful in 1m1s
Details
This commit is contained in:
parent
8ff22ca6d7
commit
d4b216b0b6
69
src/App.tsx
69
src/App.tsx
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
||||||
import {
|
import {
|
||||||
Play, Shield, User, GraduationCap, ArrowRight, Lock, Mail,
|
Play, Shield, User, GraduationCap, ArrowRight, Lock, Mail,
|
||||||
Plus, CheckCircle, ChevronRight, HelpCircle, Film, Laptop, Eye, EyeOff
|
Plus, CheckCircle, ChevronRight, HelpCircle, Film, Laptop, Eye, EyeOff
|
||||||
|
|
@ -30,6 +30,7 @@ export default function App() {
|
||||||
const [unlockingCourse, setUnlockingCourse] = useState<CourseType | null>(null);
|
const [unlockingCourse, setUnlockingCourse] = useState<CourseType | null>(null);
|
||||||
const [expandedCourseId, setExpandedCourseId] = useState<string | null>(null);
|
const [expandedCourseId, setExpandedCourseId] = useState<string | null>(null);
|
||||||
const [selectedLessonId, setSelectedLessonId] = useState<string | null>(null);
|
const [selectedLessonId, setSelectedLessonId] = useState<string | null>(null);
|
||||||
|
const lessonShelfRefs = useRef<Record<string, HTMLDivElement | null>>({});
|
||||||
|
|
||||||
// Authentication forms
|
// Authentication forms
|
||||||
const [isRegister, setIsRegister] = useState(false);
|
const [isRegister, setIsRegister] = useState(false);
|
||||||
|
|
@ -253,6 +254,13 @@ export default function App() {
|
||||||
setExpandedCourseId(null);
|
setExpandedCourseId(null);
|
||||||
} else {
|
} else {
|
||||||
setExpandedCourseId(courseId);
|
setExpandedCourseId(courseId);
|
||||||
|
// Smooth scroll to lesson shelf after brief render delay
|
||||||
|
setTimeout(() => {
|
||||||
|
const el = lessonShelfRefs.current[courseId];
|
||||||
|
if (el) {
|
||||||
|
el.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||||
|
}
|
||||||
|
}, 80);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -333,24 +341,40 @@ export default function App() {
|
||||||
|
|
||||||
{/* Expanded Lessons Shelf */}
|
{/* Expanded Lessons Shelf */}
|
||||||
{expandedCourse && expandedCourse.lessons && expandedCourse.lessons.length > 0 && (
|
{expandedCourse && expandedCourse.lessons && expandedCourse.lessons.length > 0 && (
|
||||||
<div className="bg-[#141414]/90 border border-white/5 rounded-2xl p-6 space-y-4 shadow-2xl relative overflow-hidden animate-fadeIn">
|
<div
|
||||||
<div className="absolute top-0 left-0 w-2 h-full bg-[#E50914]" />
|
ref={(el) => { lessonShelfRefs.current[expandedCourse.id] = el; }}
|
||||||
|
className="lesson-shelf-enter bg-[#141414]/95 border border-white/5 rounded-2xl p-5 space-y-4 shadow-2xl relative overflow-hidden"
|
||||||
|
>
|
||||||
|
{/* Red accent left bar */}
|
||||||
|
<div className="absolute top-0 left-0 w-1.5 h-full bg-[#E50914] rounded-l-2xl" />
|
||||||
|
|
||||||
<div className="flex items-center justify-between pl-2">
|
{/* Header: course thumbnail + title + close */}
|
||||||
<div>
|
<div className="flex items-center justify-between pl-3 gap-3">
|
||||||
<h3 className="text-sm font-bold text-[#E50914] uppercase tracking-wider">Aulas de:</h3>
|
<div className="flex items-center gap-3 min-w-0">
|
||||||
<p className="text-base md:text-lg font-extrabold text-white">{expandedCourse.title}</p>
|
{/* Course miniature */}
|
||||||
|
<img
|
||||||
|
src={expandedCourse.thumbnail}
|
||||||
|
alt={expandedCourse.title}
|
||||||
|
className="w-14 h-20 object-cover rounded-lg border border-white/10 flex-shrink-0 shadow-lg"
|
||||||
|
referrerPolicy="no-referrer"
|
||||||
|
/>
|
||||||
|
<div className="min-w-0">
|
||||||
|
<h3 className="text-[10px] font-extrabold text-[#E50914] uppercase tracking-widest">Aulas do Curso</h3>
|
||||||
|
<p className="text-sm md:text-base font-extrabold text-white leading-tight line-clamp-2">{expandedCourse.title}</p>
|
||||||
|
<p className="text-[10px] text-zinc-500 mt-0.5">{expandedCourse.lessons.length} aulas disponíveis</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => setExpandedCourseId(null)}
|
onClick={() => setExpandedCourseId(null)}
|
||||||
className="text-zinc-400 hover:text-white p-2 rounded-full hover:bg-zinc-800/50 transition-colors text-sm font-bold"
|
className="text-zinc-400 hover:text-white p-2 rounded-full hover:bg-zinc-800/50 transition-colors text-sm font-bold flex-shrink-0"
|
||||||
title="Fechar"
|
title="Fechar"
|
||||||
>
|
>
|
||||||
✕
|
✕
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex overflow-x-auto gap-4 py-2 pl-2 scrollbar-hide snap-x scroll-smooth">
|
{/* Lessons carousel - portrait cards */}
|
||||||
|
<div className="flex overflow-x-auto gap-4 py-2 pl-3 custom-scrollbar snap-x scroll-smooth">
|
||||||
{expandedCourse.lessons.map(lesson => (
|
{expandedCourse.lessons.map(lesson => (
|
||||||
<div
|
<div
|
||||||
key={lesson.id}
|
key={lesson.id}
|
||||||
|
|
@ -359,23 +383,25 @@ export default function App() {
|
||||||
setSelectedLessonId(lesson.id);
|
setSelectedLessonId(lesson.id);
|
||||||
setView('course-view');
|
setView('course-view');
|
||||||
}}
|
}}
|
||||||
className="flex-shrink-0 w-60 md:w-64 glass-card glass-card-hover rounded-xl overflow-hidden cursor-pointer snap-start transition-all duration-300 group/lesson"
|
className="flex-shrink-0 w-44 glass-card glass-card-hover rounded-xl overflow-hidden cursor-pointer snap-start transition-all duration-300 group/lesson flex flex-col"
|
||||||
>
|
>
|
||||||
<div className="relative aspect-video w-full bg-zinc-950 overflow-hidden">
|
{/* Portrait thumbnail */}
|
||||||
|
<div className="relative w-full aspect-[3/4] bg-zinc-950 overflow-hidden flex-shrink-0">
|
||||||
{lesson.thumbnailUrl ? (
|
{lesson.thumbnailUrl ? (
|
||||||
<img
|
<img
|
||||||
src={lesson.thumbnailUrl}
|
src={lesson.thumbnailUrl}
|
||||||
alt={lesson.title}
|
alt={lesson.title}
|
||||||
className="w-full h-full object-cover transition-transform duration-500 group-hover/lesson:scale-105"
|
className="w-full h-full object-cover transition-transform duration-500 group-hover/lesson:scale-110"
|
||||||
referrerPolicy="no-referrer"
|
referrerPolicy="no-referrer"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-full h-full bg-gradient-to-br from-zinc-900 to-zinc-950 flex flex-col items-center justify-center p-4 text-center">
|
<div className="w-full h-full bg-gradient-to-br from-zinc-800 via-zinc-900 to-zinc-950 flex flex-col items-center justify-center p-3 text-center">
|
||||||
<span className="text-[10px] text-zinc-500 font-extrabold tracking-widest uppercase mb-1">AULA {lesson.order}</span>
|
<span className="text-[9px] text-[#E50914] font-extrabold tracking-widest uppercase mb-1.5 bg-[#E50914]/10 px-2 py-0.5 rounded-full border border-[#E50914]/20">Aula {lesson.order}</span>
|
||||||
<span className="text-xs text-white/40 font-bold line-clamp-2">{lesson.title}</span>
|
<span className="text-xs text-white/50 font-semibold line-clamp-3 leading-snug">{lesson.title}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Play overlay */}
|
||||||
<div className="absolute inset-0 bg-black/60 opacity-0 group-hover/lesson:opacity-100 transition-opacity flex items-center justify-center">
|
<div className="absolute inset-0 bg-black/60 opacity-0 group-hover/lesson:opacity-100 transition-opacity flex items-center justify-center">
|
||||||
<div className="bg-[#E50914] text-white p-2.5 rounded-full transform scale-75 group-hover/lesson:scale-100 transition-all duration-300 shadow-lg">
|
<div className="bg-[#E50914] text-white p-2.5 rounded-full transform scale-75 group-hover/lesson:scale-100 transition-all duration-300 shadow-lg">
|
||||||
<svg className="w-5 h-5 fill-white" viewBox="0 0 24 24">
|
<svg className="w-5 h-5 fill-white" viewBox="0 0 24 24">
|
||||||
|
|
@ -383,13 +409,16 @@ export default function App() {
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Order badge */}
|
||||||
|
<span className="absolute top-2 left-2 bg-black/70 text-[#E50914] text-[9px] font-extrabold px-1.5 py-0.5 rounded backdrop-blur-sm border border-white/10">
|
||||||
|
#{lesson.order}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="p-3 space-y-1">
|
{/* Info below */}
|
||||||
<span className="text-[9px] font-bold text-[#E50914] tracking-wider uppercase">
|
<div className="p-2.5 flex-grow">
|
||||||
Aula {lesson.order}
|
<h4 className="text-[11px] font-bold text-white group-hover/lesson:text-[#E50914] transition-colors line-clamp-2 leading-snug">
|
||||||
</span>
|
|
||||||
<h4 className="text-xs font-bold text-white group-hover/lesson:text-[#E50914] transition-colors line-clamp-2 leading-relaxed">
|
|
||||||
{lesson.title}
|
{lesson.title}
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -212,7 +212,9 @@ const PaymentCheckoutCard: React.FC<PaymentCheckoutCardProps> = ({
|
||||||
const isSinglePayment = cycle === 'LIFETIME' || itemType === 'course';
|
const isSinglePayment = cycle === 'LIFETIME' || itemType === 'course';
|
||||||
|
|
||||||
const inputClass =
|
const inputClass =
|
||||||
'w-full glass-input rounded-lg p-3 text-xs text-white focus:outline-none bg-black/40 border border-white/10 focus:border-[#E50914] transition-colors';
|
'w-full rounded-lg p-3 text-xs text-white focus:outline-none border transition-colors ' +
|
||||||
|
'bg-zinc-900 border-zinc-700 focus:border-[#E50914] placeholder-zinc-600 ' +
|
||||||
|
'[color-scheme:dark] [&:-webkit-autofill]:bg-zinc-900 [&:-webkit-autofill]:[webkit-text-fill-color:white]';
|
||||||
|
|
||||||
// ── SUCCESS SCREEN ─────────────────────────────────────────────────────────
|
// ── SUCCESS SCREEN ─────────────────────────────────────────────────────────
|
||||||
if (unlocked) {
|
if (unlocked) {
|
||||||
|
|
|
||||||
|
|
@ -120,3 +120,25 @@ html, body {
|
||||||
.custom-scrollbar::-webkit-scrollbar-track { background: transparent; }
|
.custom-scrollbar::-webkit-scrollbar-track { background: transparent; }
|
||||||
.custom-scrollbar::-webkit-scrollbar-thumb { background: rgba(229,9,20,0.35); border-radius: 99px; }
|
.custom-scrollbar::-webkit-scrollbar-thumb { background: rgba(229,9,20,0.35); border-radius: 99px; }
|
||||||
.custom-scrollbar::-webkit-scrollbar-thumb:hover { background: rgba(229,9,20,0.6); }
|
.custom-scrollbar::-webkit-scrollbar-thumb:hover { background: rgba(229,9,20,0.6); }
|
||||||
|
|
||||||
|
/* ── Lesson shelf animation ── */
|
||||||
|
@keyframes lesson-shelf-in {
|
||||||
|
from { opacity: 0; transform: translateY(-12px); max-height: 0; }
|
||||||
|
to { opacity: 1; transform: translateY(0); max-height: 600px; }
|
||||||
|
}
|
||||||
|
.lesson-shelf-enter {
|
||||||
|
animation: lesson-shelf-in 0.38s cubic-bezier(0.4, 0, 0.2, 1) forwards;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Fix autocomplete white background on dark inputs ── */
|
||||||
|
input:-webkit-autofill,
|
||||||
|
input:-webkit-autofill:hover,
|
||||||
|
input:-webkit-autofill:focus,
|
||||||
|
input:-webkit-autofill:active {
|
||||||
|
-webkit-box-shadow: 0 0 0 60px #1a1a1a inset !important;
|
||||||
|
-webkit-text-fill-color: #fff !important;
|
||||||
|
caret-color: #fff;
|
||||||
|
transition: background-color 5000s ease-in-out 0s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue