const {Button,Badge}=window.VedutaDesignSystem_f6874f;
const MOODS=['Electric','Culture','Quiet','Professional','Coast','Retreat','Adventure'];
const card={background:'var(--surface-card)',border:'1px solid var(--border-subtle)',borderRadius:'var(--radius-md)',padding:'26px 28px',boxShadow:'var(--shadow-card)'};
const kicker={fontFamily:'var(--font-sans)',fontSize:'11px',letterSpacing:'0.22em',textTransform:'uppercase',fontWeight:500,color:'var(--gold)',marginBottom:'10px'};
const muted={fontFamily:'var(--font-sans)',fontWeight:300,fontSize:'14px',color:'var(--text-muted)'};
const fmtDate=s=>{if(!s)return'';const d=new Date(s+'T00:00:00');return isNaN(d)?s:d.toLocaleDateString(undefined,{month:'short',day:'numeric',year:'numeric'});};
function SignInForm({intro}){
const [email,setEmail]=React.useState('');
const [status,setStatus]=React.useState('idle');
const [error,setError]=React.useState('');
const submit=async e=>{
e.preventDefault();
const v=email.trim();
if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v)){setError('Please enter a valid email address.');return;}
setStatus('sending');setError('');
const r=await window.vedutaAuth.signIn(v);
if(r.ok)setStatus('sent');else{setStatus('idle');setError(r.error||'Something went wrong. Please try again.');}
};
if(status==='sent')return
Check your email
We sent a sign-in link to {email.trim()} . Open it on this device and you're in. No password to remember.
;
return ;
}
/* ---- Profile & preferences ---- */
function ProfileForm({profile,onSaved}){
const prefs=profile.preferences||{};
const [f,setF]=React.useState({
first_name:profile.first_name||'',last_name:profile.last_name||'',phone:profile.phone||'',home_city:profile.home_city||'',
travel_style:profile.travel_style||'',loves:profile.loves||'',happiest:profile.happiest||'',
moods:prefs.moods||[],pace:prefs.pace||'',party:prefs.party||'',budget:prefs.budget||'',work_travel:!!prefs.work_travel,
dates:(profile.travel_dates||[]).map(d=>({start:d.start||'',end:d.end||'',note:d.note||''}))
});
const [status,setStatus]=React.useState('idle');
const [error,setError]=React.useState('');
const set=k=>v=>{setF(x=>({...x,[k]:v}));if(status==='saved')setStatus('idle');};
const toggleMood=m=>set('moods')(f.moods.includes(m)?f.moods.filter(x=>x!==m):[...f.moods,m]);
const setDate=(i,k,v)=>set('dates')(f.dates.map((d,j)=>j===i?{...d,[k]:v}:d));
const addDate=()=>set('dates')([...f.dates,{start:'',end:'',note:''}]);
const removeDate=i=>set('dates')(f.dates.filter((_,j)=>j!==i));
const submit=async e=>{
e.preventDefault();
for(const d of f.dates){if(d.start&&d.end&&d.endd.start||d.end||d.note)
});
if(r.ok){setStatus('saved');onSaved&&onSaved();}else{setStatus('idle');setError(r.error||'Could not save. Please try again.');}
};
const label={fontFamily:'var(--font-sans)',fontSize:'11px',letterSpacing:'0.2em',textTransform:'uppercase',fontWeight:500,color:'var(--navy)'};
return ;
}
/* ---- Trip requests (the Concie-agent) ---- */
function TripRequestForm({cities,onCreated}){
const [f,setF]=React.useState({city_slug:'',start_date:'',end_date:'',guests:'2',notes:''});
const [status,setStatus]=React.useState('idle');
const [error,setError]=React.useState('');
const set=k=>v=>setF(x=>({...x,[k]:v}));
const submit=async e=>{
e.preventDefault();
if(f.start_date&&f.end_date&&f.end_date
Request received.
The Concie-agent will come back to you with a first itinerary. It shows under your requests below.
{setF({city_slug:'',start_date:'',end_date:'',guests:'2',notes:''});setStatus('idle');}}>Request another trip
;
return ;
}
function RequestList({requests,cities,onCancel}){
const cityName=slug=>{const c=cities.find(x=>x.slug===slug);return c?c.name:(slug||'City to be decided');};
const statusColor={requested:'var(--gold)',confirmed:'var(--olive)',declined:'var(--terracotta)',canceled:'var(--text-muted)'};
if(!requests.length)return No requests yet. Your first one shows here with its status.
;
return
{requests.map(r=>
{r.kind==='concierge'?'Trip':r.kind==='property'?'Stay':'Excursion'} · {cityName(r.city_slug)}
{r.start_date?fmtDate(r.start_date)+(r.end_date?' – '+fmtDate(r.end_date):''):'Dates open'}{r.guests?' · '+r.guests+' guest'+(r.guests===1?'':'s'):''}
{r.notes&&
{r.notes}
}
{r.status}
{r.status==='requested'&&onCancel(r.id)}>Cancel }
)}
;
}
function PortalPage({onNavigate,section}){
const auth=window.vedutaAuth.use();
const [profile,setProfile]=React.useState(null);
const [requests,setRequests]=React.useState([]);
const [cities,setCities]=React.useState([]);
const tab=['profile','requests'].includes(section)?section:'overview';
const loadProfile=()=>window.vedutaData.myProfile().then(setProfile);
const loadRequests=()=>window.vedutaData.myRequests().then(setRequests);
React.useEffect(()=>{
if(!auth.user){setProfile(null);setRequests([]);return;}
loadProfile();loadRequests();window.vedutaData.listCities().then(setCities);
},[auth.user&&auth.user.id]);
const name=(profile&&profile.first_name)||'';
const goTab=t=>onNavigate&&onNavigate(t==='overview'?'Portal':'Portal:'+t);
const tabStyle=t=>({fontFamily:'var(--font-sans)',fontSize:'12px',letterSpacing:'0.18em',textTransform:'uppercase',fontWeight:500,color:tab===t?'var(--gold)':'var(--navy)',textDecoration:'none',padding:'18px 22px',borderBottom:'2px solid '+(tab===t?'var(--gold)':'transparent'),cursor:'pointer'});
const complete=profile&&profile.first_name&&profile.home_city&&(profile.preferences||{}).moods&&(profile.preferences.moods||[]).length>0;
return
Member Portal
{auth.user?('Welcome'+(name?', '+name:'')):'Sign in'}
{auth.user&&
}
{!auth.ready?Loading…
:!auth.enabled?
Member sign-in opens soon
This is where members will sign in to their profile, preferences, the city guides, and the Concie-agent. Until launch, the waitlist is the way in, and founding members hear first.
window.vedutaScrollToWaitlist()}>Join the Waitlist
:!auth.user?
:tab==='profile'?
{profile?
:Loading your profile…
}
:tab==='requests'?
Request a trip
Tell the Concie-agent where and when. Everything else is handled.
Your requests
{await window.vedutaData.cancelRequest(id);loadRequests();}}/>
:
Your account
{profile&&(profile.first_name||profile.last_name)?[profile.first_name,profile.last_name].filter(Boolean).join(' '):'Profile not filled in yet'}
{auth.user.email}
{profile&&profile.home_city&&
{profile.home_city}
}
goTab('profile')}>{complete?'Edit profile':'Complete your profile'}
Membership
{auth.membership?auth.membership.tierName:'Waitlist'}
{auth.membership?('Status: '+auth.membership.status+(auth.membership.billing_interval?' · billed '+(auth.membership.billing_interval==='year'?'yearly':'monthly'):'')):'Founding access opens Spring 2027.'}
onNavigate&&onNavigate('Membership')}>See the tiers
Travel dates
{profile&&profile.travel_dates&&profile.travel_dates.length
?
{profile.travel_dates.slice(0,4).map((d,i)=>
{fmtDate(d.start)}{d.end?' – '+fmtDate(d.end):''}{d.note?' · '+d.note:''}
)}
:
None yet. Add the dates you know you'll be away.
}
goTab('profile')}>Add dates
Trip requests
{requests.filter(r=>r.status==='requested').length} open
{requests.length?requests.length+' in total':'Hand a trip to the Concie-agent.'}
goTab('requests')}>Request a trip
onNavigate&&onNavigate('Destinations')}>Browse the Portfolio
{await window.vedutaAuth.signOut();onNavigate&&onNavigate('Home');}}>Sign out
}
;
}
Object.assign(window,{PortalPage,SignInForm});