import React, { useState, useEffect, FormEvent } from 'react'; import axios from 'axios'; import styles from '../styles/Dashboard.module.css'; import { useNavigate, Link } from 'react-router-dom'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faTrash } from '@fortawesome/free-solid-svg-icons'; import Navbar from './Navbar'; function Dashboard() { document.title = 'LinkLogger | Dashboard'; interface Log { id: number; link: string; timestamp: string; ip: string; location: string; browser: string; os: string; userAgent: string; isp: string; } interface Link { link: string; owner: number; redirect_link: string; expire_date: string; } // Link creation states const [link, setLink] = useState(''); const [url, setURL] = useState(''); const [isSubmitting, setIsSubmitting] = useState(false); const [isCopied, setIsCopied] = useState(false); const [error, setError] = useState(null); // Log and link states const [links, setLinks] = useState([]); const [logs, setLogs] = useState([]); const [visibleLog, setVisibleLog] = useState(null); const [loadingLinks, setLoadingLinks] = useState(true); // Track loading state for links const [loadingLogs, setLoadingLogs] = useState(true); // Track loading state for logs const navigate = useNavigate(); // Handle form submission to create a new link const handleSubmit = async (e: FormEvent) => { e.preventDefault(); setIsSubmitting(true); try { const res = await axios.post('/api/links', { url }); if (res.status === 200) { setLink(res.data.link); } } catch (error: unknown) { if (axios.isAxiosError(error)) { const customErrorMessage = error.response?.data?.error || null; setError(customErrorMessage || 'An error occurred. Please try again.'); setIsSubmitting(false); } else { setError('Unknown error. Please try again.'); setIsSubmitting(false); } } }; // Copy the link to the clipboard const copyLink = () => { navigator.clipboard.writeText(`${window.location.origin}/c/${link}`); setIsCopied(true); // Wait 5 seconds, then set isCopied back to false setTimeout(() => { setIsCopied(false); }, 5000); }; // Fetch links from API useEffect(() => { axios .get('/api/links') .then((res) => { if (res.status === 200) { setLinks(res.data); } else { navigate('/login'); } }) .catch((error: unknown) => { if (axios.isAxiosError(error)) { if (error.response?.status !== 404) { navigate('/login'); } } }) .finally(() => { setLoadingLinks(false); // Set loadingLinks to false once done }); }, []); // Fetch logs from API useEffect(() => { axios .get('/api/logs') .then((res) => { if (res.status === 200) { setLogs(res.data); } else { navigate('/login'); } }) .catch((error: unknown) => { if (axios.isAxiosError(error)) { if (error.response?.status !== 404) { navigate('/login'); } } }) .finally(() => { setLoadingLogs(false); // Set loadingLogs to false once done }); }, []); /** * Display or hide logs for a specific link * @param link The link to toggle logs for */ const toggleLogRow = (link: string) => { setVisibleLog(visibleLog === link ? null : link); }; /** * Delete a specific log * Gets the log ID from the SVG element's ID attribute * @param e The event object from the click * @returns void - updates state (setLogs) if successful */ const deleteLog = (e: React.MouseEvent) => { const confirmDelete = confirm('Are you sure you want to delete this log?'); if (!confirmDelete) return; const id = parseInt(e.currentTarget.id); axios .delete(`/api/logs/${id}`) .then((res) => { if (res.status === 200) { setLogs(logs.filter((log) => log.id !== id)); } }) .catch((error: unknown) => { if (axios.isAxiosError(error)) { // Return to login if we are unauthorized for some reason if (error.response?.status === 401) { navigate('/login'); } else { alert('Failed to delete log. Please try again.'); } } }); }; // Loading spinner component const LoadingSpinner = () => (

Loading...

); return ( <>

Create a new short link by entering the long URL below

setURL(e.target.value)} required /> {link.length === 0 ? ( ) : ( )}

{error}

{/* Show loading spinner if either links or logs are still loading */} {loadingLinks || loadingLogs ? ( ) : ( {/* If there are no links, put a special message */} {links.length === 0 && ( )} {/* For every link and its logs */} {links.map((link) => ( {/* Conditionally render logs for this link */} {visibleLog === link.link && ( )} ))}
Link Visits Redirect Expire Date
You do not have any shortened links -{' '} create one here .
{logs.filter((log) => log.link === link.link).length || 0} {link.redirect_link} {new Date(link.expire_date).toLocaleDateString()}
{/* Render all logs for the link */} {logs .filter((log) => log.link === link.link) .map((log, index, filteredLogs) => ( ))} {/* If the link has no logs, put a special message */} {logs.filter((log) => log.link === link.link) .length === 0 && ( )}
ID Timestamp IP Location ISP
{filteredLogs.length - index} {new Date( log.timestamp ).toLocaleTimeString() + ', ' + new Date( log.timestamp ).toLocaleDateString()} {log.ip} {log.location} {log.isp}
No logs for this link
)} ); } export default Dashboard;