import React, { useEffect, useState, type FormEvent, type ReactNode } from 'react';
import { createRoot } from 'react-dom/client';
import { BrowserRouter, Link, NavLink, Route, Routes, useParams, useSearchParams } from 'react-router-dom';
import { ArrowUpRight, ArrowRight, Search, FlaskConical, Bookmark, BookOpen, Users, CheckCircle2, SlidersHorizontal, Link2 } from 'lucide-react';
import type { Session } from '@supabase/supabase-js';
import { z } from 'zod';
import { db, errorMessage } from './client';
import './style.css';

const labSchema=z.object({id:z.string(),slug:z.string(),name:z.string(),university:z.string(),department:z.string(),professor:z.string(),introduction:z.string(),topics:z.array(z.string()),homepage:z.string(),verified_at:z.string().nullable()});
type Lab=z.infer<typeof labSchema>;
type Post={id:string;owner_id:string;title:string;kind:string;description:string;skills:string[];location:string;status:string};
type Profile={id:string;display_name:string;affiliation:string;bio:string;skills:string[];topics:string[];is_public:boolean;accepting_proposals:boolean};
const kinds:Record<string,string>={measurement:'측정 · 실험',equipment:'장비 이용',analysis:'데이터 분석',coauthor:'공동 연구'};
const SessionContext=React.createContext<Session|null>(null);
const useSession=()=>React.useContext(SessionContext);
function useQuery<T>(key:string, fn:()=>Promise<T>){const [data,setData]=useState<T|null>(null),[error,setError]=useState(''),[loading,setLoading]=useState(true),[retry,setRetry]=useState(0);useEffect(()=>{let live=true;setData(null);setLoading(true);setError('');fn().then(d=>{if(live)setData(d)}).catch(e=>{if(live)setError(errorMessage(e))}).finally(()=>{if(live)setLoading(false)});return()=>{live=false}},[key,retry]);return{data,error,loading,reload:()=>setRetry(x=>x+1)}}
function client(){if(!db)throw Error('연결 설정을 준비하고 있습니다. 잠시 후 다시 방문해 주세요.');return db}
function Tags({values}:{values:string[]}){return <div className="tags">{values.map((v,i)=><span key={v+i}>{v}</span>)}</div>}
function State({loading,error,reload,children}:{loading:boolean;error:string;reload:()=>void;children:ReactNode}){return loading?<div className="empty" role="status">정보를 불러오고 있습니다…</div>:error?<div className="notice" role="alert"><strong>정보를 불러오지 못했어요</strong><p>{error}</p><button onClick={reload}>다시 시도</button></div>:<>{children}</>}
function Empty({children}:{children:ReactNode}){return <div className="empty"><FlaskConical size={28}/><p>{children}</p></div>}
function Header(){const session=useSession();return <header><div className="nav-wrap"><Link to="/" className="brand"><span className="brand-icon"><Link2 size={24}/></span>LabLink<span className="beta">BETA</span></Link><nav><NavLink to="/" end>연구실 탐색</NavLink><NavLink to="/collaborations">협업 찾기</NavLink><NavLink to="/researchers">연구자</NavLink><NavLink to="/me">내 활동</NavLink></nav><Link className="login" to="/me">{session?'내 프로필':'로그인'}<ArrowUpRight size={15}/></Link></div></header>}
function LabCard({lab}:{lab:Lab}){return <Link className="lab-card" to={'/labs/'+lab.slug}><div className="card-top"><span className="university-icon">{lab.university==='서울대학교'?'S':lab.university==='한양대학교'?'H':'U'}</span><span>{lab.university}<small>{lab.department}</small></span><ArrowUpRight size={19}/></div><h3>{lab.name}</h3><p className="professor">{lab.professor} 교수</p><p className="description">{lab.introduction||'연구 소개를 확인하고 있습니다.'}</p><Tags values={lab.topics}/><div className="card-bottom"><span><CheckCircle2 size={13}/>{lab.verified_at?'출처 확인 '+new Date(lab.verified_at).toLocaleDateString('ko-KR'):'공식 출처 연결'}</span><span>연구실 보기 <ArrowRight size={14}/></span></div></Link>}
function Labs(){const [params,setParams]=useSearchParams();const search=params.get('q')||'',uni=params.get('university')||'전체';const query=useQuery('labs',async()=>{const{data,error}=await client().from('labs').select('*').order('university').limit(100);if(error)throw error;return z.array(labSchema).parse(data)});const labs=(query.data||[]).filter(l=>(uni==='전체'||l.university===uni)&&[l.name,l.professor,l.university,...l.topics].join(' ').toLowerCase().includes(search.toLowerCase()));function filter(k:string,v:string){const p=new URLSearchParams(params);v?p.set(k,v):p.delete(k);setParams(p)}return <><section className="intro"><div><div className="eyebrow"><span/> YOUR NEXT RESEARCH STARTS HERE</div><h1>나의 다음 연구실,<br/><em>가능성을 연결하다.</em></h1><p>연구 주제부터 논문, 졸업 후 진로까지.<br/>공식 출처를 바탕으로 나에게 맞는 연구실을 찾아보세요.</p></div><div className="intro-graphic" aria-hidden="true"><div className="orbit o1"/><div className="orbit o2"/><div className="orbit o3"/><span className="node n1"><FlaskConical/></span><span className="node n2"><BookOpen/></span><span className="node n3"><Users/></span><span className="center-node"><Link2 size={35}/></span><div className="graphic-note">Ideas meet. Research grows.</div></div></section><div className="search-box"><Search size={21}/><input aria-label="연구실 검색" placeholder="연구실, 교수님 또는 연구 주제를 검색해 보세요" value={search} onChange={e=>filter('q',e.target.value)}/><kbd>SEARCH</kbd></div><div className="explore-layout"><aside><h3><SlidersHorizontal size={17}/> 탐색 필터</h3><label>대학</label>{['전체','서울대학교','한양대학교','UNIST'].map(u=><button className={'filter '+(u===uni?'selected':'')} key={u} onClick={()=>filter('university',u)}>{u}<span className="radio"/></button>)}<div className="aside-note"><CheckCircle2 size={19}/><strong>출처부터 투명하게</strong><p>연구실 홈페이지의 정보를 확인하고, 근거와 확인일을 함께 표시합니다.</p><Link to="/about">데이터 수집 원칙 <ArrowUpRight size={13}/></Link></div></aside><section><div className="section-head"><div><h2>연구실 둘러보기</h2><p>이차전지 · 전기화학 · 에너지 소재</p></div><span className="count">{labs.length}개 연구실</span></div><State {...query}><div className="lab-grid">{labs.map(l=><LabCard key={l.id} lab={l}/>)}</div>{!labs.length&&<Empty>{query.data?.length?'검색 조건에 맞는 연구실이 없습니다.':'첫 연구실 정보를 확인하고 있습니다. 확인이 끝난 자료부터 공개됩니다.'}</Empty>}</State><div className="collab-banner"><div className="banner-icon"><Users size={25}/></div><div><h3>혼자 풀기 어려운 연구, 함께 시작해요.</h3><p>측정과 분석부터 공동 연구까지, 서로의 전문성을 연결합니다.</p></div><Link to="/collaborations">협업 찾기 <ArrowRight size={16}/></Link></div></section></div></>}
function LabDetail(){const{slug}=useParams();const session=useSession();const[tab,setTab]=useState('research'),[message,setMessage]=useState('');const query=useQuery('lab'+slug,async()=>{const c=client();const{data,error}=await c.from('labs').select('*').eq('slug',slug!).maybeSingle();if(error)throw error;if(!data)return null;const lab=labSchema.parse(data);const[f,p]=await Promise.all([c.from('lab_facts').select('*').eq('lab_id',lab.id),c.from('lab_papers').select('*,papers(*)').eq('lab_id',lab.id)]);if(f.error)throw f.error;if(p.error)throw p.error;return{lab,facts:f.data,papers:p.data}});async function save(){try{if(!session)throw Error('로그인 후 연구실을 저장할 수 있습니다.');const{error}=await client().from('bookmarks').upsert({user_id:session.user.id,lab_id:query.data!.lab.id},{ignoreDuplicates:true});if(error)throw error;setMessage('관심 연구실에 저장했습니다.')}catch(e){setMessage(errorMessage(e))}}return <><Link className="back" to="/">← 연구실 탐색</Link><State {...query}>{query.data?<><section className="detail-hero"><div className="eyebrow">{query.data.lab.university} · {query.data.lab.department}</div><h1>{query.data.lab.name}</h1><p>{query.data.lab.professor} 교수</p><Tags values={query.data.lab.topics}/><div className="actions"><a className="button" href={query.data.lab.homepage} target="_blank" rel="noreferrer">공식 홈페이지 <ArrowUpRight size={16}/></a><button onClick={save}><Bookmark size={16}/> 연구실 저장</button></div><p role="status">{message}</p></section><div className="tabs">{Object.entries({research:'연구실 소개',papers:'논문',biography:'교수 이력',alumni:'졸업생 진로',equipment:'장비 · 기술'}).map(([k,v])=><button className={tab===k?'active':''} onClick={()=>setTab(k)} key={k}>{v}</button>)}</div><section className="panel"><h2>{tab==='research'?'어떤 연구를 하나요?':tab==='papers'?'연구실 논문':tab==='alumni'?'홈페이지에 공개된 졸업생 진로':tab==='biography'?'교수 이력':'장비와 연구 기술'}</h2>{tab==='research'&&<p className="long-copy">{query.data.lab.introduction}</p>}{tab==='papers'?<>{query.data.papers.length?query.data.papers.map(p=><article className="fact" key={p.paper_id}><small>{p.papers.year} · {p.papers.journal}</small><h3>{p.papers.title}</h3><span className="tag">{p.corresponding_status==='confirmed'?'교신저자 확인':'교신저자 확인 필요'}</span><p>{p.papers.summary_reviewed_at?p.papers.summary:'요약에 사용할 자료를 확인하고 있습니다.'}</p><a href={p.papers.source_url} target="_blank" rel="noreferrer">논문 출처 ↗</a></article>):<Empty>확인된 논문이 아직 없습니다. 논문이 없다는 의미는 아닙니다.</Empty>}</>:<>{query.data.facts.filter(f=>f.kind===tab).map(f=><article className="fact" key={f.id}><h3>{f.title}</h3><p className="long-copy">{f.body}</p><a href={f.source_url} target="_blank" rel="noreferrer">출처 확인 ↗</a><small> · {new Date(f.observed_at).toLocaleDateString('ko-KR')} 확인</small></article>)}{!query.data.facts.some(f=>f.kind===tab)&&tab!=='research'&&<Empty>공개된 정보를 확인 중입니다. 확인되지 않은 내용은 표시하지 않습니다.</Empty>}{tab==='alumni'&&<p className="muted">홈페이지에 기재된 시점의 진로이며 현재 재직 여부를 의미하지 않습니다. 일부 공개 사례를 전체 졸업생의 취업률로 해석하지 않습니다.</p>}</>}</section></>:<Empty>연구실을 찾을 수 없습니다. 주소를 확인해 주세요.</Empty>}</State></>}
function Propose({post,recipient}:{post?:string;recipient?:string}){const session=useSession();const[open,setOpen]=useState(false),[msg,setMsg]=useState(''),[text,setText]=useState(''),[busy,setBusy]=useState(false);async function submit(e:FormEvent){e.preventDefault();setBusy(true);try{const{error}=await client().rpc('send_proposal',{p_message:text,p_post_id:post||null,p_recipient_id:recipient||null});if(error)throw error;setMsg('제안을 보냈습니다. 내 활동에서 확인할 수 있습니다.');setOpen(false);setText('')}catch(e){setMsg(errorMessage(e))}finally{setBusy(false)}}return <div>{session?<button className="primary" onClick={()=>setOpen(!open)}>협업 제안하기 <ArrowUpRight size={16}/></button>:<Link className="button primary" to="/me">로그인하고 제안하기</Link>}{open&&<form onSubmit={submit}><label>제안 내용<textarea required minLength={20} maxLength={5000} value={text} onChange={e=>setText(e.target.value)} placeholder="연구 목적, 필요한 작업, 제공할 기여, 일정과 비용 협의 사항을 알려주세요."/></label><button className="primary" disabled={busy}>{busy?'전송 중…':'제안 보내기'}</button></form>}<p role="status">{msg}</p></div>}
function Collaborations(){const session=useSession();const[form,setForm]=useState(false),[msg,setMsg]=useState(''),[busy,setBusy]=useState(false);const query=useQuery<Post[]>('posts',async()=>{const{data,error}=await client().from('collaboration_posts').select('*').eq('status','open').order('created_at',{ascending:false}).limit(50);if(error)throw error;return data});async function create(e:FormEvent<HTMLFormElement>){e.preventDefault();setBusy(true);const f=new FormData(e.currentTarget);try{const{error}=await client().from('collaboration_posts').insert({title:f.get('title'),kind:f.get('kind'),description:f.get('description'),location:f.get('location'),skills:String(f.get('skills')).split(',').map(s=>s.trim()).filter(Boolean)});if(error)throw error;setForm(false);setMsg('공고를 등록했습니다.');query.reload()}catch(e){setMsg(errorMessage(e))}finally{setBusy(false)}}return <><div className="page-title"><div className="eyebrow">RESEARCH TOGETHER</div><h1>서로의 전문성이 만나는 곳</h1><p>장비와 기술, 아이디어를 연결해 연구의 다음 단계를 함께하세요.</p></div><div className="section-head"><h2>모집 중인 협업</h2>{session?<button className="primary" onClick={()=>setForm(!form)}>공고 등록 +</button>:<Link className="button primary" to="/me">로그인하고 공고 등록</Link>}</div>{form&&<form className="panel" onSubmit={create}><h2>협업 공고 작성</h2><label>제목<input name="title" required minLength={3} maxLength={160}/></label><label>협업 유형<select name="kind">{Object.entries(kinds).map(([k,v])=><option value={k} key={k}>{v}</option>)}</select></label><label>필요한 작업과 제공 가능한 기여<textarea name="description" required minLength={20} maxLength={10000}/></label><label>필요 역량 (쉼표로 구분)<input name="skills"/></label><label>지역 / 원격 가능 여부<input name="location"/></label><p className="muted">장비 이용은 소속 기관의 승인 범위를 확인해 주세요. 논문 기여와 저자 자격은 협업 당사자가 협의합니다.</p><button className="primary" disabled={busy}>{busy?'등록 중…':'공고 등록'}</button></form>}<p role="status">{msg}</p><State {...query}>{query.data?.length?query.data.map(p=><article className="panel post" key={p.id}><span className="tag">{kinds[p.kind]}</span><h2>{p.title}</h2><p className="long-copy">{p.description}</p><Tags values={p.skills}/><p className="muted">{p.location}</p>{p.owner_id!==session?.user.id?<Propose post={p.id}/>:<span className="muted">내가 등록한 공고</span>}</article>):<Empty>아직 공개된 협업 공고가 없습니다. 첫 연구 파트너를 찾아보세요.</Empty>}</State></>}
function Researchers(){const query=useQuery<Profile[]>('researchers',async()=>{const{data,error}=await client().from('profiles').select('*').eq('is_public',true).order('created_at',{ascending:false}).limit(50);if(error)throw error;return data});return <><div className="page-title"><div className="eyebrow">MEET YOUR COLLABORATORS</div><h1>함께 연구할 사람을 찾아보세요.</h1><p>공개 프로필에 등록된 연구 분야와 역량을 살펴보세요.</p></div><State {...query}><div className="lab-grid">{query.data?.map(p=><article className="panel" key={p.id}><div className="avatar">{p.display_name.slice(0,1)||'R'}</div><h2>{p.display_name||'연구자'}</h2><p>{p.affiliation}</p><p className="long-copy">{p.bio}</p><Tags values={[...p.topics,...p.skills]}/>{p.accepting_proposals&&<Propose recipient={p.id}/>}</article>)}</div>{!query.data?.length&&<Empty>공개된 연구자 프로필이 아직 없습니다.</Empty>}</State></>}
function Auth(){const[signup,setSignup]=useState(false),[msg,setMsg]=useState(''),[busy,setBusy]=useState(false);async function submit(e:FormEvent<HTMLFormElement>){e.preventDefault();setBusy(true);const f=new FormData(e.currentTarget);try{const credentials={email:String(f.get('email')),password:String(f.get('password'))};const{error}=signup?await client().auth.signUp({...credentials,options:{emailRedirectTo:location.origin+'/me'}}):await client().auth.signInWithPassword(credentials);if(error)throw error;setMsg(signup?'인증 메일을 확인해 주세요. 인증 후 로그인할 수 있습니다.':'로그인했습니다.')}catch(e){setMsg(errorMessage(e))}finally{setBusy(false)}}return <section className="auth panel"><span className="brand-icon"><Link2/></span><h1>{signup?'연구의 연결을 시작하세요':'다시 만나 반갑습니다'}</h1><p>관심 연구실을 저장하고 협업을 제안해 보세요.</p><form onSubmit={submit}><label>이메일<input type="email" name="email" autoComplete="email" required/></label><label>비밀번호<input type="password" name="password" minLength={8} autoComplete={signup?'new-password':'current-password'} required/></label><button className="primary" disabled={busy}>{busy?'처리 중…':signup?'가입하기':'로그인'}</button></form><p role="status">{msg}</p><button className="text-button" onClick={()=>setSignup(!signup)}>{signup?'이미 계정이 있나요? 로그인':'처음이신가요? 회원가입'}</button></section>}
function Account(){const session=useSession();const[msg,setMsg]=useState(''),[busy,setBusy]=useState(false);const query=useQuery('account'+session?.user.id,async()=>{if(!session)return null;const c=client();const[p,b,a,posts]=await Promise.all([c.from('profiles').select('*').eq('id',session.user.id).single(),c.from('bookmarks').select('labs(*)'),c.from('proposals').select('*').order('created_at',{ascending:false}),c.from('collaboration_posts').select('*').eq('owner_id',session.user.id)]);for(const r of[p,b,a,posts])if(r.error)throw r.error;return{profile:p.data as Profile,bookmarks:b.data??[],proposals:a.data??[],posts:posts.data??[]}});async function save(e:FormEvent<HTMLFormElement>){e.preventDefault();setBusy(true);const f=new FormData(e.currentTarget);try{const{error}=await client().from('profiles').update({display_name:f.get('name'),affiliation:f.get('affiliation'),bio:f.get('bio'),skills:String(f.get('skills')).split(',').map(s=>s.trim()).filter(Boolean),topics:String(f.get('topics')).split(',').map(s=>s.trim()).filter(Boolean),is_public:f.get('public')==='on',accepting_proposals:f.get('accepting')==='on'}).eq('id',session!.user.id);if(error)throw error;setMsg('프로필을 저장했습니다.');query.reload()}catch(e){setMsg(errorMessage(e))}finally{setBusy(false)}}async function transition(id:string,status:string){try{const{error}=await client().rpc('transition_proposal',{p_id:id,p_status:status});if(error)throw error;query.reload()}catch(e){setMsg(errorMessage(e))}}if(!session)return <Auth/>;return <><div className="section-head page-title"><h1>내 연구 활동</h1><button onClick={()=>client().auth.signOut()}>로그아웃</button></div><p role="status">{msg}</p><State {...query}>{query.data&&<><form className="panel" onSubmit={save}><h2>내 프로필</h2><div className="form-grid"><label>이름<input name="name" maxLength={100} required defaultValue={query.data.profile.display_name}/></label><label>소속<input name="affiliation" maxLength={200} defaultValue={query.data.profile.affiliation}/></label><label>연구 분야 (쉼표로 구분)<input name="topics" defaultValue={query.data.profile.topics.join(', ')}/></label><label>보유 역량 (쉼표로 구분)<input name="skills" defaultValue={query.data.profile.skills.join(', ')}/></label></div><label>연구 소개<textarea name="bio" maxLength={3000} defaultValue={query.data.profile.bio}/></label><label className="check"><input type="checkbox" name="public" defaultChecked={query.data.profile.is_public}/> 이름·소속·연구 소개·역량을 공개합니다</label><label className="check"><input type="checkbox" name="accepting" defaultChecked={query.data.profile.accepting_proposals}/> 공개 프로필에서 협업 제안을 받습니다</label><button className="primary" disabled={busy}>{busy?'저장 중…':'프로필 저장'}</button></form><section className="panel"><h2>주고받은 협업 제안</h2>{query.data.proposals.length?query.data.proposals.map(p=><article className="fact" key={p.id}><span className="tag">{p.sender_id===session.user.id?'보낸 제안':'받은 제안'} · {({pending:'검토 대기',accepted:'수락',declined:'거절',withdrawn:'철회',completed:'완료'} as Record<string,string>)[p.status]}</span><p className="long-copy">{p.message}</p><div className="actions">{p.status==='pending'&&(p.recipient_id===session.user.id?<><button onClick={()=>transition(p.id,'accepted')}>수락</button><button onClick={()=>transition(p.id,'declined')}>거절</button></>:<button onClick={()=>transition(p.id,'withdrawn')}>철회</button>)}{p.status==='accepted'&&<button onClick={()=>transition(p.id,'completed')}>완료로 변경</button>}</div></article>):<p className="muted">아직 주고받은 제안이 없습니다.</p>}</section><section className="panel"><h2>내 협업 공고</h2>{query.data.posts.map(p=><article className="fact" key={p.id}><h3>{p.title}</h3><button onClick={async()=>{const{error}=await client().from('collaboration_posts').update({status:p.status==='open'?'closed':'open'}).eq('id',p.id);if(error)setMsg(errorMessage(error));else query.reload()}}>{p.status==='open'?'모집 마감':'다시 모집'}</button></article>)}{!query.data.posts.length&&<p className="muted">등록한 공고가 없습니다.</p>}</section><h2>관심 연구실</h2><div className="lab-grid">{query.data.bookmarks.map((b,i)=>{const parsed=labSchema.safeParse(b.labs);return parsed.success?<div key={i}><LabCard lab={parsed.data}/><button onClick={async()=>{const{error}=await client().from('bookmarks').delete().eq('lab_id',parsed.data.id).eq('user_id',session.user.id);if(error)setMsg(errorMessage(error));else query.reload()}}>저장 해제</button></div>:null})}</div></>}</State></>}
function About(){return <section className="panel about"><div className="eyebrow">OUR DATA PRINCIPLES</div><h1>근거를 확인할 수 있는 연구 정보</h1><h2>공식 홈페이지에서 시작합니다.</h2><p>연구실 소개, 교수 이력, 논문 목록, 공개된 졸업생 진로를 연구실과 대학의 공식 페이지에서 확인합니다. 각 정보에 출처와 확인일을 연결합니다.</p><h2>확인되지 않은 정보는 구분합니다.</h2><p>논문 저자 목록에 포함되었다는 이유만으로 교신저자로 표시하지 않습니다. 초록과 본문이 없으면 연구 결과를 추정해 요약하지 않습니다.</p><h2>변경된 내용은 검수 후 반영합니다.</h2><p>정기 수집으로 변경 후보를 찾고 검수를 거쳐 공개합니다. 홈페이지에 기재된 졸업 후 진로는 해당 시점의 정보이며 현재 직장을 보증하지 않습니다.</p><h2>개인의 연락처는 수집하지 않습니다.</h2><p>졸업생의 개인 이메일·전화번호·사진은 수집 대상에서 제외합니다. 일부 공개 사례로 취업률이나 연구실 순위를 계산하지 않습니다.</p></section>}
class Boundary extends React.Component<{children:ReactNode},{failed:boolean}>{state={failed:false};static getDerivedStateFromError(){return{failed:true}}render(){return this.state.failed?<div className="panel"><h1>화면을 불러오는 중 문제가 생겼습니다.</h1><p>잠시 후 다시 시도해 주세요.</p><a href="/">연구실 탐색으로 돌아가기</a></div>:this.props.children}}
function App(){const[session,setSession]=useState<Session|null>(null);useEffect(()=>{if(!db)return;db.auth.getSession().then(({data})=>setSession(data.session));const{data}=db.auth.onAuthStateChange((_e,s)=>setSession(s));return()=>data.subscription.unsubscribe()},[]);return <SessionContext.Provider value={session}><BrowserRouter><Header/><main><Boundary><Routes><Route path="/" element={<Labs/>}/><Route path="/labs/:slug" element={<LabDetail/>}/><Route path="/collaborations" element={<Collaborations/>}/><Route path="/researchers" element={<Researchers/>}/><Route path="/me" element={<Account/>}/><Route path="/about" element={<About/>}/><Route path="*" element={<Empty>페이지를 찾을 수 없습니다. <Link to="/">홈으로</Link></Empty>}/></Routes></Boundary></main><footer><Link to="/" className="brand"><Link2 size={20}/> LabLink</Link><p>연구를 이해하고, 가능성을 연결합니다.</p><Link to="/about">데이터 수집 원칙 ↗</Link></footer></BrowserRouter></SessionContext.Provider>}
createRoot(document.getElementById('root')!).render(<React.StrictMode><App/></React.StrictMode>);
