almeira-web/src/types/publications.ts

220 lines
7.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @file publications.ts
* @description Strict TypeScript interfaces and types for the Publications module.
* All entities, props, and enums are defined here to enforce type safety across
* the entire publications feature.
*/
// ─────────────────────────────────────────────
// ENUMS
// ─────────────────────────────────────────────
/** Roles de usuario para el sistema RBAC */
export type UserRole = 'admin' | 'editor' | 'viewer' | 'guest';
/** Estados posibles de una publicación */
export type PostStatus = 'borrador' | 'pendiente' | 'publicado' | 'archivado';
// ─────────────────────────────────────────────
// CORE ENTITIES
// ─────────────────────────────────────────────
/** Entidad del perfil de usuario autenticado */
export interface AuthUser {
$id: string;
name: string;
email: string;
labels?: string[];
prefs: {
role?: UserRole;
[key: string]: unknown;
};
}
/** Entidad de tipo de publicación */
export interface PublicationType {
$id: string;
codigo: string;
descripcion: string;
}
/** Entidad de ítem de media */
export interface MediaItem {
$id: string;
file_id: string;
bucket_id: string;
titulo: string;
alt_text?: string;
descripcion_seo?: string;
tipo: 'imagen' | 'video';
}
/** Entidad de cliente/patrocinador */
export interface Client {
$id: string;
nombre_entidad: string;
nit?: string;
slug: string;
contacto_nombre: string;
numero_whatsapp: string;
estado: boolean;
}
/** Entidad principal de publicación (Post) */
export interface Post {
$id: string;
$createdAt: string;
$updatedAt: string;
titulo: string;
slug: string;
contenido: string;
resumen_redes?: string;
estado: PostStatus;
fecha_publicacion: string;
fecha_finalizacion?: string;
og_image_url?: string;
url_destino?: string;
video_url?: string;
seo_titulo?: string;
seo_descripcion?: string;
// Relaciones (pueden llegar como string ID o como objeto resuelto)
tipo_publicacion_id: string | PublicationType;
imagen_destacada_id?: string | MediaItem;
cliente_id?: string | Client;
resolved_media?: MediaItem;
}
// ─────────────────────────────────────────────
// COMPONENT PROPS
// ─────────────────────────────────────────────
/** Props del PostCard (componente de dashboard) */
export interface PostCardProps {
post: Post;
userRole: UserRole;
onEdit?: (post: Post) => void;
onDelete?: (postId: string) => void;
/** URL base del endpoint de Appwrite para construir URLs de imagen */
appwriteEndpoint: string;
appwriteProjectId: string;
globalSettings?: Record<string, any>;
}
/** Props del PermissionGuard HOC */
export interface PermissionGuardProps {
/** Roles que tienen acceso al contenido protegido */
allowedRoles: UserRole[];
/** Rol actual del usuario */
userRole: UserRole;
/** Contenido a renderizar si el usuario tiene permiso */
children: React.ReactNode;
/** Contenido alternativo si no tiene permiso (opcional) */
fallback?: React.ReactNode;
}
/** Props del FormSection */
export interface FormSectionProps {
title: string;
icon?: React.ReactNode;
/** Número de columnas del grid interno (1–12) */
columns?: 1 | 2 | 3 | 4 | 6 | 12;
children: React.ReactNode;
/** Clase CSS adicional */
className?: string;
}
/** Props del RelationSelectField */
export interface RelationSelectFieldProps {
/** Nombre del campo en el formulario */
name: string;
/** Label visible */
label: string;
/** Opciones actuales disponibles */
options: Array<{ $id: string; [key: string]: unknown }>;
/** Propiedad del objeto a mostrar como texto */
labelKey: string;
/** Valor actualmente seleccionado */
value?: string;
onChange?: (value: string) => void;
/** Colección de Appwrite para creación rápida */
relationCollection: string;
/** Si el campo es obligatorio */
required?: boolean;
/** Desactiva el campo */
disabled?: boolean;
}
/** Props del PublicationLayout */
export interface PublicationLayoutProps {
/** Post existente para edición, undefined para creación */
initialData?: Partial<Post>;
/** Datos de relaciones pre-cargados desde el servidor */
relationData: Record<string, Array<{ $id: string; [key: string]: unknown }>>;
/** Acción de servidor para guardar */
/** Acción de servidor para guardar. Puede retornar un objeto de éxito o void (si redirige) */
submitAction: (formData: FormData) => Promise<{ success: boolean; error?: string; data?: unknown } | void>;
/** Usuario autenticado */
user: AuthUser;
/** Config de Appwrite para URLs de media */
appwriteConfig: {
endpoint: string;
projectId: string;
databaseId: string;
};
}
// ─────────────────────────────────────────────
// STORE TYPES (Zustand)
// ─────────────────────────────────────────────
/** Estado del store de publicaciones */
export interface PublicationStoreState {
/** Cache de opciones para campos de relación — evita re-fetches */
relationCache: Record<string, Array<{ $id: string; [key: string]: unknown }>>;
/** Campo actualmente abierto en QuickCreateModal */
pendingRelationField: string | null;
/** Colección destino del modal de creación rápida */
pendingRelationCollection: string | null;
/** Estado de la UI optimista durante el guardado */
optimisticState: 'idle' | 'saving' | 'saved' | 'error';
/** Mensaje de error si aplica */
errorMessage: string | null;
}
/** Acciones del store de publicaciones */
export interface PublicationStoreActions {
/** Abre el QuickCreateModal para un campo de relación específico */
openQuickCreate: (fieldName: string, collection: string) => void;
/** Cierra el QuickCreateModal */
closeQuickCreate: () => void;
/** Inyecta un nuevo documento en el cache y en el campo padre */
injectRelationValue: (
fieldName: string,
collection: string,
newDoc: { $id: string; [key: string]: unknown }
) => void;
/** Actualiza el cache de opciones de una relación */
setRelationCache: (
collection: string,
data: Array<{ $id: string; [key: string]: unknown }>
) => void;
/** Establece el estado optimista */
setOptimisticState: (state: PublicationStoreState['optimisticState'], error?: string) => void;
/** Reinicia el store */
reset: () => void;
}
export type PublicationStore = PublicationStoreState & PublicationStoreActions;
// ─────────────────────────────────────────────
// API TYPES
// ─────────────────────────────────────────────
/** Respuesta estándar de la API de publicaciones */
export interface ApiResponse<T = unknown> {
success: boolean;
data?: T;
error?: string;
validationErrors?: Record<string, string[]>;
}