Просмотр исходного кода

fix info page, configurable external links, downloadable immages

Sviluppo_Carrello_Immagini
Lorenzo Pollutri 1 месяц назад
Родитель
Сommit
b89f2c5345
2 измененных файлов: 45 добавлений и 4 удалений
  1. +44
    -4
      app/admin/page.tsx
  2. +1
    -0
      types/index.ts

+ 44
- 4
app/admin/page.tsx Просмотреть файл

@@ -2,7 +2,7 @@
import { useState, useEffect, useRef } from 'react';
import { Card, Portal, MediaItem, CardType } from '@/types';
import { EXTERNAL_LINK_ENABLED } from '@/lib/config';
import { EXTERNAL_LINK_ENABLED as EXTERNAL_LINK_DEFAULT } from '@/lib/config';
function CardTypeSelect({
value,
@@ -72,6 +72,13 @@ const isPdfFile = (file: File) =>
const isVideoFile = (file: File) =>
file.type.startsWith('video/') || /\.(mp4|webm|mov|m4v|ogv)$/i.test(file.name);
const extractFileName = (url: string): string => {
const match = url.match(/[?&]name=([^&]+)/);
if (match) return decodeURIComponent(match[1]);
const seg = url.split('/').pop() || 'download';
return seg.split('?')[0];
};
async function uploadBlobAsImage(blob: Blob, name: string): Promise<string | null> {
const formData = new FormData();
formData.append('file', new File([blob], name, { type: blob.type || 'image/png' }));
@@ -168,6 +175,9 @@ export default function AdminDashboard() {
const [confirmDialog, setConfirmDialog] = useState<{ message: string, onConfirm: () => void } | null>(null);
const [pdfProgress, setPdfProgress] = useState<{ name: string; page: number; total: number } | null>(null);
// External Link feature flag: priorità al setting del portale, fallback alla costante in lib/config.
const externalLinksOn = portal.externalLinkEnabled ?? EXTERNAL_LINK_DEFAULT;
// Helper to show auto-dismissing toast
const showToast = (message: string) => {
setToast(message);
@@ -488,6 +498,7 @@ export default function AdminDashboard() {
{portal.logoUrl && (
<div className="mt-2 bg-gray-100 p-4 rounded inline-block relative border">
<img src={portal.logoUrl} className="h-16 object-contain" alt="Logo Preview" />
<a href={portal.logoUrl} download={extractFileName(portal.logoUrl)} className="absolute -top-2 right-6 bg-gray-700 hover:bg-gray-800 text-white w-6 h-6 rounded-full text-xs font-bold shadow flex items-center justify-center" title="Scarica" aria-label="Scarica logo">⬇</a>
<button onClick={() => setPortal({...portal, logoUrl: ''})} className="absolute -top-2 -right-2 bg-red-500 text-white w-6 h-6 rounded-full text-xs font-bold hover:bg-red-600 shadow">✕</button>
</div>
)}
@@ -501,12 +512,13 @@ export default function AdminDashboard() {
{portal.heroImageUrl && (
<div className="mt-2 relative rounded shadow border inline-block w-full">
<img src={portal.heroImageUrl} className="h-32 w-full object-cover rounded" alt="Hero Preview" />
<a href={portal.heroImageUrl} download={extractFileName(portal.heroImageUrl)} className="absolute top-2 right-12 bg-gray-700 hover:bg-gray-800 text-white w-8 h-8 flex items-center justify-center rounded-full text-sm font-bold shadow-lg" title="Scarica" aria-label="Scarica hero">⬇</a>
<button onClick={() => setPortal({...portal, heroImageUrl: ''})} className="absolute top-2 right-2 bg-red-500 text-white w-8 h-8 flex items-center justify-center rounded-full text-sm font-bold hover:bg-red-600 shadow-lg">✕</button>
</div>
)}
</div>
<div className="bg-gray-50 p-4 rounded-lg border border-gray-200">
<div className="bg-gray-50 p-4 rounded-lg border border-gray-200 space-y-3">
<label className="flex items-center gap-3 cursor-pointer">
<input type="checkbox" checked={!!portal.fadeHeroImage} onChange={e => setPortal({...portal, fadeHeroImage: e.target.checked})} className="w-5 h-5 text-blue-600 rounded" />
<div>
@@ -514,6 +526,18 @@ export default function AdminDashboard() {
<span className="block text-xs text-gray-600">Creates a smooth gradient from the top of the image into the solid theme color at the bottom.</span>
</div>
</label>
<label className="flex items-center gap-3 cursor-pointer">
<input
type="checkbox"
checked={portal.externalLinkEnabled ?? EXTERNAL_LINK_DEFAULT}
onChange={e => setPortal({...portal, externalLinkEnabled: e.target.checked})}
className="w-5 h-5 text-blue-600 rounded"
/>
<div>
<span className="block text-sm font-semibold text-gray-900">Abilita &ldquo;External Link&rdquo;</span>
<span className="block text-xs text-gray-600">Mostra il tipo &ldquo;External Link&rdquo; nel dropdown del Card Type. Le card esistenti di quel tipo restano comunque visibili e cliccabili.</span>
</div>
</label>
</div>
</div>
</div>
@@ -564,7 +588,7 @@ export default function AdminDashboard() {
options={[
{ value: 'INFO_PAGE', label: 'Info Page' },
{ value: 'IMAGE_GALLERY', label: 'Image Gallery' },
...(EXTERNAL_LINK_ENABLED ? [{ value: 'EXTERNAL_LINK' as CardType, label: 'External Link' }] : []),
...(externalLinksOn ? [{ value: 'EXTERNAL_LINK' as CardType, label: 'External Link' }] : []),
]}
/>
</div>
@@ -638,6 +662,13 @@ export default function AdminDashboard() {
{isEditing.imageUrl && (
<div className="mt-3 relative rounded-lg overflow-hidden border border-gray-200 group">
<img src={isEditing.imageUrl} className="w-full h-32 object-cover" alt="Cover preview" />
<a
href={isEditing.imageUrl}
download={extractFileName(isEditing.imageUrl)}
className="absolute top-2 right-12 bg-gray-700 hover:bg-gray-800 text-white w-8 h-8 rounded-full text-sm font-bold shadow opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center"
title="Scarica"
aria-label="Scarica cover"
>⬇</a>
<button
onClick={() => setIsEditing({...isEditing, imageUrl: ''})}
className="absolute top-2 right-2 bg-red-500 text-white w-8 h-8 rounded-full text-sm font-bold shadow opacity-0 group-hover:opacity-100 transition-opacity hover:bg-red-600"
@@ -647,7 +678,8 @@ export default function AdminDashboard() {
)}
</div>
{/* Gallery Media (images + videos + PDFs) */}
{/* Gallery Media (images + videos + PDFs) — nascosta per INFO_PAGE (solo cover ammessa) */}
{isEditing.cardType !== 'INFO_PAGE' && (
<div>
<label className="block text-sm font-semibold text-gray-800 mb-1">
Gallery Media <span className="text-gray-400 font-normal text-xs">(images, videos or PDFs — PDF pages become images)</span>
@@ -712,6 +744,13 @@ export default function AdminDashboard() {
</div>
)}
</div>
<a
href={item.url}
download={extractFileName(item.url)}
className="bg-gray-500 hover:bg-gray-600 text-white w-8 h-8 rounded-full text-sm font-bold shrink-0 flex items-center justify-center"
title="Scarica"
aria-label="Scarica"
>⬇</a>
<button
onClick={() => removeExtraMedia(i)}
className="bg-red-500 hover:bg-red-600 text-white w-8 h-8 rounded-full text-sm font-bold shrink-0"
@@ -723,6 +762,7 @@ export default function AdminDashboard() {
</div>
)}
</div>
)}
</div>
</div>


+ 1
- 0
types/index.ts Просмотреть файл

@@ -31,4 +31,5 @@ export interface Portal {
themeColor: string;
fadeHeroImage?: boolean;
maxGridColumns?: number;
externalLinkEnabled?: boolean; // se false, l'admin nasconde il tipo "External Link" nel dropdown
}

Загрузка…
Отмена
Сохранить