This commit is contained in:
skidoodle 2022-08-14 19:59:03 +02:00
parent 3a92d65900
commit 7dedfba1f9
31 changed files with 2971 additions and 1859 deletions

View file

@ -1,6 +0,0 @@
{
"extends": "next/core-web-vitals",
"rules": {
"@next/next/no-img-element": "off"
}
}

View file

@ -8,4 +8,4 @@ updates:
- package-ecosystem: "npm" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "daily"
interval: "weekly"

3
.gitignore vendored
View file

@ -34,5 +34,4 @@ yarn-error.log*
# typescript
*.tsbuildinfo
.vercel
next-env.d.ts

View file

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2022 skidoodle
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,2 +0,0 @@
# portfolio-v2
Built with Next.js ♥

View file

@ -1,11 +0,0 @@
import styles from 'styles/Home.module.scss'
const Body = ({children}: {children: any}) => {
return(
<div className={styles.bodySection}>
{children}
</div>
)
}
export default Body

View file

@ -1,35 +1,32 @@
import toast, { Toaster } from 'react-hot-toast';
import Link from 'next/link'
import toast from 'react-hot-toast'
import copy from 'copy-to-clipboard'
const Icon = ({icon, reference, copy = false} : {icon: any, reference: any, copy?: boolean}) => {
return(
<>
{
copy ? (
<a href='javascript:void(0)' onClick={() => doThings(reference)}>
{icon}
</a>
) : (
<a href={reference} target='_blank' rel='noopener noreferrer' aria-label="Icon">
{icon}
</a>
)
}
<Toaster position='bottom-center' reverseOrder={false} />
</>
)
type Icon = {
children: any,
reference: string,
copyValue?: boolean,
}
const doThings = (value: any) => {
copy(value)
toast.remove()
const notify = () => {
toast.remove(),
toast.success('Copied to clipboard', {
style: {
background: '#111',
background: '#0f1012',
color: '#fff',
fontSize: '1em'
}
})
}
)}
export const Icon = ({ children, reference, copyValue }: Icon) => {
if(copyValue) {
return <a className={`cursor-pointer`} onClick={ () => { notify(), copy(reference) }} >{ children }</a>
}
export default Icon
return (
<Link href={ reference }>
<a target='_blank' className={`cursor-pointer`}>{ children }</a>
</Link>
)
}

View file

@ -1,13 +0,0 @@
import styles from 'styles/Home.module.scss'
const IconLayout = ({children}: {children: any}) => {
return(
<>
<div className={styles.iconLayout}>
{children}
</div>
</>
)
}
export default IconLayout

View file

@ -1,14 +0,0 @@
import styles from 'styles/Home.module.scss'
const MainLayout = () => {
return(
<>
<div className={styles.mainLayout}>
<h1>albert</h1>
<p>18-year-old <b>system administrator</b> and student from Hungary</p>
</div>
</>
)
}
export default MainLayout

View file

@ -1,31 +0,0 @@
import { useLastFM } from 'use-last-fm'
import { FaSpotify } from 'react-icons/fa'
import styles from 'styles/Home.module.scss'
const Spotify = () => {
const lastFM = useLastFM('albrtadam', 'f6d19bc25ca340225c70c3d386cd4eab');
return(
<div className={styles.spotify}>
{lastFM.status === 'playing'
?
<>
<a href={lastFM.song.url} target='_blank' rel='noopener noreferrer'>
<img src={lastFM.song.art} width={50} height={50} alt=""/>
<p>Listening to <b>{lastFM.song.name}</b></p>
</a>
</>
:
<>
<img src='song_art.png' width={50} height={50} alt=""/>
<p>Not listening to anything</p>
</>
}
<div className={styles.tinyText}>
<FaSpotify /> Spotify
</div>
</div>
)
}
export default Spotify

View file

@ -1,26 +0,0 @@
import { FaClock } from 'react-icons/fa';
import { dayjs } from 'components/dayjs'
import { useEffect, useState } from 'react'
import styles from 'styles/Home.module.scss'
const now = () => dayjs().tz()
const format = 'hhA'
const Time = () => {
const [date, setDate] = useState(now())
useEffect(() => {
const timer = setInterval(() => setDate(now()), 1000)
return () => clearInterval(timer)
}, [])
return (
<p className={styles.time}>
<FaClock />
{' '}{date.format('DD/MM/YYYY • h:mm:ss A')}
</p>
)
}
export default Time

View file

@ -1,28 +0,0 @@
import styles from 'styles/Home.module.scss'
import { FaSun, FaMoon, FaCloudSun, FaCloudMoon, FaCloud, FaCloudShowersHeavy } from 'react-icons/fa'
import { BsCloudDrizzleFill, BsCloudsFill, BsCloudLightningFill, BsCloudSnowFill, BsCloudFogFill } from 'react-icons/bs'
const Weather = ({data}: {data: any}) => {
const { temp: temperature } = data.main
const { icon: weatherIcon, description: weatherDescription} = data.weather[0]
const icons: any = {
_01d: <FaSun />, _01n: <FaMoon />,
_02d: <FaCloudSun />, _02n: <FaCloudMoon />,
_03d: <FaCloud />, _03n: <FaCloud />,
_04d: <BsCloudsFill />, _04n: <BsCloudsFill />,
_09d: <BsCloudDrizzleFill />, _09n: <BsCloudDrizzleFill />,
_10d: <FaCloudShowersHeavy />, _10n: <FaCloudShowersHeavy />,
_11d: <BsCloudLightningFill />, _11n: <BsCloudLightningFill />,
_13d: <BsCloudSnowFill />, _13n: <BsCloudSnowFill />,
_50d: <BsCloudFogFill />, _50n: <BsCloudFogFill />
}
return (
<div className={styles.weather}>
{icons[`_${weatherIcon}`]} <p>It&apos;s currently <b>{parseInt(temperature)} °C</b> <span>({weatherDescription})</span> in <a href='https://weather.com/en-GB/weather/today/l/b979f874d2f515646f37e2bb434a85cc04869c5a35c6bdf1c6fba26f659313f0' target="_blank" rel='noopener noreferrer'><b>Budapest</b></a></p>
</div>
)
}
export default Weather

View file

@ -0,0 +1,39 @@
import { IconType } from 'react-icons/lib'
import { FaDiscord, FaEnvelope, FaGithub, FaInstagram, FaSteam } from 'react-icons/fa'
type Socials = {
id: number,
ref: string
icon: IconType,
copyValue?: boolean,
}
export const socials: Array<Socials> = [
{
id: 1,
ref: 'https://github.com/skidoodle',
icon: FaGithub,
},
{
id: 2,
ref: 'https://steamcommunity.com/id/_albert',
icon: FaSteam,
},
{
id: 3,
ref: 'contact@albert.lol',
icon: FaEnvelope,
copyValue: true,
},
{
id: 4,
ref: 'https://www.instagram.com/albertadam_/',
icon: FaInstagram,
},
{
id: 5,
ref: 'albert#8838',
icon: FaDiscord,
copyValue: true,
}
]

View file

@ -1,9 +0,0 @@
import dayjs from 'dayjs'
import utc from 'dayjs/plugin/utc'
import timezone from 'dayjs/plugin/timezone'
dayjs.extend(utc)
dayjs.extend(timezone)
dayjs.tz.setDefault('Europe/Budapest')
export { dayjs }

View file

@ -1,9 +1,10 @@
const path = require('path')
module.export = {
productionBrowserSourceMaps: true,
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
sassOptions: {
includePaths: [path.join(__dirname, 'styles')]
images: {
domains: ['i.arch.gay', 'cdn.discordapp.com']
}
}
module.exports = nextConfig

2308
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,39 +1,32 @@
{
"name": "portfolio",
"version": "1.0",
"version": "2.0",
"private": true,
"license": "MIT",
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"buildexport": "next build && next export"
"lint": "next lint"
},
"dependencies": {
"aws-sdk": "^2.1189.0",
"copy-to-clipboard": "^3.3.2",
"@swc/core": "^1.2.223",
"csstype": "^3.1.0",
"dayjs": "^1.11.4",
"dotenv": "^16.0.1",
"goober": "2.1.10",
"next": "12.2.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"aws-sdk": "^2.1194.0",
"next": "12.2.5",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-fade-in": "^2.0.1",
"react-hot-toast": "^2.3.0",
"react-icons": "^4.4.0",
"sass": "^1.54.3",
"use-last-fm": "^0.6.1"
"swr": "^1.3.0"
},
"devDependencies": {
"@types/react-dom": "^18.0.6",
"@types/node": "18.6.4",
"@types/react": "^18.0.15",
"@types/react-dom": "^18.0.4",
"eslint": "8.21.0",
"eslint-config-next": "12.2.4",
"@types/node": "18.7.3",
"@types/react": "18.0.17",
"@types/react-dom": "18.0.6",
"autoprefixer": "^10.4.8",
"copy-to-clipboard": "^3.3.2",
"postcss": "^8.4.16",
"react-hot-toast": "^2.3.0",
"react-icons": "^4.4.0",
"sass": "^1.54.4",
"tailwindcss": "^3.1.8",
"typescript": "4.7.4"
}
}

View file

@ -1,8 +1,21 @@
import { AppProps } from 'next/app'
import 'styles/globals.scss'
import Head from 'next/head'
const MyApp = ({ Component, pageProps }: AppProps) => {
return <Component {...pageProps} />
import { AppProps } from 'next/app'
export default function({ Component, pageProps }: AppProps) {
return(
<>
<Head>
<link rel='preconnect' href='https://vitals.vercel-insights.com' />
<meta name='title' content='albert' />
<meta name='og:title' content='albert' />
<meta name='description' content='system administrator' />
<meta name='og:description' content='system administrator' />
<meta name='theme-color' content='#000000' />
<meta property='og:image' content='/favicon.ico' />
</Head>
<Component {...pageProps} />
</>
)
}
export default MyApp

View file

@ -1,24 +0,0 @@
import { Html, Head, Main, NextScript } from 'next/document'
const Document = () => {
return (
<>
<Html lang='zxx'>
<Head>
<link rel='preconnect' href='https://vitals.vercel-insights.com' />
<link rel='preconnect' href='https://ws.audioscrobbler.com' />
<meta name='title' content='albert' />
<meta name='og:title' content='albert' />
<meta name='description' content='system administrator' />
<meta name='og:description' content='system administrator' />
<meta name='theme-color' content='#000000' />
<meta property='og:image' content='/favicon.ico' />
</Head>
<Main />
<NextScript />
</Html>
</>
)
}
export default Document

41
pages/_error.tsx Normal file
View file

@ -0,0 +1,41 @@
import { GetServerSideProps } from 'next'
import FadeIn from 'react-fade-in'
type ErrorPage = {
statusCode: number,
message: string
}
export default function({ statusCode, message }: ErrorPage) {
return (
<FadeIn>
<div className='flex flex-col justify-center items-center h-[90vh]'>
<div>
<h1 className='font-semibold text-2xl inline-block mr-[1.7rem] pr-[1.5rem] border-r-[1px] border-white'>{ statusCode }</h1>
<div className='inline-block text-left'>
<h2 className='text-lg font-extralight'>{ message }</h2>
</div>
</div>
</div>
</FadeIn>
)
}
export const getServerSideProps: GetServerSideProps = async(props) => {
const { res, err }: any = props
const statusCode = res ? res.statusCode : err ? err.statusCode : 404
let entries: any = {
404: 'Az oldal nem található',
400: 'Érvénytelen kérelem',
500: 'Szerveroldali hiba'
}
const message = entries[statusCode]
return {
props: {
statusCode,
message
}
}
}

26
pages/api/spotify.ts Normal file
View file

@ -0,0 +1,26 @@
import { NextApiRequest, NextApiResponse } from 'next'
export default async function(req: NextApiRequest, res: NextApiResponse) {
const { LASTFM_USERNAME, LASTFM_API } = process.env
const { recenttracks: response } = await fetch(`https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=${LASTFM_USERNAME}&api_key=${LASTFM_API}&format=json&limit=1`).then((res) => res.json())
const { track } = response
const { artist, name, url, image } = track[0]
let nowplaying = Boolean(track[0]['@attr']?.nowplaying)
if(nowplaying) {
return res.status(200).json({
nowplaying,
song: {
artist: artist['#text'],
title: name,
url: url,
image: image[2]['#text'],
}
})
}
return res.status(200).json({ nowplaying })
}

View file

@ -1,52 +1,62 @@
import { GetServerSideProps } from 'next'
import Head from 'next/head'
import Image from 'next/image'
import Link from 'next/link'
import React from 'react'
import useSWR from 'swr'
import FadeIn from 'react-fade-in'
import Body from 'components/Body'
import Icon from 'components/Icon'
import IconLayout from 'components/IconLayout'
import MainLayout from 'components/MainLayout'
import Spotify from 'components/Spotify'
import Weather from 'components/Weather'
import { FaSteam, FaGithub, FaEnvelope } from 'react-icons/fa'
import { RiInstagramFill } from 'react-icons/ri'
import { SiDiscord } from 'react-icons/si'
import dynamic from 'next/dynamic'
const Time = dynamic(() => import('components/Time'), {
ssr: false,
})
const Home = ({data}: any) => {
import { socials } from 'components/data/socials'
import { Icon } from 'components/Icon'
import profilePic from '../public/profile.webp'
import { Toaster } from 'react-hot-toast'
import { FaSpotify } from 'react-icons/fa'
const fetcher = (url: RequestInfo) => fetch(url).then(r => r.json())
export default function() {
const { data: spotify } = useSWR('/api/spotify', fetcher, { refreshInterval: 1000 })
if(!spotify) return
return (
<>
<Head>
<title>albert</title>
</Head>
<Body>
<FadeIn>
<MainLayout />
<IconLayout>
<Icon icon={<FaGithub />} reference={'https://github.com/skidoodle'} copy={false} />
<Icon icon={<FaSteam />} reference={'https://steamcommunity.com/id/_albert'} copy={false} />
<Icon icon={<FaEnvelope />} reference={'contact@albert.lol'} copy={true} />
<Icon icon={<RiInstagramFill />} reference={'https://instagram.com/albertadam_'} copy={false} />
<Icon icon={<SiDiscord />} reference={'albert#8838'} copy={true} />
</IconLayout>
<Time />
<Weather data={data} />
<Spotify />
</FadeIn>
</Body>
<div className='px-8 w-11/12 m-auto rounded-lg max-w-4xl'>
<div className='flex flex-col justify-center items-center mt-32 md:mt-56'>
<Image src={profilePic} className="rounded-full text-center" height={150} width={150}/>
<h1 className='text-4xl font-bold -mt-1'>albert</h1>
<p className='text-[#9ca3af] text-xl flex flex-wrap items-center justify-center whitespace-pre-wrap'>
{ Math.floor((new Date().getTime() - new Date('2004.07.22').getTime()) / (1000 * 60 * 60 * 24 * 365.25)) }
yrs old <b className='font-semibold'>system administrator</b> and student from Hungary
</p>
</div>
<hr className='border-t-[#727277] w-4/5 md:w-2/5 m-auto mt-5 md:mt-8'/>
<div className='mt-3 flex justify-center items-center'>
<FaSpotify className='text-[#32a866]' />&nbsp;
<p className='font-semibold'>Listening to
{
spotify.song
? <Link href={`${spotify.song.url}`}>
<a target='_blank' className='text-[#32a866]'> { spotify.song.title || 'nothing' }</a>
</Link>
: <a className='text-[#32a866]'> nothing</a>
}
</p>
</div>
<div className='flex justify-between items-center text-3xl mt-11 md:mt-16 max-w-sm m-auto'>
{ socials.map(social => (
<Icon key={ social.id } reference={ social.ref } copyValue={ social.copyValue }>{ React.createElement(social.icon) }</Icon>
))}
</div>
</div>
<Toaster />
</FadeIn>\
</>
)
}
export const getServerSideProps: GetServerSideProps = async () => {
const response = await fetch('https://api.openweathermap.org/data/2.5/weather?lat=47.51&lon=19.04&appid=1b3c10c18e894eaf1fd63eedde53fa54&units=metric')
const data = await response.json()
return {
props: { data }
}
}
export default Home

6
postcss.config.js Normal file
View file

@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

BIN
public/profile.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

View file

@ -1,128 +0,0 @@
@import '_variables';
.bodySection {
margin-top: 8rem;
margin-left: 10%;
margin-right: 10%;
}
.mainLayout {
h1 {
line-height: .2;
font-size: 3rem;
font-weight: 700;
}
p {
color: $secondary-color;
font-size: 1rem;
font-weight: 400;
}
}
.iconLayout {
margin-bottom: 5%;
svg {
font-size: 1.4em;
cursor: pointer;
margin-right: 6%;
margin-bottom: 1em;
}
}
.time {
svg {
font-size: .9em;
}
}
.weather {
img {
float: left;
}
p {
display: inline;
}
span {
font-size: .8rem;
}
a:hover {
text-decoration: none;
color: #C7D2FE;
}
svg {
font-size: .9em;
}
}
.spotify {
font-size: 1em;
margin-top: 5%;
padding: 1rem 1rem 1.8rem 1rem;
border: 1px solid $spotify-box-color;
border-radius: .5rem;
img {
border-radius: 10px;
float: left;
margin-right: 5%;
}
p {
display: inline;
}
.tinyText {
font-weight: 300;
font-size: .8em;
svg {
margin-top: 5px;
color: #1DB954;
}
}
}
.footerLayout {
position: absolute;
bottom: 1rem;
left: 50%;
transform: translateX(-50%);
a {
cursor: pointer;
text-decoration: none;
&:hover {
color: #C7D2FE
}
}
}
@media (min-width: 1200px) {
.bodySection {
margin-left: 30%;
margin-right: 45%;
}
.iconLayout {
margin-bottom: 2%;
}
.spotify {
margin-top: 8%;
width: 80%;
}
}
@media (min-height: 800px) {
.bodySection {
margin-top: 15rem;
}
}

View file

@ -1,6 +0,0 @@
$background-color: #000000;
$primary-color: #ffffff;
$secondary-color: #9CA3AF;
$selection-color: #8039e2;
$spotify-box-color: #521c9e;
$font: 'Inter', sans-serif;

View file

@ -1,26 +1,56 @@
@import '_variables';
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@200;400;700&display=swap');
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
html {
scrollbar-width: thin;
scrollbar-color: #8a58e0 transparent;
}
html,
body {
background-color: $background-color;
color: $primary-color;
padding: 0;
margin: 0;
font-family: $font;
font-weight: 400;
background-color: #000;
color: #fff;
}
ul, ol {
list-style: revert;
}
}
@layer components {
::selection {
background: rgba($selection-color, .7);
color: $primary-color;
background-color: #8039e2;
color: #fff;
}
a {
color: inherit;
text-decoration: none;
::-webkit-scrollbar {
width: 3px;
}
* {
box-sizing: border-box;
::-webkit-scrollbar-thumb {
background-color: #8a58e0;
border-radius: 10px;
}
}
@layer utilities {
.gradient {
color: transparent;
background-clip: text;
background-image: linear-gradient(to right, #800a18, #a82044, #9e4a60);
animation: gradient 5s ease infinite
}
@keyframes gradient {
0%, 100% {
background-size: 200%;
background-position: left center;
}
50% {
background-size: 200%;
background-position: right center;
}
}
}

11
tailwind.config.js Normal file
View file

@ -0,0 +1,11 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./pages/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}'
],
theme: {
extend: {},
},
plugins: [],
}

1773
yarn.lock

File diff suppressed because it is too large Load diff