"use client" import React, { useState, useEffect } from "react" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Badge } from "@/components/ui/badge" import { X, Bell, Clock, CheckCircle, AlertCircle } from "lucide-react" const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000" interface NotificationSubscriptionDialogProps { isOpen: boolean onClose: () => void phoneNumber?: string } interface Source { name: string article_count: number } interface Subscription { id: number phone_number: string notification_type: "whatsapp" | "sms" | "both" schedule_type: "immediate" | "daily_digest" digest_time: string | null categories: string[] | null sources: string[] | null is_active: boolean created_at: string updated_at: string } export default function NotificationSubscriptionDialog({ isOpen, onClose, phoneNumber: initialPhoneNumber = "" }: NotificationSubscriptionDialogProps) { const [phoneNumber, setPhoneNumber] = useState(initialPhoneNumber) const [notificationType, setNotificationType] = useState<"whatsapp" | "sms" | "both">("whatsapp") const [scheduleType, setScheduleType] = useState<"immediate" | "daily_digest">("immediate") const [digestTime, setDigestTime] = useState("08:00") const [selectedCategories, setSelectedCategories] = useState([]) const [selectedSources, setSelectedSources] = useState([]) const [categories, setCategories] = useState([]) const [sources, setSources] = useState([]) const [loading, setLoading] = useState(false) const [submitting, setSubmitting] = useState(false) const [message, setMessage] = useState<{ type: "success" | "error"; text: string } | null>(null) const [existingSubscription, setExistingSubscription] = useState(null) useEffect(() => { if (isOpen) { fetchCategories() fetchSources() if (phoneNumber) { checkSubscription() } } }, [isOpen]) // Debounced check for subscription when phone number changes useEffect(() => { if (!isOpen || !phoneNumber || phoneNumber.length < 10) return const timeoutId = setTimeout(() => { checkSubscription() }, 500) return () => clearTimeout(timeoutId) }, [phoneNumber, isOpen]) const fetchCategories = async () => { try { const response = await fetch(`${API_BASE_URL}/api/v1/notifications/categories`) if (response.ok) { const data = await response.json() setCategories(data.categories || []) } } catch (error) { console.error("Failed to fetch categories:", error) } } const fetchSources = async () => { try { const response = await fetch(`${API_BASE_URL}/api/v1/notifications/sources`) if (response.ok) { const data = await response.json() setSources(data.sources || []) } } catch (error) { console.error("Failed to fetch sources:", error) } } const checkSubscription = async () => { if (!phoneNumber) return setLoading(true) try { const response = await fetch(`${API_BASE_URL}/api/v1/notifications/subscription/${encodeURIComponent(phoneNumber)}`) if (response.ok) { const data = await response.json() setExistingSubscription(data) setNotificationType(data.notification_type || "whatsapp") setScheduleType(data.schedule_type || "immediate") setDigestTime(data.digest_time || "08:00") setSelectedCategories(data.categories || []) setSelectedSources(data.sources || []) } else if (response.status === 404) { setExistingSubscription(null) } } catch (error) { console.error("Failed to check subscription:", error) } finally { setLoading(false) } } const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() if (!phoneNumber.trim()) { setMessage({ type: "error", text: "Please enter a phone number" }) return } setSubmitting(true) setMessage(null) try { const payload = { phone_number: phoneNumber.trim(), notification_type: notificationType, schedule_type: scheduleType, digest_time: scheduleType === "daily_digest" ? digestTime : null, categories: selectedCategories.length > 0 ? selectedCategories : null, sources: selectedSources.length > 0 ? selectedSources : null, } const response = await fetch(`${API_BASE_URL}/api/v1/notifications/subscribe`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(payload), }) if (response.ok) { setMessage({ type: "success", text: "Successfully subscribed to news notifications!" }) setTimeout(() => { onClose() setMessage(null) }, 2000) } else { const error = await response.json() setMessage({ type: "error", text: error.detail || "Failed to subscribe" }) } } catch (error) { setMessage({ type: "error", text: "Failed to subscribe. Please try again." }) } finally { setSubmitting(false) } } const handleUnsubscribe = async () => { if (!phoneNumber.trim()) return setSubmitting(true) setMessage(null) try { const response = await fetch(`${API_BASE_URL}/api/v1/notifications/unsubscribe?phone_number=${encodeURIComponent(phoneNumber.trim())}`, { method: "POST", }) if (response.ok) { setMessage({ type: "success", text: "Successfully unsubscribed" }) setExistingSubscription(null) setTimeout(() => { onClose() setMessage(null) }, 2000) } else { const error = await response.json() setMessage({ type: "error", text: error.detail || "Failed to unsubscribe" }) } } catch (error) { setMessage({ type: "error", text: "Failed to unsubscribe. Please try again." }) } finally { setSubmitting(false) } } const toggleCategory = (category: string) => { setSelectedCategories((prev) => prev.includes(category) ? prev.filter((c) => c !== category) : [...prev, category] ) } const toggleSource = (source: string) => { setSelectedSources((prev) => prev.includes(source) ? prev.filter((s) => s !== source) : [...prev, source] ) } if (!isOpen) return null return (
News Notifications
{/* Phone Number */}
setPhoneNumber(e.target.value)} disabled={submitting || loading} required /> {phoneNumber && ( )}
{/* Notification Type */}

WhatsApp is default, SMS will be used as fallback

{/* Schedule Type */}
{scheduleType === "daily_digest" && (
setDigestTime(e.target.value)} disabled={submitting} className="w-32" />
)}
{/* Categories Filter */}
{categories.length > 0 ? ( categories.map((category) => ( toggleCategory(category)} > {category} )) ) : ( Loading categories... )}
{/* Sources Filter */}
{sources.length > 0 ? ( sources.map((source) => ( toggleSource(source.name)} > {source.name} )) ) : ( Loading sources... )}
{/* Message */} {message && (
{message.type === "success" ? ( ) : ( )} {message.text}
)} {/* Actions */}
{existingSubscription && ( )}
) }