Fix PostCard image rendering and ID display, plus image backfill automation
This commit is contained in:
parent
946eae2a25
commit
81ccb397f5
@ -1,74 +0,0 @@
|
||||
const { Client, Databases } = require('node-appwrite');
|
||||
|
||||
async function checkConfig() {
|
||||
console.log('🔍 Validando configuración de Almeira Web...\n');
|
||||
|
||||
const requiredVars = [
|
||||
'NEXT_PUBLIC_APPWRITE_ENDPOINT',
|
||||
'NEXT_PUBLIC_APPWRITE_PROJECT_ID',
|
||||
'APPWRITE_API_KEY',
|
||||
'APPWRITE_DATABASE_ID'
|
||||
];
|
||||
|
||||
let missing = false;
|
||||
requiredVars.forEach(v => {
|
||||
if (!process.env[v]) {
|
||||
console.error(`❌ Faltante: Variable de entorno ${v}`);
|
||||
missing = true;
|
||||
} else {
|
||||
console.log(`✅ Presente: ${v}`);
|
||||
}
|
||||
});
|
||||
|
||||
if (missing) {
|
||||
console.error('\n🛑 Error: Faltan variables de entorno críticas. Revisa tu archivo .env.local.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint(process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT)
|
||||
.setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID)
|
||||
.setKey(process.env.APPWRITE_API_KEY);
|
||||
|
||||
const databases = new Databases(client);
|
||||
const databaseId = process.env.APPWRITE_DATABASE_ID;
|
||||
|
||||
try {
|
||||
console.log(`\n📡 Conectando a Appwrite (${process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT})...`);
|
||||
const db = await databases.get(databaseId);
|
||||
console.log(`✅ Conexión exitosa a la base de datos: "${db.name}" (${databaseId})`);
|
||||
|
||||
const collectionsToCheck = [
|
||||
'posts',
|
||||
'settings',
|
||||
'publication_types',
|
||||
'categories',
|
||||
'whatsapp_messages',
|
||||
'entidad'
|
||||
];
|
||||
|
||||
console.log('\n📦 Verificando colecciones críticas...');
|
||||
for (const colId of collectionsToCheck) {
|
||||
try {
|
||||
const col = await databases.getCollection(databaseId, colId);
|
||||
console.log(`✅ Colección encontrada: ${col.name} (${colId})`);
|
||||
} catch (err) {
|
||||
console.error(`❌ Error en colección ${colId}: ${err.message}`);
|
||||
missing = true;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
console.error(`❌ Error de conexión a Appwrite: ${err.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (missing) {
|
||||
console.error('\n🛑 Algunas colecciones no fueron encontradas. Revisa la base de datos en Appwrite.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log('\n✨ ¡Configuración validada correctamente para producción!');
|
||||
}
|
||||
|
||||
checkConfig();
|
||||
@ -1,18 +0,0 @@
|
||||
const sdk = require('node-appwrite');
|
||||
const client = new sdk.Client();
|
||||
const databases = new sdk.Databases(client);
|
||||
|
||||
const ENDPOINT = process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT || 'https://appwrite.almeira.com.co/v1';
|
||||
const PROJECT_ID = process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID || '69b84df5003bd8957c5c';
|
||||
const API_KEY = process.env.APPWRITE_API_KEY || 'standard_ee81a4ca6784c0fa6a23abab0a983dbba9cd0c2d8e211a00c759d9801f5d8e27b43a605bfa373e13106da87acfc7673608f0ffb0eb04e10cae3dae93d2404bfda8676f96e3fcfd6bd642f6e12d69367916b796c54e98e2e4c4bc0b7a27aa42ba5048582fcbd45b1365b51c5014cb939c1853923c418048ff0e39c2237065da3c';
|
||||
|
||||
client.setEndpoint(ENDPOINT).setProject(PROJECT_ID).setKey(API_KEY);
|
||||
|
||||
async function check() {
|
||||
try {
|
||||
console.log("Chequeando Tipos de Publicacion...");
|
||||
const res = await databases.listDocuments('almeira', 'publication_types', []);
|
||||
console.log(JSON.stringify(res.documents.map(d => ({id: d.$id, c: d.codigo, d: d.descripcion})), null, 2));
|
||||
} catch(e) { console.error(e.message); }
|
||||
}
|
||||
check();
|
||||
140
import_openclaw.js
Normal file
140
import_openclaw.js
Normal file
@ -0,0 +1,140 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execSync } = require('child_process');
|
||||
const { Client, Databases, ID } = require('node-appwrite');
|
||||
require('dotenv').config({ path: '.env.local' });
|
||||
const mammoth = require('mammoth');
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint(process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT)
|
||||
.setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID)
|
||||
.setKey(process.env.APPWRITE_API_KEY);
|
||||
|
||||
const databases = new Databases(client);
|
||||
const dbId = process.env.APPWRITE_DATABASE_ID;
|
||||
const collectionId = 'posts';
|
||||
const defaultTipoPublicacionId = '69c2b91400362d5d52b6'; // NOTICIA
|
||||
|
||||
const DIR_PENDIENTES = '/home/carlos/Documentos/Pendientes';
|
||||
|
||||
function generateSlug(titulo) {
|
||||
return titulo.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/[^a-z0-9\s-]/g, '').trim().replace(/\s+/g, '-').replace(/-+/g, '-').substring(0, 100);
|
||||
}
|
||||
|
||||
function truncateString(str, length) {
|
||||
if (!str) return '';
|
||||
return str.length > length ? str.substring(0, length - 3) + '...' : str;
|
||||
}
|
||||
|
||||
function extractDate(filename) {
|
||||
const regex = /(\d{1,2})\s+DE\s+([A-Z]+)\s+DEL?\s+(\d{4})/i;
|
||||
const match = filename.match(regex);
|
||||
if (match) {
|
||||
const months = { 'enero': 0, 'febrero': 1, 'marzo': 2, 'abril': 3, 'mayo': 4, 'junio': 5, 'julio': 6, 'agosto': 7, 'septiembre': 8, 'octubre': 9, 'noviembre': 10, 'diciembre': 11 };
|
||||
const monthStr = match[2].toLowerCase();
|
||||
if (months[monthStr] !== undefined) {
|
||||
return new Date(parseInt(match[3], 10), months[monthStr], parseInt(match[1], 10), 12, 0, 0).toISOString();
|
||||
}
|
||||
}
|
||||
return new Date().toISOString();
|
||||
}
|
||||
|
||||
function extractDriveUrl(text) {
|
||||
const match = text.match(/https:\/\/drive\.google\.com\/[^\s\n]+/);
|
||||
return match ? match[0] : null;
|
||||
}
|
||||
|
||||
async function insertPost(title, contentBlocks, driveUrl, filename) {
|
||||
if (!title || contentBlocks.length === 0) return;
|
||||
|
||||
let pureContent = contentBlocks.filter(b => !b.includes('http')).map(p => `<p>${p}</p>`).join('');
|
||||
|
||||
const docData = {
|
||||
titulo: truncateString(title, 70),
|
||||
slug: generateSlug(title) + '-' + Math.random().toString(36).substring(2, 7),
|
||||
contenido: pureContent,
|
||||
resumen_redes: truncateString(contentBlocks[0], 155),
|
||||
seo_titulo: truncateString(title, 60),
|
||||
seo_descripcion: truncateString(contentBlocks[0], 155),
|
||||
estado: 'borrador',
|
||||
tipo_publicacion_id: defaultTipoPublicacionId,
|
||||
fecha_publicacion: extractDate(filename),
|
||||
...(driveUrl && { url_destino: driveUrl })
|
||||
};
|
||||
|
||||
try {
|
||||
await databases.createDocument(dbId, collectionId, ID.unique(), docData);
|
||||
console.log(`✅ Creado: ${docData.titulo}`);
|
||||
} catch (e) {
|
||||
console.error(`❌ Error en ${title}:`, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function processFile(filePath, filename) {
|
||||
const ext = path.extname(filename).toLowerCase();
|
||||
|
||||
if (ext === '.docx') {
|
||||
const result = await mammoth.extractRawText({ path: filePath });
|
||||
const lines = result.value.split('\n').map(l => l.trim()).filter(l => l.length > 0);
|
||||
if (lines.length > 0) {
|
||||
const title = lines[0];
|
||||
const contentBlocks = lines.slice(1);
|
||||
const driveUrl = extractDriveUrl(result.value);
|
||||
await insertPost(title, contentBlocks, driveUrl, filename);
|
||||
}
|
||||
} else if (ext === '.pdf') {
|
||||
const text = execSync(`pdftotext "${filePath}" -`).toString();
|
||||
const pages = text.split('\x0C').map(p => p.trim()).filter(p => p.length > 0);
|
||||
|
||||
for (const page of pages) {
|
||||
const lines = page.split('\n').map(l => l.trim()).filter(l => l.length > 0);
|
||||
if (lines.length < 3) continue;
|
||||
|
||||
let startIndex = 0;
|
||||
if (lines[0].toLowerCase().includes('boletín')) {
|
||||
startIndex = 1; // skip header
|
||||
}
|
||||
|
||||
const title = lines[startIndex];
|
||||
const contentBlocks = [];
|
||||
let currentBlock = [];
|
||||
for (let i = startIndex + 1; i < lines.length; i++) {
|
||||
currentBlock.push(lines[i]);
|
||||
if (lines[i].endsWith('.') || lines[i].includes('http')) {
|
||||
contentBlocks.push(currentBlock.join(' '));
|
||||
currentBlock = [];
|
||||
}
|
||||
}
|
||||
if (currentBlock.length > 0) contentBlocks.push(currentBlock.join(' '));
|
||||
|
||||
const driveUrl = extractDriveUrl(page);
|
||||
await insertPost(title, contentBlocks, driveUrl, filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
let files = fs.readdirSync(DIR_PENDIENTES).filter(f => f.endsWith('.pdf') || f.endsWith('.docx'));
|
||||
|
||||
files.sort((a, b) => {
|
||||
const numA = parseInt(a.match(/\d+/)) || 0;
|
||||
const numB = parseInt(b.match(/\d+/)) || 0;
|
||||
return numA - numB;
|
||||
});
|
||||
|
||||
// Only March and April: Boletines 22 to 37
|
||||
files = files.filter(f => {
|
||||
const num = parseInt(f.match(/\d+/)) || 0;
|
||||
return num >= 22 && num <= 37;
|
||||
});
|
||||
|
||||
console.log(`Procesando ${files.length} archivos de Marzo y Abril...`);
|
||||
|
||||
for (const file of files) {
|
||||
console.log(`\n📄 Iniciando: ${file}`);
|
||||
await processFile(path.join(DIR_PENDIENTES, file), file);
|
||||
}
|
||||
console.log('\n¡Proceso finalizado!');
|
||||
}
|
||||
|
||||
main();
|
||||
166
import_pendientes.js
Normal file
166
import_pendientes.js
Normal file
@ -0,0 +1,166 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { Client, Databases, ID } = require('node-appwrite');
|
||||
require('dotenv').config({ path: '.env.local' });
|
||||
const pdfParse = require('pdf-parse');
|
||||
const mammoth = require('mammoth');
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint(process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT)
|
||||
.setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID)
|
||||
.setKey(process.env.APPWRITE_API_KEY);
|
||||
|
||||
const databases = new Databases(client);
|
||||
const dbId = process.env.APPWRITE_DATABASE_ID;
|
||||
const collectionId = 'posts';
|
||||
const defaultTipoPublicacionId = '69c2b91400362d5d52b6'; // ID de NOTICIA obtenido anteriormente
|
||||
|
||||
const DIR_PENDIENTES = '/home/carlos/Documentos/Pendientes';
|
||||
const DIR_PROCESADOS = path.join(DIR_PENDIENTES, 'Procesados');
|
||||
|
||||
if (!fs.existsSync(DIR_PROCESADOS)) {
|
||||
fs.mkdirSync(DIR_PROCESADOS);
|
||||
}
|
||||
|
||||
function generateSlug(titulo) {
|
||||
return titulo
|
||||
.toLowerCase()
|
||||
.normalize('NFD')
|
||||
.replace(/[\u0300-\u036f]/g, '')
|
||||
.replace(/[^a-z0-9\s-]/g, '')
|
||||
.trim()
|
||||
.replace(/\s+/g, '-')
|
||||
.replace(/-+/g, '-')
|
||||
.substring(0, 100);
|
||||
}
|
||||
|
||||
function formatContent(text) {
|
||||
const paragraphs = text.split(/\n+/);
|
||||
const html = paragraphs
|
||||
.map(p => p.trim())
|
||||
.filter(p => p.length > 0)
|
||||
.map(p => `<p>${p}</p>`)
|
||||
.join('');
|
||||
return html;
|
||||
}
|
||||
|
||||
function extractSummary(text) {
|
||||
const paragraphs = text.split(/\n+/).map(p => p.trim()).filter(p => p.length > 30);
|
||||
if (paragraphs.length > 0) {
|
||||
return paragraphs[0].substring(0, 200) + (paragraphs[0].length > 200 ? '...' : '');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function determineTipo(filename) {
|
||||
const lower = filename.toLowerCase();
|
||||
if (lower.includes('evento')) return 'evento';
|
||||
if (lower.includes('aviso')) return 'aviso';
|
||||
if (lower.includes('comunicado')) return 'comunicado';
|
||||
if (lower.includes('galeria')) return 'galeria';
|
||||
return 'noticia';
|
||||
}
|
||||
|
||||
function formatTitle(filename) {
|
||||
let base = path.basename(filename, path.extname(filename)).replace(/\(1\)/g, '').trim();
|
||||
// Capitalize first letter of each word to look better
|
||||
return base.split(' ').map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(' ');
|
||||
}
|
||||
|
||||
function extractDate(filename, text) {
|
||||
const months = {
|
||||
'enero': 0, 'febrero': 1, 'marzo': 2, 'abril': 3, 'mayo': 4, 'junio': 5,
|
||||
'julio': 6, 'agosto': 7, 'septiembre': 8, 'octubre': 9, 'noviembre': 10, 'diciembre': 11
|
||||
};
|
||||
|
||||
// Attempt 1: From filename e.g. "28 DE MARZO DEL 2026"
|
||||
const regex = /(\d{1,2})\s+DE\s+([A-Z]+)\s+(DEL?|DE)\s+(\d{4})/i;
|
||||
let match = filename.match(regex);
|
||||
if (!match) {
|
||||
// Attempt 2: From the first few lines of text
|
||||
const firstLines = text.split('\n').slice(0, 10).join(' ');
|
||||
match = firstLines.match(regex);
|
||||
}
|
||||
|
||||
if (match) {
|
||||
const day = parseInt(match[1], 10);
|
||||
const monthStr = match[2].toLowerCase();
|
||||
const year = parseInt(match[4], 10);
|
||||
|
||||
if (months[monthStr] !== undefined) {
|
||||
const date = new Date(year, months[monthStr], day, 12, 0, 0); // Noon to avoid timezone issues
|
||||
return date.toISOString();
|
||||
}
|
||||
}
|
||||
|
||||
return new Date().toISOString(); // fallback
|
||||
}
|
||||
|
||||
async function processFile(filePath, filename) {
|
||||
const ext = path.extname(filename).toLowerCase();
|
||||
let text = '';
|
||||
|
||||
try {
|
||||
if (ext === '.pdf') {
|
||||
const { execSync } = require('child_process');
|
||||
text = execSync(`pdftotext "${filePath}" -`).toString();
|
||||
} else if (ext === '.docx') {
|
||||
const result = await mammoth.extractRawText({ path: filePath });
|
||||
text = result.value;
|
||||
} else if (ext === '.txt') {
|
||||
text = fs.readFileSync(filePath, 'utf8');
|
||||
} else {
|
||||
console.log(`Skipping unsupported file: ${filename}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!text || text.trim().length === 0) {
|
||||
console.log(`File empty or failed to extract: ${filename}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
const titulo = formatTitle(filename);
|
||||
const slug = generateSlug(titulo) + '-' + Math.random().toString(36).substring(2, 7);
|
||||
const contenido = formatContent(text);
|
||||
const tipo = determineTipo(filename);
|
||||
const resumen_redes = extractSummary(text);
|
||||
|
||||
const docData = {
|
||||
titulo,
|
||||
slug,
|
||||
contenido,
|
||||
resumen_redes,
|
||||
estado: 'publicado',
|
||||
tipo_publicacion_id: defaultTipoPublicacionId,
|
||||
fecha_publicacion: extractDate(filename, text),
|
||||
};
|
||||
|
||||
await databases.createDocument(dbId, collectionId, ID.unique(), docData);
|
||||
console.log(`✅ Publicado: ${titulo}`);
|
||||
|
||||
// Mover a procesados
|
||||
fs.renameSync(filePath, path.join(DIR_PROCESADOS, filename));
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error(`❌ Error procesando ${filename}:`, error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const files = fs.readdirSync(DIR_PENDIENTES);
|
||||
let successCount = 0;
|
||||
|
||||
for (const file of files) {
|
||||
const filePath = path.join(DIR_PENDIENTES, file);
|
||||
if (fs.statSync(filePath).isFile()) {
|
||||
console.log(`Procesando ${file}...`);
|
||||
const ok = await processFile(filePath, file);
|
||||
if (ok) successCount++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\nImportación finalizada. ${successCount} publicaciones procesadas correctamente.`);
|
||||
}
|
||||
|
||||
main();
|
||||
@ -5,11 +5,11 @@ async function setupLiveChat() {
|
||||
console.log("Configuring Live Chat collection...");
|
||||
const client = new Client()
|
||||
.setEndpoint(process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT)
|
||||
.setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT)
|
||||
.setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID)
|
||||
.setKey(process.env.APPWRITE_API_KEY);
|
||||
|
||||
const databases = new Databases(client);
|
||||
const dbId = process.env.NEXT_PUBLIC_APPWRITE_DATABASE;
|
||||
const dbId = process.env.APPWRITE_DATABASE_ID;
|
||||
|
||||
try {
|
||||
const collection = await databases.createCollection(
|
||||
|
||||
@ -5,11 +5,11 @@ async function setupStreamState() {
|
||||
console.log("Configuring Stream State collection...");
|
||||
const client = new Client()
|
||||
.setEndpoint(process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT)
|
||||
.setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT)
|
||||
.setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID)
|
||||
.setKey(process.env.APPWRITE_API_KEY);
|
||||
|
||||
const databases = new Databases(client);
|
||||
const dbId = process.env.NEXT_PUBLIC_APPWRITE_DATABASE;
|
||||
const dbId = process.env.APPWRITE_DATABASE_ID;
|
||||
|
||||
try {
|
||||
const collection = await databases.createCollection(
|
||||
|
||||
@ -91,11 +91,23 @@ export default async function EntityPage({
|
||||
const relKey = field.relationCollection as string;
|
||||
const relColId = collectionMap[relKey];
|
||||
if (!relColId) return;
|
||||
|
||||
const ids = Array.from(new Set(
|
||||
response.documents
|
||||
.map(d => typeof d[field.name] === 'object' && d[field.name] !== null ? d[field.name].$id : d[field.name])
|
||||
.filter(id => typeof id === 'string' && id.length > 0)
|
||||
));
|
||||
|
||||
if (ids.length === 0) {
|
||||
relationData[field.name] = [];
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const relRes = await client.databases.listDocuments(
|
||||
APPWRITE_ALMEIRA_CONFIG.databaseId,
|
||||
relColId,
|
||||
[Query.limit(100)]
|
||||
[Query.equal('$id', ids), Query.limit(100)]
|
||||
);
|
||||
relationData[field.name] = JSON.parse(JSON.stringify(relRes.documents));
|
||||
} catch { }
|
||||
|
||||
@ -77,9 +77,9 @@ export const PostCard: React.FC<PostCardProps> = ({
|
||||
);
|
||||
|
||||
const postType =
|
||||
typeof post.tipo_publicacion_id === 'object'
|
||||
typeof post.tipo_publicacion_id === 'object' && post.tipo_publicacion_id !== null
|
||||
? post.tipo_publicacion_id.descripcion
|
||||
: post.tipo_publicacion_id;
|
||||
: null;
|
||||
|
||||
const clickTimer = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
|
||||
@ -226,18 +226,32 @@ export const PublicationsList: React.FC<PublicationsListProps> = ({
|
||||
|
||||
{filteredAndSortedPosts.length > 0 ? (
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4">
|
||||
{filteredAndSortedPosts.map((post) => (
|
||||
<PostCard
|
||||
key={post.$id}
|
||||
post={post}
|
||||
userRole={userRole}
|
||||
onEdit={handleEdit}
|
||||
onDelete={handleDelete}
|
||||
appwriteEndpoint={appwriteConfig.endpoint}
|
||||
appwriteProjectId={appwriteConfig.projectId}
|
||||
globalSettings={globalSettings}
|
||||
/>
|
||||
))}
|
||||
{filteredAndSortedPosts.map((post) => {
|
||||
const enrichedPost = { ...post };
|
||||
|
||||
if (typeof enrichedPost.imagen_destacada_id === 'string' && relationData['imagen_destacada_id']) {
|
||||
const mediaDoc = relationData['imagen_destacada_id'].find(m => m.$id === enrichedPost.imagen_destacada_id);
|
||||
if (mediaDoc) enrichedPost.imagen_destacada_id = mediaDoc as any;
|
||||
}
|
||||
|
||||
if (typeof enrichedPost.tipo_publicacion_id === 'string' && relationData['tipo_publicacion_id']) {
|
||||
const typeDoc = relationData['tipo_publicacion_id'].find(t => t.$id === enrichedPost.tipo_publicacion_id);
|
||||
if (typeDoc) enrichedPost.tipo_publicacion_id = typeDoc as any;
|
||||
}
|
||||
|
||||
return (
|
||||
<PostCard
|
||||
key={post.$id}
|
||||
post={enrichedPost}
|
||||
userRole={userRole}
|
||||
onEdit={handleEdit}
|
||||
onDelete={handleDelete}
|
||||
appwriteEndpoint={appwriteConfig.endpoint}
|
||||
appwriteProjectId={appwriteConfig.projectId}
|
||||
globalSettings={globalSettings}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col items-center justify-center gap-4 py-20 border-2 border-dashed border-slate-800 rounded-2xl">
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
const DOMPurify = require('isomorphic-dompurify');
|
||||
const txt = "Line 1\nLine 2\n\nLine 3\n<strong>Bold</strong> text";
|
||||
console.log(DOMPurify.sanitize(txt));
|
||||
Loading…
Reference in New Issue
Block a user