feat: Return S3 keys from image uploads and resolve them for display previews in the CMS.

This commit is contained in:
pradeepkumar
2026-03-05 05:51:45 +05:30
parent d71b9a27c9
commit bf70ea1671

View File

@@ -289,15 +289,14 @@ export default function CmsPage() {
} }
} }
// Generic image upload — returns the presigned download URL // Generic image upload — returns the S3 key for permanent storage
async function handleImageUpload(file: File): Promise<string> { async function handleImageUpload(file: File): Promise<string> {
setIsUploadingImage(true); setIsUploadingImage(true);
setModalError(''); setModalError('');
try { try {
const { uploadUrl, key } = await uploadService.getPresignedUploadUrl(file.name, file.type, 'properties'); const { uploadUrl, key } = await uploadService.getPresignedUploadUrl(file.name, file.type, 'properties');
await uploadService.uploadFileToS3(uploadUrl, file); await uploadService.uploadFileToS3(uploadUrl, file);
const downloadUrl = await uploadService.getPresignedDownloadUrl(key); return key;
return downloadUrl;
} catch (err) { } catch (err) {
setModalError(getErrorMessage(err)); setModalError(getErrorMessage(err));
return ''; return '';
@@ -306,6 +305,19 @@ export default function CmsPage() {
} }
} }
// Resolve an S3 key or URL for display — returns a viewable URL
async function resolveImageUrl(value: string): Promise<string> {
if (!value) return '';
// Already a full URL or local asset path — use directly
if (value.startsWith('http') || value.startsWith('/')) return value;
// Treat as S3 key — get presigned download URL
try {
return await uploadService.getPresignedDownloadUrl(value);
} catch {
return value;
}
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Section-specific form renderers // Section-specific form renderers
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -649,14 +661,27 @@ export default function CmsPage() {
); );
} }
// Reusable image upload field // Reusable image upload field with S3 key resolution for preview
function renderImageUploadField(label: string, currentUrl: string, onUrlChange: (url: string) => void) { function ImageUploadField({ label, currentUrl, onUrlChange }: { label: string; currentUrl: string; onUrlChange: (url: string) => void }) {
const [previewUrl, setPreviewUrl] = useState('');
useEffect(() => {
if (!currentUrl) { setPreviewUrl(''); return; }
// If it's already a full URL or local path, use directly
if (currentUrl.startsWith('http') || currentUrl.startsWith('/')) {
setPreviewUrl(currentUrl);
} else {
// S3 key — resolve to presigned URL for preview
resolveImageUrl(currentUrl).then(setPreviewUrl);
}
}, [currentUrl]);
return ( return (
<div> <div>
<label className="block text-sm font-medium text-[#00293d] mb-1">{label}</label> <label className="block text-sm font-medium text-[#00293d] mb-1">{label}</label>
{currentUrl && ( {previewUrl && (
<div className="mb-3 relative rounded-lg overflow-hidden border border-[#e5e7eb]"> <div className="mb-3 relative rounded-lg overflow-hidden border border-[#e5e7eb]">
<img src={currentUrl} alt="Preview" className="w-full h-48 object-cover" onError={(e) => { (e.target as HTMLImageElement).style.display = 'none'; }} /> <img src={previewUrl} alt="Preview" className="w-full h-48 object-cover" onError={(e) => { (e.target as HTMLImageElement).style.display = 'none'; }} />
<button type="button" onClick={() => onUrlChange('')} className="absolute top-2 right-2 px-2 py-1 bg-red-600 text-white text-xs rounded-lg hover:bg-red-700 transition-colors">Remove</button> <button type="button" onClick={() => onUrlChange('')} className="absolute top-2 right-2 px-2 py-1 bg-red-600 text-white text-xs rounded-lg hover:bg-red-700 transition-colors">Remove</button>
</div> </div>
)} )}
@@ -671,11 +696,15 @@ export default function CmsPage() {
</label> </label>
<span className="text-xs text-[#666666] self-center">or enter URL below</span> <span className="text-xs text-[#666666] self-center">or enter URL below</span>
</div> </div>
<input type="text" className={inputCls} value={currentUrl} onChange={(e) => onUrlChange(e.target.value)} placeholder="https://..." /> <input type="text" className={inputCls} value={currentUrl} onChange={(e) => onUrlChange(e.target.value)} placeholder="S3 key or https://..." />
</div> </div>
); );
} }
function renderImageUploadField(label: string, currentUrl: string, onUrlChange: (url: string) => void) {
return <ImageUploadField label={label} currentUrl={currentUrl} onUrlChange={onUrlChange} />;
}
// --- About Us form renderers --- // --- About Us form renderers ---
function renderAboutHeroForm() { function renderAboutHeroForm() {