Updated UI changes

This commit is contained in:
Amey Bhagwat 2026-08-18 13:55:29 +05:30
parent 0b44e15b50
commit 9a66c82db6
6 changed files with 128 additions and 73 deletions

View File

@ -49,7 +49,7 @@ export function PhotoWallCropModal({
return (
<div className="fixed inset-0 z-50 flex items-end justify-center bg-black/70 p-4 sm:items-center">
<div className="flex w-full max-w-[420px] flex-col overflow-hidden rounded-2xl card-bg shadow-xl">
<div className="border-b border-[var(--theme-card-border-color)]/5 px-4 py-3">
<div className="border-b border-[var(--theme-card-text-color)]/10 px-4 py-3">
<p className="font-inter text-base font-semibold text-[var(--theme-card-text-color)]">Crop your photo</p>
<p className="mt-0.5 font-inter text-xs text-[var(--theme-card-text-color)]/80">
Adjust to match the wall photo ratio before uploading.

View File

@ -260,7 +260,6 @@ export function LoginPage() {
<div className="min-w-0 space-y-1">
<p className="text-xl font-bold leading-tight text-[var(--theme-card-text-color)] sm:text-2xl">
{headline}
{/* Novel Lung Health Summit 2026 Kochi 22nd & 23rd August 2026 */}
</p>
{preview?.subtitle ? (
<p className="text-sm font-medium text-[var(--theme-card-text-color)]/80">{preview.subtitle}</p>

View File

@ -191,7 +191,7 @@ function MatchCard({
<button
type="button"
onClick={() => void onNativeShare()}
className="inline-flex h-[34px] w-[34px] items-center justify-center rounded-lg border-0 border-[var(--theme-card-text-color)]/45 bg-[var(--theme-card-color)]/80 text-xs font-semibold text-[var(--theme-card-text-color)]"
className="inline-flex h-[34px] w-[34px] items-center justify-center rounded-lg border-0 border-[var(--theme-card-text-color)]/45 bg-[var(--theme-card-text-color)]/50 text-xs font-semibold text-[var(--theme-card-text-color)]"
>
<Share2 className="size-5" />
</button>
@ -202,7 +202,7 @@ function MatchCard({
key={item.key}
type="button"
onClick={() => void onShare(item.key)}
className="inline-flex h-[34px] w-[34px] items-center justify-center rounded-lg border-0 border-[var(--theme-card-text-color)]/45 bg-[var(--theme-card-color)]/80 text-xs font-semibold text-[var(--theme-card-text-color)]"
className="inline-flex h-[34px] w-[34px] items-center justify-center rounded-lg border-0 border-[var(--theme-card-text-color)]/45 bg-[var(--theme-card-text-color)]/50 text-xs font-semibold text-[var(--theme-card-text-color)]"
>
<SharePlatformIcon platform={item.key} />
{/* {item.label} */}

View File

@ -59,9 +59,11 @@ export function PhotoWallPresentPage() {
return (
<div className="eb-gradient-auth min-h-dvh text-white">
<div className="px-4 pb-3 pt-4 text-center card-bg rounded-lg shadow-sm max-w-[400px] mx-auto mb-3">
<p className="text-xs uppercase tracking-[0.2em] text-[var(--theme-card-text-color)]/70">Photo Wall</p>
<h1 className="text-xl font-semibold sm:text-2xl text-[var(--theme-card-text-color)]">{data.eventName}</h1>
<div className="px-4 pb-3 pt-4 text-center card-bg rounded-lg shadow-sm max-w-[720px] mx-auto mb-3">
<p className="text-xs uppercase tracking-[0.2em] text-[var(--theme-card-text-color)]/70 mb-1">Photo Wall</p>
<h1 className="text-lg font-semibold sm:text-xl text-[var(--theme-card-text-color)]">
{data.eventName}
</h1>
</div>
{data.photos.length === 0 ? (
@ -70,7 +72,7 @@ export function PhotoWallPresentPage() {
</div>
) : (
<div
className="mx-auto w-full max-w-[100vw] px-3 pb-6"
className="mx-auto w-full max-w-[100vw] px-3 pb-6 "
style={{
display: "grid",
gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))`,
@ -82,13 +84,13 @@ export function PhotoWallPresentPage() {
{data.photos.map((p) => (
<div
key={p._id}
className="flex w-full flex-col items-center"
className="flex w-full flex-col items-center bg-[var(--theme-card-color)]/50 rounded-xl"
style={{ maxWidth: maxW }}
>
<img
src={p.url}
alt=""
className="w-full rounded-md object-cover shadow-lg"
alt={`${p.displayName || 'Photo Wall'}`}
className={`w-full object-cover shadow-lg relative z-1`}
style={{
maxWidth: maxW,
maxHeight: maxH,
@ -98,7 +100,7 @@ export function PhotoWallPresentPage() {
/>
{showNames && p.displayName ? (
<p
className="mt- w-full truncate text-center text-xs font-medium text-[var(--theme-card-text-color)]/90 bg-[var(--theme-card-color)] rounded-md p-1 sm:text-sm"
className="w-full truncate text-center text-sm font-bold text-[var(--theme-card-text-color)]/90 rounded-md p-2"
title={p.displayName}
>
{p.displayName}

View File

@ -10,12 +10,14 @@ import {
import {
Download,
ImagePlus,
Loader2,
Share2,
} from "lucide-react";
import { useParams } from "react-router-dom";
import { AuthShell } from "../components/auth/AuthShell";
import { HeaderMobile } from "../components/HeaderMobile";
import { MobileShell } from "../components/MobileShell";
import { PhotoWallCropModal } from "../components/PhotoWallCropModal";
import { PhotoWallEventHeader } from "../components/PhotoWallEventHeader";
import { PublicEventChrome } from "../components/PublicEventChrome";
import { SharePlatformIcon } from "../components/SharePlatformIcon";
import {
getPhotoWallUploadMeta,
@ -45,13 +47,29 @@ function fieldKey(f: PhotoWallPublicFormField): string {
return String(f.name || f.label || "").trim();
}
function WhiteCard({ children }: { children: ReactNode }) {
function PhotoWallLayout({
children,
meta,
}: {
children: ReactNode;
meta?: PhotoWallUploadMeta | null;
}) {
return (
<div className="flex w-full max-w-[354px] flex-col items-center">
<div className="w-full rounded-xl shadow-[0_8px_12px_6px_rgba(0,0,0,0.15),0_4px_4px_rgba(0,0,0,0.3)]">
<div className="rounded-xl card-bg text-[var(--theme-card-text-color)] shadow-md px-6 py-6">{children}</div>
</div>
</div>
<MobileShell className="relative bg-transparent">
{meta ? (
<PublicEventChrome
event={meta.event}
eventHeader={meta.eventHeader}
eventNameFallback={meta.eventName}
eventId={meta.eventId}
/>
) : (
<HeaderMobile showRewardsAndLogout={false} showBack={false} />
)}
<main className="relative z-0 flex w-full max-w-none flex-1 flex-col pb-8 pt-6">
<div className="flex w-full max-w-none flex-col px-4">{children}</div>
</main>
</MobileShell>
);
}
@ -91,6 +109,7 @@ export function PhotoWallUploadPage() {
const [submitting, setSubmitting] = useState(false);
const [picking, setPicking] = useState(false);
const [done, setDone] = useState<UploadDoneState | null>(null);
const [resultImageReady, setResultImageReady] = useState(false);
const [actionMsg, setActionMsg] = useState<string | null>(null);
const [cropSession, setCropSession] = useState<CropSession | null>(null);
/** Cached bytes for share/download — avoids CORS/network fail when opening the OS sheet. */
@ -148,6 +167,15 @@ export function PhotoWallUploadPage() {
if (fileRef.current) fileRef.current.value = "";
}, []);
useEffect(() => {
const src = done?.previewUrl || done?.fileUrl;
if (!done || !src) return;
const img = resultImgRef.current;
if (img?.complete && img.naturalWidth > 0) {
setResultImageReady(true);
}
}, [done]);
useEffect(() => {
if (!done?.fileUrl) return;
let cancelled = false;
@ -165,6 +193,7 @@ export function PhotoWallUploadPage() {
const resetForNewUpload = useCallback(() => {
setDone(null);
setResultImageReady(false);
setShareFile(null);
setActionMsg(null);
setErr(null);
@ -221,6 +250,7 @@ export function PhotoWallUploadPage() {
return;
}
setSubmitting(true);
setResultImageReady(false);
setErr(null);
setActionMsg(null);
try {
@ -350,31 +380,46 @@ export function PhotoWallUploadPage() {
if (!uploadToken) {
return (
<AuthShell showLogo={false}>
<WhiteCard>
<p className="text-center text-sm text-red-600">Missing upload link</p>
</WhiteCard>
</AuthShell>
<PhotoWallLayout>
<p className="text-center text-sm text-red-600">Missing upload link</p>
</PhotoWallLayout>
);
}
if (loading) {
return (
<AuthShell showLogo={false}>
<WhiteCard>
<p className="text-center text-sm text-[var(--theme-card-text-color)]/70">Loading…</p>
</WhiteCard>
</AuthShell>
<PhotoWallLayout>
<p className="text-center text-sm text-[var(--theme-card-text-color)]/80">Loading…</p>
</PhotoWallLayout>
);
}
if (err && !meta) {
return (
<AuthShell showLogo={false}>
<WhiteCard>
<p className="text-center text-sm text-red-600">{err}</p>
</WhiteCard>
</AuthShell>
<PhotoWallLayout>
<p className="text-center text-sm text-red-600">{err}</p>
</PhotoWallLayout>
);
}
if (submitting) {
const framing = Boolean(meta?.frameEnabled);
return (
<PhotoWallLayout meta={meta}>
<section className="w-full card-bg rounded-lg shadow-md p-6">
<div className="flex flex-col items-center py-8 text-center">
<div className="mb-5 flex h-16 w-16 items-center justify-center rounded-full bg-[var(--theme-button-color)]/10">
<Loader2 className="h-8 w-8 animate-spin text-[var(--theme-button-color)]" aria-hidden />
</div>
<h2 className="mb-2 text-[22px] font-semibold text-[var(--theme-card-text-color)]">
{framing ? "Creating your photo" : "Uploading your photo"}
</h2>
<p className="text-sm text-[var(--theme-card-text-color)]/80" aria-live="polite">
{framing ? "Hang tight — we're adding your frame." : "This may take a moment."}
</p>
</div>
</section>
</PhotoWallLayout>
);
}
@ -382,13 +427,8 @@ export function PhotoWallUploadPage() {
const canDownload = Boolean(meta?.allowFramedDownload && (done.fileUrl || done.previewUrl));
const showShare = Boolean((done.fileUrl || done.previewUrl) && enabledSharePlatforms.length > 0);
return (
<AuthShell showLogo={false}>
<WhiteCard>
{meta?.event ? (
<div className="mb-4">
<PhotoWallEventHeader event={meta.event} display={meta.eventHeader || null} />
</div>
) : null}
<PhotoWallLayout meta={meta}>
<section className="w-full card-bg rounded-lg shadow-md p-6">
<h1 className="mb-2 text-center text-[22px] font-semibold text-[var(--theme-card-text-color)]">
{done.framed ? "Your framed photo" : "Photo uploaded"}
</h1>
@ -396,17 +436,34 @@ export function PhotoWallUploadPage() {
{done.awaitingApproval
? "Thanks! Your photo is waiting for approval."
: done.framed
? "Here’s your generated photo. You can edit, download, or share it."
? "Here's your generated photo. You can edit, download, or share it."
: "Thanks! Your photo was added to the wall."}
</p>
{(done.fileUrl || done.previewUrl) ? (
<div className="mb-4 overflow-hidden rounded-lg border border-[#e5e5e5] bg-[#f8f8f8]">
<div className="relative mb-4 min-h-[220px] overflow-hidden rounded-lg bg-[var(--theme-card-text-color)]/5">
{!resultImageReady ? (
<div
className="absolute inset-0 z-10 flex flex-col items-center justify-center gap-3"
role="status"
aria-live="polite"
aria-label="Loading photo"
>
<div
className="size-10 animate-spin rounded-full border-[3px] border-[var(--theme-button-color)]/25 border-t-[var(--theme-button-color)]"
aria-hidden
/>
<p className="text-xs font-medium text-[var(--theme-card-text-color)]/70">Preparing your photo…</p>
</div>
) : null}
<img
ref={resultImgRef}
crossOrigin="anonymous"
src={done.previewUrl || done.fileUrl || undefined}
alt={done.framed ? "Your framed photo" : "Uploaded photo"}
className="mx-auto max-h-[360px] w-full object-contain"
className={`mx-auto max-h-[360px] w-full object-contain transition-opacity duration-300 ${
resultImageReady ? "opacity-100" : "opacity-0"
}`}
onLoad={() => setResultImageReady(true)}
onError={(e) => {
const img = e.currentTarget;
// Retry without CORS mode if CDN blocks anonymous reads.
@ -417,7 +474,9 @@ export function PhotoWallUploadPage() {
}
if (done.fileUrl && img.src !== done.fileUrl) {
img.src = done.fileUrl;
return;
}
setResultImageReady(true);
}}
/>
</div>
@ -426,18 +485,18 @@ export function PhotoWallUploadPage() {
<button
type="button"
onClick={resetForNewUpload}
className="inline-flex h-11 w-full items-center justify-center gap-2 rounded-lg bg-[#D32F2F theme-btn-bg px-4 text-sm font-semibold text-white hover:bg-[#b71c1c]"
className="inline-flex h-11 w-full items-center justify-center gap-2 rounded-lg theme-btn-bg px-4 text-sm font-semibold"
>
<ImagePlus className="h-4 w-4" />
<ImagePlus className="size-5" />
Edit / Upload new photo
</button>
{canDownload ? (
<button
type="button"
onClick={() => void onDownload()}
className="inline-flex h-11 w-full items-center justify-center gap-2 rounded-lg border border-[#ddd] bg-white px-4 text-sm font-semibold text-[#333] hover:bg-[#fafafa]"
className="inline-flex h-11 w-full items-center justify-center gap-2 rounded-lg border border-zinc-300 bg-zinc-100 text-gray-600 px-4 text-sm font-semibold"
>
<Download className="h-4 w-4" />
<Download className="size-5" />
Download photo
</button>
) : null}
@ -451,42 +510,37 @@ export function PhotoWallUploadPage() {
<button
type="button"
onClick={() => void onNativeShare()}
className="inline-flex h-11 w-full items-center justify-center gap-2 rounded-lg border border-[#ddd] bg-white px-4 text-sm font-semibold text-[#333] hover:bg-[#fafafa]"
className="inline-flex h-11 w-full items-center justify-center gap-2 rounded-lg border border-zinc-300 bg-zinc-100 text-gray-600 px-4 text-sm font-semibold"
>
<Share2 className="h-4 w-4" />
<Share2 className="size-5" />
Share
</button>
) : (
<div className="grid grid-cols-2 gap-2">
<div className="flex flex-wrap justify-center gap-2">
{enabledSharePlatforms.map((item) => (
<button
key={item.key}
type="button"
onClick={() => void onShare(item.key)}
className="inline-flex h-10 items-center justify-center gap-2 rounded-lg border border-[#ddd] bg-white px-3 text-xs font-semibold text-[#333] hover:bg-[#fafafa]"
className="inline-flex h-10 items-center justify-center gap-2 rounded-lg border border-zinc-300 bg-zinc-100 text-gray-600 px-3 text-xs font-semibold"
>
<SharePlatformIcon platform={item.key} />
{item.label}
{/* {item.label} */}
</button>
))}
</div>
)}
</div>
) : null}
{actionMsg ? <p className="mt-3 text-center text-xs text-[#666]">{actionMsg}</p> : null}
</WhiteCard>
</AuthShell>
{actionMsg ? <p className="mt-3 text-center text-xs text-[var(--theme-card-text-color)]/80">{actionMsg}</p> : null}
</section>
</PhotoWallLayout>
);
}
return (
<AuthShell showLogo={false}>
<WhiteCard>
{meta?.event ? (
<div className="mb-4">
<PhotoWallEventHeader event={meta.event} display={meta.eventHeader || null} />
</div>
) : null}
<PhotoWallLayout meta={meta}>
<section className="w-full card-bg rounded-lg shadow-md p-6">
<h1 className="mb-1 text-center text-[22px] font-semibold text-[var(--theme-card-text-color)]">Upload your photo</h1>
<p className="mb-5 text-center text-sm text-[var(--theme-card-text-color)]/80">
{meta?.form?.description || "Add a selfie to the event photo wall."}
@ -518,13 +572,13 @@ export function PhotoWallUploadPage() {
type="button"
disabled={picking}
onClick={() => fileRef.current?.click()}
className="flex w-full flex-col items-center justify-center gap-2 rounded-lg border-2 border-dashed border-[#ccc] bg-[#fafafa] px-4 py-8 text-sm text-[#666] hover:border-[#D32F2F] hover:bg-[#fff5f5]"
className="flex w-full flex-col items-center justify-center gap-2 rounded-lg border-2 border-dashed border-[var(--theme-card-text-color)]/30 bg-[var(--theme-card-color)]/80 px-4 py-4 text-sm text-[var(--theme-card-text-color)] hover:border-[var(--theme-button-color)]/70"
>
{previewUrl ? (
<img src={previewUrl} alt="Preview" className="max-h-48 rounded-md object-contain" />
) : (
<>
<ImagePlus className="h-8 w-8 text-[#D32F2F]" />
<ImagePlus className="size-8 text-[var(--theme-button-color)]" />
<span>{picking ? "Preparing…" : "Tap to take or choose a photo"}</span>
</>
)}
@ -566,7 +620,7 @@ export function PhotoWallUploadPage() {
onChange={(e) => setValues((v) => ({ ...v, [key]: e.target.value }))}
required={required}
rows={3}
className="w-full rounded-lg border border-[rgba(116,112,112,0.3)] px-3 py-2 text-sm transition-colors duration-300 ease-in-out focus:border-[var(--theme-button-color)] focus:outline-none focus:ring-2 focus:ring-[var(--theme-button-color)]/30"
className="w-full rounded-lg border border-[var(--theme-card-text-color)]/10 bg-[var(--theme-card-color)]/40 px-3 py-2 text-sm transition-colors duration-300 ease-in-out focus:border-[var(--theme-button-color)] focus:outline-none focus:ring-2 focus:ring-[var(--theme-button-color)]/30"
/>
</label>
);
@ -582,7 +636,7 @@ export function PhotoWallUploadPage() {
value={String(values[key] ?? "")}
onChange={(e) => setValues((v) => ({ ...v, [key]: e.target.value }))}
required={required}
className="h-10 w-full rounded-lg border border-[rgba(116,112,112,0.3)] px-3 text-sm transition-colors duration-300 ease-in-out focus:border-[var(--theme-button-color)] focus:outline-none focus:ring-2 focus:ring-[var(--theme-button-color)]/30"
className="h-10 w-full rounded-lg border border-[var(--theme-card-text-color)]/10 bg-[var(--theme-card-color)]/40 px-3 text-sm transition-colors duration-300 ease-in-out focus:border-[var(--theme-button-color)] focus:outline-none focus:ring-2 focus:ring-[var(--theme-button-color)]/30"
/>
</label>
);
@ -593,13 +647,13 @@ export function PhotoWallUploadPage() {
<button
type="submit"
disabled={!file || submitting || picking}
className="inline-flex h-11 w-full items-center justify-center rounded-lg bg-[#D32F2F theme-btn-bg px-4 text-sm font-semibold text-white hover:bg-[#b71c1c] disabled:opacity-50"
className="inline-flex h-11 w-full items-center justify-center rounded-lg theme-btn-bg px-4 text-sm font-semibold text-white disabled:opacity-50"
>
{submitting ? "Uploading…" : "Upload"}
</button>
</form>
)}
</WhiteCard>
</section>
{cropSession && meta ? (
<PhotoWallCropModal
@ -620,6 +674,6 @@ export function PhotoWallUploadPage() {
}}
/>
) : null}
</AuthShell>
</PhotoWallLayout>
);
}

View File

@ -397,7 +397,7 @@ export function SocialSharePage() {
Here's your framed photo. You can download or share it.
</p>
{(done.fileUrl || done.previewUrl) ? (
<div className="relative mb-4 min-h-[220px] overflow-hidden rounded-lg bg-[var(--theme-card-text-color)]/5">
<div className="relative mb-4 min-h-[220px] overflow-hidden rounded-lg">
{!resultImageReady ? (
<div
className="absolute inset-0 z-10 flex flex-col items-center justify-center gap-3"
@ -489,7 +489,7 @@ export function SocialSharePage() {
)}
</div>
) : null}
{actionMsg ? <p className="mt-3 text-center text-xs text-[#666]">{actionMsg}</p> : null}
{actionMsg ? <p className="mt-3 text-center text-xs text-[var(--theme-card-text-color)]/80">{actionMsg}</p> : null}
</section>
</SocialShareLayout>
);