feat: Return S3 keys from image uploads and resolve them for display previews in the CMS.
This commit is contained in:
@@ -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> {
|
||||
setIsUploadingImage(true);
|
||||
setModalError('');
|
||||
try {
|
||||
const { uploadUrl, key } = await uploadService.getPresignedUploadUrl(file.name, file.type, 'properties');
|
||||
await uploadService.uploadFileToS3(uploadUrl, file);
|
||||
const downloadUrl = await uploadService.getPresignedDownloadUrl(key);
|
||||
return downloadUrl;
|
||||
return key;
|
||||
} catch (err) {
|
||||
setModalError(getErrorMessage(err));
|
||||
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
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -649,14 +661,27 @@ export default function CmsPage() {
|
||||
);
|
||||
}
|
||||
|
||||
// Reusable image upload field
|
||||
function renderImageUploadField(label: string, currentUrl: string, onUrlChange: (url: string) => void) {
|
||||
// Reusable image upload field with S3 key resolution for preview
|
||||
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 (
|
||||
<div>
|
||||
<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]">
|
||||
<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>
|
||||
</div>
|
||||
)}
|
||||
@@ -671,11 +696,15 @@ export default function CmsPage() {
|
||||
</label>
|
||||
<span className="text-xs text-[#666666] self-center">or enter URL below</span>
|
||||
</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>
|
||||
);
|
||||
}
|
||||
|
||||
function renderImageUploadField(label: string, currentUrl: string, onUrlChange: (url: string) => void) {
|
||||
return <ImageUploadField label={label} currentUrl={currentUrl} onUrlChange={onUrlChange} />;
|
||||
}
|
||||
|
||||
// --- About Us form renderers ---
|
||||
|
||||
function renderAboutHeroForm() {
|
||||
|
||||
Reference in New Issue
Block a user