feat: implement solutions page, use cases, and update landing page sections including MagicBento, ScrollStack, and interactive components
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
'use client';
|
||||
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { gsap } from 'gsap';
|
||||
import { ScrollTrigger } from 'gsap/ScrollTrigger';
|
||||
|
||||
if (typeof window !== 'undefined') gsap.registerPlugin(ScrollTrigger);
|
||||
|
||||
const feed = [
|
||||
{ store: 'Daily Login', pts: +50, time: '2m ago', type: 'earn' },
|
||||
@@ -16,6 +20,57 @@ export default function EarnRewardsSection() {
|
||||
const [pts, setPts] = useState(0);
|
||||
const panelRef = useRef<HTMLDivElement>(null);
|
||||
const rafRef = useRef<number>(0);
|
||||
const rowRefs = useRef<(HTMLDivElement | null)[]>([]);
|
||||
const activeRowRef = useRef(0);
|
||||
|
||||
// Move the highlighted feed card as the section scrolls through the viewport
|
||||
useEffect(() => {
|
||||
if (typeof window === 'undefined') return;
|
||||
const el = panelRef.current;
|
||||
if (!el) return;
|
||||
|
||||
const applyRow = (idx: number) => {
|
||||
if (idx === activeRowRef.current) return;
|
||||
activeRowRef.current = idx;
|
||||
rowRefs.current.forEach((row, i) => {
|
||||
if (!row) return;
|
||||
const active = i === idx;
|
||||
row.style.background = active ? '#16a34a' : 'rgba(23,21,18,0.55)';
|
||||
row.style.borderColor = active ? '#16a34a' : 'rgba(255,255,255,0.14)';
|
||||
row.style.transform = active ? 'translateX(10px) scale(1.06)' : 'translateX(0) scale(1)';
|
||||
row.style.boxShadow = active ? '0 16px 40px -12px rgba(22,163,74,0.5)' : 'none';
|
||||
row.style.zIndex = active ? '2' : '1';
|
||||
|
||||
// Flip inner text/icon colors for contrast per background
|
||||
const title = row.querySelector<HTMLElement>('.feed-title');
|
||||
if (title) title.style.color = '#ffffff';
|
||||
const time = row.querySelector<HTMLElement>('.feed-time');
|
||||
if (time) time.style.color = active ? 'rgba(255,255,255,0.75)' : 'rgba(255,255,255,0.55)';
|
||||
const icon = row.querySelector<HTMLElement>('.feed-icon');
|
||||
if (icon) {
|
||||
icon.style.background = active ? 'rgba(255,255,255,0.15)' : 'rgba(74,222,128,0.1)';
|
||||
icon.style.borderColor = active ? 'rgba(255,255,255,0.5)' : 'rgba(74,222,128,0.45)';
|
||||
icon.style.color = active ? '#ffffff' : '#4ade80';
|
||||
}
|
||||
const points = row.querySelector<HTMLElement>('.feed-pts');
|
||||
if (points) {
|
||||
points.style.color = active ? '#ffffff' : '#4ade80';
|
||||
points.style.background = active ? 'rgba(255,255,255,0.15)' : 'rgba(74,222,128,0.12)';
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const st = ScrollTrigger.create({
|
||||
trigger: el,
|
||||
start: 'top 65%',
|
||||
end: 'bottom 60%',
|
||||
onUpdate: (self) => {
|
||||
applyRow(Math.min(feed.length - 1, Math.floor(self.progress * feed.length)));
|
||||
},
|
||||
});
|
||||
|
||||
return () => st.kill();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const el = panelRef.current;
|
||||
@@ -48,7 +103,7 @@ export default function EarnRewardsSection() {
|
||||
background: '#f0fdf4', border: '1.5px solid #16a34a', borderRadius: 100, padding: '7px 16px',
|
||||
}}>
|
||||
<span style={{ width: 7, height: 7, borderRadius: '50%', background: '#16a34a', flexShrink: 0, animation: 'fpPulse 1.5s infinite' }} />
|
||||
<span style={{ fontSize: 11, fontWeight: 700, color: '#16a34a', fontFamily: 'Sora, sans-serif', letterSpacing: '0.1em', textTransform: 'uppercase' }}>Live points balance</span>
|
||||
<span style={{ fontSize: 11, fontWeight: 700, color: '#16a34a', fontFamily: 'Sora, sans-serif', letterSpacing: '0.1em', textTransform: 'uppercase' }}>Live LYT balance</span>
|
||||
</div>
|
||||
|
||||
{/* Giant gold number */}
|
||||
@@ -70,7 +125,7 @@ export default function EarnRewardsSection() {
|
||||
<span style={{ fontSize: 13, color: '#4f463a', fontFamily: 'Sora, sans-serif' }}>redeemable value</span>
|
||||
</div>
|
||||
|
||||
<div style={{ marginBottom: 16 }}><span className="fp-kicker fp-rise">Earn · 03</span></div>
|
||||
<div style={{ marginBottom: 16 }}><span className="fp-kicker fp-rise">Earn<span className="fp-kicker-num">03</span></span></div>
|
||||
|
||||
<h2 style={{
|
||||
fontSize: 'clamp(28px, 3.4vw, 48px)', fontWeight: 800, lineHeight: 1.08,
|
||||
@@ -84,7 +139,7 @@ export default function EarnRewardsSection() {
|
||||
<p className="fp-rise" style={{
|
||||
fontSize: 15, lineHeight: 1.8, color: '#4f463a', fontFamily: 'Sora, sans-serif', maxWidth: 380, margin: 0,
|
||||
}}>
|
||||
Earn Loyaly Coins through daily app login, completing 5,000-step walking challenges, playing Maze, Spin Wheel, Scratch Card, and Match Master games, or visiting Selfie Booths at partner malls. Every activity puts more coins in your wallet.
|
||||
Earn LYT (Loyaly Tokens) through daily app login, completing 5,000-step walking challenges, playing Maze, Spin Wheel, Scratch Card, and Match Master games, or visiting Selfie Booths at partner malls. Every activity adds more LYT to your balance.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -107,31 +162,39 @@ export default function EarnRewardsSection() {
|
||||
{/* Feed rows */}
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
||||
{feed.map((tx, i) => (
|
||||
<div key={i} style={{
|
||||
<div key={i} ref={el => { rowRefs.current[i] = el; }} style={{
|
||||
position: 'relative', zIndex: i === 0 ? 2 : 1,
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'space-between',
|
||||
padding: '12px 14px', borderRadius: 12,
|
||||
background: i === 0 ? '#f0fdf4' : '#fafafa',
|
||||
border: `1.5px solid ${i === 0 ? '#16a34a' : 'rgba(23,21,18,0.07)'}`,
|
||||
background: i === 0 ? '#16a34a' : 'rgba(23,21,18,0.55)',
|
||||
border: `1.5px solid ${i === 0 ? '#16a34a' : 'rgba(255,255,255,0.14)'}`,
|
||||
backdropFilter: 'blur(16px)',
|
||||
WebkitBackdropFilter: 'blur(16px)',
|
||||
transform: i === 0 ? 'translateX(10px) scale(1.06)' : 'translateX(0) scale(1)',
|
||||
boxShadow: i === 0 ? '0 16px 40px -12px rgba(22,163,74,0.5)' : 'none',
|
||||
transition: 'background 0.35s ease, border-color 0.35s ease, transform 0.35s cubic-bezier(0.34,1.56,0.64,1), box-shadow 0.35s ease',
|
||||
}}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||
<div style={{
|
||||
<div className="feed-icon" style={{
|
||||
width: 36, height: 36, borderRadius: 10, flexShrink: 0,
|
||||
background: tx.type === 'earn' ? '#f0fdf4' : '#fef2f2',
|
||||
border: `1.5px solid ${tx.type === 'earn' ? '#16a34a' : '#ef4444'}`,
|
||||
background: i === 0 ? 'rgba(255,255,255,0.15)' : 'rgba(74,222,128,0.1)',
|
||||
border: `1.5px solid ${i === 0 ? 'rgba(255,255,255,0.5)' : 'rgba(74,222,128,0.45)'}`,
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
fontSize: 15, fontWeight: 900,
|
||||
color: tx.type === 'earn' ? '#16a34a' : '#ef4444',
|
||||
color: i === 0 ? '#ffffff' : '#4ade80',
|
||||
transition: 'background 0.35s ease, border-color 0.35s ease, color 0.35s ease',
|
||||
}}>{tx.type === 'earn' ? '↑' : '↓'}</div>
|
||||
<div>
|
||||
<div style={{ fontSize: 13, fontWeight: 700, color: '#171512', fontFamily: 'Sora, sans-serif' }}>{tx.store}</div>
|
||||
<div style={{ fontSize: 11, color: 'rgba(23,21,18,0.45)', fontFamily: 'Sora, sans-serif', marginTop: 1 }}>{tx.time}</div>
|
||||
<div className="feed-title" style={{ fontSize: 13, fontWeight: 700, color: '#ffffff', fontFamily: 'Sora, sans-serif', transition: 'color 0.35s ease' }}>{tx.store}</div>
|
||||
<div className="feed-time" style={{ fontSize: 11, color: i === 0 ? 'rgba(255,255,255,0.75)' : 'rgba(255,255,255,0.55)', fontFamily: 'Sora, sans-serif', marginTop: 1, transition: 'color 0.35s ease' }}>{tx.time}</div>
|
||||
</div>
|
||||
</div>
|
||||
<span style={{
|
||||
<span className="feed-pts" style={{
|
||||
fontSize: 15, fontWeight: 900, fontFamily: 'Sora, sans-serif',
|
||||
color: tx.type === 'earn' ? '#16a34a' : '#ef4444', letterSpacing: '-0.02em',
|
||||
background: tx.type === 'earn' ? '#f0fdf4' : '#fef2f2',
|
||||
color: i === 0 ? '#ffffff' : '#4ade80', letterSpacing: '-0.02em',
|
||||
background: i === 0 ? 'rgba(255,255,255,0.15)' : 'rgba(74,222,128,0.12)',
|
||||
padding: '4px 10px', borderRadius: 8,
|
||||
transition: 'color 0.35s ease, background 0.35s ease',
|
||||
}}>
|
||||
{tx.pts > 0 ? '+' : ''}{tx.pts}
|
||||
</span>
|
||||
@@ -144,7 +207,7 @@ export default function EarnRewardsSection() {
|
||||
marginTop: 16, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10,
|
||||
}}>
|
||||
{[
|
||||
{ label: 'Earned this month', val: '660 pts', color: '#16a34a', bg: '#f0fdf4', border: '#16a34a' },
|
||||
{ label: 'Earned this month', val: '660 LYT', color: '#16a34a', bg: '#f0fdf4', border: '#16a34a' },
|
||||
{ label: 'Redeemable value', val: '₹660', color: '#f4be28', bg: '#fff7e0', border: '#f4be28' },
|
||||
].map((s, i) => (
|
||||
<div key={i} style={{ padding: '14px 16px', borderRadius: 14, background: s.bg, border: `1.5px solid ${s.border}` }}>
|
||||
|
||||
Reference in New Issue
Block a user