PageSourceSearch

https://valyn.ai/assets/MobileCapturePage-BOOttEp1.js

js valyn.ai collected 2026-10-05 14:46:15 UTC 22,553 bytes, 6 lines download raw bytes

1import{j as e}from"./vendor-radix-BbF15g50.js";import{r as n,a as ae,f as ne,e as re}from"./vendor-router-yRCO-o9X.js";import{a as oe,B as Z}from"./index-CQhanil9.js";import{a as H,X as ie,L as K,C as le,m as ce,c as de,u as G,I as ue}from"./vendor-icons-Dx5PMbX1.js";import{r as pe,a as D}from"./captureLinks-DOuDke7N.js";import{A as fe,a as he,b as me}from"./alert-CcJxyedT.js";import"./vendor-supabase-DXeGmUyu.js";const ge=({className:l,uniColor:s})=>e.jsxs("svg",{className:oe("size-6",l),viewBox:"0 0 180 220",fill:"none",xmlns:"http://www.w3.org/2000/svg",children:[e.jsx("path",{fillRule:"evenodd",clipRule:"evenodd",d:"M80 100H28C12.536 100 0 87.464 0 72V28C0 12.536 12.536 0 28 0H72C87.464 0 100 12.536 100 28V80H160C171.046 80 180 88.9543 180 100V167.639C180 175.215 175.72 182.14 168.944 185.528L103.416 218.292C101.17 219.415 98.6923 220 96.1803 220C87.2442 220 80 212.756 80 203.82V100ZM28 20C23.5817 20 20 23.5817 20 28V72C20 76.4183 23.5817 80 28 80H80V28C80 23.5817 76.4183 20 72 20H28ZM100 100H152C156.418 100 160 103.582 160 108V165.092C160 168.103 158.309 170.859 155.625 172.224L111.625 194.591C106.303 197.296 100 193.429 100 187.459V100Z",fill:s?"currentColor":"url(#paint_logo)"}),e.jsx("defs",{children:e.jsxs("linearGradient",{id:"paint_logo",x1:"90",y1:"0",x2:"90",y2:"220",gradientUnits:"userSpaceOnUse",children:[e.jsx("stop",{stopColor:"#9B99FE"}),e.jsx("stop",{offset:"1",stopColor:"#2BC8B7"})]})})]}),xe=({onPhotosFinished:l,onPhotoCaptured:s,onClose:r,onCameraReady:i,onCameraError:o})=>{const[m,O]=n.useState(null),[S,g]=n.useState(null),[_,Q]=n.useState([]),[v,W]=n.useState(0),[j,M]=n.useState(!1),[z,E]=n.useState(null),k=n.useRef(null),N=n.useRef(null),P=n.useRef(null),T=n.useRef(!1);n.useEffect(()=>{let c=!1;return(async()=>{var b;try{if(!((b=navigator.mediaDevices)!=null&&b.getUserMedia)){const d="Your browser doesn't support camera access.";g(d),o==null||o("camera_unavailable",d);return}const x=await navigator.mediaDevices.getUserMedia({video:{facingMode:{ideal:"environment"},width:{ideal:1920},height:{ideal:1080}},audio:!1});if(c){x.getTracks().forEach(d=>d.stop());return}N.current=x,O(x)}catch(x){const d=x;if((d==null?void 0:d.name)==="NotAllowedError"||(d==null?void 0:d.name)==="PermissionDeniedError"){const w="Camera access was denied. Enable it in your browser settings.";g(w),o==null||o("camera_denied",w)}else if((d==null?void 0:d.name)==="NotFoundError"){const w="No camera found on this device.";g(w),o==null||o("camera_unavailable",w)}else{const w="Could not access the camera.";g(w),o==null||o("camera_failed",w)}}})(),()=>{var b;c=!0,(b=N.current)==null||b.getTracks().forEach(x=>x.stop()),N.current=null,P.current&&clearTimeout(P.current)}},[o]),n.useEffect(()=>{const c=k.current;if(!c||!m)return;c.srcObject=m;let p=!1;const b=()=>{p||T.current||c.videoWidth===0||c.videoHeight===0||(T.current=!0,i==null||i())};c.addEventListener("loadeddata",b),c.play().then(b).catch(()=>{});const x=window.setTimeout(()=>{if(p||T.current)return;const d="The camera opened but did not provide a preview.";g(d),o==null||o("camera_failed",d)},8e3);return()=>{p=!0,window.clearTimeout(x),c.removeEventListener("loadeddata",b)}},[o,i,m]);const I=n.useCallback(c=>{var p;"vibrate"in navigator&&((p=navigator.vibrate)==null||p.call(navigator,35)),E(c),P.current&&clearTimeout(P.current),P.current=setTimeout(()=>E(null),650)},[]),L=n.useCallback(async()=>{const c=k.current;if(!c||!m)return;const p=m.getVideoTracks()[0];if(!p)return;const b=p.getSettings(),x=b.width||c.videoWidth||1280,d=b.height||c.videoHeight||720,w=document.createElement("canvas");w.width=x,w.height=d;const B=w.getContext("2d");if(!B)return;B.drawImage(c,0,0,x,d);const $=await new Promise(R=>w.toBlob(R,"image/jpeg",.9));if(!$)return;const q=v+1;if(W(q),I(q),!s){Q(R=>[...R,$]);return}g(null),Promise.resolve(s($,q)).catch(R=>{console.error("[CaptureRecorder] background photo upload failed:",R),g(R instanceof Error?R.message:"Could not upload a photo. Tap Done to retry syncing.")})},[s,v,m,I]),V=n.useCallback(async()=>{if(!(v===0||j)){M(!0),g(null);try{await l(s?[]:_)}catch(c){console.error("[CaptureRecorder] photo finalize failed:",c),g(c instanceof Error?c.message:"Could not finish this capture. Try again."),M(!1)}}},[s,l,v,_,j]),A=e.jsxs("div",{className:"fixed inset-0 z-50 bg-black flex flex-col",children:[e.jsx("video",{ref:k,autoPlay:!0,playsInl
1ine:!0,muted:!0,className:"absolute inset-0 w-full h-full object-cover"}),z!==null&&e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"pointer-events-none absolute inset-0 z-10 border-[10px] border-white/60"}),e.jsxs("div",{className:"pointer-events-none absolute left-1/2 top-[max(env(safe-area-inset-top),18px)] z-20 -translate-x-1/2 rounded-full bg-black/60 px-3 py-1.5 text-[12px] font-semibold text-white backdrop-blur-md",children:["Photo ",z," captured"]})]}),S&&e.jsxs("div",{className:"absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 max-w-[80%] rounded-xl bg-black/80 backdrop-blur-md p-5 text-center text-white",children:[e.jsx(H,{className:"h-7 w-7 mx-auto mb-3 text-red-400",strokeWidth:1.6}),e.jsx("p",{className:"text-[14px] leading-relaxed",children:S}),e.jsx("button",{onClick:()=>{m?g(null):r()},className:"mt-4 px-4 h-10 rounded-full bg-white text-black text-[14px] font-semibold",children:m?"Try again":"Close"})]}),e.jsxs("div",{className:"relative z-10 flex items-center justify-between px-5 pt-[max(env(safe-area-inset-top),16px)]",children:[e.jsx("button",{onClick:()=>{j||r()},disabled:j,"aria-label":"Close",className:"h-9 w-9 rounded-full bg-black/50 backdrop-blur-md flex items-center justify-center text-white disabled:opacity-50",children:e.jsx(ie,{className:"h-5 w-5",strokeWidth:2})}),v>0?e.jsx("button",{onClick:V,disabled:j,className:"px-4 h-9 rounded-full bg-black/55 backdrop-blur-md text-white text-[14px] font-semibold disabled:opacity-60",children:j?"Sending…":"Done"}):e.jsx("div",{className:"h-9 w-9"})]}),e.jsx("div",{className:"flex-1"}),v>0&&e.jsx("div",{className:"relative z-10 mb-3 mx-auto h-9 min-w-9 px-3 rounded-full bg-black/60 backdrop-blur-md text-white text-[14px] font-semibold inline-flex items-center justify-center",children:v}),e.jsx("div",{className:"relative z-10 mb-[max(env(safe-area-inset-bottom),24px)] flex items-center justify-center",children:e.jsx("button",{onClick:L,disabled:!m||!!S,"aria-label":"Take photo",className:"h-[68px] w-[68px] rounded-full bg-white border-[5px] border-white/30 active:scale-95 transition disabled:opacity-50"})})]});return ae.createPortal(A,document.body)},X=l=>new Promise(s=>window.setTimeout(s,l)),J=l=>l.status===408||l.status===425||l.status===429||l.status>=500&&l.status<600;async function F(l,s,r=3){let i=null;for(let o=1;o<=r;o++){try{const m=await fetch(l,s);if(!J(m)||o===r)return m;i=new Error(`HTTP ${m.status}`)}catch(m){if(i=m,o===r)throw m}await X(350*o)}throw i instanceof Error?i:new Error("Request failed")}const be=(l,s)=>{switch(l){case"resolving":return"Loading link…";case"invalid":return"This link isn't active";case"ready":return"Ready to capture";case"uploading":return(s==null?void 0:s.step)==="uploading"&&s.progress.total!==1?"Syncing photos":"Uploading photos";case"processing":{if(!s)return"Processing";const r=s.progress.total,i=s.progress.done;switch(s.step){case"queued":return"Queued for processing";case"analyzing":return r?`Analyzing ${r} photos`:"Analyzing photos";case"extracting":return r?`Preparing item photos ${i}/${r}`:"Preparing item photos";case"valuing":return r?`Valuing items ${i}/${r}`:"Valuing items";case"completed":return"Finalizing";default:return s.message||"Processing"}}case"finalizing":return"Finalizing inventory";case"complete":return"Capture complete";case"failed":return"Something went wrong"}},we=(l,s)=>{if(l==="uploading")return(s==null?void 0:s.step)==="uploading"&&s.progress.total!==1?s.message||"Syncing captured photos.":"Uploading photos to secure storage.";if(l==="finalizing")return"Saving rooms, items, photos, and values to the dashboard.";if(!s)return"Preparing the capture job.";const r=s.progress.total||0,i=s.progress.done||0;if(s.message)return s.message;switch(s.step){case"queued":return"Queued. Waiting for a worker to start.";case"analyzing":return r?`Reading ${r} captured photo${r===1?"":"s"} for visible items.`:"Reading captured photos for visible items.";case"extracting":return r?`Preparing item photos ${Math.min(i+1,r)} of ${r}.`:"Detecting items and grouping them by room.";case"valuing":return r?`Pricing item ${Math.min(i+1,r)} of ${r} and checking condition.`:"Pricing detected items and checking condition.";case"completed":return"Detection is complete. Preparing the final inventory.";default:return"Processing the capture in the background."}};function Re(){const l=ne(),{shortcode:s}=re(),[r,i]=n.useState("resolving"),[o,m]=n.useState(null),[O,S]=n.useState(null),[g,_]=n.useState(null),[Q,v]=n.useState(0),[W,j]=n.useState(!1),[M,z]=n.useState(null),[,E]=n.useState({queued:0,uploading:0,uploaded:0,failed:0}),k=n.useRef({jobId:"",jobCreated:!1,bucket:"",objectPaths:[]}),N=n.useRef([]),P=n.useRef(!1),T=n.useRef([]),I=n.useRef(null),L=n.useRef(!1),V=n.useRef(null),A=n.useCallback(()=>{window.history.length>1?l(-1):l("/")},[l]),c=n.useCallback(()=>
1{if(r==="uploading"){window.alert("Keep this page open until the upload finishes. You can leave once processing starts.");return}A()},[A,r]);n.useEffect(()=>{if(r!=="uploading")return;const t=a=>{a.preventDefault(),a.returnValue=""};return window.addEventListener("beforeunload",t),()=>window.removeEventListener("beforeunload",t)},[r]),n.useEffect(()=>{if(!s){S("No link provided."),i("invalid");return}k.current={jobId:"",jobCreated:!1,bucket:"",objectPaths:[]},N.current=[],P.current=!1,T.current=[],I.current=null,L.current=!1,j(!1),z(null),E({queued:0,uploading:0,uploaded:0,failed:0});let t=!1;return pe(s).then(a=>{if(!t){if(!a){S("This capture link isn't active. Ask the property owner for a fresh one."),i("invalid");return}m(a),i("ready"),D(s,"opened")}}),()=>{t=!0}},[s]),n.useEffect(()=>{if(!s||!g||r==="uploading"||g.status==="completed"||g.status==="failed")return;let t=!1;const a=async()=>{try{const f=await F(`/api/capture-links/status?shortcode=${s}&jobId=${g.jobId}`,{cache:"no-store"},2);if(f.status===304||J(f))return;if(!f.ok)throw new Error(await f.text());const u=await f.json();if(t)return;_(u);const y=u.progress.total||1,U=u.progress.done||0;let C=30;if(u.step==="queued"?C=35:u.step==="analyzing"?C=50:u.step==="extracting"?C=55+Math.round(U/y*15):u.step==="valuing"?C=70+Math.round(U/y*25):u.step==="completed"&&(C=95),v(C),u.status==="failed")S(u.error||"Processing failed"),i("failed");else if(u.status==="completed"){i("finalizing");const Y=await fetch("/api/capture-links/finalize",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({shortcode:s,jobId:u.jobId})});if(!Y.ok){const se=await Y.text();throw new Error(se)}v(100),i("complete")}}catch(f){if(t)return;console.warn("[MobileCapturePage] status poll failed; keeping job active",f)}},h=window.setInterval(a,2e3);return()=>{t=!0,window.clearInterval(h)}},[s,g,r]);const p=n.useCallback(()=>{const t={queued:0,uploading:0,uploaded:k.current.objectPaths.length,failed:0};for(const a of N.current)a.status==="pending"?t.queued+=1:a.status==="uploading"?t.uploading+=1:a.status==="failed"&&(t.failed+=1);if(E(t),k.current.jobId){const a=Math.max(1,t.uploaded+t.queued+t.uploading+t.failed);_({jobId:k.current.jobId,status:"queued",step:"uploading",message:t.uploading||t.queued?`Syncing ${t.uploaded}/${a} photos`:`Uploaded ${t.uploaded} photo${t.uploaded===1?"":"s"}`,progress:{done:t.uploaded,total:a}})}if(t.queued===0&&t.uploading===0||t.failed>0){const a=T.current.splice(0);for(const h of a)h()}},[]),b=n.useCallback(async(t,a)=>{if(!s)return;const h=t.type.startsWith("image/")?t.type:"image/jpeg",f=k.current;L.current||(L.current=!0,D(s,"first_photo_started")),f.jobId||(f.jobId=typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`),console.log(`[MobileCapturePage] Photo ${a}: requesting signed URL`);const u=await F("/api/capture-links/start",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({shortcode:s,mimeType:h,createJob:!f.jobCreated,jobId:f.jobId})});if(!u.ok)throw new Error(`start ${u.status}: ${await u.text()}`);const y=await u.json();y.jobId&&(f.jobId=y.jobId),f.jobCreated=!0,f.bucket=y.bucket,p(),console.log(`[MobileCapturePage] Photo ${a}: PUT to storage (${t.size} bytes)`);const U=await F(y.signedUrl,{method:"PUT",headers:{"Content-Type":h},body:t});if(!U.ok)throw new Error(`Upload failed for photo ${a}: ${U.status} ${await U.text().catch(()=>"")}`);const C=await F("/api/capture-links/append-photo",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({shortcode:s,jobId:f.jobId,objectPath:y.objectPath,bucket:y.bucket})});if(!C.ok)throw new Error(`append-photo ${C.status}: ${await C.text()}`);f.objectPaths.includes(y.objectPath)||f.objectPaths.push(y.objectPath),f.bucket=y.bucket,p()},[p,s]),x=n.useCallback(()=>{P.current||(P.current=!0,(async()=>{try{for(;!I.current;){const t=N.current.find(a=>a.status==="pending");if(!t)break;t.status="uploading",p();try{if(!t.photo)throw new Error("Photo data is no longer available");await b(t.photo,t.photoNumber),t.status="uploaded",t.photo=null,t.error=void 0}catch(a){if(t.attempts+=1,t.error=a instanceof Error?a.message:"Photo upload failed",t.attempts<5){t.status="pending",p(),await X(Math.min(5e3,700*t.attempts));continue}
1t.status="failed",I.current=new Error(t.error)}p()}}finally{P.current=!1,p(),!I.current&&N.current.some(t=>t.status==="pending")&&x()}})())},[p,b]),d=n.useCallback((t,a)=>{const h=typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`;N.current.push({id:h,photo:t,photoNumber:a,attempts:0,status:"pending"}),p(),x()},[p,x]),w=n.useCallback(async()=>{for(x();;){const t=I.current;if(t)throw t;if(!N.current.some(h=>h.status==="pending"||h.status==="uploading"))return;await new Promise(h=>{T.current.push(h)})}},[x]),B=n.useCallback((t,a)=>{d(t,a)},[d]),$=n.useCallback(async t=>{if(s){j(!1),S(null),i("uploading"),v(8);try{if(t.length>0)for(let u=0;u<t.length;u++)await b(t[u],u+1),v(8+Math.round((u+1)/t.length*20));else await w();const a=k.current;if(!a.jobId||a.objectPaths.length===0)throw new Error("No uploaded photos were recorded for this capture.");console.log("[MobileCapturePage] All photos uploaded; calling /api/capture-links/process-photos");const h=await F("/api/capture-links/process-photos",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({shortcode:s,objectPaths:a.objectPaths,bucket:a.bucket,jobId:a.jobId})});if(!h.ok&&!J(h))throw new Error(`process-photos ${h.status}: ${await h.text()}`);const f=h.ok?await h.json():{jobId:a.jobId,shortcode:s};_({jobId:f.jobId,status:"queued",step:"queued",message:null,progress:{done:0,total:a.objectPaths.length}}),i("processing")}catch(a){throw console.error("[MobileCapturePage] handlePhotosFinished failed",a),S(a instanceof Error?a.message:"Upload failed"),i("failed"),a}}},[s,b,w]),q=n.useCallback(()=>{s&&(z(null),j(!0),D(s,"camera_started"))},[s]),R=n.useCallback(()=>{s&&D(s,"camera_ready")},[s]),ee=n.useCallback((t,a)=>{j(!1),z(a),s&&D(s,t)},[s]),te=n.useCallback(t=>{const a=Array.from(t.target.files||[]);t.target.value="",a.length!==0&&(z(null),$(a).catch(()=>{}))},[$]);if(r==="resolving")return e.jsx("main",{className:"grid min-h-dvh place-items-center bg-[#f7f7f8] px-5",children:e.jsx(K,{className:"h-6 w-6 animate-spin text-[#6f7988]"})});if(r==="invalid")return e.jsx("main",{className:"grid min-h-dvh place-items-center bg-[#f7f7f8] px-5 text-[#1c1d1f]",children:e.jsxs("div",{className:"w-full max-w-sm rounded-2xl border border-[#e4e7ec] bg-white p-6 text-center shadow-[0_8px_30px_rgba(15,23,42,0.06)]",children:[e.jsx("div",{className:"mx-auto grid size-12 place-items-center rounded-full bg-amber-50 text-amber-700",children:e.jsx(H,{className:"size-6"})}),e.jsx("h1",{className:"mt-4 text-xl font-semibold",children:"Link unavailable"}),e.jsx("p",{className:"mt-2 text-sm leading-relaxed text-[#6f7988]",children:O||"This capture link isn't active anymore. Ask the property owner for a fresh one."})]})});if(r==="complete")return e.jsx("main",{className:"grid min-h-dvh place-items-center bg-[#f7f7f8] px-5 text-[#1c1d1f]",children:e.jsxs("div",{className:"w-full max-w-sm rounded-2xl border border-[#e4e7ec] bg-white p-6 text-center shadow-[0_8px_30px_rgba(15,23,42,0.06)]",children:[e.jsx("div",{className:"mx-auto grid size-12 place-items-center rounded-full bg-emerald-50 text-emerald-700",children:e.jsx(le,{className:"size-6"})}),e.jsx("h1",{className:"mt-4 text-xl font-semibold",children:"Capture complete"}),e.jsxs("p",{className:"mt-2 text-sm leading-relaxed text-[#6f7988]",children:["Items have been added to ",e.jsx("span",{className:"font-semibold text-[#1c1d1f]",children:o==null?void 0:o.propertyName})," on the owner's dashboard. You can close this window."]}),(o==null?void 0:o.propertyId)&&e.jsxs("button",{type:"button",onClick:()=>l(`/properties/${o.propertyId}`),className:"mt-5 inline-flex h-11 w-full items-center justify-center gap-2 rounded-[10px] bg-[#2563eb] px-4 text-sm font-semibold text-white hover:bg-[#1d4fd8] focus:outline-none focus:ring-2 focus:ring-[#2563eb]/25 focus:ring-offset-2",children:[e.jsx(ce,{className:"size-4",strokeWidth:2}),e.jsx("span",{children:"Back to property"})]})]})});if(r==="failed")return e.jsx("main",{className:"grid min-h-dvh place-items-center bg-[#f7f7f8] px-5 text-[#1c1d1f]",children:e.jsxs("div",{className:"w-full max-w-sm rounded-2xl border border-[#e4e7ec] bg-white p-6 text-center shadow-[0_8px_30px_rgba(15,23,42,0.06)]",children:[e.jsx("div",{className:"mx-auto grid size-12 place-items-center rounded-full bg-rose-50 text-rose-700",children:e.jsx(H,{className:"size-6"})}),e.jsx("h1",{className:"mt-4 text-xl font-semibold",children:"Couldn't process the capture"}),e.jsx("p",{className:"mt-2 text-sm leading-relaxed text-[#6f7988]",children:O||"Something went wrong. Refresh and try capturing again."}),e.jsx("button",{onClick:()=>window.location.reload(),className:"mt-5 h-10 rounded-[10px] bg-[#2563eb] px-4 text-sm font-semibold text-white hover:bg-[#1d4fd8]",children:"Try again"})]})});if(r==="uploading"||r==="processing"||r==="finalizing"){const t=we(r,g);return e.jsxs("main",{className:"relative grid min-h-dvh place-items-center bg-[#f7f7f8] px-5 text-[#1c1d1f]",children:[e.jsx("button",{type:"button",onClick:c,"aria-label":r==="uploading"?"Upload in progress":"Leave capture",className:"absolute left-4 top-[calc(env(safe-area-inset-top)+14px)] grid size-10 place-items-center rounded-[10px] text-[#6f7988] hover:bg-white/80",children:e.jsx(de,{className:"size-5",strokeWidth:1.9})}),e.jsxs("div",{className:"w-full max-w-sm rounded-2xl border border-[#e4e7ec] bg-white p-6 text-center shadow-[0_8px_30px_rgba(15,23,42,0.06)]",children:[e.jsx("div",{className:"mx-auto grid size-12 place-items-center rounded-full bg-blue-50 text-blue-700",children:e.jsx(K,{className:"size-6 animate-spin"})}),e.jsx("h1",{className:"mt-4 text-xl font-semibold",children:be(r,g)}),e.jsx("p",{"aria-live":"polite",className:"mt-2 min-h-[40px] text-sm leading-relaxed text-[#6f7988] animate-[captureStatusFade_2200ms_ease-in-out_infinite]",children:t},t),e.jsx("div",{className:"mt-4 h-1.5 rounded-full bg-[#eef0f3] overflow-hidden",children:e.jsx("div",{className:"h-full rounded-full bg-[#2563eb] transition-[width] duration-500 ease-out",style:{width:`${Q}%`}})}),r!=="uploading"&&e.jsx("p",{className:"mt-4 text-xs font-medium text-[#6f7988]",children:"You can leave this page. Processing will keep running."}),r==="uploading"&&e.jsx("p",{className:"mt-4 text-xs font-medium text-[#6f7988]",children:"Keep this page open until the upload finishes."})]}),e.jsx("style",{children:`
2          @keyframes captureStatusFade {
3            0%, 100% { opacity: 0.55; }
4            50% { opacity: 1; }
5          }
6        `})]})}return e.jsxs("main",{className:"min-h-dvh bg-[#f7f7f8] text-[#1c1d1f]",children:[e.jsxs("header",{className:"flex h-14 items-center justify-between border-b border-[#e4e7ec] bg-white px-4",children:[e.jsxs("div",{className:"flex min-w-0 items-center gap-2",children:[e.jsx(ge,{className:"h-7 w-auto shrink-0"}),e.jsxs("div",{className:"min-w-0",children:[e.jsx("div",{className:"truncate text-sm font-semibold",children:(o==null?void 0:o.propertyName)||"Valyn capture"}),e.jsxs("div",{className:"truncate text-xs text-[#6f7988]",children:["Single-use link · session ",s]})]})]}),e.jsxs("div",{className:"inline-flex items-center gap-1.5 rounded-full bg-[#eef4ff] px-2.5 py-1 text-xs font-semibold text-[#2563eb]",children:[e.jsx("span",{className:"size-1.5 rounded-full bg-[#2563eb]"}),"PHOTO"]})]}),e.jsxs("section",{className:"mx-auto flex w-full max-w-md flex-col gap-4 px-4 py-6",children:[e.jsx("input",{ref:V,type:"file",accept:"image/*",multiple:!0,className:"hidden",onChange:te}),M&&e.jsxs(fe,{variant:"destructive",children:[e.jsx(H,{}),e.jsx(he,{children:"Camera unavailable"}),e.jsxs(me,{children:[M," You can choose photos from this device instead."]})]}),e.jsxs("div",{className:"flex flex-col items-center gap-5 rounded-2xl border bg-card p-6 text-center shadow-sm",children:[e.jsx("div",{className:"grid size-12 place-items-center rounded-full bg-primary/10 text-primary",children:e.jsx(G,{className:"size-6","aria-hidden":"true"})}),e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsx("h1",{className:"text-xl font-semibold text-foreground",children:"Photograph this property"}),e.jsx("p",{className:"text-sm leading-relaxed text-muted-foreground",children:"Take clear room photos. Include walls, floors, shelves, and close-ups of valuables."})]}),e.jsxs("div",{className:"flex w-full flex-col gap-2",children:[e.jsxs(Z,{type:"button",size:"lg",onClick:q,children:[e.jsx(G,{"data-icon":"inl
6ine-start"}),"Start camera"]}),e.jsxs(Z,{type:"button",size:"lg",variant:"outline",onClick:()=>{var t;return(t=V.current)==null?void 0:t.click()},children:[e.jsx(ue,{"data-icon":"inline-start"}),"Choose photos instead"]})]}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"No Valyn account is required."})]})]}),W&&e.jsx(xe,{onPhotoCaptured:B,onPhotosFinished:$,onClose:()=>j(!1),onCameraReady:R,onCameraError:ee})]})}export{Re as MobileCapturePage};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.