move components to root, remove analytics and speed insights, cleanup on manifesto, update music link text and tilde icon on header, minor home page improvements/tweaks

This commit is contained in:
Aidan 2025-02-01 21:44:25 -05:00
parent 1909a6d9fe
commit 1253a7e0a1
29 changed files with 65 additions and 59 deletions

View file

@ -0,0 +1,29 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faGitAlt, faGithub } from '@fortawesome/free-brands-svg-icons'
import { faStar, faCodeBranch } from '@fortawesome/free-solid-svg-icons'
import featuredProjects from '@/public/data/featured.json'
import Link from 'next/link'
export default function GitHubFeatured() {
return (
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{featuredProjects.map((project) => (
<div key={project.id} className="bg-gray-800 p-6 rounded-lg shadow-md min-h-[200px] flex flex-col">
<div className="flex-1">
<h3 className="text-xl font-bold text-gray-100 mb-3">
<FontAwesomeIcon icon={project.github ? faGithub : faGitAlt} className="mr-2" /> {project.name}
</h3>
<p className="text-gray-300 flex-grow">{project.description}</p>
</div>
<div className="pt-4 border-t border-gray-700 flex justify-between items-center mt-auto">
<Link href={project.url} className="text-blue-400 hover:underline">View Repo</Link>
<div className="flex items-center text-gray-400">
<FontAwesomeIcon icon={faStar} className="mr-1" /> {project.stars}
<FontAwesomeIcon icon={faCodeBranch} className="ml-4 mr-1" /> {project.forks}
</div>
</div>
</div>
))}
</div>
)
}

View file

@ -0,0 +1,64 @@
"use client";
import React, { useEffect, useState } from 'react';
import Image from 'next/image';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faLastfm } from '@fortawesome/free-brands-svg-icons'
import { faCompactDisc, faUser } from '@fortawesome/free-solid-svg-icons'
interface Track {
name: string;
artist: { '#text': string };
album: { '#text': string };
image: { '#text': string; size: string }[];
url: string;
'@attr'?: { nowplaying: string };
}
const LastPlayed: React.FC = () => {
const [track, setTrack] = useState<Track | null>(null);
const apiUrl = process.env.LASTFM_API_URL || 'https://lastfm-last-played.biancarosa.com.br/aidxn_/latest-song';
useEffect(() => {
fetch(apiUrl)
.then(response => response.json())
.then(data => setTrack(data.track))
.catch(error => console.error('Error fetching now playing:', error));
}, [apiUrl]);
if (!track) {
return (
<div className="max-w-2xl mx-auto mb-12">
<h2 className="text-2xl font-bold mb-4 text-gray-200">Last Played Song</h2>
<div className="flex justify-center items-center border border-gray-300 rounded-lg p-4 max-w-md mt-8">
<span className="spinner-border animate-spin inline-block w-8 h-8 border-4 rounded-full" role="status"></span>
</div>
</div>
);
}
return (
<div className="max-w-2xl mx-auto mb-12">
<h2 className="text-2xl font-bold mb-4 text-gray-200">Last Played Song</h2>
<div className="now-playing flex items-center border border-gray-300 rounded-lg p-4 max-w-md mt-8 bg-white bg-opacity-10 backdrop-filter backdrop-blur-lg">
<Image
src={track.image.find(img => img.size === 'large')?.['#text'] || '/placeholder.png'}
alt={track.name}
width={96}
height={96}
className="rounded-lg mr-4"
/>
<div>
<p className="font-bold">{track.name}</p>
<p><FontAwesomeIcon icon={faCompactDisc} className="mr-1" /> {track.album['#text']}</p>
<i><FontAwesomeIcon icon={faUser} className="mr-1" /> {track.artist['#text']}</i>
<a href={track.url} target="_blank" rel="noopener noreferrer" className="text-blue-500 flex items-center">
<FontAwesomeIcon icon={faLastfm} className="mr-2" /> View on Last.fm
</a>
</div>
</div>
</div>
);
};
export default LastPlayed;

View file

@ -0,0 +1,101 @@
"use client";
import { useState, useEffect } from 'react';
import Image from 'next/image';
import { Play, SkipBack, SkipForward } from 'lucide-react';
import LoadingSpinner from '../objects/LoadingSpinner';
interface Song {
albumArt: string;
name: string;
artist: string;
duration: string;
link?: string;
}
interface Period {
timePeriod: string;
songs: Song[];
}
export default function Home() {
const [timePeriod, setTimePeriod] = useState('Early Summer 2024');
const [songs, setSongs] = useState<Song[]>([]);
const [currentIndex, setCurrentIndex] = useState(0);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
setIsLoading(true);
fetch('/data/music.json')
.then(response => response.json())
.then((data: Period[]) => {
const selectedPeriod = data.find((period) => period.timePeriod === timePeriod);
const songsList = selectedPeriod ? selectedPeriod.songs : [];
setSongs(songsList);
setCurrentIndex(Math.floor(Math.random() * songsList.length));
setIsLoading(false);
})
.catch(error => {
console.error('Error fetching music data:', error);
setIsLoading(false);
});
}, [timePeriod]);
const handleNext = () => {
setCurrentIndex((currentIndex + 1) % songs.length);
};
const handlePrevious = () => {
setCurrentIndex((currentIndex - 1 + songs.length) % songs.length);
};
return (
<div className="max-w-2xl mx-auto">
<section id="music-carousel" className="mb-12">
<h2 className="text-3xl font-semibold mb-4 text-gray-200">Music By Time Period</h2>
<div className="mb-4 pb-4">
<label htmlFor="timePeriod" className="text-gray-300">Time Period:</label>
<select
id="timePeriod"
value={timePeriod}
onChange={(e) => setTimePeriod(e.target.value)}
className="ml-2 p-2 bg-gray-700 text-gray-300 rounded"
>
<option value="Early Summer 2024">Early Summer 2024</option>
</select>
</div>
{isLoading && <LoadingSpinner />}
{!isLoading && songs.length > 0 && (
<div className="relative">
<div className="text-center">
<Image
src={songs[currentIndex].albumArt}
alt={songs[currentIndex].name}
width={300}
height={300}
className="mx-auto mb-4 rounded-lg"
/>
<h3 className="text-2xl font-bold text-gray-100">{songs[currentIndex].name}</h3>
<p className="text-gray-300">{songs[currentIndex].artist}</p>
<p className="text-gray-300">{songs[currentIndex].duration}</p>
<div className="mt-4">
<button onClick={handlePrevious} className="mr-4 text-gray-300">
<SkipBack className="w-8 h-8" />
</button>
<button className="mr-4 text-gray-300" onClick={() => window.open(songs[currentIndex]?.link, '_blank')}>
<Play className="w-8 h-8" />
</button>
<button onClick={handleNext} className="text-gray-300">
<SkipForward className="w-8 h-8" />
</button>
</div>
</div>
</div>
)}
</section>
</div>
);
}