Ilha Marketing

This commit is contained in:
Flatlogic Bot 2026-07-28 20:58:59 +00:00
parent f606b41ddd
commit a1cd43641c
3 changed files with 525 additions and 146 deletions

View File

@ -1,6 +1,5 @@
import React, {useEffect, useRef} from 'react'
import React, { useEffect, useRef, useState } from 'react'
import Link from 'next/link'
import { useState } from 'react'
import { mdiChevronUp, mdiChevronDown } from '@mdi/js'
import BaseDivider from './BaseDivider'
import BaseIcon from './BaseIcon'

View File

@ -1,5 +1,4 @@
import React, { ReactNode, useEffect } from 'react'
import { useState } from 'react'
import React, { ReactNode, useEffect, useState } from 'react'
import jwt from 'jsonwebtoken';
import { mdiForwardburger, mdiBackburger, mdiMenu } from '@mdi/js'
import menuAside from '../menuAside'

View File

@ -1,166 +1,547 @@
import React, { useEffect, useState } from 'react';
import type { ReactElement } from 'react';
import React, { FormEvent, ReactElement, useEffect, useMemo, useState } from 'react';
import Head from 'next/head';
import Link from 'next/link';
import BaseButton from '../components/BaseButton';
import CardBox from '../components/CardBox';
import SectionFullScreen from '../components/SectionFullScreen';
import LayoutGuest from '../layouts/Guest';
import BaseDivider from '../components/BaseDivider';
import BaseButtons from '../components/BaseButtons';
import { getPageTitle } from '../config';
import { useAppSelector } from '../stores/hooks';
import CardBoxComponentTitle from "../components/CardBoxComponentTitle";
import { getPexelsImage, getPexelsVideo } from '../helpers/pexels';
type MarketPlan = {
id: string;
marketName: string;
deliveryFocus: string;
expectedCustomers: number;
weather: string;
tourism: string;
createdAt: string;
checklist: string[];
featuredProducts: string[];
revenueEstimate: number;
};
export default function Starter() {
const [illustrationImage, setIllustrationImage] = useState({
src: undefined,
photographer: undefined,
photographer_url: undefined,
})
const [illustrationVideo, setIllustrationVideo] = useState({video_files: []})
const [contentType, setContentType] = useState('video');
const [contentPosition, setContentPosition] = useState('left');
const textColor = useAppSelector((state) => state.style.linkColor);
type PlanForm = {
marketName: string;
deliveryFocus: string;
expectedCustomers: string;
weather: string;
tourism: string;
};
const title = 'Ilha Market'
const storageKey = 'ilha-market-day-plans';
// Fetch Pexels image/video
useEffect(() => {
async function fetchData() {
const image = await getPexelsImage();
const video = await getPexelsVideo();
setIllustrationImage(image);
setIllustrationVideo(video);
}
fetchData();
}, []);
const featuredByFocus: Record<string, string[]> = {
tropical: ['Coco gelado', 'Açaí da ilha', 'Banana-prata', 'Suco de caju'],
bakery: ['Pão francês', 'Bolo de fubá', 'Pão de queijo', 'Café coado'],
fishing: ['Tilápia fresca', 'Camarão', 'Isca artesanal', 'Gelo para pesca'],
farming: ['Mandioca', 'Couve', 'Tomate cereja', 'Flores tropicais'],
};
const imageBlock = (image) => (
<div
className='hidden md:flex flex-col justify-end relative flex-grow-0 flex-shrink-0 w-1/3'
style={{
backgroundImage: `${
image
? `url(${image?.src?.original})`
: 'linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.5))'
}`,
backgroundSize: 'cover',
backgroundPosition: 'left center',
backgroundRepeat: 'no-repeat',
}}
>
<div className='flex justify-center w-full bg-blue-300/20'>
<a
className='text-[8px]'
href={image?.photographer_url}
target='_blank'
rel='noreferrer'
>
Photo by {image?.photographer} on Pexels
</a>
</div>
</div>
);
const focusLabels: Record<string, string> = {
tropical: 'Tropical essentials',
bakery: 'Bakery morning rush',
fishing: 'Fishing dock supply',
farming: 'Farm-to-market harvest',
};
const videoBlock = (video) => {
if (video?.video_files?.length > 0) {
return (
<div className='hidden md:flex flex-col justify-end relative flex-grow-0 flex-shrink-0 w-1/3'>
<video
className='absolute top-0 left-0 w-full h-full object-cover'
autoPlay
loop
muted
>
<source src={video?.video_files[0]?.link} type='video/mp4'/>
Your browser does not support the video tag.
</video>
<div className='flex justify-center w-full bg-blue-300/20 z-10'>
<a
className='text-[8px]'
href={video?.user?.url}
target='_blank'
rel='noreferrer'
>
Video by {video.user.name} on Pexels
</a>
</div>
</div>)
}
const weatherLabels: Record<string, string> = {
sunny: 'Sunny beach day',
rainy: 'Warm tropical rain',
storm: 'Storm watch',
fog: 'Foggy mountain morning',
};
const tourismLabels: Record<string, string> = {
low: 'Local residents only',
normal: 'Balanced village day',
high: 'Cruise/tourist surge',
};
const currencyFormatter = new Intl.NumberFormat('pt-BR', {
style: 'currency',
currency: 'BRL',
});
const dateFormatter = new Intl.DateTimeFormat('en-US', {
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
});
function buildChecklist(form: PlanForm, customers: number) {
const base = [
`Receive ${focusLabels[form.deliveryFocus].toLowerCase()} delivery at Porto da Esperança.`,
'Restock hero shelf, cold aisle, and checkout impulse basket before opening.',
'Assign one employee to PIX/card checkout and one to shelf recovery.',
];
if (form.weather === 'storm') {
base.push('Move umbrellas, candles, batteries, and bottled water to the front display.');
}
if (form.weather === 'sunny') {
base.push('Boost cold drinks, beach snacks, sunscreen, and fresh fruit near the entrance.');
}
if (form.tourism === 'high') {
base.push('Prepare bilingual welcome signs and souvenir bundles for tourists.');
}
if (customers > 80) {
base.push('Schedule an afternoon micro-delivery so shelves do not empty after lunch.');
}
return base;
}
function calculateRevenue(form: PlanForm, customers: number) {
const focusMultiplier = form.deliveryFocus === 'bakery' ? 31 : form.deliveryFocus === 'fishing' ? 44 : form.deliveryFocus === 'farming' ? 35 : 38;
const tourismMultiplier = form.tourism === 'high' ? 1.34 : form.tourism === 'low' ? 0.82 : 1;
const weatherMultiplier = form.weather === 'storm' ? 0.72 : form.weather === 'rainy' ? 0.9 : form.weather === 'sunny' ? 1.18 : 0.96;
return Math.round(customers * focusMultiplier * tourismMultiplier * weatherMultiplier);
}
function createPlan(form: PlanForm): MarketPlan {
const expectedCustomers = Number(form.expectedCustomers);
return {
id: `${Date.now()}`,
marketName: form.marketName.trim(),
deliveryFocus: form.deliveryFocus,
expectedCustomers,
weather: form.weather,
tourism: form.tourism,
createdAt: new Date().toISOString(),
checklist: buildChecklist(form, expectedCustomers),
featuredProducts: featuredByFocus[form.deliveryFocus],
revenueEstimate: calculateRevenue(form, expectedCustomers),
};
}
export default function IlhaMarketLanding() {
const [form, setForm] = useState<PlanForm>({
marketName: 'Ilha Market',
deliveryFocus: 'tropical',
expectedCustomers: '64',
weather: 'sunny',
tourism: 'normal',
});
const [plans, setPlans] = useState<MarketPlan[]>([]);
const [selectedPlanId, setSelectedPlanId] = useState<string | null>(null);
const [error, setError] = useState('');
const [success, setSuccess] = useState('');
useEffect(() => {
try {
const savedPlans = window.localStorage.getItem(storageKey);
if (savedPlans) {
const parsedPlans = JSON.parse(savedPlans) as MarketPlan[];
setPlans(parsedPlans);
setSelectedPlanId(parsedPlans[0]?.id ?? null);
}
} catch (err) {
console.error('Failed to load Ilha Market plans from localStorage:', err);
}
}, []);
useEffect(() => {
try {
window.localStorage.setItem(storageKey, JSON.stringify(plans));
} catch (err) {
console.error('Failed to save Ilha Market plans to localStorage:', err);
}
}, [plans]);
const draftPlan = useMemo(() => {
const customers = Number(form.expectedCustomers) || 0;
return {
checklist: buildChecklist(form, customers),
featuredProducts: featuredByFocus[form.deliveryFocus],
revenueEstimate: calculateRevenue(form, customers),
};
}, [form]);
const selectedPlan = plans.find((plan) => plan.id === selectedPlanId) ?? plans[0] ?? null;
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
setError('');
setSuccess('');
const expectedCustomers = Number(form.expectedCustomers);
if (form.marketName.trim().length < 3) {
setError('Name the market with at least 3 characters.');
return;
}
if (!Number.isFinite(expectedCustomers) || expectedCustomers < 10 || expectedCustomers > 250) {
setError('Expected customers must be between 10 and 250 for this playable slice.');
return;
}
const nextPlan = createPlan(form);
setPlans((currentPlans) => [nextPlan, ...currentPlans].slice(0, 6));
setSelectedPlanId(nextPlan.id);
setSuccess(`${nextPlan.marketName} is ready to open with ${nextPlan.featuredProducts.length} featured product groups.`);
};
const removePlan = (planId: string) => {
setPlans((currentPlans) => {
const nextPlans = currentPlans.filter((plan) => plan.id !== planId);
setSelectedPlanId(nextPlans[0]?.id ?? null);
return nextPlans;
});
setSuccess('Market day removed from the planner.');
};
return (
<div
style={
contentPosition === 'background'
? {
backgroundImage: `${
illustrationImage
? `url(${illustrationImage.src?.original})`
: 'linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.5))'
}`,
backgroundSize: 'cover',
backgroundPosition: 'left center',
backgroundRepeat: 'no-repeat',
}
: {}
}
>
<>
<Head>
<title>{getPageTitle('Starter Page')}</title>
<title>{getPageTitle('Ilha Market')}</title>
<meta
name="description"
content="Ilha Market is a cozy Brazilian island supermarket management and life simulation game concept."
/>
</Head>
<SectionFullScreen bg='violet'>
<div
className={`flex ${
contentPosition === 'right' ? 'flex-row-reverse' : 'flex-row'
} min-h-screen w-full`}
>
{contentType === 'image' && contentPosition !== 'background'
? imageBlock(illustrationImage)
: null}
{contentType === 'video' && contentPosition !== 'background'
? videoBlock(illustrationVideo)
: null}
<div className='flex items-center justify-center flex-col space-y-4 w-full lg:w-full'>
<CardBox className='w-full md:w-3/5 lg:w-2/3'>
<CardBoxComponentTitle title="Welcome to your Ilha Market app!"/>
<div className="space-y-3">
<p className='text-center text-gray-500'>This is a React.js/Node.js app generated by the <a className={`${textColor}`} href="https://flatlogic.com/generator">Flatlogic Web App Generator</a></p>
<p className='text-center text-gray-500'>For guides and documentation please check
your local README.md and the <a className={`${textColor}`} href="https://flatlogic.com/documentation">Flatlogic documentation</a></p>
<main className="min-h-screen overflow-hidden bg-[#fff7e8] text-[#183d35]">
<section className="relative isolate px-5 py-6 sm:px-8 lg:px-12">
<div className="absolute inset-0 -z-10 bg-[radial-gradient(circle_at_15%_15%,rgba(255,181,93,0.36),transparent_28%),radial-gradient(circle_at_80%_4%,rgba(42,157,143,0.26),transparent_32%),linear-gradient(135deg,#fff7e8_0%,#dff7ed_48%,#ffe3b3_100%)]" />
<div className="absolute -right-20 top-24 -z-10 h-72 w-72 rounded-full bg-[#ff7a59]/20 blur-3xl" />
<div className="absolute -bottom-20 left-10 -z-10 h-80 w-80 rounded-full bg-[#2a9d8f]/20 blur-3xl" />
<nav className="mx-auto flex max-w-7xl items-center justify-between rounded-full border border-white/70 bg-white/55 px-5 py-3 shadow-sm backdrop-blur">
<Link href="/" className="flex items-center gap-3 font-semibold tracking-tight text-[#183d35]">
<span className="grid h-10 w-10 place-items-center rounded-full bg-[#ff7a59] text-xl shadow-lg shadow-[#ff7a59]/25">🏝</span>
<span className="text-lg">Ilha Market</span>
</Link>
<div className="flex items-center gap-2 text-sm font-semibold">
<a href="#planner" className="hidden rounded-full px-4 py-2 text-[#24564b] transition hover:bg-white/70 sm:inline-flex">
Market planner
</a>
<Link href="/login" className="rounded-full bg-[#183d35] px-4 py-2 text-white shadow-lg shadow-[#183d35]/20 transition hover:bg-[#25574d] focus:outline-none focus:ring-2 focus:ring-[#ffb55d]">
Admin login
</Link>
</div>
<BaseButtons>
<BaseButton
href='/login'
label='Login'
color='info'
className='w-full'
/>
</nav>
</BaseButtons>
</CardBox>
</div>
</div>
</SectionFullScreen>
<div className='bg-black text-white flex flex-col text-center justify-center md:flex-row'>
<p className='py-6 text-sm'>© 2026 <span>{title}</span>. All rights reserved</p>
<Link className='py-6 ml-4 text-sm' href='/privacy-policy/'>
Privacy Policy
</Link>
</div>
<div className="mx-auto grid max-w-7xl items-center gap-12 py-16 lg:grid-cols-[1.05fr_0.95fr] lg:py-24">
<div>
<div className="mb-6 inline-flex items-center gap-2 rounded-full border border-white/80 bg-white/60 px-4 py-2 text-sm font-semibold text-[#2a6f62] shadow-sm backdrop-blur">
<span className="h-2 w-2 rounded-full bg-[#2a9d8f]" />
Cozy Rio-coast life sim + supermarket management
</div>
<h1 className="max-w-4xl text-5xl font-black leading-[0.95] tracking-[-0.04em] text-[#163830] sm:text-6xl lg:text-7xl">
Reopen the islands only market and bring the community back to life.
</h1>
<p className="mt-6 max-w-2xl text-lg leading-8 text-[#42665e]">
Plan delivery days, stock tropical products, prepare PIX/card checkout, and build the first playable management loop for <strong>Ilha Market</strong> a warm, stylized Brazilian island game concept.
</p>
<div className="mt-8 flex flex-col gap-3 sm:flex-row">
<a href="#planner" className="rounded-full bg-[#ff7a59] px-6 py-3 text-center font-bold text-white shadow-xl shadow-[#ff7a59]/25 transition hover:-translate-y-0.5 hover:bg-[#ef6846] focus:outline-none focus:ring-2 focus:ring-[#183d35]">
Start a market day
</a>
<Link href="/login" className="rounded-full border border-[#183d35]/15 bg-white/70 px-6 py-3 text-center font-bold text-[#183d35] shadow-sm transition hover:-translate-y-0.5 hover:bg-white focus:outline-none focus:ring-2 focus:ring-[#ffb55d]">
Open admin interface
</Link>
</div>
</div>
<dl className="mt-10 grid max-w-2xl grid-cols-3 gap-3">
{[
['9+', 'Island regions'],
['4', 'Payment modes'],
['24h', 'Living routines'],
].map(([value, label]) => (
<div key={label} className="rounded-3xl border border-white/70 bg-white/55 p-4 text-center shadow-sm backdrop-blur">
<dt className="text-2xl font-black text-[#183d35]">{value}</dt>
<dd className="text-xs font-semibold uppercase tracking-[0.18em] text-[#5f8278]">{label}</dd>
</div>
))}
</dl>
</div>
<div className="relative">
<div className="absolute -left-6 top-12 h-24 w-24 rounded-full bg-[#ffcf6d] blur-2xl" />
<div className="relative rounded-[2.5rem] border border-white/80 bg-white/65 p-4 shadow-2xl shadow-[#2a6f62]/15 backdrop-blur">
<div className="overflow-hidden rounded-[2rem] bg-[#8fd8c1]">
<div className="relative min-h-[460px] bg-[linear-gradient(180deg,#7ed2e6_0%,#dff7ed_38%,#fff0bd_39%,#fff0bd_51%,#2a9d8f_52%,#1f7c70_100%)] p-6">
<div className="absolute left-8 top-8 h-20 w-20 rounded-full bg-[#fff2a8] shadow-[0_0_60px_rgba(255,242,168,0.85)]" />
<div className="absolute bottom-10 left-6 right-6 h-20 rounded-[100%] bg-[#106a73]/25 blur-sm" />
<div className="absolute bottom-16 left-10 right-10 h-24 rounded-[100%] border-t-4 border-white/60" />
<div className="absolute bottom-20 left-1/2 w-[78%] -translate-x-1/2 rounded-[2rem] bg-[#fff7e8] p-4 shadow-2xl">
<div className="rounded-[1.5rem] bg-[#ff7a59] px-5 py-3 text-center text-xl font-black text-white shadow-lg">ILHA MARKET</div>
<div className="mt-4 grid grid-cols-3 gap-3">
<div className="h-24 rounded-2xl bg-[#2a9d8f] p-2 shadow-inner">
<div className="h-full rounded-xl bg-[#b7eadb]" />
</div>
<div className="h-24 rounded-2xl bg-[#f4a261] p-2 shadow-inner">
<div className="h-full rounded-xl bg-[#ffe0a8]" />
</div>
<div className="h-24 rounded-2xl bg-[#2a9d8f] p-2 shadow-inner">
<div className="h-full rounded-xl bg-[#b7eadb]" />
</div>
</div>
<div className="mt-4 flex items-center justify-between rounded-2xl bg-[#183d35] px-4 py-3 text-sm font-bold text-white">
<span>PIX Credit Cash</span>
<span>Open 08:00</span>
</div>
</div>
<div className="absolute bottom-24 right-10 grid h-16 w-16 place-items-center rounded-full bg-[#4b2e21] text-3xl shadow-xl">🧺</div>
<div className="absolute bottom-24 left-12 grid h-14 w-14 place-items-center rounded-full bg-[#24564b] text-2xl shadow-xl">🐟</div>
<div className="absolute left-10 top-36 text-6xl drop-shadow-lg">🌴</div>
<div className="absolute right-12 top-28 text-5xl drop-shadow-lg">🌺</div>
</div>
</div>
</div>
</div>
</div>
</section>
<section className="bg-[#183d35] px-5 py-10 text-white sm:px-8 lg:px-12">
<div className="mx-auto grid max-w-7xl gap-4 md:grid-cols-4">
{[
['Explore', 'Village, beach, forest, mangrove, farm, harbor, lookout, caves.'],
['Manage', 'Deliveries, shelves, pricing, staff, reports, PIX and card checkout.'],
['Connect', 'NPC routines, families, friendships, quests, festivals and reputation.'],
['Grow', 'Farm, fish, craft, restore buildings, upgrade tourism and the harbor.'],
].map(([title, text]) => (
<article key={title} className="rounded-3xl border border-white/10 bg-white/10 p-5 shadow-xl shadow-black/5">
<h2 className="text-lg font-black text-[#ffcf6d]">{title}</h2>
<p className="mt-2 text-sm leading-6 text-white/75">{text}</p>
</article>
))}
</div>
</section>
<section id="planner" className="px-5 py-16 sm:px-8 lg:px-12">
<div className="mx-auto max-w-7xl">
<div className="mb-8 max-w-3xl">
<p className="text-sm font-black uppercase tracking-[0.26em] text-[#e46b4e]">Playable MVP slice</p>
<h2 className="mt-3 text-4xl font-black tracking-[-0.03em] text-[#183d35] sm:text-5xl">Market Day Planner</h2>
<p className="mt-4 text-[#54756d]">
Create a daily operating plan for Dona Percílias reopened supermarket. The planner validates inputs, generates a stocking checklist, saves recent plans in this browser, and lets you inspect or remove each plan.
</p>
</div>
<div className="grid gap-6 lg:grid-cols-[0.9fr_1.1fr]">
<form onSubmit={handleSubmit} className="rounded-[2rem] border border-[#e7d9bd] bg-white p-6 shadow-xl shadow-[#dcae72]/10">
<div className="mb-6 flex items-center justify-between gap-4">
<div>
<h3 className="text-2xl font-black text-[#183d35]">Plan opening day</h3>
<p className="text-sm text-[#66877f]">Tune demand, weather, and delivery focus.</p>
</div>
<span className="rounded-full bg-[#dff7ed] px-3 py-1 text-xs font-black uppercase tracking-[0.2em] text-[#2a6f62]">Live</span>
</div>
<div className="space-y-4">
<label className="block">
<span className="text-sm font-bold text-[#24564b]">Market name</span>
<input
value={form.marketName}
onChange={(event) => setForm({ ...form, marketName: event.target.value })}
className="mt-2 w-full rounded-2xl border border-[#d9cdb5] bg-[#fffaf1] px-4 py-3 text-[#183d35] outline-none transition focus:border-[#2a9d8f] focus:ring-4 focus:ring-[#2a9d8f]/15"
placeholder="Ilha Market"
/>
</label>
<div className="grid gap-4 sm:grid-cols-2">
<label className="block">
<span className="text-sm font-bold text-[#24564b]">Delivery focus</span>
<select
value={form.deliveryFocus}
onChange={(event) => setForm({ ...form, deliveryFocus: event.target.value })}
className="mt-2 w-full rounded-2xl border border-[#d9cdb5] bg-[#fffaf1] px-4 py-3 text-[#183d35] outline-none transition focus:border-[#2a9d8f] focus:ring-4 focus:ring-[#2a9d8f]/15"
>
{Object.entries(focusLabels).map(([value, label]) => (
<option key={value} value={value}>{label}</option>
))}
</select>
</label>
<label className="block">
<span className="text-sm font-bold text-[#24564b]">Expected customers</span>
<input
type="number"
min="10"
max="250"
value={form.expectedCustomers}
onChange={(event) => setForm({ ...form, expectedCustomers: event.target.value })}
className="mt-2 w-full rounded-2xl border border-[#d9cdb5] bg-[#fffaf1] px-4 py-3 text-[#183d35] outline-none transition focus:border-[#2a9d8f] focus:ring-4 focus:ring-[#2a9d8f]/15"
/>
</label>
</div>
<div className="grid gap-4 sm:grid-cols-2">
<label className="block">
<span className="text-sm font-bold text-[#24564b]">Weather</span>
<select
value={form.weather}
onChange={(event) => setForm({ ...form, weather: event.target.value })}
className="mt-2 w-full rounded-2xl border border-[#d9cdb5] bg-[#fffaf1] px-4 py-3 text-[#183d35] outline-none transition focus:border-[#2a9d8f] focus:ring-4 focus:ring-[#2a9d8f]/15"
>
{Object.entries(weatherLabels).map(([value, label]) => (
<option key={value} value={value}>{label}</option>
))}
</select>
</label>
<label className="block">
<span className="text-sm font-bold text-[#24564b]">Tourism demand</span>
<select
value={form.tourism}
onChange={(event) => setForm({ ...form, tourism: event.target.value })}
className="mt-2 w-full rounded-2xl border border-[#d9cdb5] bg-[#fffaf1] px-4 py-3 text-[#183d35] outline-none transition focus:border-[#2a9d8f] focus:ring-4 focus:ring-[#2a9d8f]/15"
>
{Object.entries(tourismLabels).map(([value, label]) => (
<option key={value} value={value}>{label}</option>
))}
</select>
</label>
</div>
</div>
{error && <p className="mt-5 rounded-2xl bg-[#fff0ed] px-4 py-3 text-sm font-semibold text-[#ad3826]">{error}</p>}
{success && <p className="mt-5 rounded-2xl bg-[#e8fbf3] px-4 py-3 text-sm font-semibold text-[#1d725d]">{success}</p>}
<button type="submit" className="mt-6 w-full rounded-2xl bg-[#183d35] px-5 py-4 font-black text-white shadow-xl shadow-[#183d35]/20 transition hover:-translate-y-0.5 hover:bg-[#25574d] focus:outline-none focus:ring-4 focus:ring-[#ffb55d]/50">
Generate and save market plan
</button>
</form>
<div className="grid gap-6 xl:grid-cols-[0.92fr_1.08fr]">
<article className="rounded-[2rem] border border-[#e7d9bd] bg-[#fffdf8] p-6 shadow-xl shadow-[#dcae72]/10">
<div className="flex items-start justify-between gap-3">
<div>
<h3 className="text-2xl font-black text-[#183d35]">Live forecast</h3>
<p className="text-sm text-[#66877f]">Preview before saving.</p>
</div>
<span className="rounded-2xl bg-[#ffcf6d] px-3 py-2 text-sm font-black text-[#704313]">
{currencyFormatter.format(draftPlan.revenueEstimate)}
</span>
</div>
<div className="mt-5 flex flex-wrap gap-2">
{draftPlan.featuredProducts.map((product) => (
<span key={product} className="rounded-full bg-[#dff7ed] px-3 py-1 text-sm font-bold text-[#24564b]">{product}</span>
))}
</div>
<ol className="mt-6 space-y-3">
{draftPlan.checklist.map((item, index) => (
<li key={item} className="flex gap-3 rounded-2xl bg-[#f5ecdc] p-3 text-sm leading-6 text-[#42665e]">
<span className="grid h-7 w-7 shrink-0 place-items-center rounded-full bg-white font-black text-[#e46b4e]">{index + 1}</span>
{item}
</li>
))}
</ol>
</article>
<article className="rounded-[2rem] border border-[#e7d9bd] bg-white p-6 shadow-xl shadow-[#dcae72]/10">
<div className="flex items-start justify-between gap-4">
<div>
<h3 className="text-2xl font-black text-[#183d35]">Saved market days</h3>
<p className="text-sm text-[#66877f]">Recent plans are stored locally for this MVP.</p>
</div>
<span className="rounded-full bg-[#183d35] px-3 py-1 text-sm font-black text-white">{plans.length}</span>
</div>
{plans.length === 0 ? (
<div className="mt-6 rounded-3xl border border-dashed border-[#d9cdb5] bg-[#fffaf1] p-8 text-center">
<div className="text-4xl">🛒</div>
<p className="mt-3 font-black text-[#183d35]">No saved market days yet.</p>
<p className="mt-1 text-sm text-[#66877f]">Generate a plan to create the first playable management checkpoint.</p>
</div>
) : (
<div className="mt-6 space-y-3">
{plans.map((plan) => (
<button
key={plan.id}
type="button"
onClick={() => setSelectedPlanId(plan.id)}
className={`w-full rounded-3xl border p-4 text-left transition focus:outline-none focus:ring-4 focus:ring-[#2a9d8f]/15 ${selectedPlan?.id === plan.id ? 'border-[#2a9d8f] bg-[#e8fbf3]' : 'border-[#efe2ca] bg-[#fffaf1] hover:border-[#ffb55d]'}`}
>
<div className="flex items-center justify-between gap-3">
<span className="font-black text-[#183d35]">{plan.marketName}</span>
<span className="text-sm font-black text-[#e46b4e]">{currencyFormatter.format(plan.revenueEstimate)}</span>
</div>
<p className="mt-1 text-sm text-[#66877f]">
{focusLabels[plan.deliveryFocus]} {weatherLabels[plan.weather]} {plan.expectedCustomers} customers
</p>
</button>
))}
</div>
)}
</article>
</div>
</div>
<article className="mt-6 rounded-[2rem] border border-[#e7d9bd] bg-[#183d35] p-6 text-white shadow-2xl shadow-[#183d35]/15">
{selectedPlan ? (
<div className="grid gap-6 lg:grid-cols-[0.8fr_1.2fr]">
<div>
<p className="text-sm font-black uppercase tracking-[0.24em] text-[#ffcf6d]">Selected plan detail</p>
<h3 className="mt-3 text-3xl font-black">{selectedPlan.marketName}</h3>
<p className="mt-3 text-white/70">
Created {dateFormatter.format(new Date(selectedPlan.createdAt))}. Prepare for {tourismLabels[selectedPlan.tourism].toLowerCase()} with {weatherLabels[selectedPlan.weather].toLowerCase()}.
</p>
<div className="mt-6 grid grid-cols-2 gap-3">
<div className="rounded-3xl bg-white/10 p-4">
<p className="text-xs font-bold uppercase tracking-[0.18em] text-white/50">Customers</p>
<p className="mt-1 text-2xl font-black">{selectedPlan.expectedCustomers}</p>
</div>
<div className="rounded-3xl bg-white/10 p-4">
<p className="text-xs font-bold uppercase tracking-[0.18em] text-white/50">Forecast</p>
<p className="mt-1 text-2xl font-black">{currencyFormatter.format(selectedPlan.revenueEstimate)}</p>
</div>
</div>
<button
type="button"
onClick={() => removePlan(selectedPlan.id)}
className="mt-5 rounded-full border border-white/20 px-5 py-3 text-sm font-bold text-white/80 transition hover:border-[#ffcf6d] hover:text-[#ffcf6d] focus:outline-none focus:ring-4 focus:ring-[#ffcf6d]/30"
>
Remove selected plan
</button>
</div>
<div className="grid gap-4 md:grid-cols-2">
<div className="rounded-3xl bg-white p-5 text-[#183d35]">
<h4 className="font-black">Featured shelf set</h4>
<div className="mt-4 grid gap-2">
{selectedPlan.featuredProducts.map((product) => (
<div key={product} className="rounded-2xl bg-[#fff0d0] px-4 py-3 text-sm font-bold">{product}</div>
))}
</div>
</div>
<div className="rounded-3xl bg-white p-5 text-[#183d35]">
<h4 className="font-black">Opening checklist</h4>
<ul className="mt-4 space-y-2 text-sm leading-6 text-[#54756d]">
{selectedPlan.checklist.map((item) => (
<li key={item} className="flex gap-2">
<span className="text-[#2a9d8f]"></span>
<span>{item}</span>
</li>
))}
</ul>
</div>
</div>
</div>
) : (
<div className="py-8 text-center">
<div className="text-5xl">🌅</div>
<h3 className="mt-4 text-2xl font-black">Select or create a plan to inspect the playable detail view.</h3>
<p className="mt-2 text-white/70">This is the first working loop for the Ilha Market management game.</p>
</div>
)}
</article>
</div>
</section>
</main>
</>
);
}
Starter.getLayout = function getLayout(page: ReactElement) {
IlhaMarketLanding.getLayout = function getLayout(page: ReactElement) {
return <LayoutGuest>{page}</LayoutGuest>;
};