refactor: improve project integrity with shared types and strict Appwrite document casting
- Create shared Settings interface in @/types/settings. - Refactor NewsTicker, AdWrappers, and page components to use shared types. - Remove risky 'as any' casting from Appwrite document resolution. - Update Post interface to include resolved_media for consistent data handling. - Verify Zod error handling consistency across the src directory.
This commit is contained in:
parent
ea551469b7
commit
339ac0125e
@ -4,6 +4,8 @@ import { Query } from 'node-appwrite';
|
|||||||
import { NewsCard } from '@/components/NewsCard';
|
import { NewsCard } from '@/components/NewsCard';
|
||||||
import { GlobalSidebar, hasSidebarContent } from '@/components/AdWrappers';
|
import { GlobalSidebar, hasSidebarContent } from '@/components/AdWrappers';
|
||||||
import { Sparkles } from 'lucide-react';
|
import { Sparkles } from 'lucide-react';
|
||||||
|
import { Settings } from '@/types/settings';
|
||||||
|
import { Post, MediaItem } from '@/types/publications';
|
||||||
|
|
||||||
async function fetchClasificadosData() {
|
async function fetchClasificadosData() {
|
||||||
try {
|
try {
|
||||||
@ -11,7 +13,7 @@ async function fetchClasificadosData() {
|
|||||||
|
|
||||||
// 1. Settings
|
// 1. Settings
|
||||||
const settingsRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'settings', []);
|
const settingsRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'settings', []);
|
||||||
const settings: any = settingsRes.documents.length > 0 ? settingsRes.documents[0] : {};
|
const settings = (settingsRes.documents.length > 0 ? settingsRes.documents[0] : {}) as Settings;
|
||||||
|
|
||||||
const postsCount = settings.feed_news_count || 12;
|
const postsCount = settings.feed_news_count || 12;
|
||||||
const cardSettings = {
|
const cardSettings = {
|
||||||
@ -26,7 +28,7 @@ async function fetchClasificadosData() {
|
|||||||
Query.equal('codigo', 'CLASIFICADO')
|
Query.equal('codigo', 'CLASIFICADO')
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let posts: any[] = [];
|
let posts: Post[] = [];
|
||||||
if (typeRes.documents.length > 0) {
|
if (typeRes.documents.length > 0) {
|
||||||
const typeId = typeRes.documents[0].$id;
|
const typeId = typeRes.documents[0].$id;
|
||||||
const postsRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'posts', [
|
const postsRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'posts', [
|
||||||
@ -35,16 +37,19 @@ async function fetchClasificadosData() {
|
|||||||
Query.orderDesc('fecha_publicacion'),
|
Query.orderDesc('fecha_publicacion'),
|
||||||
Query.limit(postsCount)
|
Query.limit(postsCount)
|
||||||
]);
|
]);
|
||||||
posts = postsRes.documents;
|
posts = postsRes.documents as unknown as Post[];
|
||||||
|
|
||||||
// 3. Resolve Media
|
// 3. Resolve Media
|
||||||
const mediaIds = posts.map(p => p.imagen_destacada_id).filter(id => id && typeof id === 'string');
|
const mediaIds = posts.map(p => p.imagen_destacada_id).filter(id => id && typeof id === 'string') as string[];
|
||||||
if (mediaIds.length > 0) {
|
if (mediaIds.length > 0) {
|
||||||
const mediaRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'media', [
|
const mediaRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'media', [
|
||||||
Query.equal('$id', mediaIds)
|
Query.equal('$id', mediaIds)
|
||||||
]);
|
]);
|
||||||
const mediaMap = new Map(mediaRes.documents.map(m => [m.$id, m]));
|
const mediaMap = new Map<string, MediaItem>(mediaRes.documents.map(m => [m.$id, m as unknown as MediaItem]));
|
||||||
posts = posts.map(p => ({ ...p, resolved_media: mediaMap.get(p.imagen_destacada_id) }));
|
posts = posts.map(p => ({
|
||||||
|
...p,
|
||||||
|
resolved_media: mediaMap.get(p.imagen_destacada_id as string)
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,8 @@ import { Query } from 'node-appwrite';
|
|||||||
import { NewsCard } from '@/components/NewsCard';
|
import { NewsCard } from '@/components/NewsCard';
|
||||||
import { GlobalSidebar, hasSidebarContent } from '@/components/AdWrappers';
|
import { GlobalSidebar, hasSidebarContent } from '@/components/AdWrappers';
|
||||||
import { Calendar } from 'lucide-react';
|
import { Calendar } from 'lucide-react';
|
||||||
|
import { Settings } from '@/types/settings';
|
||||||
|
import { Post, MediaItem } from '@/types/publications';
|
||||||
|
|
||||||
async function fetchEventosData() {
|
async function fetchEventosData() {
|
||||||
try {
|
try {
|
||||||
@ -11,7 +13,7 @@ async function fetchEventosData() {
|
|||||||
|
|
||||||
// 1. Settings
|
// 1. Settings
|
||||||
const settingsRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'settings', []);
|
const settingsRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'settings', []);
|
||||||
const settings: any = settingsRes.documents.length > 0 ? settingsRes.documents[0] : {};
|
const settings = (settingsRes.documents.length > 0 ? settingsRes.documents[0] : {}) as Settings;
|
||||||
|
|
||||||
const postsCount = settings.feed_news_count || 12;
|
const postsCount = settings.feed_news_count || 12;
|
||||||
const cardSettings = {
|
const cardSettings = {
|
||||||
@ -26,7 +28,7 @@ async function fetchEventosData() {
|
|||||||
Query.equal('codigo', 'EVENTO')
|
Query.equal('codigo', 'EVENTO')
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let posts: any[] = [];
|
let posts: Post[] = [];
|
||||||
if (typeRes.documents.length > 0) {
|
if (typeRes.documents.length > 0) {
|
||||||
const typeId = typeRes.documents[0].$id;
|
const typeId = typeRes.documents[0].$id;
|
||||||
const postsRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'posts', [
|
const postsRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'posts', [
|
||||||
@ -35,16 +37,19 @@ async function fetchEventosData() {
|
|||||||
Query.orderDesc('fecha_publicacion'),
|
Query.orderDesc('fecha_publicacion'),
|
||||||
Query.limit(postsCount)
|
Query.limit(postsCount)
|
||||||
]);
|
]);
|
||||||
posts = postsRes.documents;
|
posts = postsRes.documents as unknown as Post[];
|
||||||
|
|
||||||
// 3. Resolve Media
|
// 3. Resolve Media
|
||||||
const mediaIds = posts.map(p => p.imagen_destacada_id).filter(id => id && typeof id === 'string');
|
const mediaIds = posts.map(p => p.imagen_destacada_id).filter(id => id && typeof id === 'string') as string[];
|
||||||
if (mediaIds.length > 0) {
|
if (mediaIds.length > 0) {
|
||||||
const mediaRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'media', [
|
const mediaRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'media', [
|
||||||
Query.equal('$id', mediaIds)
|
Query.equal('$id', mediaIds)
|
||||||
]);
|
]);
|
||||||
const mediaMap = new Map(mediaRes.documents.map(m => [m.$id, m]));
|
const mediaMap = new Map<string, MediaItem>(mediaRes.documents.map(m => [m.$id, m as unknown as MediaItem]));
|
||||||
posts = posts.map(p => ({ ...p, resolved_media: mediaMap.get(p.imagen_destacada_id) }));
|
posts = posts.map(p => ({
|
||||||
|
...p,
|
||||||
|
resolved_media: mediaMap.get(p.imagen_destacada_id as string)
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,33 +6,31 @@ import { GlobalSidebar, hasSidebarContent } from '@/components/AdWrappers';
|
|||||||
import { RadioTower, Sparkles, ChevronDown } from 'lucide-react';
|
import { RadioTower, Sparkles, ChevronDown } from 'lucide-react';
|
||||||
import { decodeHtmlEntities } from '@/lib/utils';
|
import { decodeHtmlEntities } from '@/lib/utils';
|
||||||
|
|
||||||
|
import { Settings } from '@/types/settings';
|
||||||
|
import { Post, MediaItem } from '@/types/publications';
|
||||||
|
|
||||||
async function fetchHomeData() {
|
async function fetchHomeData() {
|
||||||
try {
|
try {
|
||||||
const client = await createAdminClient();
|
const client = await createAdminClient();
|
||||||
|
|
||||||
// 1. Settings with fallback
|
// 1. Settings
|
||||||
let settings: any = {};
|
const settingsRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'settings', []);
|
||||||
try {
|
const settings = (settingsRes.documents.length > 0 ? settingsRes.documents[0] : {}) as Settings;
|
||||||
const settingsRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'settings', [Query.limit(1)]);
|
|
||||||
settings = settingsRes.documents.length > 0 ? settingsRes.documents[0] : {};
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Settings fetch error", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
const postsCount = settings?.feed_news_count || 12;
|
const postsCount = settings.feed_news_count || 12;
|
||||||
const cardSettings = {
|
const cardSettings = {
|
||||||
showCategory: settings?.news_card_show_category ?? false,
|
showCategory: settings.news_card_show_category ?? false,
|
||||||
showFooterDate: settings?.show_news_date ?? true,
|
showFooterDate: settings.show_news_date ?? false,
|
||||||
showFooterButton: settings?.news_card_show_footer_button ?? true,
|
showFooterButton: settings.news_card_show_footer_button ?? true,
|
||||||
footerText: settings?.news_card_footer_text || ""
|
footerText: settings.news_card_footer_text || ""
|
||||||
};
|
};
|
||||||
|
|
||||||
// 2. Get NOTICIA type ID
|
// 2. Main News (NOTICIA)
|
||||||
const typeRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'publication_types', [
|
const typeRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'publication_types', [
|
||||||
Query.equal('codigo', 'NOTICIA')
|
Query.equal('codigo', 'NOTICIA')
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let posts: any[] = [];
|
let posts: Post[] = [];
|
||||||
if (typeRes.documents.length > 0) {
|
if (typeRes.documents.length > 0) {
|
||||||
const typeId = typeRes.documents[0].$id;
|
const typeId = typeRes.documents[0].$id;
|
||||||
const postsRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'posts', [
|
const postsRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'posts', [
|
||||||
@ -41,19 +39,18 @@ async function fetchHomeData() {
|
|||||||
Query.orderDesc('fecha_publicacion'),
|
Query.orderDesc('fecha_publicacion'),
|
||||||
Query.limit(postsCount)
|
Query.limit(postsCount)
|
||||||
]);
|
]);
|
||||||
posts = postsRes.documents;
|
posts = postsRes.documents as unknown as Post[];
|
||||||
|
|
||||||
// 3. Resolve Media documents for images
|
// 3. Resolve Media
|
||||||
const mediaIds = posts.map(p => p.imagen_destacada_id).filter(id => id && typeof id === 'string');
|
const mediaIds = posts.map(p => p.imagen_destacada_id).filter(id => id && typeof id === 'string');
|
||||||
if (mediaIds.length > 0) {
|
if (mediaIds.length > 0) {
|
||||||
const mediaRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'media', [
|
const mediaRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'media', [
|
||||||
Query.equal('$id', mediaIds)
|
Query.equal('$id', mediaIds)
|
||||||
]);
|
]);
|
||||||
const mediaMap = new Map(mediaRes.documents.map(m => [m.$id, m]));
|
const mediaMap = new Map<string, MediaItem>(mediaRes.documents.map(m => [m.$id, m as unknown as MediaItem]));
|
||||||
|
posts = posts.map(p => ({
|
||||||
posts = posts.map(p => ({
|
...p,
|
||||||
...p,
|
resolved_media: mediaMap.get(p.imagen_destacada_id as string)
|
||||||
resolved_media: mediaMap.get(p.imagen_destacada_id)
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,11 +3,7 @@ import { createAdminClient } from '@/lib/appwrite.server';
|
|||||||
import TopBanner from './TopBanner';
|
import TopBanner from './TopBanner';
|
||||||
import ClientSidebars from './ClientSidebars';
|
import ClientSidebars from './ClientSidebars';
|
||||||
|
|
||||||
interface Settings {
|
import { Settings } from '@/types/settings';
|
||||||
rotation_seconds: number;
|
|
||||||
ticker_count: number;
|
|
||||||
[key: string]: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchSettings(): Promise<Settings> {
|
async function fetchSettings(): Promise<Settings> {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { createSessionClient } from '@/lib/appwrite.server';
|
|||||||
import { Query } from 'node-appwrite';
|
import { Query } from 'node-appwrite';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { MessageCircle } from 'lucide-react';
|
import { MessageCircle } from 'lucide-react';
|
||||||
|
import { Settings } from '@/types/settings';
|
||||||
|
|
||||||
export default async function NewsTicker() {
|
export default async function NewsTicker() {
|
||||||
let tickerItems: any[] = [];
|
let tickerItems: any[] = [];
|
||||||
@ -18,7 +19,7 @@ export default async function NewsTicker() {
|
|||||||
|
|
||||||
// 1. Fetch Settings
|
// 1. Fetch Settings
|
||||||
const settingsRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'settings', []);
|
const settingsRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'settings', []);
|
||||||
const settings: any = settingsRes.documents.length > 0 ? settingsRes.documents[0] : {};
|
const settings = (settingsRes.documents.length > 0 ? settingsRes.documents[0] : {}) as Settings;
|
||||||
const tickerCount = settings.ticker_count || 10;
|
const tickerCount = settings.ticker_count || 10;
|
||||||
|
|
||||||
// 2. Fetch News (NOTICIA)
|
// 2. Fetch News (NOTICIA)
|
||||||
|
|||||||
@ -80,6 +80,7 @@ export interface Post {
|
|||||||
tipo_publicacion_id: string | PublicationType;
|
tipo_publicacion_id: string | PublicationType;
|
||||||
imagen_destacada_id?: string | MediaItem;
|
imagen_destacada_id?: string | MediaItem;
|
||||||
cliente_id?: string | Client;
|
cliente_id?: string | Client;
|
||||||
|
resolved_media?: MediaItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─────────────────────────────────────────────
|
// ─────────────────────────────────────────────
|
||||||
|
|||||||
10
src/types/settings.ts
Normal file
10
src/types/settings.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* @file settings.ts
|
||||||
|
* @description Shared interfaces for Appwrite settings documents.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface Settings {
|
||||||
|
rotation_seconds: number;
|
||||||
|
ticker_count: number;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user