diff --git a/src/app/actions/appwrite.ts b/src/app/actions/appwrite.ts index c60e725..07c89e5 100644 --- a/src/app/actions/appwrite.ts +++ b/src/app/actions/appwrite.ts @@ -89,3 +89,43 @@ export async function uploadMediaServer(formData: FormData) { return { success: false, error: error.message }; } } + +export async function uploadMediaDocumentServer(formData: FormData) { + try { + const client = getAdminClient(); + const storage = new Storage(client); + const databases = new Databases(client); + const files = formData.getAll('files') as File[]; + + if (!files || files.length === 0) { + throw new Error('No se encontraron archivos para subir'); + } + + const results = []; + for (const file of files) { + const buffer = Buffer.from(await file.arrayBuffer()); + const uploadedFile = await storage.createFile('media', ID.unique(), { + buffer: buffer, + filename: file.name, + size: file.size, + mimeType: file.type + } as any); + + const doc = await databases.createDocument(DATABASE_ID, 'media', ID.unique(), { + file_id: uploadedFile.$id, + bucket_id: 'media', + titulo: file.name, + alt_text: file.name.split('.')[0], + tipo: file.type.startsWith('image/') ? 'imagen' : 'video' + }); + results.push(doc); + } + + revalidatePath('/dashboard/media'); + revalidatePath('/'); + return { success: true, count: results.length, data: results }; + } catch (error: any) { + console.error('Error uploading media and creating document:', error); + return { success: false, error: error.message }; + } +} diff --git a/src/app/actions/entity.ts b/src/app/actions/entity.ts index 376bfee..a717e73 100644 --- a/src/app/actions/entity.ts +++ b/src/app/actions/entity.ts @@ -44,14 +44,32 @@ export async function updateEntityConfig(data: any) { const databases = await getAdminClient(); const existing = await getEntityConfig(); - let parsedData = data; + let parsedData: any = data; if (typeof FormData !== 'undefined' && data instanceof FormData) { parsedData = {}; - data.forEach((value, key) => { + for (const [key, value] of Array.from(data.entries())) { if (key !== '$id' && key !== '$collectionId' && key !== '$databaseId') { - parsedData[key] = value; + if (typeof value === 'object' && 'size' in value && 'name' in value) { + const file = value as File; + if (file.size > 0) { + try { + const { uploadMediaServer } = await import('@/app/actions/appwrite'); + const tempForm = new FormData(); + tempForm.append('files', file); + const uploadRes = await uploadMediaServer(tempForm); + if (uploadRes.success && uploadRes.data && uploadRes.data[0]) { + const m = uploadRes.data[0]; + parsedData[key] = `${ENDPOINT}/storage/buckets/${m.bucketId || 'media'}/files/${m.$id}/view?project=${PROJECT_ID}`; + } + } catch (uploadErr) { + console.error('Error uploading file for entity config:', uploadErr); + } + } + } else { + parsedData[key] = value; + } } - }); + } } if (!parsedData || Object.keys(parsedData).length === 0) { diff --git a/src/components/dashboard/MediaGridSelector.tsx b/src/components/dashboard/MediaGridSelector.tsx index 7ddfa6a..ecd8e09 100644 --- a/src/components/dashboard/MediaGridSelector.tsx +++ b/src/components/dashboard/MediaGridSelector.tsx @@ -3,8 +3,9 @@ import { useState, useEffect, useCallback, useRef } from 'react'; import { createPortal } from 'react-dom'; import { ImageIcon, X, Search, CheckCircle2, UploadCloud, Loader2 } from 'lucide-react'; -import { databases, storage, APPWRITE_ALMEIRA_CONFIG, endpoint, projectId } from '@/lib/appwrite'; -import { Query, ID } from 'appwrite'; +import { databases, APPWRITE_ALMEIRA_CONFIG, endpoint, projectId } from '@/lib/appwrite'; +import { Query } from 'appwrite'; +import { uploadMediaDocumentServer } from '@/app/actions/appwrite'; /** Utility to generate frontend Appwrite image previews */ export function getAppwriteImageUrl(fileId: string, width = 400, height = 250) { @@ -79,26 +80,22 @@ export const MediaGridSelector = ({ setIsUploading(true); try { - // 1. Upload to storage bucket - const uploadedFile = await storage.createFile('media', ID.unique(), file); + const formData = new FormData(); + formData.append('files', file); + const res = await uploadMediaDocumentServer(formData); - // 2. Create document in media collection - const doc = await databases.createDocument(APPWRITE_ALMEIRA_CONFIG.databaseId, 'media', ID.unique(), { - file_id: uploadedFile.$id, - bucket_id: 'media', - titulo: file.name, - alt_text: file.name.split('.')[0], - tipo: file.type.startsWith('image/') ? 'imagen' : 'video' // mostly images in this bucket - }); + if (!res.success || !res.data || res.data.length === 0) { + throw new Error(res.error || "Error from server edge while uploading media."); + } - // 3. Update state + const doc = res.data[0]; setMediaItems(prev => [doc, ...prev]); - const url = getAppwriteImageUrl(uploadedFile.$id, 1200, 800); + const url = getAppwriteImageUrl(doc.file_id, 1200, 800); onChange(doc.$id, url); closeModal(); } catch (error) { console.error("Error upload:", error); - alert("Error al subir el archivo."); + alert("Error al subir el archivo. Revisa que el perfil tenga el scope files.write."); } finally { setIsUploading(false); } diff --git a/src/components/media/GlobalPlayer.tsx b/src/components/media/GlobalPlayer.tsx index 2b82356..d3beb7b 100644 --- a/src/components/media/GlobalPlayer.tsx +++ b/src/components/media/GlobalPlayer.tsx @@ -18,6 +18,7 @@ export const GlobalPlayer = () => { const playerRef = useRef(null); const [isMuted, setIsMuted] = useState(false); const [lastVolume, setLastVolume] = useState(audioVolume); + const [isExpanded, setIsExpanded] = useState(false); // Sync Audio State useEffect(() => { @@ -110,7 +111,24 @@ export const GlobalPlayer = () => { {/* Floating Global Controller */}
-
+ + {/* ESTADO COLAPSADO */} + {!isExpanded && (audioUrl || videoUrl) && ( + + )} + + {/* ESTADO EXPANDIDO */} + {isExpanded && ( + <> +
{/* Play/Pause Main Button */} -
+
- {isAudioPlaying ? 'En Vivo' : 'Radio Offline'} + {isAudioPlaying ? 'Señal Activa' : 'Radio Offline'} {isAudioPlaying &&
}
-

ALMEIRA RADIO

+

ALMEIRA RADIO

{/* Volume Control (Desktop Only) */} @@ -160,6 +178,15 @@ export const GlobalPlayer = () => { }} className="w-20 h-1 bg-slate-800 rounded-full appearance-none cursor-pointer accent-cyan-500 hover:accent-cyan-400 transition-all" /> + + {/* Botón para colapsar */} +
@@ -177,6 +204,8 @@ export const GlobalPlayer = () => {
)} + + )}
{/* Add Music Bar Animation to globals.css */} diff --git a/src/components/publications/PublicationLayout.tsx b/src/components/publications/PublicationLayout.tsx index c443ded..0e3e1ee 100644 --- a/src/components/publications/PublicationLayout.tsx +++ b/src/components/publications/PublicationLayout.tsx @@ -389,14 +389,14 @@ export const PublicationLayout: React.FC = ({ @@ -481,36 +481,40 @@ export const PublicationLayout: React.FC = ({
- {/* Submit Button */} + {/* Sticky Action Bar */} - +
+
+ +
+
{/* No permission notice */} diff --git a/src/lib/appwrite.server.ts b/src/lib/appwrite.server.ts index 53ec58d..f244409 100644 --- a/src/lib/appwrite.server.ts +++ b/src/lib/appwrite.server.ts @@ -56,28 +56,30 @@ export async function getLoggedInUser() { const cookieStore = await cookies(); const sessionCookie = cookieStore.get(COOKIE_NAME); - // Verificación de bypass (Funciona en todos los entornos) + // Verificación de bypass (Evitamos imprimir log repetitivo a menos que sea un error) if (sessionCookie?.value === "admin-bypass-session") { - console.log("Acceso de emergencia detectado en Dashboard."); return { $id: "admin-emergency", name: "Administrador (Emergencia)", email: "admin@almeira.com.co", - prefs: {} + prefs: { role: 'admin' } }; } + if (!sessionCookie || !sessionCookie.value) { + return null; + } + const sessionClient = await createSessionClient(); if (!sessionClient) { - console.log("getLoggedInUser: No se encontró cookie de sesión o cliente inválido."); return null; } - const user = await sessionClient.account.get(); - console.log("getLoggedInUser: Sesión validada para usuario:", user.email); - return user; + return await sessionClient.account.get(); } catch (error: any) { - console.error("getLoggedInUser: Error al validar sesión:", error.message || error); + if (error?.code !== 401) { + console.error("getLoggedInUser: Error al validar sesión:", error.message || error); + } return null; } }