Fotografías y documentos
Originales protegidos con hash SHA-256, autor, fecha y ubicación opcional. Formatos permitidos: JPG, PNG, WebP y PDF de hasta 15 MB.
{error &&Fotografías
{media.description}
}Documentos
{media.description}
}import { SearchableSelect } from '../../components/SearchableSelect';
import { useEffect, useRef, useState } from 'react';
import type { FormEvent } from 'react';
import { Alert, LoadingBlock, errorMessage } from '../../components/Feedback';
import { Icon } from '../../components/Icon';
import {
getAssetMediaBlob,
listAssetMedia,
removeAssetMedia,
updateAssetMedia,
uploadAssetMedia,
} from '../../lib/api';
import type { AssetMedia, AssetMediaKind } from '../../lib/api';
import { formatDate } from '../../lib/format';
function localDateTime(value: string | number | Date): string {
const date = new Date(value);
if (Number.isNaN(date.getTime())) return '';
const local = new Date(date.getTime() - date.getTimezoneOffset() * 60_000);
return local.toISOString().slice(0, 16);
}
function fileSize(value: number) {
return value >= 1024 * 1024
? `${(value / (1024 * 1024)).toFixed(1)} MB`
: `${Math.max(1, Math.round(value / 1024))} KB`;
}
function AssetPhotoPreview({ media }: { media: AssetMedia }) {
const [url, setUrl] = useState('');
useEffect(() => {
let active = true;
let objectUrl = '';
getAssetMediaBlob(media.id)
.then((blob) => {
if (!active) return;
objectUrl = URL.createObjectURL(blob);
setUrl(objectUrl);
})
.catch(() => undefined);
return () => {
active = false;
if (objectUrl) URL.revokeObjectURL(objectUrl);
};
}, [media.id]);
return url
?
:
Originales protegidos con hash SHA-256, autor, fecha y ubicación opcional. Formatos permitidos: JPG, PNG, WebP y PDF de hasta 15 MB.
{error &&{media.description}
}{media.description}
}