diff --git a/fix_lint.mjs b/fix_lint.mjs new file mode 100644 index 00000000..e019e80e --- /dev/null +++ b/fix_lint.mjs @@ -0,0 +1,43 @@ +import fs from 'fs'; +import path from 'path'; + +// Fix toast dependencies +const toastFiles = [ + 'src/components/FocusTimer.tsx', + 'src/components/GroupPomodoro.tsx', + 'src/hooks/useCreateSession.ts', + 'src/hooks/useSessions.ts', + 'src/hooks/useSkillEndorsements.ts', + 'src/pages/Portfolio.tsx', + 'src/pages/ReviewSubmission.tsx' +]; + +for(const file of toastFiles) { + let content = fs.readFileSync(file, 'utf-8'); + content = content.replace(/(?<=(\[|,)\s*)toast(?=\s*(,|\]))/g, ''); + content = content.replace(/,\s*,/g, ','); + content = content.replace(/\[\s*,/g, '['); + content = content.replace(/,\s*\]/g, ']'); + fs.writeFileSync(file, content, 'utf-8'); +} + +// Fix fast refresh warnings +const fastRefreshFiles = [ + 'src/components/markdown/MarkdownRenderer.tsx', + 'src/components/theme-provider.tsx', + 'src/components/ui/sonner.tsx' +]; + +for(const file of fastRefreshFiles) { + let content = fs.readFileSync(file, 'utf-8'); + if (!content.includes('eslint-disable-next-line react-refresh/only-export-components') && !content.includes('eslint-disable react-refresh/only-export-components')) { + content = `/* eslint-disable react-refresh/only-export-components */\n` + content; + fs.writeFileSync(file, content, 'utf-8'); + } +} + +// Fix seedTestimonials dependency +const testimonialsFile = 'src/components/landing/Testimonials.tsx'; +let testContent = fs.readFileSync(testimonialsFile, 'utf-8'); +testContent = testContent.replace(/\[searchTerm, categoryFilter\]/g, '[searchTerm, categoryFilter, seedTestimonials]'); +fs.writeFileSync(testimonialsFile, testContent, 'utf-8'); diff --git a/src/App.tsx b/src/App.tsx index ef7a46e9..c1aa9e5e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,7 +2,6 @@ import React, { useEffect, Suspense, useState, useRef } from "react"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { BrowserRouter, Routes, Route, Navigate, Router, useLocation } from "react-router-dom"; -import { Toaster } from "@/components/ui/toaster"; import { Toaster as Sonner } from "@/components/ui/sonner"; import { TooltipProvider } from "@/components/ui/tooltip"; @@ -399,7 +398,6 @@ function App() { - diff --git a/src/components/AvatarUpload.tsx b/src/components/AvatarUpload.tsx index 1f17d11c..b6fcfd39 100644 --- a/src/components/AvatarUpload.tsx +++ b/src/components/AvatarUpload.tsx @@ -2,7 +2,6 @@ import React, { useRef, useState } from "react"; import { Camera, Loader2 } from "lucide-react"; import { supabase } from "@/integrations/supabase/client"; import { API_BASE_URL } from "@/config/api"; - type AvatarUploadProps = { currentAvatarUrl: string; onUploadSuccess: (url: string) => void; diff --git a/src/components/FloatingAI.tsx b/src/components/FloatingAI.tsx index a8de44a2..77b3a483 100644 --- a/src/components/FloatingAI.tsx +++ b/src/components/FloatingAI.tsx @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ + import { useState } from "react"; import { Bot, Send, X, User } from "lucide-react"; import { API_BASE_URL } from "@/config/api"; diff --git a/src/components/FocusTimer.tsx b/src/components/FocusTimer.tsx index d5965bd1..b0d00891 100644 --- a/src/components/FocusTimer.tsx +++ b/src/components/FocusTimer.tsx @@ -8,7 +8,7 @@ import { DropdownMenuTrigger, DropdownMenuContent, } from '@/components/ui/dropdown-menu'; -import { useToast } from '@/components/ui/use-toast'; +import { toast } from "sonner"; const clampDuration = (value: string, min: number, max: number, fallback: number) => { const next = Number(value); @@ -18,7 +18,7 @@ const clampDuration = (value: string, min: number, max: number, fallback: number export default function FocusTimer() { const { user } = useAuth(); - const { toast } = useToast(); + const [isActive, setIsActive] = useState(false); const [isBreak, setIsBreak] = useState(false); @@ -49,10 +49,9 @@ export default function FocusTimer() { const handleTimerComplete = useCallback(async () => { if (!isBreak) { - toast({ - title: "Focus Session Complete! 🎉", - description: `Great job focusing for ${workDuration} minutes! Time for a break.`, - }); + toast("Focus Session Complete! 🎉", { + description: `Great job focusing for ${workDuration} minutes! Time for a break.` +}); setIsBreak(true); setTimeLeft(breakDuration * 60); @@ -66,15 +65,14 @@ export default function FocusTimer() { .eq('id', user.id); } } else { - toast({ - title: "Break Over!", - description: "Ready for another focus session?", - }); + toast("Break Over!", { + description: "Ready for another focus session?" +}); setIsBreak(false); setIsActive(false); setTimeLeft(workDuration * 60); } - }, [isBreak, workDuration, breakDuration, focusTimeThisWeek, user, toast]); + }, [isBreak, workDuration, breakDuration, focusTimeThisWeek, user]); // Timer logic useEffect(() => { diff --git a/src/components/GroupPomodoro.tsx b/src/components/GroupPomodoro.tsx index bc02012e..7c15e7e4 100644 --- a/src/components/GroupPomodoro.tsx +++ b/src/components/GroupPomodoro.tsx @@ -3,7 +3,7 @@ import { supabase } from '@/integrations/supabase/client'; import { useAuth } from '@/contexts/useAuth'; import { Play, Square, Coffee, Clock } from 'lucide-react'; import { Button } from '@/components/ui/button'; -import { useToast } from '@/components/ui/use-toast'; +import { toast } from "sonner"; import { motion } from 'framer-motion'; import { logError } from '@/utils/logger'; @@ -31,7 +31,7 @@ const formatTime = (seconds: number) => { }; export default memo(function GroupPomodoro({ roomId, creatorId }: GroupPomodoroProps) { - const { toast } = useToast(); + const { user } = useAuth(); const isCreator = creatorId !== null && user?.id === creatorId; @@ -75,11 +75,9 @@ export default memo(function GroupPomodoro({ roomId, creatorId }: GroupPomodoroP } } catch (err: any) { logError(err, { context: "GroupPomodoro.fetchTimerState", roomId }); - toast({ - title: "Failed to load timer", - description: err.message || "Could not sync the room's timer state.", - variant: "destructive", - }); + toast.error("Failed to load timer", { + description: err.message || "Could not sync the room's timer state." +}); } }; @@ -105,7 +103,7 @@ export default memo(function GroupPomodoro({ roomId, creatorId }: GroupPomodoroP active = false; supabase.removeChannel(channel); }; - }, [roomId, toast]); + }, [roomId]); const clampDurations = useCallback(() => ({ work: Math.min(WORK_MAX, Math.max(WORK_MIN, Math.floor(workDuration))), @@ -137,31 +135,27 @@ export default memo(function GroupPomodoro({ roomId, creatorId }: GroupPomodoroP } } catch (err: any) { logError(err, { context: "GroupPomodoro.setGroupTimer", roomId, newState }); - toast({ - title: 'Timer update failed', - description: err.message || 'Could not sync the timer. Please try again.', - variant: 'destructive', - }); + toast.error('Timer update failed', { + description: err.message || 'Could not sync the timer. Please try again.' +}); } - }, [clampDurations, roomId, toast]); + }, [clampDurations, roomId]); const handleTimerComplete = useCallback(async () => { if (!isCreator) return; if (timerState === 'work') { - toast({ - title: 'Group Focus Session Complete! 🎉', - description: 'Great job focusing! Time for a short break.', - }); + toast('Group Focus Session Complete! 🎉', { + description: 'Great job focusing! Time for a short break.' +}); await setGroupTimer('break'); } else if (timerState === 'break') { - toast({ - title: 'Break Over!', - description: 'Back to focus?', - }); + toast('Break Over!', { + description: 'Back to focus?' +}); await setGroupTimer('idle'); } - }, [isCreator, timerState, setGroupTimer, toast]); + }, [isCreator, timerState, setGroupTimer]); // Countdown logic useEffect(() => { diff --git a/src/components/NotificationsDropdown.tsx b/src/components/NotificationsDropdown.tsx index 6505cb30..103f4662 100644 --- a/src/components/NotificationsDropdown.tsx +++ b/src/components/NotificationsDropdown.tsx @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ + import { useState, useEffect, useRef } from "react"; import { Bell } from "lucide-react"; import { supabase } from "@/integrations/supabase/client"; diff --git a/src/components/Room/ChatBox.tsx b/src/components/Room/ChatBox.tsx index 6f8f6219..3a8c2fb0 100644 --- a/src/components/Room/ChatBox.tsx +++ b/src/components/Room/ChatBox.tsx @@ -9,7 +9,7 @@ const MarkdownRenderer = React.lazy(() => ); interface ChatBoxProps { - // eslint-disable-next-line @typescript-eslint/no-explicit-any + messages: any[]; user: User | null; onSendMessage: (msg: string) => Promise; diff --git a/src/components/Room/InviteMenu.tsx b/src/components/Room/InviteMenu.tsx index bc0ab93a..46d528ef 100644 --- a/src/components/Room/InviteMenu.tsx +++ b/src/components/Room/InviteMenu.tsx @@ -14,7 +14,7 @@ export const InviteMenu = React.memo(function InviteMenu({ roomId }: InviteMenuP const handleInvite = async () => { if (!inviteEmail.trim()) return; setIsInviting(true); - // eslint-disable-next-line @typescript-eslint/no-explicit-any + const { error } = await (supabase.rpc as any)("invite_to_study_room", { p_room_id: roomId, p_user_email: inviteEmail, diff --git a/src/components/Sparkles.tsx b/src/components/Sparkles.tsx index d90eb227..f3b5068e 100644 --- a/src/components/Sparkles.tsx +++ b/src/components/Sparkles.tsx @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ + import React, { useEffect, useRef } from 'react'; const Sparkles: React.FC = () => { diff --git a/src/components/StudyRooms.tsx b/src/components/StudyRooms.tsx index 40171c28..10368ba3 100644 --- a/src/components/StudyRooms.tsx +++ b/src/components/StudyRooms.tsx @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ + import { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { supabase } from '@/integrations/supabase/client'; diff --git a/src/components/Whiteboard/Canvas.tsx b/src/components/Whiteboard/Canvas.tsx index 8a573967..aab58b03 100644 --- a/src/components/Whiteboard/Canvas.tsx +++ b/src/components/Whiteboard/Canvas.tsx @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ + import { useCallback, useEffect, useRef, useState } from "react"; import { supabase } from "@/integrations/supabase/client"; import { useAuth } from "@/contexts/useAuth"; diff --git a/src/components/dashboard/RecentActivity.tsx b/src/components/dashboard/RecentActivity.tsx index 675a56db..a0f5b005 100644 --- a/src/components/dashboard/RecentActivity.tsx +++ b/src/components/dashboard/RecentActivity.tsx @@ -49,7 +49,7 @@ export default function RecentActivity() { supabase .from("resources") .select("id, title, created_at") - .eq("user_id", user.id) + .eq("uploaded_by", user.id) .order("created_at", { ascending: false }) .limit(3), supabase diff --git a/src/components/landing/Testimonials.tsx b/src/components/landing/Testimonials.tsx index 1dc4a126..c14367e2 100644 --- a/src/components/landing/Testimonials.tsx +++ b/src/components/landing/Testimonials.tsx @@ -50,6 +50,89 @@ function mapDbRowToTestimonial(row: { }; } +const seedTestimonials: Testimonial[] = [ + { + text: "PeerLearn helped me crack my first internship interview.", + name: "Aisha Khan", + role: "AIML Student", + rating: 5, + avatar: "https://i.pravatar.cc/150?img=32", + verified: true, + skills: ["Machine Learning", "Python", "DSA"], + outcome: "Secured Internship at Google", + }, + { + text: "I started mentoring juniors and improved my communication skills.", + name: "Rahul Sharma", + role: "Senior Mentor", + rating: 5, + avatar: "https://i.pravatar.cc/150?img=64", + verified: true, + skills: ["Mentoring", "System Design", "Leadership"], + outcome: "Became a Top-Rated Mentor", + }, + { + text: "Found amazing teammates for hackathons and projects.", + name: "John Patel", + role: "Web Developer", + rating: 4, + avatar: "https://i.pravatar.cc/150?img=45", + verified: true, + skills: ["React", "Next.js", "Tailwind"], + outcome: "Won 2 Hackathons", + }, + { + text: "Built a polished project portfolio with mentor guidance.", + name: "Maya Singh", + role: "Frontend Dev", + rating: 5, + avatar: "https://i.pravatar.cc/150?img=12", + verified: true, + skills: ["UI/UX", "JavaScript", "CSS"], + outcome: "Landed First Remote Job", + }, + { + text: "The mock interviews were exactly what I needed to build confidence.", + name: "David Kim", + role: "Software Engineer", + rating: 5, + avatar: "https://i.pravatar.cc/150?img=11", + verified: true, + skills: ["Interview Prep", "Algorithms", "Java"], + outcome: "Passed FAANG Interview", + }, + { + text: "Found a study buddy and we kept each other accountable every day.", + name: "Sarah Jenkins", + role: "Data Science Learner", + rating: 4, + avatar: "https://i.pravatar.cc/150?img=5", + verified: true, + skills: ["Data Analysis", "SQL", "Tableau"], + outcome: "Completed Certification", + }, + { + text: "Gained hands-on experience by reviewing peers' code submissions.", + name: "Omar Farooq", + role: "Backend Dev", + rating: 5, + avatar: "https://i.pravatar.cc/150?img=53", + verified: true, + skills: ["Node.js", "PostgreSQL", "Code Review"], + outcome: "Promoted to Mid-Level", + }, + { + text: "The community is incredible. Never felt stuck on a bug for long.", + name: "Elena Rodriguez", + role: "Full Stack Student", + rating: 5, + avatar: "https://i.pravatar.cc/150?img=44", + verified: true, + skills: ["React", "Express", "MongoDB"], + outcome: "Launched MVP", + } +]; + export function Testimonials() { const scrollRef = useRef(null); const testimonialAutoScrollRef = useRef(null); @@ -63,71 +146,6 @@ export function Testimonials() { const [isSubmitting, setIsSubmitting] = useState(false); const [liveTestimonials, setLiveTestimonials] = useState([]); - // Seeded fallback content — always shown so the carousel never looks empty - // while real submissions are still trickling in. - const seedTestimonials: Testimonial[] = [ - { - text: "PeerLearn helped me crack my first internship interview.", - name: "Aisha Khan", - role: "AIML Student", - rating: 5, - avatar: "https://i.pravatar.cc/150?img=32", - verified: true, - skills: ["Machine Learning", "Python", "DSA"], - outcome: "Secured Internship at Google", - }, - { - text: "I started mentoring juniors and improved my communication skills.", - name: "Rahul Sharma", - role: "Senior Mentor", - rating: 5, - avatar: "https://i.pravatar.cc/150?img=64", - verified: true, - skills: ["Mentoring", "System Design", "Leadership"], - outcome: "Became a Top-Rated Mentor", - }, - { - text: "Found amazing teammates for hackathons and projects.", - name: "John Patel", - role: "Web Developer", - rating: 4, - avatar: "https://i.pravatar.cc/150?img=45", - verified: true, - skills: ["React", "Next.js", "Tailwind"], - outcome: "Won 2 Hackathons", - }, - { - text: "Built a polished project portfolio with mentor guidance.", - name: "Maya Singh", - role: "Frontend Developer", - rating: 5, - avatar: "https://i.pravatar.cc/150?img=47", - verified: true, - skills: ["TypeScript", "Framer Motion", "UI/UX"], - outcome: "3 Projects Added to Portfolio", - }, - { - text: "Mentors gave real-world advice that helped my internship prep.", - name: "Priya Malhotra", - role: "ML Intern", - rating: 5, - avatar: "https://i.pravatar.cc/150?img=33", - verified: true, - skills: ["TensorFlow", "Computer Vision", "Research"], - outcome: "Improved DSA Rating by 450", - }, - { - text: "Great community for interview practice and study groups.", - name: "Gautam Reddy", - role: "DSA Enthusiast", - rating: 4, - avatar: "https://i.pravatar.cc/150?img=68", - verified: true, - skills: ["LeetCode", "Competitive Programming"], - outcome: "First Open Source Contribution", - }, - ]; - const fetchTestimonials = useCallback(async () => { const { data, error } = await supabase .from("testimonials") diff --git a/src/components/markdown/MarkdownRenderer.tsx b/src/components/markdown/MarkdownRenderer.tsx index b9b4ac8b..7203e0fc 100644 --- a/src/components/markdown/MarkdownRenderer.tsx +++ b/src/components/markdown/MarkdownRenderer.tsx @@ -1 +1,2 @@ +/* eslint-disable react-refresh/only-export-components */ export { MarkdownRenderer, default } from "@/components/MarkdownRenderer"; \ No newline at end of file diff --git a/src/components/mentor/MentorForm.tsx b/src/components/mentor/MentorForm.tsx index 6f94c175..a29d6b6e 100644 --- a/src/components/mentor/MentorForm.tsx +++ b/src/components/mentor/MentorForm.tsx @@ -1,3 +1,4 @@ + import { useState, useEffect } from "react"; import { motion } from "framer-motion"; import { CheckCircle2, Loader2, Plus, X } from "lucide-react"; diff --git a/src/components/theme-provider.tsx b/src/components/theme-provider.tsx index 33c1494b..68696cd4 100644 --- a/src/components/theme-provider.tsx +++ b/src/components/theme-provider.tsx @@ -1 +1,2 @@ +/* eslint-disable react-refresh/only-export-components */ export { ThemeProvider, useTheme } from "@/contexts/ThemeContext"; \ No newline at end of file diff --git a/src/components/ui/sonner.tsx b/src/components/ui/sonner.tsx index 1ffd27e3..67947863 100644 --- a/src/components/ui/sonner.tsx +++ b/src/components/ui/sonner.tsx @@ -1,3 +1,4 @@ +/* eslint-disable react-refresh/only-export-components */ import { Toaster as Sonner, toast } from "sonner"; type ToasterProps = React.ComponentProps; diff --git a/src/components/ui/textarea.tsx b/src/components/ui/textarea.tsx index 5fd19089..703ef7ed 100644 --- a/src/components/ui/textarea.tsx +++ b/src/components/ui/textarea.tsx @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-empty-object-type, no-unsafe-finally, @typescript-eslint/no-unused-expressions, @typescript-eslint/ban-ts-comment, @typescript-eslint/no-require-imports */ + import * as React from "react"; import { cn } from "@/lib/utils"; diff --git a/src/components/ui/toast.tsx b/src/components/ui/toast.tsx deleted file mode 100644 index 0699548f..00000000 --- a/src/components/ui/toast.tsx +++ /dev/null @@ -1,111 +0,0 @@ -import * as React from "react"; -import * as ToastPrimitives from "@radix-ui/react-toast"; -import { cva, type VariantProps } from "class-variance-authority"; -import { X } from "lucide-react"; - -import { cn } from "@/lib/utils"; - -const ToastProvider = ToastPrimitives.Provider; - -const ToastViewport = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -ToastViewport.displayName = ToastPrimitives.Viewport.displayName; - -const toastVariants = cva( - "group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full", - { - variants: { - variant: { - default: "border bg-background text-foreground", - destructive: "destructive group border-destructive bg-destructive text-destructive-foreground", - }, - }, - defaultVariants: { - variant: "default", - }, - }, -); - -const Toast = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & VariantProps ->(({ className, variant, ...props }, ref) => { - return ; -}); -Toast.displayName = ToastPrimitives.Root.displayName; - -const ToastAction = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -ToastAction.displayName = ToastPrimitives.Action.displayName; - -const ToastClose = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - - - -)); -ToastClose.displayName = ToastPrimitives.Close.displayName; - -const ToastTitle = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -ToastTitle.displayName = ToastPrimitives.Title.displayName; - -const ToastDescription = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -ToastDescription.displayName = ToastPrimitives.Description.displayName; - -type ToastProps = React.ComponentPropsWithoutRef; - -type ToastActionElement = React.ReactElement; - -export { - type ToastProps, - type ToastActionElement, - ToastProvider, - ToastViewport, - Toast, - ToastTitle, - ToastDescription, - ToastClose, - ToastAction, -}; diff --git a/src/components/ui/toaster.tsx b/src/components/ui/toaster.tsx deleted file mode 100644 index 2cba1c82..00000000 --- a/src/components/ui/toaster.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { useToast } from "@/hooks/use-toast"; -import { Toast, ToastClose, ToastDescription, ToastProvider, ToastTitle, ToastViewport } from "@/components/ui/toast"; - -export function Toaster() { - const { toasts } = useToast(); - - return ( - - {toasts.map(function ({ id, title, description, action, ...props }) { - return ( - -
- {title && {title}} - {description && {description}} -
- {action} - -
- ); - })} - -
- ); -} diff --git a/src/components/ui/use-toast.ts b/src/components/ui/use-toast.ts deleted file mode 100644 index b0aef21b..00000000 --- a/src/components/ui/use-toast.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { useToast, toast } from "@/hooks/use-toast"; - -export { useToast, toast }; diff --git a/src/features/notifications/pushNotifications.ts b/src/features/notifications/pushNotifications.ts index ccf968f7..54859249 100644 --- a/src/features/notifications/pushNotifications.ts +++ b/src/features/notifications/pushNotifications.ts @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ + import { supabase } from "@/integrations/supabase/client"; import { env } from "@/env"; import { sanitizeNotificationActionUrl } from "./actionUrl"; diff --git a/src/features/notifications/useNotifications.ts b/src/features/notifications/useNotifications.ts index 8820f1e7..872d010f 100644 --- a/src/features/notifications/useNotifications.ts +++ b/src/features/notifications/useNotifications.ts @@ -1,5 +1,5 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { useCallback, useEffect, useState } from "react"; + +import { useCallback, useEffect, useMemo, useState } from "react"; import { supabase } from "@/integrations/supabase/client"; import type { Notification } from "./types"; import { showBrowserNotification } from "./pushNotifications"; diff --git a/src/hooks/use-toast.ts b/src/hooks/use-toast.ts deleted file mode 100644 index ca1316d6..00000000 --- a/src/hooks/use-toast.ts +++ /dev/null @@ -1,186 +0,0 @@ -import * as React from "react"; - -import type { ToastActionElement, ToastProps } from "@/components/ui/toast"; - -const TOAST_LIMIT = 1; -const TOAST_REMOVE_DELAY = 1000000; - -type ToasterToast = ToastProps & { - id: string; - title?: React.ReactNode; - description?: React.ReactNode; - action?: ToastActionElement; -}; - -const actionTypes = { - ADD_TOAST: "ADD_TOAST", - UPDATE_TOAST: "UPDATE_TOAST", - DISMISS_TOAST: "DISMISS_TOAST", - REMOVE_TOAST: "REMOVE_TOAST", -} as const; - -let count = 0; - -function genId() { - count = (count + 1) % Number.MAX_SAFE_INTEGER; - return count.toString(); -} - -type ActionType = typeof actionTypes; - -type Action = - | { - type: ActionType["ADD_TOAST"]; - toast: ToasterToast; - } - | { - type: ActionType["UPDATE_TOAST"]; - toast: Partial; - } - | { - type: ActionType["DISMISS_TOAST"]; - toastId?: ToasterToast["id"]; - } - | { - type: ActionType["REMOVE_TOAST"]; - toastId?: ToasterToast["id"]; - }; - -interface State { - toasts: ToasterToast[]; -} - -const toastTimeouts = new Map>(); - -const addToRemoveQueue = (toastId: string) => { - if (toastTimeouts.has(toastId)) { - return; - } - - const timeout = setTimeout(() => { - toastTimeouts.delete(toastId); - dispatch({ - type: "REMOVE_TOAST", - toastId: toastId, - }); - }, TOAST_REMOVE_DELAY); - - toastTimeouts.set(toastId, timeout); -}; - -export const reducer = (state: State, action: Action): State => { - switch (action.type) { - case "ADD_TOAST": - return { - ...state, - toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT), - }; - - case "UPDATE_TOAST": - return { - ...state, - toasts: state.toasts.map((t) => (t.id === action.toast.id ? { ...t, ...action.toast } : t)), - }; - - case "DISMISS_TOAST": { - const { toastId } = action; - - // ! Side effects ! - This could be extracted into a dismissToast() action, - // but I'll keep it here for simplicity - if (toastId) { - addToRemoveQueue(toastId); - } else { - state.toasts.forEach((toast) => { - addToRemoveQueue(toast.id); - }); - } - - return { - ...state, - toasts: state.toasts.map((t) => - t.id === toastId || toastId === undefined - ? { - ...t, - open: false, - } - : t, - ), - }; - } - case "REMOVE_TOAST": - if (action.toastId === undefined) { - return { - ...state, - toasts: [], - }; - } - return { - ...state, - toasts: state.toasts.filter((t) => t.id !== action.toastId), - }; - } -}; - -const listeners: Array<(state: State) => void> = []; - -let memoryState: State = { toasts: [] }; - -function dispatch(action: Action) { - memoryState = reducer(memoryState, action); - listeners.forEach((listener) => { - listener(memoryState); - }); -} - -type Toast = Omit; - -function toast({ ...props }: Toast) { - const id = genId(); - - const update = (props: ToasterToast) => - dispatch({ - type: "UPDATE_TOAST", - toast: { ...props, id }, - }); - const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id }); - - dispatch({ - type: "ADD_TOAST", - toast: { - ...props, - id, - open: true, - onOpenChange: (open) => { - if (!open) dismiss(); - }, - }, - }); - - return { - id: id, - dismiss, - update, - }; -} - -function useToast() { - const [state, setState] = React.useState(memoryState); - - React.useEffect(() => { - listeners.push(setState); - return () => { - const index = listeners.indexOf(setState); - if (index > -1) { - listeners.splice(index, 1); - } - }; - }, [state]); - - return { - ...state, - toast, - dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }), - }; -} - -export { useToast, toast }; diff --git a/src/hooks/useAwardXP.ts b/src/hooks/useAwardXP.ts index 07efa27f..1372d852 100644 --- a/src/hooks/useAwardXP.ts +++ b/src/hooks/useAwardXP.ts @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ + import { useMutation, useQueryClient } from "@tanstack/react-query"; import { supabase } from "@/integrations/supabase/client"; import { getXPForActivity } from "@/lib/gamification"; diff --git a/src/hooks/useCreateSession.ts b/src/hooks/useCreateSession.ts index 619d2ac2..eb8158c5 100644 --- a/src/hooks/useCreateSession.ts +++ b/src/hooks/useCreateSession.ts @@ -5,7 +5,7 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { format, addHours } from "date-fns"; import { useAuth } from "@/contexts/useAuth"; import { supabase } from "@/integrations/supabase/client"; -import { useToast } from "@/hooks/use-toast"; +import { toast } from "sonner"; import { useAwardXP } from "@/hooks/useAwardXP"; export const formSchema = z @@ -45,7 +45,7 @@ interface UseCreateSessionProps { export function useCreateSession({ onSuccess, setOpen }: UseCreateSessionProps) { const { user } = useAuth(); - const { toast } = useToast(); + const [isLoading, setIsLoading] = useState(false); const [selectedPreset, setSelectedPreset] = useState(60); const [useCustom, setUseCustom] = useState(false); @@ -74,11 +74,9 @@ export function useCreateSession({ onSuccess, setOpen }: UseCreateSessionProps) const onSubmit = useCallback(async (values: FormValues) => { if (!user) { - toast({ - title: "Error", - description: "You must be logged in to create a session.", - variant: "destructive", - }); + toast.error("Error", { + description: "You must be logged in to create a session." +}); return; } @@ -104,10 +102,9 @@ export function useCreateSession({ onSuccess, setOpen }: UseCreateSessionProps) if (error) throw error; - toast({ - title: "Session scheduled! 🎉", - description: `"${values.title}" is scheduled for ${format(scheduledAt, "PPP 'at' p")}.`, - }); + toast("Session scheduled! 🎉", { + description: `"${values.title}" is scheduled for ${format(scheduledAt, "PPP 'at' p")}.` +}); form.reset(); setSelectedPreset(60); @@ -118,15 +115,13 @@ export function useCreateSession({ onSuccess, setOpen }: UseCreateSessionProps) } catch (error: unknown) { const msg = error instanceof Error ? error.message : "Something went wrong."; - toast({ - title: "Error", - description: msg, - variant: "destructive", - }); + toast.error("Error", { + description: msg +}); } finally { setIsLoading(false); } - }, [user, resolveDurationMinutes, form, toast, awardXP, onSuccess, setOpen]); + }, [user, resolveDurationMinutes, form, awardXP, onSuccess, setOpen]); return { form, diff --git a/src/hooks/useMessages.ts b/src/hooks/useMessages.ts index 3beb06b9..a282faff 100644 --- a/src/hooks/useMessages.ts +++ b/src/hooks/useMessages.ts @@ -1,7 +1,7 @@ import { useState, useEffect, useMemo, useCallback, useRef, Dispatch, SetStateAction } from "react"; import { supabase } from "@/integrations/supabase/client"; import { useAwardXP } from "@/hooks/useAwardXP"; -import { toast } from "@/hooks/use-toast"; +import { toast } from "sonner"; import { logError } from "@/utils/logger"; export type ProfileSummary = { @@ -245,11 +245,9 @@ export function useMessages( } catch (err: any) { logError(err, { context: "useMessages.getUsers" }); setError("Failed to load profiles"); - toast({ - title: "Failed to load profiles", - description: err.message || "An unexpected error occurred", - variant: "destructive", - }); + toast.error("Failed to load profiles", { + description: err.message || "An unexpected error occurred" +}); } finally { setLoadingUsers(false); } @@ -285,11 +283,9 @@ export function useMessages( if (cancelled) return; logError(err, { context: "useMessages.getConversationSummaries" }); setError("Failed to load conversations"); - toast({ - title: "Failed to load conversations", - description: err.message || "An unexpected error occurred", - variant: "destructive", - }); + toast.error("Failed to load conversations", { + description: err.message || "An unexpected error occurred" +}); } finally { if (!cancelled) setLoadingConversations(false); } @@ -378,11 +374,9 @@ export function useMessages( if (cancelled) return; logError(err, { context: "useMessages.loadInitialThread" }); setError("Failed to load messages"); - toast({ - title: "Failed to load messages", - description: err.message || "An unexpected error occurred", - variant: "destructive", - }); + toast.error("Failed to load messages", { + description: err.message || "An unexpected error occurred" +}); } finally { if (!cancelled) { setLoadingThreadMessages(false); @@ -425,11 +419,9 @@ export function useMessages( } } catch (err: any) { logError(err, { context: "useMessages.loadMoreThreadMessages" }); - toast({ - title: "Failed to load earlier messages", - description: err.message || "An unexpected error occurred", - variant: "destructive", - }); + toast.error("Failed to load earlier messages", { + description: err.message || "An unexpected error occurred" +}); } finally { setLoadingMoreThreadMessages(false); } @@ -544,11 +536,9 @@ export function useMessages( ); } catch (err: any) { logError(err, { context: "useMessages.markAsRead" }); - toast({ - title: "Failed to mark messages as read", - description: err.message || "An unexpected error occurred", - variant: "destructive", - }); + toast.error("Failed to mark messages as read", { + description: err.message || "An unexpected error occurred" +}); } }; @@ -568,11 +558,9 @@ export function useMessages( if (!content || !selectedUser || !currentUserId) return false; if (content.length > 1000) { - toast({ - title: "Message too long", - description: "Message exceeds the 1000 character limit.", - variant: "destructive", - }); + toast.error("Message too long", { + description: "Message exceeds the 1000 character limit." +}); return false; } @@ -607,11 +595,9 @@ export function useMessages( return true; } catch (err: any) { logError(err, { context: "useMessages.sendMessage" }); - toast({ - title: "Failed to send message", - description: err.message || "An unexpected error occurred", - variant: "destructive", - }); + toast.error("Failed to send message", { + description: err.message || "An unexpected error occurred" +}); return false; } }, [currentUserId, selectedUser, awardXP, upsertRawSummary]); diff --git a/src/hooks/useResources.ts b/src/hooks/useResources.ts index 443e0761..8b6c6627 100644 --- a/src/hooks/useResources.ts +++ b/src/hooks/useResources.ts @@ -1,7 +1,7 @@ import { useCallback, useEffect, useRef, useState } from "react"; -import { toast } from "@/hooks/use-toast"; +import { toast } from "sonner"; import { supabase } from "@/integrations/supabase/client"; import { isAbortError, normalizeError, safeSupabaseCall } from "@/lib/http"; import { logError } from "@/utils/logger"; @@ -121,11 +121,9 @@ export const useResources = (filters?: ResourceFilters) => { setError(normalized.message); setResources([]); - toast({ - title: "Resource load failed", - description: normalized.message, - variant: "destructive", - }); + toast.error("Resource load failed", { + description: normalized.message +}); } finally { if (!isMountedRef.current || requestId !== requestIdRef.current || controller.signal.aborted) { diff --git a/src/hooks/useRoomChat.ts b/src/hooks/useRoomChat.ts index 062bc1af..8a9a7e78 100644 --- a/src/hooks/useRoomChat.ts +++ b/src/hooks/useRoomChat.ts @@ -4,13 +4,13 @@ import { supabase } from "@/integrations/supabase/client"; import { User } from "@supabase/supabase-js"; export function useRoomChat(id: string | undefined, user: User | null) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any + const [messages, setMessages] = useState([]); const fetchMessages = useCallback(async () => { if (!id) return; const { data, error } = await supabase - // eslint-disable-next-line @typescript-eslint/no-explicit-any + .from('study_room_messages' as any) .select('*, profiles(name, avatar_url)') .eq('room_id', id) @@ -30,7 +30,7 @@ export function useRoomChat(id: string | undefined, user: User | null) { const handleSendMessage = useCallback(async (newMessage: string) => { if (!newMessage.trim() || !user || !id) return false; - // eslint-disable-next-line @typescript-eslint/no-explicit-any + const { error } = await supabase.from('study_room_messages' as any).insert([ { room_id: id, profile_id: user.id, content: newMessage } ]); diff --git a/src/hooks/useRoomDetails.ts b/src/hooks/useRoomDetails.ts index ad3f7efa..11cae018 100644 --- a/src/hooks/useRoomDetails.ts +++ b/src/hooks/useRoomDetails.ts @@ -5,7 +5,7 @@ import { supabase } from "@/integrations/supabase/client"; import { User } from "@supabase/supabase-js"; export function useRoomDetails(id: string | undefined, user: User | null) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any + const [room, setRoom] = useState(null); const navigate = useNavigate(); @@ -13,7 +13,7 @@ export function useRoomDetails(id: string | undefined, user: User | null) { if (!id) return; const fetchRoomDetails = async () => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any + const { data, error } = await supabase.from('study_rooms' as any).select('*').eq('id', id).single(); if (error) { console.error("Error fetching room:", error); diff --git a/src/hooks/useRoomPresence.ts b/src/hooks/useRoomPresence.ts index 68fc4d66..320fc95c 100644 --- a/src/hooks/useRoomPresence.ts +++ b/src/hooks/useRoomPresence.ts @@ -3,18 +3,18 @@ import { supabase } from "@/integrations/supabase/client"; import { User } from "@supabase/supabase-js"; export function useRoomPresence(id: string | undefined, user: User | null, fetchMessages: () => void, setActivities: React.Dispatch>) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any + const [participants, setParticipants] = useState([]); useEffect(() => { if (!id || !user) return; - // eslint-disable-next-line @typescript-eslint/no-explicit-any + let roomChannel: any; let cancelled = false; const initializeChat = async () => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any + const { data } = await supabase.from('profiles' as any).select('name').eq('id', user.id).single() as any; // The effect may have been cleaned up (user left/switched rooms) while @@ -31,7 +31,7 @@ export function useRoomPresence(id: string | undefined, user: User | null, fetch roomChannel .on('presence', { event: 'sync' }, () => { const newState = roomChannel.presenceState(); - // eslint-disable-next-line @typescript-eslint/no-explicit-any + const onlineUsers = Object.values(newState).map((p: any) => p[0]); setParticipants(onlineUsers); diff --git a/src/hooks/useSessions.ts b/src/hooks/useSessions.ts index 6d3d96ee..d1c49584 100644 --- a/src/hooks/useSessions.ts +++ b/src/hooks/useSessions.ts @@ -1,7 +1,7 @@ import { useState, useEffect, useRef, useMemo, useCallback } from "react"; import { supabase } from "@/integrations/supabase/client"; import { useAwardXP } from "@/hooks/useAwardXP"; -import { useToast } from "@/hooks/use-toast"; +import { toast } from "sonner"; import { API_BASE_URL } from "@/config/api"; // UI tab labels don't match the DB's session status values, so each tab // must be translated to the status (or statuses) it represents before filtering. @@ -13,7 +13,7 @@ const TAB_TO_STATUS: Record = { export function useSessions(user: any) { const { mutate: awardXP } = useAwardXP(); - const { toast } = useToast(); + const [sessions, setSessions] = useState([]); const [messages, setMessages] = useState([]); @@ -130,9 +130,12 @@ export function useSessions(user: any) { .order("created_at", { ascending: true }); if (error) { - console.error("Failed to fetch messages:", error); - toast({ title: "Error", description: "Failed to load messages.", variant: "destructive" }); - return; + console.error("Failed to fetch session messages:", error); + toast.error("Failed to load messages", { + description: "Could not load session messages. Please try again." +}); + } else { + setMessages(data || []); } setMessages(data || []); @@ -258,12 +261,16 @@ export function useSessions(user: any) { const { error } = await supabase.rpc("join_session", { p_session_id: sessionId }); if (error) { if (error.message.includes("Session is full")) { - toast({ title: "Session Full", description: "This session has reached its seat limit.", variant: "destructive" }); + toast.error("Session Full", { + description: "This session has reached its seat limit." +}); } else { throw error; } } else { - toast({ title: "Success! 🎉", description: "You have joined the session." }); + toast("Success! 🎉", { + description: "You have joined the session." +}); // Only award XP here, after join_session has actually succeeded and // confirmed the user as a participant. This is the single source of @@ -275,9 +282,11 @@ export function useSessions(user: any) { } } } catch (err: any) { - toast({ title: "Error", description: err.message || "Failed to join session.", variant: "destructive" }); + toast.error("Error", { + description: err.message || "Failed to join session." +}); } - }, [user, awardXP, toast]); + }, [user, awardXP]); const sendMessage = useCallback(async (msgText: string) => { if (!msgText.trim() || !selectedSession) return; @@ -308,9 +317,11 @@ export function useSessions(user: any) { if (error) throw error; } catch (err: any) { - toast({ title: "Failed to send message", description: err.message || "An unexpected error occurred.", variant: "destructive" }); + toast.error("Failed to send message", { + description: err.message || "An unexpected error occurred." +}); } - }, [selectedSession, user, toast]); + }, [selectedSession, user]); const togglePinMessage = useCallback(async (messageId: string, currentPinnedState: boolean) => { if (!selectedSession) return; diff --git a/src/hooks/useSkillEndorsements.test.ts b/src/hooks/useSkillEndorsements.test.ts index 961103fb..b6d581a6 100644 --- a/src/hooks/useSkillEndorsements.test.ts +++ b/src/hooks/useSkillEndorsements.test.ts @@ -2,7 +2,7 @@ import { renderHook, act, waitFor } from "@testing-library/react"; import { describe, it, expect, vi, beforeEach } from "vitest"; import { useSkillEndorsements } from "./useSkillEndorsements"; import { supabase } from "@/integrations/supabase/client"; -import { useToast } from "@/hooks/use-toast"; +import { toast } from "sonner"; // Mock supabase vi.mock("@/integrations/supabase/client", () => ({ @@ -14,17 +14,17 @@ vi.mock("@/integrations/supabase/client", () => ({ }, })); -// Mock useToast -vi.mock("@/hooks/use-toast", () => ({ - useToast: vi.fn(), -})); +// Mock sonner +vi.mock("sonner", () => { + const mockToast = vi.fn() as any; + mockToast.success = vi.fn(); + mockToast.error = vi.fn(); + return { toast: mockToast }; +}); describe("useSkillEndorsements rapid interactions", () => { - const mockToast = vi.fn(); - beforeEach(() => { vi.clearAllMocks(); - (useToast as any).mockReturnValue({ toast: mockToast }); // Default auth mock (supabase.auth.getUser as any).mockResolvedValue({ diff --git a/src/hooks/useSkillEndorsements.ts b/src/hooks/useSkillEndorsements.ts index af8f000b..3e585d42 100644 --- a/src/hooks/useSkillEndorsements.ts +++ b/src/hooks/useSkillEndorsements.ts @@ -1,6 +1,6 @@ import { useState, useEffect, useCallback, useMemo, useRef } from "react"; import { supabase } from "@/integrations/supabase/client"; -import { useToast } from "@/hooks/use-toast"; +import { toast } from "sonner"; export interface SkillEndorsementData { count: number; @@ -23,7 +23,7 @@ export function useSkillEndorsements({ profileUserId, skills, }: UseSkillEndorsementsOptions): UseSkillEndorsementsReturn { - const { toast } = useToast(); + const skillsKey = JSON.stringify(skills); const stableSkills = useMemo(() => JSON.parse(skillsKey) as string[], [skillsKey]); @@ -99,20 +99,16 @@ export function useSkillEndorsements({ const toggleEndorsement = useCallback( async (skill: string) => { if (!currentUserId) { - toast({ - title: "Sign in required", - description: "Please sign in to endorse skills.", - variant: "destructive", - }); + toast.error("Sign in required", { + description: "Please sign in to endorse skills." +}); return; } if (currentUserId === profileUserId) { - toast({ - title: "Can't endorse yourself", - description: "You cannot endorse skills on your own profile.", - variant: "destructive", - }); + toast.error("Can't endorse yourself", { + description: "You cannot endorse skills on your own profile." +}); return; } @@ -162,16 +158,14 @@ export function useSkillEndorsements({ hasEndorsed: isRemoving, }, })); - toast({ - title: "Something went wrong", - description: "Could not update endorsement. Please try again.", - variant: "destructive", - }); + toast.error("Something went wrong", { + description: "Could not update endorsement. Please try again." +}); } finally { pendingSkillsRef.current.delete(skill); } }, - [currentUserId, profileUserId, endorsements, toast] + [currentUserId, profileUserId, endorsements] ); return { endorsements, loading, toggleEndorsement, currentUserId }; diff --git a/src/integrations/supabase/types.ts b/src/integrations/supabase/types.ts index a2c7b37a..ca3b4b0e 100644 --- a/src/integrations/supabase/types.ts +++ b/src/integrations/supabase/types.ts @@ -6,4 +6,892 @@ export type Json = | { [key: string]: Json | undefined } | Json[] -export type Database = any; +export type Database = { + // Allows to automatically instantiate createClient with right options + // instead of createClient(URL, KEY) + __InternalSupabase: { + PostgrestVersion: "14.5" + } + public: { + Tables: { + mentorship_paths: { + Row: { + id: string + mentor_id: string + mentee_id: string + goal: string + status: string + created_at: string + updated_at: string + } + Insert: { + id?: string + mentor_id: string + mentee_id: string + goal: string + status?: string + created_at?: string + updated_at?: string + } + Update: { + id?: string + mentor_id?: string + mentee_id?: string + goal?: string + status?: string + created_at?: string + updated_at?: string + } + Relationships: [ + { + foreignKeyName: "mentorship_paths_mentee_id_fkey" + columns: ["mentee_id"] + isOneToOne: false + referencedRelation: "profiles" + referencedColumns: ["id"] + }, + { + foreignKeyName: "mentorship_paths_mentor_id_fkey" + columns: ["mentor_id"] + isOneToOne: false + referencedRelation: "profiles" + referencedColumns: ["id"] + } + ] + } + mentorship_milestones: { + Row: { + id: string + path_id: string + title: string + description: string | null + is_completed: boolean + due_date: string | null + created_at: string + updated_at: string + } + Insert: { + id?: string + path_id: string + title: string + description?: string | null + is_completed?: boolean + due_date?: string | null + created_at?: string + updated_at?: string + } + Update: { + id?: string + path_id?: string + title?: string + description?: string | null + is_completed?: boolean + due_date?: string | null + created_at?: string + updated_at?: string + } + Relationships: [ + { + foreignKeyName: "mentorship_milestones_path_id_fkey" + columns: ["path_id"] + isOneToOne: false + referencedRelation: "mentorship_paths" + referencedColumns: ["id"] + } + ] + } + peer_submissions: { + Row: { + id: string + user_id: string + title: string + description: string | null + content_url: string | null + content: string | null + is_anonymous: boolean + status: string + created_at: string + } + Insert: { + id?: string + user_id: string + title: string + description?: string | null + content_url?: string | null + content?: string | null + is_anonymous?: boolean + status?: string + created_at?: string + } + Update: { + id?: string + user_id?: string + title?: string + description?: string | null + content_url?: string | null + content?: string | null + is_anonymous?: boolean + status?: string + created_at?: string + } + Relationships: [ + { + foreignKeyName: "peer_submissions_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "profiles" + referencedColumns: ["id"] + } + ] + } + peer_reviews: { + Row: { + id: string + submission_id: string + reviewer_id: string + feedback: string + rating: number | null + created_at: string + } + Insert: { + id?: string + submission_id: string + reviewer_id: string + feedback: string + rating?: number | null + created_at?: string + } + Update: { + id?: string + submission_id?: string + reviewer_id?: string + feedback?: string + rating?: number | null + created_at?: string + } + Relationships: [ + { + foreignKeyName: "peer_reviews_submission_id_fkey" + columns: ["submission_id"] + isOneToOne: false + referencedRelation: "peer_submissions" + referencedColumns: ["id"] + }, + { + foreignKeyName: "peer_reviews_reviewer_id_fkey" + columns: ["reviewer_id"] + isOneToOne: false + referencedRelation: "profiles" + referencedColumns: ["id"] + } + ] + } + + chat_messages: { + Row: { + created_at: string + id: number + } + Insert: { + created_at?: string + id?: number + } + Update: { + created_at?: string + id?: number + } + Relationships: [] + } + messages: { + Row: { + content: string | null + created_at: string | null + id: string + message: string | null + read_at: string | null + receiver_id: string | null + sender_id: string | null + text: string | null + } + Insert: { + content?: string | null + created_at?: string | null + id?: string + message?: string | null + read_at?: string | null + receiver_id?: string | null + sender_id?: string | null + text?: string | null + } + Update: { + content?: string | null + created_at?: string | null + id?: string + message?: string | null + read_at?: string | null + receiver_id?: string | null + sender_id?: string | null + text?: string | null + } + Relationships: [] + } + leaderboard: { + Row: { + id: string + user_id: string + username: string + avatar_url: string | null + xp: number + streak: number + sessions_joined: number + badges: string[] + updated_at: string + } + Insert: { + id?: string + user_id: string + username: string + avatar_url?: string | null + xp?: number + streak?: number + sessions_joined?: number + badges?: string[] + updated_at?: string + } + Update: { + id?: string + user_id?: string + username?: string + avatar_url?: string | null + xp?: number + streak?: number + sessions_joined?: number + badges?: string[] + updated_at?: string + } + Relationships: [ + { + foreignKeyName: "leaderboard_user_id_fkey" + columns: ["user_id"] + isOneToOne: true + referencedRelation: "profiles" + referencedColumns: ["id"] + } + ] + } + profiles: { + Row: { + avatar_url: string | null + bio: string | null + created_at: string | null + email: string | null + id: string + last_seen: string | null + name: string | null + skills: string[] | null + is_mentor: boolean + is_learner: boolean + points: number | null + sessions_completed: number | null + rating: number | null + badges: string[] | null + interests: string[] | null + teach_subjects: string[] | null + learn_subjects: string[] | null + updated_at: string | null + streak: number + last_active: string | null + restoration_used_today: boolean + restoration_date: string | null + is_in_focus_mode: boolean | null + focus_time_this_week: number | null + learning_style: string | null + availability: string | null + preferred_language: string | null + timezone: string | null + } + Insert: { + avatar_url?: string | null + bio?: string | null + created_at?: string | null + email?: string | null + id: string + last_seen?: string | null + name?: string | null + skills?: string[] | null + is_mentor?: boolean + is_learner?: boolean + points?: number | null + sessions_completed?: number | null + rating?: number | null + badges?: string[] | null + interests?: string[] | null + teach_subjects?: string[] | null + learn_subjects?: string[] | null + updated_at?: string | null + streak?: number + last_active?: string | null + restoration_used_today?: boolean + restoration_date?: string | null + learning_style?: string | null + availability?: string | null + preferred_language?: string | null + timezone?: string | null + } + Update: { + avatar_url?: string | null + bio?: string | null + created_at?: string | null + email?: string | null + id?: string + last_seen?: string | null + name?: string | null + skills?: string[] | null + is_mentor?: boolean + is_learner?: boolean + points?: number | null + sessions_completed?: number | null + rating?: number | null + badges?: string[] | null + interests?: string[] | null + teach_subjects?: string[] | null + learn_subjects?: string[] | null + updated_at?: string | null + streak?: number + last_active?: string | null + restoration_used_today?: boolean + restoration_date?: string | null + learning_style?: string | null + availability?: string | null + preferred_language?: string | null + timezone?: string | null + } + Relationships: [] + } + resources: { + Row: { + id: string + title: string + description: string | null + file_url: string + file_size: number | null + tags: string[] | null + file_type: string + uploaded_by: string + created_at: string + } + Insert: { + id?: string + title: string + description?: string | null + file_url: string + file_size?: number | null + tags?: string[] | null + file_type: string + uploaded_by: string + created_at?: string + } + Update: { + id?: string + title?: string + description?: string | null + file_url?: string + file_size?: number | null + tags?: string[] | null + file_type?: string + uploaded_by?: string + created_at?: string + } + Relationships: [] + } + resource_votes: { + Row: { + id: string + resource_id: string + user_id: string + vote_type: number + created_at: string + } + Insert: { + id?: string + resource_id: string + user_id: string + vote_type: number + created_at?: string + } + Update: { + id?: string + resource_id?: string + user_id?: string + vote_type?: number + created_at?: string + } + Relationships: [] + } + saved_resources: { + Row: { + id: string + resource_id: string + user_id: string + created_at: string + } + Insert: { + id?: string + resource_id: string + user_id: string + created_at?: string + } + Update: { + id?: string + resource_id?: string + user_id?: string + created_at?: string + } + Relationships: [] + } + skill_endorsements: { + Row: { + id: string + skill: string + endorsed_user_id: string + endorser_id: string + created_at: string + } + Insert: { + id?: string + skill: string + endorsed_user_id: string + endorser_id: string + created_at?: string + } + Update: { + id?: string + skill?: string + endorsed_user_id?: string + endorser_id?: string + created_at?: string + } + Relationships: [] + } + testimonials: { + Row: { + id: string + user_id: string + name: string | null + rating: number | null + review: string + status: string + created_at: string + } + Insert: { + id?: string + user_id: string + name?: string | null + rating?: number | null + review: string + status?: string + created_at?: string + } + Update: { + id?: string + user_id?: string + name?: string | null + rating?: number | null + review?: string + status?: string + created_at?: string + } + Relationships: [ + { + foreignKeyName: "testimonials_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] + } + ] + } + study_rooms: { + Row: { + id: string + topic: string + created_by: string | null + created_at: string + is_private: boolean + } + Insert: { + id?: string + topic: string + created_by?: string | null + created_at?: string + is_private?: boolean + } + Update: { + id?: string + topic?: string + created_by?: string | null + created_at?: string + is_private?: boolean + } + Relationships: [ + { + foreignKeyName: "study_rooms_created_by_fkey" + columns: ["created_by"] + isOneToOne: false + referencedRelation: "profiles" + referencedColumns: ["id"] + } + ] + } + study_room_messages: { + Row: { + id: string + room_id: string | null + profile_id: string | null + content: string + created_at: string + } + Insert: { + id?: string + room_id?: string | null + profile_id?: string | null + content: string + created_at?: string + } + Update: { + id?: string + room_id?: string | null + profile_id?: string | null + content?: string + created_at?: string + } + Relationships: [ + { + foreignKeyName: "study_room_messages_profile_id_fkey" + columns: ["profile_id"] + isOneToOne: false + referencedRelation: "profiles" + referencedColumns: ["id"] + }, + { + foreignKeyName: "study_room_messages_room_id_fkey" + columns: ["room_id"] + isOneToOne: false + referencedRelation: "study_rooms" + referencedColumns: ["id"] + } + ] + } + study_room_participants: { + Row: { + room_id: string + profile_id: string + joined_at: string + } + Insert: { + room_id: string + profile_id: string + joined_at?: string + } + Update: { + room_id?: string + profile_id?: string + joined_at?: string + } + Relationships: [ + { + foreignKeyName: "study_room_participants_profile_id_fkey" + columns: ["profile_id"] + isOneToOne: false + referencedRelation: "profiles" + referencedColumns: ["id"] + }, + { + foreignKeyName: "study_room_participants_room_id_fkey" + columns: ["room_id"] + isOneToOne: false + referencedRelation: "study_rooms" + referencedColumns: ["id"] + } + ] + } + sessions: { + Row: { + created_at: string + description: string | null + id: number + scheduled_at: string | null + /** duration_minutes – NEW column added by session scheduling migration */ + duration_minutes: number + /** status values: 'scheduled' | 'live' | 'ended' */ + status: string + student_id: string | null + mentor_id: string | null + seat_limit: number | null + participants: number + title: string | null + tags: string[] | null + } + Insert: { + created_at?: string + description?: string | null + id?: number + scheduled_at?: string | null + duration_minutes?: number + status?: string + student_id?: string | null + mentor_id?: string | null + seat_limit?: number | null + participants?: number + title?: string | null + tags?: string[] | null + } + Update: { + created_at?: string + description?: string | null + id?: number + scheduled_at?: string | null + duration_minutes?: number + status?: string + student_id?: string | null + mentor_id?: string | null + seat_limit?: number | null + participants?: number + title?: string | null + tags?: string[] | null + } + Relationships: [] + } + users: { + Row: { + created_at: string | null + email: string | null + id: string + learning_goals: string | null + name: string | null + skills: string | null + } + Insert: { + created_at?: string | null + email?: string | null + id?: string + learning_goals?: string | null + name?: string | null + skills?: string | null + } + Update: { + created_at?: string | null + email?: string | null + id?: string + learning_goals?: string | null + name?: string | null + skills?: string | null + } + Relationships: [] + } + } + Views: { + skill_endorsement_counts: { + Row: { + endorsed_user_id: string + skill: string + endorsement_count: number + } + Relationships: [] + } + } + Functions: { + submit_peer_review: { + Args: { + p_submission_id: string + p_feedback: string + } + Returns: { + id: string + submission_id: string + reviewer_id: string + feedback: string + rating: number | null + created_at: string + } + } + award_activity_xp: { + Args: { _activity_type: string } + Returns: undefined + } + get_user_rank: { + Args: { + p_user_id: string + p_filter?: string + } + Returns: number + } + has_role: { + Args: { + _role: string + _user_id: string + } + Returns: boolean + } + invite_to_study_room: { + Args: { + p_room_id: string + p_user_email: string + } + Returns: undefined + } + join_leaderboard: { + Args: { + _username: string + _avatar_url: string | null + } + Returns: undefined + } + join_public_study_room: { + Args: { p_room_id: string } + Returns: undefined + } + join_session: { + Args: { p_session_id: string } + Returns: undefined + } + mark_messages_as_read: { + Args: { message_ids: string[] } + Returns: undefined + } + tick_session_statuses: { + Args: Record + Returns: undefined + } + } + Enums: { + [_ in never]: never + } + CompositeTypes: { + [_ in never]: never + } + } +} + +type DatabaseWithoutInternals = Omit + +type DefaultSchema = DatabaseWithoutInternals[Extract] + +export type Tables< + DefaultSchemaTableNameOrOptions extends + | keyof (DefaultSchema["Tables"] & DefaultSchema["Views"]) + | { schema: keyof DatabaseWithoutInternals }, + TableName extends DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & + DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"]) + : never = never, +> = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & + DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"])[TableName] extends { + Row: infer R + } + ? R + : never + : DefaultSchemaTableNameOrOptions extends keyof (DefaultSchema["Tables"] & + DefaultSchema["Views"]) + ? (DefaultSchema["Tables"] & + DefaultSchema["Views"])[DefaultSchemaTableNameOrOptions] extends { + Row: infer R + } + ? R + : never + : never + +export type TablesInsert< + DefaultSchemaTableNameOrOptions extends + | keyof DefaultSchema["Tables"] + | { schema: keyof DatabaseWithoutInternals }, + TableName extends DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] + : never = never, +> = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { + Insert: infer I + } + ? I + : never + : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"] + ? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends { + Insert: infer I + } + ? I + : never + : never + +export type TablesUpdate< + DefaultSchemaTableNameOrOptions extends + | keyof DefaultSchema["Tables"] + | { schema: keyof DatabaseWithoutInternals }, + TableName extends DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] + : never = never, +> = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { + Update: infer U + } + ? U + : never + : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"] + ? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends { + Update: infer U + } + ? U + : never + : never + +export type Enums< + DefaultSchemaEnumNameOrOptions extends + | keyof DefaultSchema["Enums"] + | { schema: keyof DatabaseWithoutInternals }, + EnumName extends DefaultSchemaEnumNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"] + : never = never, +> = DefaultSchemaEnumNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"][EnumName] + : DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema["Enums"] + ? DefaultSchema["Enums"][DefaultSchemaEnumNameOrOptions] + : never + +export type CompositeTypes< + PublicCompositeTypeNameOrOptions extends + | keyof DefaultSchema["CompositeTypes"] + | { schema: keyof DatabaseWithoutInternals }, + CompositeTypeName extends PublicCompositeTypeNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"] + : never = never, +> = PublicCompositeTypeNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName] + : PublicCompositeTypeNameOrOptions extends keyof DefaultSchema["CompositeTypes"] + ? DefaultSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions] + : never + +export const Constants = { + public: { + Enums: {}, + }, +} as const diff --git a/src/lib/http.ts b/src/lib/http.ts index 9723da71..e48bd920 100644 --- a/src/lib/http.ts +++ b/src/lib/http.ts @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ + import { toast } from "sonner"; type UnknownRecord = Record; diff --git a/src/lib/rewardXP.ts b/src/lib/rewardXP.ts index d091fcec..33bb9ae9 100644 --- a/src/lib/rewardXP.ts +++ b/src/lib/rewardXP.ts @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ + import { supabase } from "@/integrations/supabase/client"; export const rewardXP = async ( diff --git a/src/lib/streakSystem.ts b/src/lib/streakSystem.ts index df4b4e52..c2fcc02f 100644 --- a/src/lib/streakSystem.ts +++ b/src/lib/streakSystem.ts @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ + /** * streakSystem.ts * diff --git a/src/pages/Chat.tsx b/src/pages/Chat.tsx index 7a061d44..a33b748b 100644 --- a/src/pages/Chat.tsx +++ b/src/pages/Chat.tsx @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ + import React, { memo, Suspense, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react"; import { ArrowLeft, MessageCircle, Search, Send } from "lucide-react"; import { useChatShortcuts } from "@/hooks/useChatShortcuts"; diff --git a/src/pages/Contact.test.tsx b/src/pages/Contact.test.tsx index ca4459e0..4f296596 100644 --- a/src/pages/Contact.test.tsx +++ b/src/pages/Contact.test.tsx @@ -3,7 +3,7 @@ import { MemoryRouter } from "react-router-dom"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import Contact from "./Contact"; import { supabase } from "@/integrations/supabase/client"; -import { useToast } from "@/hooks/use-toast"; +import { toast } from "sonner"; vi.mock("framer-motion", () => ({ motion: { @@ -19,19 +19,20 @@ vi.mock("@/integrations/supabase/client", () => ({ }, })); -vi.mock("@/hooks/use-toast", () => ({ - useToast: vi.fn(), -})); +vi.mock("sonner", () => { + const mockToast = vi.fn() as any; + mockToast.success = vi.fn(); + mockToast.error = vi.fn(); + return { toast: mockToast }; +}); describe("Contact", () => { - const toast = vi.fn(); const insert = vi.fn(); beforeEach(() => { vi.clearAllMocks(); - localStorage?.clear(); + localStorage?.clear?.(); - (useToast as any).mockReturnValue({ toast }); (supabase.from as any).mockReturnValue({ insert }); insert.mockResolvedValue({ error: null }); }); @@ -72,16 +73,11 @@ describe("Contact", () => { await waitFor(() => { expect(toast).toHaveBeenCalledWith( - expect.objectContaining({ - title: expect.stringMatching(/^Message Sent!/), - }) + expect.stringMatching(/^Message Sent!/), + expect.any(Object) ); }); - expect(toast).not.toHaveBeenCalledWith( - expect.objectContaining({ - title: "Submission Failed", - }) - ); + expect(toast.error).not.toHaveBeenCalled(); }); }); diff --git a/src/pages/Contact.tsx b/src/pages/Contact.tsx index 0aa91698..fa5b85b0 100644 --- a/src/pages/Contact.tsx +++ b/src/pages/Contact.tsx @@ -14,7 +14,7 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Link } from "react-router-dom"; -import { useToast } from "@/hooks/use-toast"; +import { toast } from "sonner"; import { supabase } from "@/integrations/supabase/client"; type ContactFormData = { @@ -138,7 +138,7 @@ function parseSupabaseError(errorMessage: string): string { } export default function Contact() { - const { toast } = useToast(); + const [formData, setFormData] = useState({ first_name: "", last_name: "", @@ -174,22 +174,18 @@ export default function Contact() { e.preventDefault(); if (!validate()) { - toast({ - title: "Validation Error", - description: "Please check the form for errors.", - variant: "destructive", - }); + toast.error("Validation Error", { + description: "Please check the form for errors." +}); return; } // Client-side rate limit check (fast, no DB round-trip needed) const rateLimitCheck = checkRateLimit(formData.message); if (!rateLimitCheck.allowed) { - toast({ - title: "Slow Down", - description: rateLimitCheck.reason, - variant: "destructive", - }); + toast.error("Slow Down", { + description: rateLimitCheck.reason +}); return; } @@ -218,11 +214,9 @@ export default function Contact() { // Record locally so future submissions are caught client-side first recordSubmission(formData.message); - toast({ - title: "Message Sent! 🎉", - description: - "We've received your message and will get back to you shortly.", - }); + toast("Message Sent! 🎉", { + description: "We've received your message and will get back to you shortly." +}); setFormData({ first_name: "", @@ -237,11 +231,9 @@ export default function Contact() { error instanceof Error ? error.message : "An unexpected error occurred. Please try again."; - toast({ - title: "Submission Failed", - description: message, - variant: "destructive", - }); + toast.error("Submission Failed", { + description: message +}); } finally { setIsSubmitting(false); } diff --git a/src/pages/ContributorDashboard.tsx b/src/pages/ContributorDashboard.tsx index 3be0a156..e454c0ab 100644 --- a/src/pages/ContributorDashboard.tsx +++ b/src/pages/ContributorDashboard.tsx @@ -5,7 +5,7 @@ import RecentActivity from "@/components/dashboard/RecentActivity"; import LearningProgress from "@/components/dashboard/LearningProgress"; import Leaderboard from "@/components/dashboard/Leaderboard"; import { ErrorBanner } from "@/components/ui/error-banner"; -import { toast } from "@/hooks/use-toast"; +import { toast } from "sonner"; import { normalizeError, safeFetchJson } from "@/lib/http"; function ContributorDashboard() { @@ -95,11 +95,9 @@ function ContributorDashboard() { setError(normalized.message); - toast({ - title: "Contributor dashboard unavailable", - description: normalized.message, - variant: "destructive", - }); + toast.error("Contributor dashboard unavailable", { + description: normalized.message +}); } finally { if (active && !controller.signal.aborted) { setLoading(false); diff --git a/src/pages/Dashboard.tsx b/src/pages/Dashboard.tsx index 9a49c9bd..3887c533 100644 --- a/src/pages/Dashboard.tsx +++ b/src/pages/Dashboard.tsx @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ + import { Suspense, lazy, useCallback, useEffect, useState } from "react"; import { motion } from "framer-motion"; import { useNavigate } from "react-router-dom"; diff --git a/src/pages/Discover.tsx b/src/pages/Discover.tsx index 3c351ac2..37f01368 100644 --- a/src/pages/Discover.tsx +++ b/src/pages/Discover.tsx @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ + import { memo, useCallback, useEffect, useMemo, useState } from "react"; import { motion } from "framer-motion"; import { diff --git a/src/pages/Leaderboard.tsx b/src/pages/Leaderboard.tsx index 43f56af3..39343767 100644 --- a/src/pages/Leaderboard.tsx +++ b/src/pages/Leaderboard.tsx @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ + import { memo, useEffect, useRef, useState, useCallback } from "react"; import { motion } from "framer-motion"; import { diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx index 64cb1504..c8795e28 100644 --- a/src/pages/Login.tsx +++ b/src/pages/Login.tsx @@ -9,7 +9,7 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { useAuth } from "@/contexts/useAuth"; -import { useToast } from "@/hooks/use-toast"; +import { toast } from "sonner"; import { AUTH_SERVICE_UNAVAILABLE_MESSAGE, runSupabaseAuthRequest } from "@/lib/supabaseAuthErrors"; @@ -27,7 +27,7 @@ const Login = () => { const [errors, setErrors] = useState({}); const { user, loading, signIn } = useAuth(); - const { toast } = useToast(); + const navigate = useNavigate(); if (!loading && user) return ; @@ -57,15 +57,11 @@ const Login = () => { if (error) { setAuthError(error.message); - toast({ - title: "Login failed", - description: error.message, - variant: "destructive", - }); + toast.error("Login failed", { + description: error.message +}); } else { - toast({ - title: "Welcome back 🚀", - }); + toast("Welcome back 🚀"); navigate("/dashboard"); } @@ -76,11 +72,9 @@ const Login = () => { if (supabaseMisconfigured) { setAuthError(AUTH_SERVICE_UNAVAILABLE_MESSAGE); - toast({ - title: "Not configured", - description: AUTH_SERVICE_UNAVAILABLE_MESSAGE, - variant: "destructive", - }); + toast.error("Not configured", { + description: AUTH_SERVICE_UNAVAILABLE_MESSAGE +}); return; } @@ -98,11 +92,9 @@ const Login = () => { if (error) { setIsLoading(false); setAuthError(error.message); - toast({ - title: "Google login failed", - description: error.message, - variant: "destructive", - }); + toast.error("Google login failed", { + description: error.message +}); return; } diff --git a/src/pages/MentorDashboard.tsx b/src/pages/MentorDashboard.tsx index 9bd9dc29..18190955 100644 --- a/src/pages/MentorDashboard.tsx +++ b/src/pages/MentorDashboard.tsx @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ + import { useEffect, useState } from "react"; import { useRole } from "@/contexts/RoleContext"; import { useAuth } from "@/contexts/useAuth"; diff --git a/src/pages/Notifications.tsx b/src/pages/Notifications.tsx index 3a7a1ad5..0de6e2c2 100644 --- a/src/pages/Notifications.tsx +++ b/src/pages/Notifications.tsx @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ + import { useEffect, useState } from "react"; import { supabase } from "@/integrations/supabase/client"; diff --git a/src/pages/Onboarding.tsx b/src/pages/Onboarding.tsx index 3c984d63..b0389336 100644 --- a/src/pages/Onboarding.tsx +++ b/src/pages/Onboarding.tsx @@ -10,7 +10,7 @@ import { CardHeader, CardTitle, } from "@/components/ui/card"; -import { useToast } from "@/hooks/use-toast"; +import { toast } from "sonner"; import { useAuth } from "@/contexts/useAuth"; import { supabase } from "@/integrations/supabase/client"; @@ -52,7 +52,7 @@ const roleOptions: { const Onboarding = () => { const navigate = useNavigate(); - const { toast } = useToast(); + const { setNeedsOnboarding } = useAuth(); const [selectedRole, setSelectedRole] = useState(null); @@ -79,11 +79,9 @@ const Onboarding = () => { const timeout = setTimeout(() => { isTimeout = true; setSelectedRole(null); - toast({ - title: "Selection timed out", - description: "The request to the server timed out. Please try again.", - variant: "destructive", - }); + toast.error("Selection timed out", { + description: "The request to the server timed out. Please try again." +}); }, 10_000); try { @@ -96,11 +94,9 @@ const Onboarding = () => { if (!user) { clearTimeout(timeout); setSelectedRole(null); - toast({ - title: "Authentication required", - description: "Please log in to continue.", - variant: "destructive", - }); + toast.error("Authentication required", { + description: "Please log in to continue." +}); navigate("/login", { replace: true }); return; } @@ -115,11 +111,9 @@ const Onboarding = () => { if (error) { setSelectedRole(null); - toast({ - title: "Could not update role", - description: error.message, - variant: "destructive", - }); + toast.error("Could not update role", { + description: error.message +}); return; } @@ -129,11 +123,9 @@ const Onboarding = () => { if (isTimeout) return; clearTimeout(timeout); setSelectedRole(null); - toast({ - title: "Could not update role", - description: err instanceof Error ? err.message : "An unexpected error occurred.", - variant: "destructive", - }); + toast.error("Could not update role", { + description: err instanceof Error ? err.message : "An unexpected error occurred." +}); } }; diff --git a/src/pages/Portfolio.tsx b/src/pages/Portfolio.tsx index 62010a77..04e8647b 100644 --- a/src/pages/Portfolio.tsx +++ b/src/pages/Portfolio.tsx @@ -18,7 +18,7 @@ import { Label } from "@/components/ui/label"; import { Progress } from "@/components/ui/progress"; import { Switch } from "@/components/ui/switch"; import { Textarea } from "@/components/ui/textarea"; -import { useToast } from "@/hooks/use-toast"; +import { toast } from "sonner"; import { useAuth } from "@/contexts/useAuth"; import { supabase } from "@/integrations/supabase/client"; @@ -86,7 +86,7 @@ const normalizeProjects = (value: unknown): Project[] => const Portfolio = () => { const { user } = useAuth(); - const { toast } = useToast(); + const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [profileName, setProfileName] = useState(""); @@ -125,11 +125,9 @@ const Portfolio = () => { timeout = setTimeout(() => { if (isMounted) { setLoading(false); - toast({ - title: "Loading timed out", - description: "Some data may not have loaded. Please refresh to try again.", - variant: "destructive", - }); + toast.error("Loading timed out", { + description: "Some data may not have loaded. Please refresh to try again." +}); } }, 10_000); @@ -145,8 +143,7 @@ const Portfolio = () => { .from("portfolio_profiles") .select("*") .eq("profile_id", user.id) - .maybeSingle(), - ]); + .maybeSingle()]); clearTimeout(timeout); if (!isMounted) return; @@ -155,22 +152,18 @@ const Portfolio = () => { const { data: portfolio, error: portfolioError } = portfolioResult; if (profileError) { - toast({ - title: "Profile could not load", - description: profileError.message, - variant: "destructive", - }); + toast.error("Profile could not load", { + description: profileError.message +}); } const fallbackSlug = slugify(profile?.name || user.email?.split("@")[0] || "learner"); setProfileName(profile?.name || user.email?.split("@")[0] || "Learner"); if (portfolioError) { - toast({ - title: "Portfolio could not load", - description: portfolioError.message, - variant: "destructive", - }); + toast.error("Portfolio could not load", { + description: portfolioError.message +}); } if (portfolio) { @@ -202,11 +195,9 @@ const Portfolio = () => { clearTimeout(timeout); if (!isMounted) return; - toast({ - title: "Portfolio could not load", - description: error instanceof Error ? error.message : "Please try again.", - variant: "destructive", - }); + toast.error("Portfolio could not load", { + description: error instanceof Error ? error.message : "Please try again." +}); } finally { if (isMounted) setLoading(false); } @@ -218,7 +209,7 @@ const Portfolio = () => { isMounted = false; clearTimeout(timeout); }; - }, [user, toast]); + }, [user]); const updateAchievement = (index: number, achievement: Achievement) => { setForm((current) => ({ @@ -245,11 +236,9 @@ const Portfolio = () => { const slug = slugify(form.slug); if (!slug) { - toast({ - title: "Choose a public URL", - description: "Your portfolio needs a short slug before it can be saved.", - variant: "destructive", - }); + toast.error("Choose a public URL", { + description: "Your portfolio needs a short slug before it can be saved." +}); return; } @@ -263,21 +252,17 @@ const Portfolio = () => { if (slugCheckError) { setSaving(false); - toast({ - title: "Error checking URL", - description: "Failed to verify if the URL is available.", - variant: "destructive", - }); + toast.error("Error checking URL", { + description: "Failed to verify if the URL is available." +}); return; } if (existingSlugUser && (existingSlugUser as any).profile_id !== user.id) { setSaving(false); - toast({ - title: "URL already taken", - description: "This public URL is already in use by someone else. Please choose another one.", - variant: "destructive", - }); + toast.error("URL already taken", { + description: "This public URL is already in use by someone else. Please choose another one." +}); return; } @@ -301,11 +286,9 @@ const Portfolio = () => { const timeout = setTimeout(() => { isTimeout = true; setSaving(false); - toast({ - title: "Save timed out", - description: "The connection to the database timed out. Please check your connection and try again.", - variant: "destructive", - }); + toast.error("Save timed out", { + description: "The connection to the database timed out. Please check your connection and try again." +}); }, 10_000); try { @@ -321,19 +304,16 @@ const Portfolio = () => { if (error) { console.error("Portfolio upsert returned database error:", error); - toast({ - title: "Portfolio was not saved", - description: error.message, - variant: "destructive", - }); + toast.error("Portfolio was not saved", { + description: error.message +}); return; } setForm((current) => ({ ...current, slug })); - toast({ - title: "Portfolio saved", - description: form.is_published ? "Your public page is live." : "Your draft is saved.", - }); + toast("Portfolio saved", { + description: form.is_published ? "Your public page is live." : "Your draft is saved." +}); } catch (error) { if (isTimeout) { console.warn("Portfolio save threw exception, but it already timed out locally."); @@ -341,11 +321,9 @@ const Portfolio = () => { } clearTimeout(timeout); console.error("Portfolio save threw exception:", error); - toast({ - title: "Portfolio was not saved", - description: error instanceof Error ? error.message : "An unexpected error occurred.", - variant: "destructive", - }); + toast.error("Portfolio was not saved", { + description: error instanceof Error ? error.message : "An unexpected error occurred." +}); } finally { if (!isTimeout) { setSaving(false); @@ -356,7 +334,7 @@ const Portfolio = () => { const copyShareLink = async () => { if (!publicUrl) return; await navigator.clipboard.writeText(publicUrl); - toast({ title: "Share link copied" }); + toast("Share link copied"); }; if (loading) { diff --git a/src/pages/ReviewSubmission.tsx b/src/pages/ReviewSubmission.tsx index 7aad750f..70475234 100644 --- a/src/pages/ReviewSubmission.tsx +++ b/src/pages/ReviewSubmission.tsx @@ -4,7 +4,7 @@ import { useAuth } from "@/contexts/useAuth"; import { supabase } from "@/integrations/supabase/client"; import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; -import { useToast } from "@/components/ui/use-toast"; +import { toast } from "sonner"; import { getSafePeerReviewSubmissionUrl } from "@/utils/peerReviewUrl"; import { ArrowLeft, Send, Star, ExternalLink, Code } from "lucide-react"; @@ -12,7 +12,7 @@ export default function ReviewSubmission() { const { id } = useParams<{ id: string }>(); const { user } = useAuth(); const navigate = useNavigate(); - const { toast } = useToast(); + const [submission, setSubmission] = useState(null); const [reviews, setReviews] = useState([]); @@ -35,7 +35,9 @@ export default function ReviewSubmission() { .single(); if (subError || !subData) { - toast({ title: "Not Found", description: "Submission not found or deleted.", variant: "destructive" }); + toast.error("Not Found", { + description: "Submission not found or deleted." +}); navigate("/peer-review"); return; } @@ -54,7 +56,7 @@ export default function ReviewSubmission() { }; fetchDetails(); - }, [id, user, navigate, toast]); + }, [id, user, navigate]); const handleFeedbackSubmit = async () => { if (!user || !id || !feedback.trim()) return; @@ -80,11 +82,9 @@ export default function ReviewSubmission() { ); if (rpcError || !reviewRow) { - toast({ - title: "Error", - description: rpcError?.message || "Could not submit feedback.", - variant: "destructive", - }); + toast.error("Error", { + description: rpcError?.message || "Could not submit feedback." +}); setSubmitting(false); return; } @@ -102,8 +102,7 @@ export default function ReviewSubmission() { .from("peer_submissions") .select("status") .eq("id", id) - .single(), - ]); + .single()]); if (reviewFetchError) { // The review was created successfully (the RPC returned it); this is @@ -116,7 +115,9 @@ export default function ReviewSubmission() { console.error("Failed to refresh submission status:", submissionFetchError); } - toast({ title: "Success", description: "Feedback submitted successfully." }); + toast.success("Success", { + description: "Feedback submitted successfully." +}); setReviews((prev) => [...prev, reviewWithProfile ?? reviewRow]); setFeedback(""); @@ -136,9 +137,13 @@ export default function ReviewSubmission() { .eq('id', reviewId); if (error) { - toast({ title: "Error", description: "Could not save rating.", variant: "destructive" }); + toast.error("Error", { + description: "Could not save rating." +}); } else { - toast({ title: "Rated", description: "Thanks for rating the feedback!" }); + toast("Rated", { + description: "Thanks for rating the feedback!" +}); setReviews(reviews.map(r => r.id === reviewId ? { ...r, rating: ratingValue } : r)); } diff --git a/src/pages/Signup.tsx b/src/pages/Signup.tsx index 3905f73e..19ce9307 100644 --- a/src/pages/Signup.tsx +++ b/src/pages/Signup.tsx @@ -7,7 +7,7 @@ import googleIcon from "@/assets/google-icon.svg"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { useAuth } from "@/contexts/useAuth"; -import { useToast } from "@/hooks/use-toast"; +import { toast } from "sonner"; import { AUTH_SERVICE_UNAVAILABLE_MESSAGE, runSupabaseAuthRequest } from "@/lib/supabaseAuthErrors"; // ✅ Proper TypeScript type @@ -30,7 +30,7 @@ const Signup = () => { const [errors, setErrors] = useState({}); const { user, loading, signUp } = useAuth(); - const { toast } = useToast(); + const navigate = useNavigate(); if (!loading && user) return ; @@ -88,21 +88,17 @@ const Signup = () => { // Handle signup errors if (error) { setAuthError(error.message); - toast({ - title: "Signup failed", - description: error.message, - variant: "destructive", - }); + toast.error("Signup failed", { + description: error.message +}); return; } // Successful signup - toast({ - title: "Account created!", - description: - "Please check your email and verify your account before logging in.", - }); + toast("Account created!", { + description: "Please check your email and verify your account before logging in." +}); // Redirect user to login page navigate("/login"); @@ -113,11 +109,9 @@ const Signup = () => { if (supabaseMisconfigured) { setAuthError(AUTH_SERVICE_UNAVAILABLE_MESSAGE); - toast({ - title: "Not configured", - description: AUTH_SERVICE_UNAVAILABLE_MESSAGE, - variant: "destructive", - }); + toast.error("Not configured", { + description: AUTH_SERVICE_UNAVAILABLE_MESSAGE +}); return; } @@ -135,11 +129,9 @@ const Signup = () => { if (error) { setIsLoading(false); setAuthError(error.message); - toast({ - title: "Google login failed", - description: error.message, - variant: "destructive", - }); + toast.error("Google login failed", { + description: error.message +}); return; } diff --git a/src/pages/SubmitForReview.tsx b/src/pages/SubmitForReview.tsx index f5f32699..fd77368f 100644 --- a/src/pages/SubmitForReview.tsx +++ b/src/pages/SubmitForReview.tsx @@ -7,7 +7,7 @@ import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Switch } from "@/components/ui/switch"; import { Label } from "@/components/ui/label"; -import { useToast } from "@/components/ui/use-toast"; +import { toast } from "sonner"; import { getSafePeerReviewSubmissionUrl } from "@/utils/peerReviewUrl"; import { ArrowLeft, Send } from "lucide-react"; import { Link } from "react-router-dom"; @@ -15,7 +15,7 @@ import { Link } from "react-router-dom"; export default function SubmitForReview() { const { user } = useAuth(); const navigate = useNavigate(); - const { toast } = useToast(); + const [loading, setLoading] = useState(false); const [formData, setFormData] = useState({ @@ -32,20 +32,16 @@ export default function SubmitForReview() { const safeContentUrl = getSafePeerReviewSubmissionUrl(formData.content_url); if (safeContentUrl === null) { - toast({ - title: "Validation Error", - description: "Please enter a valid http:// or https:// link.", - variant: "destructive" - }); + toast.error("Validation Error", { + description: "Please enter a valid http:// or https:// link." +}); return; } if (!formData.title || (!safeContentUrl && !formData.content)) { - toast({ - title: "Validation Error", - description: "Please provide a title and either a link or text content.", - variant: "destructive" - }); + toast.error("Validation Error", { + description: "Please provide a title and either a link or text content." +}); return; } @@ -66,16 +62,13 @@ export default function SubmitForReview() { setLoading(false); if (error) { - toast({ - title: "Error", - description: error.message, - variant: "destructive" - }); + toast.error("Error", { + description: error.message +}); } else { - toast({ - title: "Success", - description: "Your work has been submitted for peer review." - }); + toast.success("Success", { + description: "Your work has been submitted for peer review." +}); navigate("/peer-review"); } }; diff --git a/src/pages/aipage.tsx b/src/pages/aipage.tsx index c37336a5..5c41f969 100644 --- a/src/pages/aipage.tsx +++ b/src/pages/aipage.tsx @@ -1,3 +1,4 @@ + import { useState } from "react"; import { Bot, Send, User } from "lucide-react"; import { supabase } from "@/integrations/supabase/client"; diff --git a/tailwind.config.ts b/tailwind.config.ts index 8bc0aa2a..a1d86d3e 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-empty-object-type, no-unsafe-finally, @typescript-eslint/no-unused-expressions, @typescript-eslint/ban-ts-comment, @typescript-eslint/no-require-imports */ + import type { Config } from "tailwindcss"; export default { diff --git a/temp2_types.ts b/temp2_types.ts new file mode 100644 index 00000000..56d607d3 --- /dev/null +++ b/temp2_types.ts @@ -0,0 +1,845 @@ +export type Json = + | string + | number + | boolean + | null + | { [key: string]: Json | undefined } + | Json[] + +export type Database = { + // Allows to automatically instantiate createClient with right options + // instead of createClient(URL, KEY) + __InternalSupabase: { + PostgrestVersion: "14.5" + } + public: { + Tables: { + mentorship_paths: { + Row: { + id: string + mentor_id: string + mentee_id: string + goal: string + status: string + created_at: string + updated_at: string + } + Insert: { + id?: string + mentor_id: string + mentee_id: string + goal: string + status?: string + created_at?: string + updated_at?: string + } + Update: { + id?: string + mentor_id?: string + mentee_id?: string + goal?: string + status?: string + created_at?: string + updated_at?: string + } + Relationships: [ + { + foreignKeyName: "mentorship_paths_mentee_id_fkey" + columns: ["mentee_id"] + isOneToOne: false + referencedRelation: "profiles" + referencedColumns: ["id"] + }, + { + foreignKeyName: "mentorship_paths_mentor_id_fkey" + columns: ["mentor_id"] + isOneToOne: false + referencedRelation: "profiles" + referencedColumns: ["id"] + } + ] + } + mentorship_milestones: { + Row: { + id: string + path_id: string + title: string + description: string | null + is_completed: boolean + due_date: string | null + created_at: string + updated_at: string + } + Insert: { + id?: string + path_id: string + title: string + description?: string | null + is_completed?: boolean + due_date?: string | null + created_at?: string + updated_at?: string + } + Update: { + id?: string + path_id?: string + title?: string + description?: string | null + is_completed?: boolean + due_date?: string | null + created_at?: string + updated_at?: string + } + Relationships: [ + { + foreignKeyName: "mentorship_milestones_path_id_fkey" + columns: ["path_id"] + isOneToOne: false + referencedRelation: "mentorship_paths" + referencedColumns: ["id"] + } + ] + } + peer_submissions: { + Row: { + id: string + user_id: string + title: string + description: string | null + content_url: string | null + content: string | null + is_anonymous: boolean + status: string + created_at: string + } + Insert: { + id?: string + user_id: string + title: string + description?: string | null + content_url?: string | null + content?: string | null + is_anonymous?: boolean + status?: string + created_at?: string + } + Update: { + id?: string + user_id?: string + title?: string + description?: string | null + content_url?: string | null + content?: string | null + is_anonymous?: boolean + status?: string + created_at?: string + } + Relationships: [ + { + foreignKeyName: "peer_submissions_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "profiles" + referencedColumns: ["id"] + } + ] + } + peer_reviews: { + Row: { + id: string + submission_id: string + reviewer_id: string + feedback: string + rating: number | null + created_at: string + } + Insert: { + id?: string + submission_id: string + reviewer_id: string + feedback: string + rating?: number | null + created_at?: string + } + Update: { + id?: string + submission_id?: string + reviewer_id?: string + feedback?: string + rating?: number | null + created_at?: string + } + Relationships: [ + { + foreignKeyName: "peer_reviews_submission_id_fkey" + columns: ["submission_id"] + isOneToOne: false + referencedRelation: "peer_submissions" + referencedColumns: ["id"] + }, + { + foreignKeyName: "peer_reviews_reviewer_id_fkey" + columns: ["reviewer_id"] + isOneToOne: false + referencedRelation: "profiles" + referencedColumns: ["id"] + } + ] + } + + chat_messages: { + Row: { + created_at: string + id: number + } + Insert: { + created_at?: string + id?: number + } + Update: { + created_at?: string + id?: number + } + Relationships: [] + } + messages: { + Row: { + content: string | null + created_at: string | null + id: string + message: string | null + read_at: string | null + receiver_id: string | null + sender_id: string | null + text: string | null + } + Insert: { + content?: string | null + created_at?: string | null + id?: string + message?: string | null + read_at?: string | null + receiver_id?: string | null + sender_id?: string | null + text?: string | null + } + Update: { + content?: string | null + created_at?: string | null + id?: string + message?: string | null + read_at?: string | null + receiver_id?: string | null + sender_id?: string | null + text?: string | null + } + Relationships: [] + } + leaderboard: { + Row: { + id: string + user_id: string + username: string + avatar_url: string | null + xp: number + streak: number + sessions_joined: number + badges: string[] + updated_at: string + } + Insert: { + id?: string + user_id: string + username: string + avatar_url?: string | null + xp?: number + streak?: number + sessions_joined?: number + badges?: string[] + updated_at?: string + } + Update: { + id?: string + user_id?: string + username?: string + avatar_url?: string | null + xp?: number + streak?: number + sessions_joined?: number + badges?: string[] + updated_at?: string + } + Relationships: [ + { + foreignKeyName: "leaderboard_user_id_fkey" + columns: ["user_id"] + isOneToOne: true + referencedRelation: "profiles" + referencedColumns: ["id"] + } + ] + } + profiles: { + Row: { + avatar_url: string | null + bio: string | null + created_at: string | null + email: string | null + id: string + last_seen: string | null + name: string | null + skills: string[] | null + is_mentor: boolean + is_learner: boolean + points: number | null + sessions_completed: number | null + rating: number | null + badges: string[] | null + interests: string[] | null + teach_subjects: string[] | null + learn_subjects: string[] | null + updated_at: string | null + streak: number + last_active: string | null + restoration_used_today: boolean + restoration_date: string | null + is_in_focus_mode: boolean | null + focus_time_this_week: number | null + learning_style: string | null + availability: string | null + preferred_language: string | null + timezone: string | null + } + Insert: { + avatar_url?: string | null + bio?: string | null + created_at?: string | null + email?: string | null + id: string + last_seen?: string | null + name?: string | null + skills?: string[] | null + is_mentor?: boolean + is_learner?: boolean + points?: number | null + sessions_completed?: number | null + rating?: number | null + badges?: string[] | null + interests?: string[] | null + teach_subjects?: string[] | null + learn_subjects?: string[] | null + updated_at?: string | null + streak?: number + last_active?: string | null + restoration_used_today?: boolean + restoration_date?: string | null + learning_style?: string | null + availability?: string | null + preferred_language?: string | null + timezone?: string | null + } + Update: { + avatar_url?: string | null + bio?: string | null + created_at?: string | null + email?: string | null + id?: string + last_seen?: string | null + name?: string | null + skills?: string[] | null + is_mentor?: boolean + is_learner?: boolean + points?: number | null + sessions_completed?: number | null + rating?: number | null + badges?: string[] | null + interests?: string[] | null + teach_subjects?: string[] | null + learn_subjects?: string[] | null + updated_at?: string | null + streak?: number + last_active?: string | null + restoration_used_today?: boolean + restoration_date?: string | null + learning_style?: string | null + availability?: string | null + preferred_language?: string | null + timezone?: string | null + } + Relationships: [] + } + resources: { + Row: { + id: string + title: string + description: string | null + file_url: string + file_size: number | null + tags: string[] | null + file_type: string + uploaded_by: string + created_at: string + } + Insert: { + id?: string + title: string + description?: string | null + file_url: string + file_size?: number | null + tags?: string[] | null + file_type: string + uploaded_by: string + created_at?: string + } + Update: { + id?: string + title?: string + description?: string | null + file_url?: string + file_size?: number | null + tags?: string[] | null + file_type?: string + uploaded_by?: string + created_at?: string + } + Relationships: [] + } + resource_votes: { + Row: { + id: string + resource_id: string + user_id: string + vote_type: number + created_at: string + } + Insert: { + id?: string + resource_id: string + user_id: string + vote_type: number + created_at?: string + } + Update: { + id?: string + resource_id?: string + user_id?: string + vote_type?: number + created_at?: string + } + Relationships: [] + } + saved_resources: { + Row: { + id: string + resource_id: string + user_id: string + created_at: string + } + Insert: { + id?: string + resource_id: string + user_id: string + created_at?: string + } + Update: { + id?: string + resource_id?: string + user_id?: string + created_at?: string + } + Relationships: [] + } + skill_endorsements: { + Row: { + id: string + skill: string + endorsed_user_id: string + endorser_id: string + created_at: string + } + Insert: { + id?: string + skill: string + endorsed_user_id: string + endorser_id: string + created_at?: string + } + Update: { + id?: string + skill?: string + endorsed_user_id?: string + endorser_id?: string + created_at?: string + } + Relationships: [] + } + study_rooms: { + Row: { + id: string + topic: string + created_by: string | null + created_at: string + is_private: boolean + } + Insert: { + id?: string + topic: string + created_by?: string | null + created_at?: string + is_private?: boolean + } + Update: { + id?: string + topic?: string + created_by?: string | null + created_at?: string + is_private?: boolean + } + Relationships: [ + { + foreignKeyName: "study_rooms_created_by_fkey" + columns: ["created_by"] + isOneToOne: false + referencedRelation: "profiles" + referencedColumns: ["id"] + } + ] + } + study_room_messages: { + Row: { + id: string + room_id: string | null + profile_id: string | null + content: string + created_at: string + } + Insert: { + id?: string + room_id?: string | null + profile_id?: string | null + content: string + created_at?: string + } + Update: { + id?: string + room_id?: string | null + profile_id?: string | null + content?: string + created_at?: string + } + Relationships: [ + { + foreignKeyName: "study_room_messages_profile_id_fkey" + columns: ["profile_id"] + isOneToOne: false + referencedRelation: "profiles" + referencedColumns: ["id"] + }, + { + foreignKeyName: "study_room_messages_room_id_fkey" + columns: ["room_id"] + isOneToOne: false + referencedRelation: "study_rooms" + referencedColumns: ["id"] + } + ] + } + study_room_participants: { + Row: { + room_id: string + profile_id: string + joined_at: string + } + Insert: { + room_id: string + profile_id: string + joined_at?: string + } + Update: { + room_id?: string + profile_id?: string + joined_at?: string + } + Relationships: [ + { + foreignKeyName: "study_room_participants_profile_id_fkey" + columns: ["profile_id"] + isOneToOne: false + referencedRelation: "profiles" + referencedColumns: ["id"] + }, + { + foreignKeyName: "study_room_participants_room_id_fkey" + columns: ["room_id"] + isOneToOne: false + referencedRelation: "study_rooms" + referencedColumns: ["id"] + } + ] + } + sessions: { + Row: { + created_at: string + description: string | null + id: number + scheduled_at: string | null + /** duration_minutes – NEW column added by session scheduling migration */ + duration_minutes: number + /** status values: 'scheduled' | 'live' | 'ended' */ + status: string + student_id: string | null + mentor_id: string | null + seat_limit: number | null + participants: number + title: string | null + tags: string[] | null + } + Insert: { + created_at?: string + description?: string | null + id?: number + scheduled_at?: string | null + duration_minutes?: number + status?: string + student_id?: string | null + mentor_id?: string | null + seat_limit?: number | null + participants?: number + title?: string | null + tags?: string[] | null + } + Update: { + created_at?: string + description?: string | null + id?: number + scheduled_at?: string | null + duration_minutes?: number + status?: string + student_id?: string | null + mentor_id?: string | null + seat_limit?: number | null + participants?: number + title?: string | null + tags?: string[] | null + } + Relationships: [] + } + users: { + Row: { + created_at: string | null + email: string | null + id: string + learning_goals: string | null + name: string | null + skills: string | null + } + Insert: { + created_at?: string | null + email?: string | null + id?: string + learning_goals?: string | null + name?: string | null + skills?: string | null + } + Update: { + created_at?: string | null + email?: string | null + id?: string + learning_goals?: string | null + name?: string | null + skills?: string | null + } + Relationships: [] + } + } + Views: { + skill_endorsement_counts: { + Row: { + endorsed_user_id: string + skill: string + endorsement_count: number + } + Relationships: [] + } + } + Functions: { + award_activity_xp: { + Args: { _activity_type: string } + Returns: undefined + } + get_user_rank: { + Args: { + p_user_id: string + p_filter?: string + } + Returns: number + } + has_role: { + Args: { + _role: string + _user_id: string + } + Returns: boolean + } + invite_to_study_room: { + Args: { + p_room_id: string + p_user_email: string + } + Returns: undefined + } + join_leaderboard: { + Args: { + _username: string + _avatar_url: string | null + } + Returns: undefined + } + join_public_study_room: { + Args: { p_room_id: string } + Returns: undefined + } + join_session: { + Args: { p_session_id: string } + Returns: undefined + } + mark_messages_as_read: { + Args: { message_ids: string[] } + Returns: undefined + } + tick_session_statuses: { + Args: Record + Returns: undefined + } + } + Enums: { + [_ in never]: never + } + CompositeTypes: { + [_ in never]: never + } + } +} + +type DatabaseWithoutInternals = Omit + +type DefaultSchema = DatabaseWithoutInternals[Extract] + +export type Tables< + DefaultSchemaTableNameOrOptions extends + | keyof (DefaultSchema["Tables"] & DefaultSchema["Views"]) + | { schema: keyof DatabaseWithoutInternals }, + TableName extends DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & + DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"]) + : never = never, +> = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & + DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"])[TableName] extends { + Row: infer R + } + ? R + : never + : DefaultSchemaTableNameOrOptions extends keyof (DefaultSchema["Tables"] & + DefaultSchema["Views"]) + ? (DefaultSchema["Tables"] & + DefaultSchema["Views"])[DefaultSchemaTableNameOrOptions] extends { + Row: infer R + } + ? R + : never + : never + +export type TablesInsert< + DefaultSchemaTableNameOrOptions extends + | keyof DefaultSchema["Tables"] + | { schema: keyof DatabaseWithoutInternals }, + TableName extends DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] + : never = never, +> = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { + Insert: infer I + } + ? I + : never + : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"] + ? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends { + Insert: infer I + } + ? I + : never + : never + +export type TablesUpdate< + DefaultSchemaTableNameOrOptions extends + | keyof DefaultSchema["Tables"] + | { schema: keyof DatabaseWithoutInternals }, + TableName extends DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] + : never = never, +> = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { + Update: infer U + } + ? U + : never + : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"] + ? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends { + Update: infer U + } + ? U + : never + : never + +export type Enums< + DefaultSchemaEnumNameOrOptions extends + | keyof DefaultSchema["Enums"] + | { schema: keyof DatabaseWithoutInternals }, + EnumName extends DefaultSchemaEnumNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"] + : never = never, +> = DefaultSchemaEnumNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"][EnumName] + : DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema["Enums"] + ? DefaultSchema["Enums"][DefaultSchemaEnumNameOrOptions] + : never + +export type CompositeTypes< + PublicCompositeTypeNameOrOptions extends + | keyof DefaultSchema["CompositeTypes"] + | { schema: keyof DatabaseWithoutInternals }, + CompositeTypeName extends PublicCompositeTypeNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"] + : never = never, +> = PublicCompositeTypeNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName] + : PublicCompositeTypeNameOrOptions extends keyof DefaultSchema["CompositeTypes"] + ? DefaultSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions] + : never + +export const Constants = { + public: { + Enums: {}, + }, +} as const diff --git a/temp_types.ts b/temp_types.ts new file mode 100644 index 00000000..ed2c217a --- /dev/null +++ b/temp_types.ts @@ -0,0 +1,814 @@ +export type Json = + | string + | number + | boolean + | null + | { [key: string]: Json | undefined } + | Json[] + +export type Database = { + // Allows to automatically instantiate createClient with right options + // instead of createClient(URL, KEY) + __InternalSupabase: { + PostgrestVersion: "14.5" + } + public: { + Tables: { + mentorship_paths: { + Row: { + id: string + mentor_id: string + mentee_id: string + goal: string + status: string + created_at: string + updated_at: string + } + Insert: { + id?: string + mentor_id: string + mentee_id: string + goal: string + status?: string + created_at?: string + updated_at?: string + } + Update: { + id?: string + mentor_id?: string + mentee_id?: string + goal?: string + status?: string + created_at?: string + updated_at?: string + } + Relationships: [ + { + foreignKeyName: "mentorship_paths_mentee_id_fkey" + columns: ["mentee_id"] + isOneToOne: false + referencedRelation: "profiles" + referencedColumns: ["id"] + }, + { + foreignKeyName: "mentorship_paths_mentor_id_fkey" + columns: ["mentor_id"] + isOneToOne: false + referencedRelation: "profiles" + referencedColumns: ["id"] + } + ] + } + mentorship_milestones: { + Row: { + id: string + path_id: string + title: string + description: string | null + is_completed: boolean + due_date: string | null + created_at: string + updated_at: string + } + Insert: { + id?: string + path_id: string + title: string + description?: string | null + is_completed?: boolean + due_date?: string | null + created_at?: string + updated_at?: string + } + Update: { + id?: string + path_id?: string + title?: string + description?: string | null + is_completed?: boolean + due_date?: string | null + created_at?: string + updated_at?: string + } + Relationships: [ + { + foreignKeyName: "mentorship_milestones_path_id_fkey" + columns: ["path_id"] + isOneToOne: false + referencedRelation: "mentorship_paths" + referencedColumns: ["id"] + } + ] + } + peer_submissions: { + Row: { + id: string + user_id: string + title: string + description: string | null + content_url: string | null + content: string | null + is_anonymous: boolean + status: string + created_at: string + } + Insert: { + id?: string + user_id: string + title: string + description?: string | null + content_url?: string | null + content?: string | null + is_anonymous?: boolean + status?: string + created_at?: string + } + Update: { + id?: string + user_id?: string + title?: string + description?: string | null + content_url?: string | null + content?: string | null + is_anonymous?: boolean + status?: string + created_at?: string + } + Relationships: [ + { + foreignKeyName: "peer_submissions_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "profiles" + referencedColumns: ["id"] + } + ] + } + peer_reviews: { + Row: { + id: string + submission_id: string + reviewer_id: string + feedback: string + rating: number | null + created_at: string + } + Insert: { + id?: string + submission_id: string + reviewer_id: string + feedback: string + rating?: number | null + created_at?: string + } + Update: { + id?: string + submission_id?: string + reviewer_id?: string + feedback?: string + rating?: number | null + created_at?: string + } + Relationships: [ + { + foreignKeyName: "peer_reviews_submission_id_fkey" + columns: ["submission_id"] + isOneToOne: false + referencedRelation: "peer_submissions" + referencedColumns: ["id"] + }, + { + foreignKeyName: "peer_reviews_reviewer_id_fkey" + columns: ["reviewer_id"] + isOneToOne: false + referencedRelation: "profiles" + referencedColumns: ["id"] + } + ] + } + + chat_messages: { + Row: { + created_at: string + id: number + } + Insert: { + created_at?: string + id?: number + } + Update: { + created_at?: string + id?: number + } + Relationships: [] + } + messages: { + Row: { + content: string | null + created_at: string | null + id: string + message: string | null + read_at: string | null + receiver_id: string | null + sender_id: string | null + text: string | null + } + Insert: { + content?: string | null + created_at?: string | null + id?: string + message?: string | null + read_at?: string | null + receiver_id?: string | null + sender_id?: string | null + text?: string | null + } + Update: { + content?: string | null + created_at?: string | null + id?: string + message?: string | null + read_at?: string | null + receiver_id?: string | null + sender_id?: string | null + text?: string | null + } + Relationships: [] + } + leaderboard: { + Row: { + id: string + user_id: string + username: string + avatar_url: string | null + xp: number + streak: number + sessions_joined: number + badges: string[] + updated_at: string + } + Insert: { + id?: string + user_id: string + username: string + avatar_url?: string | null + xp?: number + streak?: number + sessions_joined?: number + badges?: string[] + updated_at?: string + } + Update: { + id?: string + user_id?: string + username?: string + avatar_url?: string | null + xp?: number + streak?: number + sessions_joined?: number + badges?: string[] + updated_at?: string + } + Relationships: [ + { + foreignKeyName: "leaderboard_user_id_fkey" + columns: ["user_id"] + isOneToOne: true + referencedRelation: "profiles" + referencedColumns: ["id"] + } + ] + } + profiles: { + Row: { + avatar_url: string | null + bio: string | null + created_at: string | null + email: string | null + id: string + last_seen: string | null + name: string | null + skills: string[] | null + is_mentor: boolean + is_learner: boolean + points: number | null + sessions_completed: number | null + rating: number | null + badges: string[] | null + interests: string[] | null + teach_subjects: string[] | null + learn_subjects: string[] | null + updated_at: string | null + streak: number + last_active: string | null + restoration_used_today: boolean + restoration_date: string | null + is_in_focus_mode: boolean | null + focus_time_this_week: number | null + learning_style: string | null + availability: string | null + preferred_language: string | null + timezone: string | null + } + Insert: { + avatar_url?: string | null + bio?: string | null + created_at?: string | null + email?: string | null + id: string + last_seen?: string | null + name?: string | null + skills?: string[] | null + is_mentor?: boolean + is_learner?: boolean + points?: number | null + sessions_completed?: number | null + rating?: number | null + badges?: string[] | null + interests?: string[] | null + teach_subjects?: string[] | null + learn_subjects?: string[] | null + updated_at?: string | null + streak?: number + last_active?: string | null + restoration_used_today?: boolean + restoration_date?: string | null + learning_style?: string | null + availability?: string | null + preferred_language?: string | null + timezone?: string | null + } + Update: { + avatar_url?: string | null + bio?: string | null + created_at?: string | null + email?: string | null + id?: string + last_seen?: string | null + name?: string | null + skills?: string[] | null + is_mentor?: boolean + is_learner?: boolean + points?: number | null + sessions_completed?: number | null + rating?: number | null + badges?: string[] | null + interests?: string[] | null + teach_subjects?: string[] | null + learn_subjects?: string[] | null + updated_at?: string | null + streak?: number + last_active?: string | null + restoration_used_today?: boolean + restoration_date?: string | null + learning_style?: string | null + availability?: string | null + preferred_language?: string | null + timezone?: string | null + } + Relationships: [] + } + resources: { + Row: { + id: string + title: string + description: string | null + file_url: string + file_size: number | null + tags: string[] | null + file_type: string + uploaded_by: string + created_at: string + } + Insert: { + id?: string + title: string + description?: string | null + file_url: string + file_size?: number | null + tags?: string[] | null + file_type: string + uploaded_by: string + created_at?: string + } + Update: { + id?: string + title?: string + description?: string | null + file_url?: string + file_size?: number | null + tags?: string[] | null + file_type?: string + uploaded_by?: string + created_at?: string + } + Relationships: [] + } + resource_votes: { + Row: { + id: string + resource_id: string + user_id: string + vote_type: number + created_at: string + } + Insert: { + id?: string + resource_id: string + user_id: string + vote_type: number + created_at?: string + } + Update: { + id?: string + resource_id?: string + user_id?: string + vote_type?: number + created_at?: string + } + Relationships: [] + } + saved_resources: { + Row: { + id: string + resource_id: string + user_id: string + created_at: string + } + Insert: { + id?: string + resource_id: string + user_id: string + created_at?: string + } + Update: { + id?: string + resource_id?: string + user_id?: string + created_at?: string + } + Relationships: [] + } + study_rooms: { + Row: { + id: string + topic: string + created_by: string | null + created_at: string + is_private: boolean + } + Insert: { + id?: string + topic: string + created_by?: string | null + created_at?: string + is_private?: boolean + } + Update: { + id?: string + topic?: string + created_by?: string | null + created_at?: string + is_private?: boolean + } + Relationships: [ + { + foreignKeyName: "study_rooms_created_by_fkey" + columns: ["created_by"] + isOneToOne: false + referencedRelation: "profiles" + referencedColumns: ["id"] + } + ] + } + study_room_messages: { + Row: { + id: string + room_id: string | null + profile_id: string | null + content: string + created_at: string + } + Insert: { + id?: string + room_id?: string | null + profile_id?: string | null + content: string + created_at?: string + } + Update: { + id?: string + room_id?: string | null + profile_id?: string | null + content?: string + created_at?: string + } + Relationships: [ + { + foreignKeyName: "study_room_messages_profile_id_fkey" + columns: ["profile_id"] + isOneToOne: false + referencedRelation: "profiles" + referencedColumns: ["id"] + }, + { + foreignKeyName: "study_room_messages_room_id_fkey" + columns: ["room_id"] + isOneToOne: false + referencedRelation: "study_rooms" + referencedColumns: ["id"] + } + ] + } + study_room_participants: { + Row: { + room_id: string + profile_id: string + joined_at: string + } + Insert: { + room_id: string + profile_id: string + joined_at?: string + } + Update: { + room_id?: string + profile_id?: string + joined_at?: string + } + Relationships: [ + { + foreignKeyName: "study_room_participants_profile_id_fkey" + columns: ["profile_id"] + isOneToOne: false + referencedRelation: "profiles" + referencedColumns: ["id"] + }, + { + foreignKeyName: "study_room_participants_room_id_fkey" + columns: ["room_id"] + isOneToOne: false + referencedRelation: "study_rooms" + referencedColumns: ["id"] + } + ] + } + sessions: { + Row: { + created_at: string + description: string | null + id: number + scheduled_at: string | null + /** duration_minutes – NEW column added by session scheduling migration */ + duration_minutes: number + /** status values: 'scheduled' | 'live' | 'ended' */ + status: string + student_id: string | null + mentor_id: string | null + seat_limit: number | null + participants: number + title: string | null + tags: string[] | null + } + Insert: { + created_at?: string + description?: string | null + id?: number + scheduled_at?: string | null + duration_minutes?: number + status?: string + student_id?: string | null + mentor_id?: string | null + seat_limit?: number | null + participants?: number + title?: string | null + tags?: string[] | null + } + Update: { + created_at?: string + description?: string | null + id?: number + scheduled_at?: string | null + duration_minutes?: number + status?: string + student_id?: string | null + mentor_id?: string | null + seat_limit?: number | null + participants?: number + title?: string | null + tags?: string[] | null + } + Relationships: [] + } + users: { + Row: { + created_at: string | null + email: string | null + id: string + learning_goals: string | null + name: string | null + skills: string | null + } + Insert: { + created_at?: string | null + email?: string | null + id?: string + learning_goals?: string | null + name?: string | null + skills?: string | null + } + Update: { + created_at?: string | null + email?: string | null + id?: string + learning_goals?: string | null + name?: string | null + skills?: string | null + } + Relationships: [] + } + } + Views: { + [_ in never]: never + } + Functions: { + award_activity_xp: { + Args: { _activity_type: string } + Returns: undefined + } + get_user_rank: { + Args: { + p_user_id: string + p_filter?: string + } + Returns: number + } + has_role: { + Args: { + _role: string + _user_id: string + } + Returns: boolean + } + invite_to_study_room: { + Args: { + p_room_id: string + p_user_email: string + } + Returns: undefined + } + join_leaderboard: { + Args: { + _username: string + _avatar_url: string | null + } + Returns: undefined + } + join_public_study_room: { + Args: { p_room_id: string } + Returns: undefined + } + join_session: { + Args: { p_session_id: string } + Returns: undefined + } + mark_messages_as_read: { + Args: { message_ids: string[] } + Returns: undefined + } + tick_session_statuses: { + Args: Record + Returns: undefined + } + } + Enums: { + [_ in never]: never + } + CompositeTypes: { + [_ in never]: never + } + } +} + +type DatabaseWithoutInternals = Omit + +type DefaultSchema = DatabaseWithoutInternals[Extract] + +export type Tables< + DefaultSchemaTableNameOrOptions extends + | keyof (DefaultSchema["Tables"] & DefaultSchema["Views"]) + | { schema: keyof DatabaseWithoutInternals }, + TableName extends DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & + DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"]) + : never = never, +> = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & + DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"])[TableName] extends { + Row: infer R + } + ? R + : never + : DefaultSchemaTableNameOrOptions extends keyof (DefaultSchema["Tables"] & + DefaultSchema["Views"]) + ? (DefaultSchema["Tables"] & + DefaultSchema["Views"])[DefaultSchemaTableNameOrOptions] extends { + Row: infer R + } + ? R + : never + : never + +export type TablesInsert< + DefaultSchemaTableNameOrOptions extends + | keyof DefaultSchema["Tables"] + | { schema: keyof DatabaseWithoutInternals }, + TableName extends DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] + : never = never, +> = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { + Insert: infer I + } + ? I + : never + : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"] + ? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends { + Insert: infer I + } + ? I + : never + : never + +export type TablesUpdate< + DefaultSchemaTableNameOrOptions extends + | keyof DefaultSchema["Tables"] + | { schema: keyof DatabaseWithoutInternals }, + TableName extends DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] + : never = never, +> = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { + Update: infer U + } + ? U + : never + : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"] + ? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends { + Update: infer U + } + ? U + : never + : never + +export type Enums< + DefaultSchemaEnumNameOrOptions extends + | keyof DefaultSchema["Enums"] + | { schema: keyof DatabaseWithoutInternals }, + EnumName extends DefaultSchemaEnumNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"] + : never = never, +> = DefaultSchemaEnumNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"][EnumName] + : DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema["Enums"] + ? DefaultSchema["Enums"][DefaultSchemaEnumNameOrOptions] + : never + +export type CompositeTypes< + PublicCompositeTypeNameOrOptions extends + | keyof DefaultSchema["CompositeTypes"] + | { schema: keyof DatabaseWithoutInternals }, + CompositeTypeName extends PublicCompositeTypeNameOrOptions extends { + schema: keyof DatabaseWithoutInternals + } + ? keyof DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"] + : never = never, +> = PublicCompositeTypeNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName] + : PublicCompositeTypeNameOrOptions extends keyof DefaultSchema["CompositeTypes"] + ? DefaultSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions] + : never + +export const Constants = { + public: { + Enums: {}, + }, +} as const diff --git a/uploads/profiles/profile-user-948-test-1784561617295-464135339.png b/uploads/profiles/profile-user-948-test-1784561617295-464135339.png new file mode 100644 index 00000000..8f0aa88d Binary files /dev/null and b/uploads/profiles/profile-user-948-test-1784561617295-464135339.png differ diff --git a/uploads/profiles/profile-user-948-test-1784561617302-351406894.png b/uploads/profiles/profile-user-948-test-1784561617302-351406894.png new file mode 100644 index 00000000..8f0aa88d Binary files /dev/null and b/uploads/profiles/profile-user-948-test-1784561617302-351406894.png differ diff --git a/uploads/profiles/profile-user-948-test-1784561651066-713364992.png b/uploads/profiles/profile-user-948-test-1784561651066-713364992.png new file mode 100644 index 00000000..8f0aa88d Binary files /dev/null and b/uploads/profiles/profile-user-948-test-1784561651066-713364992.png differ diff --git a/uploads/profiles/profile-user-948-test-1784561651074-10055956.png b/uploads/profiles/profile-user-948-test-1784561651074-10055956.png new file mode 100644 index 00000000..8f0aa88d Binary files /dev/null and b/uploads/profiles/profile-user-948-test-1784561651074-10055956.png differ diff --git a/uploads/profiles/profile-user-948-test-1784561670283-611470428.png b/uploads/profiles/profile-user-948-test-1784561670283-611470428.png new file mode 100644 index 00000000..8f0aa88d Binary files /dev/null and b/uploads/profiles/profile-user-948-test-1784561670283-611470428.png differ diff --git a/uploads/profiles/profile-user-948-test-1784561670289-914776455.png b/uploads/profiles/profile-user-948-test-1784561670289-914776455.png new file mode 100644 index 00000000..8f0aa88d Binary files /dev/null and b/uploads/profiles/profile-user-948-test-1784561670289-914776455.png differ diff --git a/uploads/profiles/profile-user-948-test-1784561692897-404468012.png b/uploads/profiles/profile-user-948-test-1784561692897-404468012.png new file mode 100644 index 00000000..8f0aa88d Binary files /dev/null and b/uploads/profiles/profile-user-948-test-1784561692897-404468012.png differ diff --git a/uploads/profiles/profile-user-948-test-1784561692905-19697109.png b/uploads/profiles/profile-user-948-test-1784561692905-19697109.png new file mode 100644 index 00000000..8f0aa88d Binary files /dev/null and b/uploads/profiles/profile-user-948-test-1784561692905-19697109.png differ