Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 3 additions & 51 deletions frontend/src/LandingPage/LandingPage.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import React from "react";
import { Link } from "react-router-dom";
import { ArrowRight, History, Bookmark } from "lucide-react";
import { ArrowRight, Bookmark } from "lucide-react";
import Home from "./sections/Home/Home";
import HowItWorks from "./sections/HowItWorks/HowItWorks";
import WhyNavin from "./sections/WhyNavin/WhyNavin";
import Features from "./sections/Features/Features";
import FAQSection from "./sections/FAQSection/FAQSection";
import LogoStrip from "./sections/LogoStrip/LogoStrip";
import CoreFeatures from "./sections/CoreFeatures/CoreFeatures";
Expand All @@ -13,59 +12,15 @@ import { useReturningVisitor } from "@hooks/useReturningVisitor";
import { useAuthContext } from "@context/AuthContext";

const LandingPage: React.FC = () => {
const { isReturning, visitCount, daysSinceLastVisit } = useReturningVisitor();
const { isReturning } = useReturningVisitor();
const { isAuthenticated } = useAuthContext();

const showReturningBanner = isReturning && !isAuthenticated;

return (
<main className="scroll-smooth w-full">
{/* Returning visitor banner */}
{showReturningBanner && (
<div
className="bg-[rgba(98,255,255,0.06)] border-b border-[rgba(98,255,255,0.15)] px-6 py-3"
role="region"
aria-label="Welcome back"
>
<div className="max-w-6xl mx-auto flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3">
<div className="flex items-center gap-3">
<div className="w-8 h-8 rounded-full bg-[rgba(98,255,255,0.15)] flex items-center justify-center shrink-0">
<History size={16} className="text-[#62ffff]" />
</div>
<div>
<p className="text-sm text-white font-medium">
Welcome back{visitCount > 1 ? ` — visit #${visitCount}` : ''}!
{daysSinceLastVisit !== null && daysSinceLastVisit > 0 && (
<span className="text-slate-400 font-normal">
{' '}It's been {daysSinceLastVisit} {daysSinceLastVisit === 1 ? 'day' : 'days'} since your last visit.
</span>
)}
</p>
</div>
</div>
<div className="flex items-center gap-2">
<Link
to="/login"
className="inline-flex items-center gap-1.5 px-4 py-1.5 rounded-lg bg-[rgba(98,255,255,0.1)] hover:bg-[rgba(98,255,255,0.2)] border border-[rgba(98,255,255,0.3)] text-sm font-medium text-[#62ffff] transition-colors no-underline"
>
Sign In
<ArrowRight size={14} />
</Link>
<Link
to="/signup"
className="inline-flex items-center gap-1.5 px-4 py-1.5 rounded-lg bg-[#62ffff] hover:bg-[#4ae8e8] text-sm font-semibold text-black transition-colors no-underline"
>
Get Started
</Link>
</div>
</div>
</div>
)}

{/* Quick-access for authenticated returning visitors */}
{isReturning && isAuthenticated && (
<div
className="bg-[rgba(98,255,255,0.06)] border-b border-[rgba(98,255,255,0.15)] px-6 py-3"
className="bg-[rgba(98,255,255,0.06)] border-b border-[rgba(98,255,255,0.15)] px-6 pt-20 pb-3"
role="region"
aria-label="Quick access"
>
Expand Down Expand Up @@ -96,9 +51,6 @@ const LandingPage: React.FC = () => {
<section id="why-navin">
<WhyNavin />
</section>
<section id="features">
<Features />
</section>
<section id="core-features">
<CoreFeatures />
</section>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,7 @@
import { render, screen } from '@testing-library/react';
import { describe, expect, it, beforeAll } from 'vitest';
import { describe, expect, it } from 'vitest';
import CoreFeatures from './CoreFeatures';

beforeAll(() => {
const IntersectionObserverMock = class {
observe() { }
unobserve() { }
disconnect() { }
};
Object.defineProperty(window, 'IntersectionObserver', {
writable: true,
configurable: true,
value: IntersectionObserverMock,
});
Object.defineProperty(globalThis, 'IntersectionObserver', {
writable: true,
configurable: true,
value: IntersectionObserverMock,
});
});

describe('CoreFeatures', () => {
it('renders the section title and subtitle', () => {
render(<CoreFeatures />);
Expand All @@ -31,55 +13,37 @@ describe('CoreFeatures', () => {
).toBeInTheDocument();
});

it('renders all four feature blocks', () => {
it('renders all four feature callouts', () => {
render(<CoreFeatures />);

expect(screen.getByText('Track every delivery, every step of the way')).toBeInTheDocument();
expect(screen.getByText("Don't trust. Verify.")).toBeInTheDocument();
expect(screen.getByText('Payments that release themselves')).toBeInTheDocument();
expect(screen.getByText('Everyone sees what they need to see')).toBeInTheDocument();
expect(screen.getByText('Tokenized Assets')).toBeInTheDocument();
expect(screen.getByText('IoT-Verified Milestones')).toBeInTheDocument();
expect(screen.getByText('Smart Settlements')).toBeInTheDocument();
expect(screen.getByText('Parity Dashboards')).toBeInTheDocument();
});

it('renders feature descriptions', () => {
render(<CoreFeatures />);

expect(
screen.getByText(/Navin gives you full visibility into every shipment/i)
).toBeInTheDocument();
expect(screen.getByText(/Hash-and-Emit architecture/i)).toBeInTheDocument();
expect(screen.getByText(/smart contract escrow/i)).toBeInTheDocument();
expect(screen.getByText(/four roles — Company, Carrier, Customer, and Admin/i)).toBeInTheDocument();
expect(screen.getByText(/unique digital identity on the Stellar blockchain/i)).toBeInTheDocument();
expect(screen.getByText(/hashed and anchored on-chain/i)).toBeInTheDocument();
expect(screen.getByText(/released automatically via smart contract/i)).toBeInTheDocument();
expect(screen.getByText(/single source of truth for Company, Carrier, Customer, and Admin/i)).toBeInTheDocument();
});

it('renders bullet points for each feature', () => {
it('renders the bar-chart visualization', () => {
render(<CoreFeatures />);

expect(screen.getByText('Live status updates at every checkpoint')).toBeInTheDocument();
expect(screen.getByText('IoT-powered temperature and condition monitoring')).toBeInTheDocument();
expect(screen.getByText(/SHA-256 hashes of all shipment data/i)).toBeInTheDocument();
expect(screen.getByText('Funds locked on shipment creation')).toBeInTheDocument();
expect(screen.getByText(/On-chain role assignment/i)).toBeInTheDocument();
expect(
screen.getByRole('img', { name: /illustrative visualization of on-chain shipment activity/i })
).toBeInTheDocument();
});

it('renders images for each feature with proper alt text', () => {
it('renders the Track Your Shipment CTA', () => {
render(<CoreFeatures />);

expect(screen.getByAltText('Real-time shipment tracking visualization')).toBeInTheDocument();
expect(screen.getByAltText('Blockchain verification data flow diagram')).toBeInTheDocument();
expect(screen.getByAltText('Smart escrow payment flow illustration')).toBeInTheDocument();
expect(screen.getByAltText('Role-based access control diagram')).toBeInTheDocument();
});

it('applies correct layout classes for zigzag pattern', () => {
const { container } = render(<CoreFeatures />);

// Check for flex containers (no longer using .feature-block class)
const flexContainers = container.querySelectorAll('.flex.flex-col');
expect(flexContainers.length).toBeGreaterThanOrEqual(4);

// Verify the structure has alternating row/row-reverse layout
const parentDiv = container.querySelector('.space-y-24');
expect(parentDiv).toBeInTheDocument();
expect(parentDiv?.children).toHaveLength(4);
const cta = screen.getByRole('link', { name: /track your shipment/i });
expect(cta).toBeInTheDocument();
expect(cta).toHaveAttribute('href', '#track');
});
});
Loading
Loading