### Manage Novel Data with React Hooks Source: https://context7.com/arielxfelipe/arfel/llms.txt Provides custom hooks to fetch and display localized novel data. It includes the UnifiedNovel interface for consistent data structures and examples for rendering grids and detail pages. ```tsx import { useNovels, useNovel, UnifiedNovel } from "@/hooks/use-novels"; interface UnifiedNovel { id: string; dbId?: string; isDb?: boolean; title: string; author: string; cover: string; synopsis: string; genre: string; genreLabel: string; chapters: number; views: string; rating: number; status: string; lastUpdate: string; chapterList: Chapter[]; tags?: string[]; } const NovelGrid = () => { const { allNovels, loading, refetch } = useNovels(); if (loading) return ; return (
{allNovels.map((novel) => ( ))}
); }; const NovelDetail = () => { const { id } = useParams(); const { novel, loading } = useNovel(id); if (loading) return ; if (!novel) return

Novel not found

; return (

{novel.title}

by {novel.author}

{novel.synopsis}

{novel.chapterList.map((ch) => ( Chapter {ch.number}: {ch.title} ))}
); }; ``` -------------------------------- ### Configure Supabase Client and Database Operations Source: https://context7.com/arielxfelipe/arfel/llms.txt Sets up the Supabase client with environment variables and demonstrates common database operations including querying, inserting, upserting, and calling RPC functions. ```typescript import { createClient } from '@supabase/supabase-js'; import type { Database } from './types'; const SUPABASE_URL = import.meta.env.VITE_SUPABASE_URL; const SUPABASE_PUBLISHABLE_KEY = import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY; export const supabase = createClient(SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY, { auth: { storage: localStorage, persistSession: true, autoRefreshToken: true, } }); const { data: novels } = await supabase.from("db_novels").select("*").order("created_at", { ascending: false }); const { data: chapters } = await supabase.from("db_chapters").select("*").eq("novel_id", novelId).eq("approval_status", "approved").order("number", { ascending: true }); await supabase.from("bookmarks").insert({ user_id: userId, novel_id: novelId }); await supabase.from("reading_progress").upsert({ user_id: userId, novel_id: novelId, chapter_number: chapterNum }, { onConflict: "user_id,novel_id,chapter_number" }); const { data: isAdmin } = await supabase.rpc("has_role", { _user_id: userId, _role: "admin" }); ``` -------------------------------- ### Define PostgreSQL Database Schema and Functions Source: https://context7.com/arielxfelipe/arfel/llms.txt Defines the core tables for novels, chapters, user roles, bookmarks, and reading progress. Includes a security-definer function for verifying user roles within the database. ```sql CREATE TABLE db_novels ( id UUID PRIMARY KEY, slug TEXT UNIQUE NOT NULL, title TEXT NOT NULL, title_en TEXT, title_es TEXT, author TEXT NOT NULL, cover_url TEXT, synopsis TEXT, synopsis_en TEXT, synopsis_es TEXT, genre TEXT, genre_label TEXT, status TEXT DEFAULT 'Em andamento', tags TEXT[], approval_mode TEXT DEFAULT 'manual', created_by UUID REFERENCES auth.users(id), created_at TIMESTAMPTZ DEFAULT now() ); CREATE TABLE db_chapters ( id UUID PRIMARY KEY, novel_id UUID REFERENCES db_novels(id), number INTEGER NOT NULL, title TEXT NOT NULL, content TEXT, content_pt TEXT, content_en TEXT, content_es TEXT, original_language TEXT DEFAULT 'pt', approval_status TEXT DEFAULT 'pending_upload', submitted_by UUID REFERENCES auth.users(id), approved_by UUID, published_at TIMESTAMPTZ DEFAULT now() ); CREATE TABLE user_roles ( id UUID PRIMARY KEY, user_id UUID REFERENCES auth.users(id), role app_role NOT NULL ); CREATE TABLE bookmarks ( id UUID PRIMARY KEY, user_id UUID REFERENCES auth.users(id), novel_id TEXT NOT NULL, UNIQUE(user_id, novel_id) ); CREATE TABLE reading_progress ( id UUID PRIMARY KEY, user_id UUID REFERENCES auth.users(id), novel_id TEXT NOT NULL, chapter_number INTEGER NOT NULL, read_at TIMESTAMPTZ DEFAULT now(), UNIQUE(user_id, novel_id, chapter_number) ); CREATE FUNCTION has_role(_role app_role, _user_id UUID) RETURNS BOOLEAN AS $$ SELECT EXISTS ( SELECT 1 FROM user_roles WHERE user_id = _user_id AND role = _role ); $$ LANGUAGE sql SECURITY DEFINER; ``` -------------------------------- ### Parse and Submit Chapters with TypeScript Source: https://context7.com/arielxfelipe/arfel/llms.txt Handles the extraction of chapter content from TXT and DOCX files and manages the database submission process. It includes logic to determine approval status based on user permissions and handles language-specific content storage. ```tsx import mammoth from "mammoth"; interface ParsedChapter { number: number; title: string; content: string; } const parseTxtFile = async (file: File, startNumber: number): Promise => { const text = await file.text(); const title = file.name.replace(/\.txt$/i, "").trim(); return [{ number: startNumber, title, content: text.trim() }]; }; const parseDocxFile = async (file: File, startNumber: number): Promise => { const arrayBuffer = await file.arrayBuffer(); const result = await mammoth.convertToHtml({ arrayBuffer }); const parser = new DOMParser(); const doc = parser.parseFromString(`
${result.value}
`, "text/html"); const chapters: ParsedChapter[] = []; return chapters; }; const handleSubmit = async (form: ChapterForm, userId: string, novelId: string) => { const { data: permission } = await supabase .from("uploader_permissions") .select("approval_mode") .eq("user_id", userId) .eq("novel_id", novelId) .single(); const autoApprove = permission?.approval_mode === "auto"; await supabase.from("db_chapters").insert({ novel_id: novelId, number: form.number, title: form.title, content: form.content, original_language: form.lang, submitted_by: userId, approval_status: autoApprove ? "approved" : "pending_upload", ...(form.lang === "en" ? { content_en: form.content } : { content_pt: form.content }), }); }; ``` -------------------------------- ### Automate AI Translation with Supabase Edge Functions Source: https://context7.com/arielxfelipe/arfel/llms.txt Implements a server-side Edge Function to translate novel chapters into multiple languages using the Google Gemini API. The function processes translation requests in parallel and updates the database with the generated content. ```typescript async function translateText(content: string, from: string, to: string, apiKey: string): Promise { const response = await fetch("https://ai.gateway.lovable.dev/v1/chat/completions", { method: "POST", headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json", }, body: JSON.stringify({ model: "google/gemini-2.5-flash", messages: [ { role: "system", content: `You are a professional literary translator. Translate the following novel chapter from ${LANG_NAMES[from]} to ${LANG_NAMES[to]}. Preserve all formatting and literary style. Output ONLY the translated text.`, }, { role: "user", content }, ], }), }); const data = await response.json(); return data.choices?.[0]?.message?.content ?? ""; } serve(async (req) => { const { chapterId, content, originalLanguage } = await req.json(); const targetLangs = ["pt", "en", "es"].filter(l => l !== originalLanguage); const translations = await Promise.all( targetLangs.map(async (lang) => ({ lang, translated: await translateText(content, originalLanguage, lang, apiKey), })) ); const updateFields: Record = { original_language: originalLanguage, content: content, [`content_${originalLanguage}`]: content, }; for (const { lang, translated } of translations) { if (translated) updateFields[`content_${lang}`] = translated; } await serviceClient.from("db_chapters").update(updateFields).eq("id", chapterId); return new Response(JSON.stringify({ success: true })); }); ``` -------------------------------- ### Authentication Context Management (React/TypeScript) Source: https://context7.com/arielxfelipe/arfel/llms.txt Manages Supabase authentication state, providing user session, loading status, and sign-out functionality to the application. It uses React's Context API for global state management. ```tsx import { createContext, useContext, useEffect, useState, ReactNode } from "react"; import { Session, User } from "@supabase/supabase-js"; import { supabase } from "@/integrations/supabase/client"; interface AuthContextType { session: Session | null; user: User | null; loading: boolean; signOut: () => Promise; } const AuthContext = createContext({ session: null, user: null, loading: true, signOut: async () => {}, }); export const useAuth = () => useContext(AuthContext); // Usage in components: const MyComponent = () => { const { user, loading, signOut } = useAuth(); if (loading) return
Loading...
; if (!user) return ; return (

Welcome, {user.email}

); }; ``` -------------------------------- ### Chapter Rating System using React and Supabase Source: https://context7.com/arielxfelipe/arfel/llms.txt Implements a 1-5 star rating system for novel chapters. It fetches average ratings, total votes, and the user's specific rating from Supabase. Handles submitting new ratings and updating existing ones. Requires `AuthContext` for user authentication and `supabase` client integration. ```tsx import { useAuth } from "@/contexts/AuthContext"; import { supabase } from "@/integrations/supabase/client"; interface Props { novelId: string; chapterNumber: number; } const ChapterRating = ({ novelId, chapterNumber }: Props) => { const { user } = useAuth(); const [userRating, setUserRating] = useState(0); const [avgRating, setAvgRating] = useState(0); const [totalRatings, setTotalRatings] = useState(0); // Load all ratings for this chapter const loadRatings = async () => { const { data: all } = await supabase .from("chapter_ratings") .select("rating") .eq("novel_id", novelId) .eq("chapter_number", chapterNumber); if (all && all.length > 0) { setAvgRating(all.reduce((s, r) => s + r.rating, 0) / all.length); setTotalRatings(all.length); } // Load user's specific rating if (user) { const { data } = await supabase .from("chapter_ratings") .select("rating") .eq("novel_id", novelId) .eq("chapter_number", chapterNumber) .eq("user_id", user.id) .maybeSingle(); if (data) setUserRating(data.rating); } }; // Submit or update rating const handleRate = async (rating: number) => { if (!user) return; const { data: existing } = await supabase .from("chapter_ratings") .select("id") .eq("novel_id", novelId) .eq("chapter_number", chapterNumber) .eq("user_id", user.id) .maybeSingle(); if (existing) { await supabase.from("chapter_ratings").update({ rating }).eq("id", existing.id); } else { await supabase.from("chapter_ratings").insert({ user_id: user.id, novel_id: novelId, chapter_number: chapterNumber, rating, }); } loadRatings(); }; return (

Average: {avgRating.toFixed(1)} ({totalRatings} votes)

{[1, 2, 3, 4, 5].map((star) => ( ))}
); }; ``` -------------------------------- ### Comments System with Moderation using React and Supabase Source: https://context7.com/arielxfelipe/arfel/llms.txt A comprehensive comments system for novels, supporting comments at both novel and chapter levels. Includes moderation features like spoiler tagging and content censoring. Fetches comments and associated user profiles from Supabase. Requires `AuthContext` for user authentication and `useRoles` hook for permission checks. ```tsx import { useRoles } from "@/hooks/use-roles"; import { supabase } from "@/integrations/supabase/client"; interface Comment { id: string; content: string; created_at: string; user_id: string; is_spoiler: boolean; is_censored: boolean; profile?: { display_name: string | null; avatar_url: string | null }; } const NovelComments = ({ novelId }: { novelId: string }) => { const { user } = useAuth(); const { isModerator, isAdmin } = useRoles(); const [comments, setComments] = useState([]); // Load comments with user profiles const loadComments = async () => { const { data } = await supabase .from("novel_comments") .select("*") .eq("novel_id", novelId) .order("created_at", { ascending: true }); if (data) { const userIds = [...new Set(data.map((c) => c.user_id))]; const { data: profiles } = await supabase .from("profiles") .select("user_id, display_name, avatar_url") .in("user_id", userIds); const profileMap = new Map(profiles?.map((p) => [p.user_id, p])); setComments(data.map((c) => ({ ...c, profile: profileMap.get(c.user_id) }))); } }; // Post new comment const handlePost = async (content: string) => { if (!user) return; await supabase.from("novel_comments").insert({ user_id: user.id, novel_id: novelId, content: content.trim(), }); loadComments(); }; // Moderation: toggle spoiler flag const handleToggleSpoiler = async (commentId: string, current: boolean) => { await supabase.from("novel_comments") .update({ is_spoiler: !current }) .eq("id", commentId); loadComments(); }; // Moderation: censor comment const handleCensor = async (commentId: string) => { await supabase.from("novel_comments") .update({ is_censored: true }) .eq("id", commentId); loadComments(); }; // Delete comment (owner or moderator) const handleDelete = async (commentId: string) => { await supabase.from("novel_comments").delete().eq("id", commentId); loadComments(); }; const canModerate = isModerator || isAdmin; // ... render component }; ``` -------------------------------- ### Implement Protected React Router Navigation Source: https://context7.com/arielxfelipe/arfel/llms.txt Configures application routes using React Router, implementing a ProtectedRoute wrapper to ensure authentication. It handles public, reader-specific, and role-based administrative routes. ```tsx import { BrowserRouter, Route, Routes, Navigate } from "react-router-dom"; const ProtectedRoute = ({ children }: { children: React.ReactNode }) => { const { user, loading } = useAuth(); if (loading) return null; if (!user) return ; return <>{children}; }; const AppRoutes = () => ( } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> ); ``` -------------------------------- ### Internationalization and Language Management (React/TypeScript) Source: https://context7.com/arielxfelipe/arfel/llms.txt Handles user language preferences (Portuguese, English, Spanish) and provides a translation function `t()` for localized strings. Language settings are persisted in the user's profile. ```tsx import { useLanguage } from "@/contexts/LanguageContext"; import { t } from "@/lib/i18n"; // Language type definition type Language = "pt" | "en" | "es"; // Using the language context const MyComponent = () => { const { language, setLanguage, loading } = useLanguage(); // Change language (persists to user profile) const handleLanguageChange = async (newLang: Language) => { await setLanguage(newLang); }; return (

{t("nav.home", language)}

{t("hero.subtitle", language)}

); }; // Translation function usage const translations: Record> = { "nav.home": { pt: "Início", en: "Home", es: "Inicio" }, "nav.explore": { pt: "Explorar", en: "Explore", es: "Explorar" }, "chapter.chapter": { pt: "Capítulo", en: "Chapter", es: "Capítulo" }, }; export function t(key: string, language: Language): string { return translations[key]?.[language] ?? translations[key]?.["en"] ?? key; } ``` -------------------------------- ### Role-Based Access Control Hook (React/TypeScript) Source: https://context7.com/arielxfelipe/arfel/llms.txt Provides a hook `useRoles` to manage and check user roles (admin, moderator, uploader, revisor). It fetches roles from the `user_roles` table and offers boolean flags for easy access control. ```tsx import { useRoles, AppRole } from "@/hooks/use-roles"; // Available roles type AppRole = "admin" | "moderator" | "uploader" | "revisor" | "user"; // Using the roles hook const AdminGuard = ({ children }: { children: React.ReactNode }) => { const { isAdmin, isModerator, isUploader, isRevisor, hasRole, loading, roles } = useRoles(); if (loading) return null; // Check specific roles if (!isAdmin && !isModerator) { return ; } // Or use hasRole for custom checks if (!hasRole("admin")) { return
Access denied
; } return <>{children}; }; // Database query for roles const loadRoles = async (userId: string) => { const { data } = await supabase .from("user_roles") .select("role") .eq("user_id", userId); return data?.map((r) => r.role) as AppRole[]; }; ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.