upgrade
This commit is contained in:
@@ -1,26 +0,0 @@
|
||||
import React from 'react';
|
||||
import Header from '../components/Header';
|
||||
import Footer from '../components/Footer';
|
||||
|
||||
const About: React.FC = () => {
|
||||
return (
|
||||
<div className="min-h-screen">
|
||||
<Header />
|
||||
<main className="py-20">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="text-center">
|
||||
<h1 className="text-4xl font-bold text-slate-800 sm:text-5xl">
|
||||
About
|
||||
</h1>
|
||||
<p className="mt-4 text-lg text-slate-600 max-w-2xl mx-auto">
|
||||
Ask Meku to generate content for this page.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default About;
|
||||
@@ -1,26 +0,0 @@
|
||||
import React from 'react';
|
||||
import Header from '../components/Header';
|
||||
import Footer from '../components/Footer';
|
||||
|
||||
const Blog: React.FC = () => {
|
||||
return (
|
||||
<div className="min-h-screen">
|
||||
<Header />
|
||||
<main className="py-20">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="text-center">
|
||||
<h1 className="text-4xl font-bold text-slate-800 sm:text-5xl">
|
||||
Blog
|
||||
</h1>
|
||||
<p className="mt-4 text-lg text-slate-600 max-w-2xl mx-auto">
|
||||
Ask Meku to generate content for this page.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Blog;
|
||||
@@ -1,26 +0,0 @@
|
||||
import React from 'react';
|
||||
import Header from '../components/Header';
|
||||
import Footer from '../components/Footer';
|
||||
|
||||
const Contact: React.FC = () => {
|
||||
return (
|
||||
<div className="min-h-screen">
|
||||
<Header />
|
||||
<main className="py-20">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="text-center">
|
||||
<h1 className="text-4xl font-bold text-slate-800 sm:text-5xl">
|
||||
Contact
|
||||
</h1>
|
||||
<p className="mt-4 text-lg text-slate-600 max-w-2xl mx-auto">
|
||||
Ask Meku to generate content for this page.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Contact;
|
||||
@@ -1,30 +0,0 @@
|
||||
import React from 'react';
|
||||
import Header from '../components/Header';
|
||||
import Hero from '../components/Hero';
|
||||
import Skills from '../components/Skills';
|
||||
import Portfolio from '../components/Portfolio';
|
||||
import DroneVideoCarousel from '../components/DroneVideoCarousel';
|
||||
import WebsiteScreenshots from '../components/WebsiteScreenshots';
|
||||
import TradingGraph from '../components/TradingGraph';
|
||||
import DonateShop from '../components/DonateShop';
|
||||
import Footer from '../components/Footer';
|
||||
|
||||
const Home: React.FC = () => {
|
||||
return (
|
||||
<div className="min-h-screen">
|
||||
<Header />
|
||||
<main>
|
||||
<Hero />
|
||||
<Skills />
|
||||
<Portfolio />
|
||||
<DroneVideoCarousel />
|
||||
<WebsiteScreenshots />
|
||||
<TradingGraph />
|
||||
<DonateShop />
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Home;
|
||||
@@ -1,179 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const NotFound: React.FC = () => {
|
||||
return (
|
||||
<section className="relative flex py-10 min-h-screen items-center justify-center overflow-hidden bg-black">
|
||||
<div className="mx-auto relative z-30 w-full max-w-[600px] text-center px-4">
|
||||
{/* Large 404 Text */}
|
||||
<div className="mb-8">
|
||||
<svg
|
||||
width="472"
|
||||
height="158"
|
||||
viewBox="0 0 472 158"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M26.4028 0.5224C29.8616 0.5224 32.6655 3.3263 32.6655 6.7851V58.7187H75.8726V9.4306C75.8726 5.97182 78.6765 3.16794 82.1353 3.16791H102.236C105.694 3.16817 108.499 5.97196 108.499 9.4306V151.215C108.498 154.673 105.694 157.477 102.235 157.477H82.1353C78.6766 157.477 75.8727 154.673 75.8726 151.215V91.3437H23.0571C19.5983 91.3437 16.7944 88.5398 16.7944 85.081V78.1181H6.30322C2.84444 78.1181 0.0405444 75.3142 0.0405273 71.8554V6.7851C0.0405355 3.32631 2.84444 0.522409 6.30322 0.5224H26.4028Z"
|
||||
fill="url(#paint0_linear_11881_2293)"
|
||||
/>
|
||||
<path
|
||||
d="M262.328 82.4706C263.99 82.4708 265.338 83.8187 265.338 85.4814V97.8544H277.712C279.375 97.8546 280.723 99.2018 280.723 100.864V116.31C280.723 117.973 279.375 119.321 277.712 119.321H260.835C259.173 119.321 257.825 117.973 257.825 116.31V103.937H214.175V116.31C214.175 117.973 212.827 119.321 211.165 119.321H194.289C192.626 119.321 191.278 117.973 191.278 116.31V100.864C191.278 99.2017 192.626 97.8544 194.289 97.8544H207.02V85.4814C207.02 83.8186 208.368 82.4706 210.031 82.4706H262.328Z"
|
||||
fill="url(#paint1_linear_11881_2293)"
|
||||
/>
|
||||
<path
|
||||
d="M222.614 41.3251C224.277 41.3251 225.625 42.6731 225.625 44.3359V59.7812C225.625 61.4439 224.277 62.7919 222.614 62.7919H205.737C204.074 62.7917 202.727 61.4438 202.727 59.7812V44.3359C202.727 42.6733 204.074 41.3254 205.737 41.3251H222.614Z"
|
||||
fill="url(#paint2_linear_11881_2293)"
|
||||
/>
|
||||
<path
|
||||
d="M266.263 41.3251C267.926 41.3252 269.274 42.6732 269.274 44.3359V59.7812C269.274 61.4439 267.926 62.7918 266.263 62.7919H249.386C247.724 62.7918 246.375 61.4439 246.375 59.7812V44.3359C246.375 42.6732 247.724 41.3252 249.386 41.3251H266.263Z"
|
||||
fill="url(#paint3_linear_11881_2293)"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M291.231 3.16693C313.322 3.16693 331.231 21.0755 331.231 43.1669V117.477L331.218 118.51C330.671 140.124 312.977 157.477 291.231 157.477H180.769C158.678 157.477 140.769 139.569 140.769 117.477V43.1669C140.769 21.0755 158.678 3.16693 180.769 3.16693H291.231ZM180.769 27.1669C171.932 27.1669 164.769 34.3304 164.769 43.1669V117.477C164.769 126.314 171.932 133.477 180.769 133.477H291.231C300.068 133.477 307.231 126.314 307.231 117.477V43.1669C307.231 34.3304 300.068 27.1669 291.231 27.1669H180.769Z"
|
||||
fill="url(#paint4_linear_11881_2293)"
|
||||
/>
|
||||
<path
|
||||
d="M389.865 0.5224C393.324 0.522421 396.127 3.32632 396.127 6.7851V58.7187H439.334V9.4306C439.334 5.9718 442.138 3.16791 445.597 3.16791H465.697C469.156 3.16791 471.959 5.9718 471.959 9.4306V151.215C471.959 154.673 469.155 157.477 465.697 157.477H445.597C442.138 157.477 439.335 154.673 439.334 151.215V91.3437H386.518C383.059 91.3436 380.255 88.5397 380.255 85.081V78.1181H369.765C366.306 78.1181 363.502 75.3142 363.502 71.8554V6.7851C363.502 3.3263 366.306 0.5224 369.765 0.5224H389.865Z"
|
||||
fill="url(#paint5_linear_11881_2293)"
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="paint0_linear_11881_2293"
|
||||
x1="471.959"
|
||||
y1="157.477"
|
||||
x2="448.654"
|
||||
y2="-49.8952"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#3D404D" />
|
||||
<stop offset="1" stopColor="#8F95B2" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint1_linear_11881_2293"
|
||||
x1="471.959"
|
||||
y1="157.477"
|
||||
x2="448.654"
|
||||
y2="-49.8952"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#3D404D" />
|
||||
<stop offset="1" stopColor="#8F95B2" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint2_linear_11881_2293"
|
||||
x1="471.959"
|
||||
y1="157.477"
|
||||
x2="448.654"
|
||||
y2="-49.8952"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#3D404D" />
|
||||
<stop offset="1" stopColor="#8F95B2" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint3_linear_11881_2293"
|
||||
x1="471.959"
|
||||
y1="157.477"
|
||||
x2="448.654"
|
||||
y2="-49.8952"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#3D404D" />
|
||||
<stop offset="1" stopColor="#8F95B2" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint4_linear_11881_2293"
|
||||
x1="471.959"
|
||||
y1="157.477"
|
||||
x2="448.654"
|
||||
y2="-49.8952"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#3D404D" />
|
||||
<stop offset="1" stopColor="#8F95B2" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint5_linear_11881_2293"
|
||||
x1="471.959"
|
||||
y1="157.477"
|
||||
x2="448.654"
|
||||
y2="-49.8952"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#3D404D" />
|
||||
<stop offset="1" stopColor="#8F95B2" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h1 className="mb-4 text-3xl font-bold text-white sm:text-4xl">
|
||||
OPPS! Page Not Found
|
||||
</h1>
|
||||
<p className="mb-8 text-base text-white/60 sm:text-lg">
|
||||
We can't seem to find the page you are looking for!
|
||||
</p>
|
||||
<a
|
||||
href={typeof window !== 'undefined' ? window.location.origin : '/'}
|
||||
className="inline-flex items-center gap-2 rounded-full bg-white px-6 py-3 text-sm font-medium text-black transition-colors hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-black"
|
||||
>
|
||||
Back to homepage
|
||||
</a>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="mt-16">
|
||||
<p className="text-sm text-gray-600">
|
||||
© {new Date().getFullYear()} - Meku.dev
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="absolute inset-0 bg-[url(https://meku.dev/images/grain.png)] bg-cover bg-center opacity-60 mix-blend-soft-light z-20"></div>
|
||||
|
||||
<div className="absolute bottom-0 left-0 right-0 z-10">
|
||||
<svg
|
||||
width="2192"
|
||||
height="771"
|
||||
viewBox="0 0 2192 771"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g opacity="0.35" filter="url(#filter0_f_3740_75)">
|
||||
<path
|
||||
d="M199.999 258.919C199.999 86.6144 601.152 347.404 1096 347.404C1590.85 347.404 1992 86.6146 1992 258.919C1992 431.223 1590.85 570.904 1096 570.904C601.152 570.904 199.999 431.223 199.999 258.919Z"
|
||||
fill="#C0C2CF"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter
|
||||
id="filter0_f_3740_75"
|
||||
x="-0.000732422"
|
||||
y="0.0515137"
|
||||
width="2192"
|
||||
height="770.852"
|
||||
filterUnits="userSpaceOnUse"
|
||||
colorInterpolationFilters="sRGB"
|
||||
>
|
||||
<feFlood floodOpacity="0" result="BackgroundImageFix" />
|
||||
<feBlend
|
||||
mode="normal"
|
||||
in="SourceGraphic"
|
||||
in2="BackgroundImageFix"
|
||||
result="shape"
|
||||
/>
|
||||
<feGaussianBlur
|
||||
stdDeviation="100"
|
||||
result="effect1_foregroundBlur_3740_75"
|
||||
/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default NotFound;
|
||||
@@ -1,26 +0,0 @@
|
||||
import React from 'react';
|
||||
import Header from '../components/Header';
|
||||
import Footer from '../components/Footer';
|
||||
|
||||
const Portfolio: React.FC = () => {
|
||||
return (
|
||||
<div className="min-h-screen">
|
||||
<Header />
|
||||
<main className="py-20">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="text-center">
|
||||
<h1 className="text-4xl font-bold text-slate-800 sm:text-5xl">
|
||||
Portfolio
|
||||
</h1>
|
||||
<p className="mt-4 text-lg text-slate-600 max-w-2xl mx-auto">
|
||||
Ask Meku to generate content for this page.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Portfolio;
|
||||
@@ -1,26 +0,0 @@
|
||||
import React from 'react';
|
||||
import Header from '../components/Header';
|
||||
import Footer from '../components/Footer';
|
||||
|
||||
const Services: React.FC = () => {
|
||||
return (
|
||||
<div className="min-h-screen">
|
||||
<Header />
|
||||
<main className="py-20">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="text-center">
|
||||
<h1 className="text-4xl font-bold text-slate-800 sm:text-5xl">
|
||||
Services
|
||||
</h1>
|
||||
<p className="mt-4 text-lg text-slate-600 max-w-2xl mx-auto">
|
||||
Ask Meku to generate content for this page.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Services;
|
||||
14
frontend/src/pages/contact/ContactPage.tsx
Normal file
14
frontend/src/pages/contact/ContactPage.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
export default function ContactPage(){
|
||||
return (
|
||||
<section className="section">
|
||||
<div className="container">
|
||||
<h2 className="text-2xl md:text-3xl font-bold mb-4">Get in Touch</h2>
|
||||
<p className="text-[var(--c-lines)] mb-6">Reach out via email or phone. Social links are in the footer.</p>
|
||||
<div className="glass p-6 max-w-lg">
|
||||
<p><strong>Email:</strong> <a href="mailto:brunovontor@gmail.com" className="hover:text-[var(--c-other)]">brunovontor@gmail.com</a></p>
|
||||
<p className="mt-2"><strong>Phone:</strong> <a href="tel:+420605512624" className="hover:text-[var(--c-other)]">+420 605 512 624</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
4
frontend/src/pages/donate/DonateShopPage.tsx
Normal file
4
frontend/src/pages/donate/DonateShopPage.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
import DonationShop from "../../components/donate/DonationShop";
|
||||
export default function DonateShopPage(){
|
||||
return <DonationShop />;
|
||||
}
|
||||
206
frontend/src/pages/downloader/Downloader.tsx
Normal file
206
frontend/src/pages/downloader/Downloader.tsx
Normal file
@@ -0,0 +1,206 @@
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import {
|
||||
fetchInfo,
|
||||
downloadImmediate,
|
||||
FORMAT_EXTS,
|
||||
type InfoResponse,
|
||||
parseContentDispositionFilename,
|
||||
} from "../../api/apps/Downloader";
|
||||
|
||||
export default function Downloader() {
|
||||
const [url, setUrl] = useState("");
|
||||
const [probing, setProbing] = useState(false);
|
||||
const [downloading, setDownloading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [info, setInfo] = useState<InfoResponse | null>(null);
|
||||
|
||||
const [ext, setExt] = useState<typeof FORMAT_EXTS[number]>("mp4");
|
||||
const [videoRes, setVideoRes] = useState<string | undefined>(undefined);
|
||||
const [audioRes, setAudioRes] = useState<string | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
if (info?.video_resolutions?.length && !videoRes) {
|
||||
setVideoRes(info.video_resolutions[0]);
|
||||
}
|
||||
if (info?.audio_resolutions?.length && !audioRes) {
|
||||
setAudioRes(info.audio_resolutions[0]);
|
||||
}
|
||||
}, [info]);
|
||||
|
||||
async function onProbe(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
setError(null);
|
||||
setInfo(null);
|
||||
setProbing(true);
|
||||
try {
|
||||
const res = await fetchInfo(url);
|
||||
setInfo(res);
|
||||
// reset selections from fresh info
|
||||
setVideoRes(res.video_resolutions?.[0]);
|
||||
setAudioRes(res.audio_resolutions?.[0]);
|
||||
} catch (e: any) {
|
||||
setError(
|
||||
e?.response?.data?.error ||
|
||||
e?.response?.data?.detail ||
|
||||
e?.message ||
|
||||
"Failed to get info."
|
||||
);
|
||||
} finally {
|
||||
setProbing(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function onDownload() {
|
||||
setError(null);
|
||||
setDownloading(true);
|
||||
try {
|
||||
const { blob, filename } = await downloadImmediate({
|
||||
url,
|
||||
ext,
|
||||
videoResolution: videoRes,
|
||||
audioResolution: audioRes,
|
||||
});
|
||||
const name = filename || parseContentDispositionFilename("") || `download.${ext}`;
|
||||
const href = URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = href;
|
||||
a.download = name;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(href);
|
||||
} catch (e: any) {
|
||||
setError(
|
||||
e?.response?.data?.error ||
|
||||
e?.response?.data?.detail ||
|
||||
e?.message ||
|
||||
"Download failed."
|
||||
);
|
||||
} finally {
|
||||
setDownloading(false);
|
||||
}
|
||||
}
|
||||
|
||||
const canDownload = useMemo(
|
||||
() => !!url && !!ext && !!videoRes && !!audioRes,
|
||||
[url, ext, videoRes, audioRes]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="max-w-3xl mx-auto p-4 space-y-4">
|
||||
<h1 className="text-2xl font-semibold">Downloader</h1>
|
||||
|
||||
{error && <div className="rounded border border-red-300 bg-red-50 text-red-700 p-2">{error}</div>}
|
||||
|
||||
<form onSubmit={onProbe} className="grid gap-3">
|
||||
<label className="grid gap-1">
|
||||
<span className="text-sm font-medium">URL</span>
|
||||
<input
|
||||
type="url"
|
||||
required
|
||||
placeholder="https://example.com/video"
|
||||
value={url}
|
||||
onChange={(e) => setUrl(e.target.value)}
|
||||
className="w-full border rounded p-2"
|
||||
/>
|
||||
</label>
|
||||
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
type="submit"
|
||||
disabled={!url || probing}
|
||||
className="px-3 py-2 rounded bg-blue-600 text-white disabled:opacity-50"
|
||||
>
|
||||
{probing ? "Probing..." : "Get info"}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onDownload}
|
||||
disabled={!canDownload || downloading}
|
||||
className="px-3 py-2 rounded bg-emerald-600 text-white disabled:opacity-50"
|
||||
>
|
||||
{downloading ? "Downloading..." : "Download"}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{info && (
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-start gap-3">
|
||||
{info.thumbnail && (
|
||||
<img
|
||||
src={info.thumbnail}
|
||||
alt={info.title || "thumbnail"}
|
||||
className="w-40 h-24 object-cover rounded border"
|
||||
/>
|
||||
)}
|
||||
<div className="text-sm text-gray-800 space-y-1">
|
||||
<div>
|
||||
<span className="font-medium">Title:</span> {info.title || "-"}
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-medium">Duration:</span>{" "}
|
||||
{info.duration ? `${Math.round(info.duration)} s` : "-"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-3 gap-3">
|
||||
<label className="grid gap-1">
|
||||
<span className="text-sm font-medium">Container</span>
|
||||
<select
|
||||
value={ext}
|
||||
onChange={(e) => setExt(e.target.value as any)}
|
||||
className="border rounded p-2"
|
||||
>
|
||||
{FORMAT_EXTS.map((x) => (
|
||||
<option key={x} value={x}>
|
||||
{x.toUpperCase()}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label className="grid gap-1">
|
||||
<span className="text-sm font-medium">Video resolution</span>
|
||||
<select
|
||||
value={videoRes || ""}
|
||||
onChange={(e) => setVideoRes(e.target.value || undefined)}
|
||||
className="border rounded p-2"
|
||||
>
|
||||
{info.video_resolutions?.length ? (
|
||||
info.video_resolutions.map((r) => (
|
||||
<option key={r} value={r}>
|
||||
{r}
|
||||
</option>
|
||||
))
|
||||
) : (
|
||||
<option value="">-</option>
|
||||
)}
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label className="grid gap-1">
|
||||
<span className="text-sm font-medium">Audio bitrate</span>
|
||||
<select
|
||||
value={audioRes || ""}
|
||||
onChange={(e) => setAudioRes(e.target.value || undefined)}
|
||||
className="border rounded p-2"
|
||||
>
|
||||
{info.audio_resolutions?.length ? (
|
||||
info.audio_resolutions.map((r) => (
|
||||
<option key={r} value={r}>
|
||||
{r}
|
||||
</option>
|
||||
))
|
||||
) : (
|
||||
<option value="">-</option>
|
||||
)}
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
240
frontend/src/pages/home/Home.module.css
Normal file
240
frontend/src/pages/home/Home.module.css
Normal file
@@ -0,0 +1,240 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Doto:wght@100..900&family=Roboto+Mono:ital,wght@0,100..700;1,100..700&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Doto:wght@300&family=Roboto+Mono:ital,wght@0,100..700;1,100..700&display=swap');
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Exo:ital,wght@0,100..900;1,100..900&display=swap');
|
||||
|
||||
:root{
|
||||
--c-background: #031D44; /*background*/
|
||||
--c-background-light: #04395E; /*background-highlight*/
|
||||
--c-boxes: #24719f;; /*boxes*/
|
||||
--c-lines: #87a9da; /*lines*/
|
||||
--c-text: #CAF0F8; /*text*/
|
||||
--c-other: #70A288; /*other*/
|
||||
}
|
||||
|
||||
html{
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body{
|
||||
font-family: "Exo", serif;
|
||||
|
||||
|
||||
font-optical-sizing: auto;
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.doto-font{
|
||||
font-family: "Doto", serif;
|
||||
font-optical-sizing: auto;
|
||||
font-weight: 300;
|
||||
font-style: normal;
|
||||
font-variation-settings: "ROND" 0;
|
||||
}
|
||||
.bebas-neue-regular {
|
||||
font-family: "Bebas Neue", sans-serif;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
|
||||
.introduction {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: var(--c-text);
|
||||
|
||||
padding-bottom: 10em;
|
||||
margin-top: 6em;
|
||||
|
||||
width: 100%;
|
||||
position: relative;
|
||||
top:0;
|
||||
|
||||
/* gap: 4em;*/
|
||||
}
|
||||
|
||||
.introduction h1{
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.introduction article {
|
||||
/*background-color: cadetblue;*/
|
||||
|
||||
padding: 2em;
|
||||
border-radius: 1em;
|
||||
}
|
||||
|
||||
.introduction article header {}
|
||||
|
||||
.introduction article:nth-child(1) {
|
||||
width: 100%;
|
||||
/* transform: rotate(5deg); */
|
||||
align-self: center;
|
||||
text-align: center;
|
||||
font-size: 2em;
|
||||
}
|
||||
/*
|
||||
.introduction article:nth-child(2) {
|
||||
width: 50%;
|
||||
transform: rotate(3deg);
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.introduction article:nth-child(3) {
|
||||
width: 50%;
|
||||
transform: rotate(-2deg);
|
||||
align-self: flex-start;
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
|
||||
.animation-introduction{
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
z-index: -2;
|
||||
}
|
||||
|
||||
.animation-introduction ul{
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
/*overflow: hidden; ZAPNOUT KDYŽ NECHCEŠ ANIMACI PŘECHÁZET DO OSTATNÍCH DIVŮ*/
|
||||
}
|
||||
|
||||
.animation-introduction ul li{
|
||||
position: absolute;
|
||||
display: block;
|
||||
list-style: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: rgba(255, 255, 255, 35%);
|
||||
animation: animate 4s linear infinite;
|
||||
bottom: -150px;
|
||||
|
||||
}
|
||||
|
||||
.animation-introduction ul li:nth-child(1){
|
||||
left: 25%;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
animation-delay: 0s;
|
||||
}
|
||||
|
||||
|
||||
.animation-introduction ul li:nth-child(2){
|
||||
left: 10%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
animation-delay: 2s;
|
||||
animation-duration: 12s;
|
||||
}
|
||||
|
||||
.animation-introduction ul li:nth-child(3){
|
||||
left: 70%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
animation-delay: 4s;
|
||||
}
|
||||
|
||||
.animation-introduction ul li:nth-child(4){
|
||||
left: 40%;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
animation-delay: 0s;
|
||||
animation-duration: 18s;
|
||||
}
|
||||
|
||||
.animation-introduction ul li:nth-child(5){
|
||||
left: 65%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
animation-delay: 0s;
|
||||
}
|
||||
|
||||
.animation-introduction ul li:nth-child(6){
|
||||
left: 75%;
|
||||
width: 110px;
|
||||
height: 110px;
|
||||
animation-delay: 3s;
|
||||
}
|
||||
|
||||
.animation-introduction ul li:nth-child(7){
|
||||
left: 35%;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
animation-delay: 7s;
|
||||
}
|
||||
|
||||
.animation-introduction ul li:nth-child(8){
|
||||
left: 50%;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
animation-delay: 15s;
|
||||
animation-duration: 45s;
|
||||
}
|
||||
|
||||
.animation-introduction ul li:nth-child(9){
|
||||
left: 20%;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
animation-delay: 2s;
|
||||
animation-duration: 35s;
|
||||
}
|
||||
|
||||
.animation-introduction ul li:nth-child(10){
|
||||
left: 85%;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
animation-delay: 0s;
|
||||
animation-duration: 11s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@keyframes animate {
|
||||
|
||||
0%{
|
||||
transform: translateY(0) rotate(0deg);
|
||||
opacity: 1;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
100%{
|
||||
transform: translateY(-1000px) rotate(720deg);
|
||||
opacity: 0;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@media only screen and (max-width: 990px) {
|
||||
.animation-introduction ul li:nth-child(6){
|
||||
left: 67%;
|
||||
}
|
||||
.animation-introduction ul li:nth-child(10) {
|
||||
left: 60%;
|
||||
}
|
||||
.introduction {
|
||||
margin: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.introduction article {
|
||||
width: auto !important;
|
||||
transform: none !important;
|
||||
align-self: none !important;
|
||||
}
|
||||
}
|
||||
49
frontend/src/pages/home/home.tsx
Normal file
49
frontend/src/pages/home/home.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import { useEffect } from "react";
|
||||
import HeroCarousel from "../../components/hero/HeroCarousel";
|
||||
import PortfolioGrid from "../../components/portfolio/PortfolioGrid";
|
||||
import TradingGraph from "../../components/trading/TradingGraph";
|
||||
import DonationShop from "../../components/donate/DonationShop";
|
||||
import SkillsSection from "../../components/skills/SkillsSection";
|
||||
import HostingSecuritySection from "../../components/hosting/HostingSecuritySection";
|
||||
|
||||
export default function Home() {
|
||||
// Optional: keep spark effect for fun
|
||||
useEffect(() => {
|
||||
const handleClick = (event: MouseEvent) => {
|
||||
const spark = document.createElement("div");
|
||||
spark.className = "spark-cursor";
|
||||
document.body.appendChild(spark);
|
||||
spark.style.top = `${event.pageY}px`;
|
||||
spark.style.left = `${event.pageX}px`;
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const span = document.createElement("span");
|
||||
span.style.transform = `rotate(${i * 45}deg)`;
|
||||
spark.appendChild(span);
|
||||
}
|
||||
setTimeout(() => {
|
||||
spark.querySelectorAll("span").forEach((s) => {
|
||||
(s as HTMLElement).classList.add("animate");
|
||||
});
|
||||
}, 10);
|
||||
setTimeout(() => spark.remove(), 1000);
|
||||
};
|
||||
document.body.addEventListener("click", handleClick);
|
||||
return () => document.body.removeEventListener("click", handleClick);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<main>
|
||||
<HeroCarousel />
|
||||
<div className="divider" />
|
||||
<PortfolioGrid />
|
||||
<div className="divider" />
|
||||
<TradingGraph />
|
||||
<div className="divider" />
|
||||
<DonationShop />
|
||||
<div className="divider" />
|
||||
<SkillsSection />
|
||||
<div className="divider" />
|
||||
<HostingSecuritySection />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
192
frontend/src/pages/home/introduction.css
Normal file
192
frontend/src/pages/home/introduction.css
Normal file
@@ -0,0 +1,192 @@
|
||||
.introduction {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: var(--c-text);
|
||||
|
||||
padding-bottom: 10em;
|
||||
margin-top: 6em;
|
||||
|
||||
width: 100%;
|
||||
position: relative;
|
||||
top:0;
|
||||
|
||||
/* gap: 4em;*/
|
||||
}
|
||||
.introduction h1{
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.introduction article {
|
||||
/*background-color: cadetblue;*/
|
||||
|
||||
padding: 2em;
|
||||
border-radius: 1em;
|
||||
}
|
||||
|
||||
.introduction article header {}
|
||||
|
||||
.introduction article:nth-child(1) {
|
||||
width: 100%;
|
||||
/* transform: rotate(5deg); */
|
||||
align-self: center;
|
||||
text-align: center;
|
||||
font-size: 2em;
|
||||
}
|
||||
/*
|
||||
.introduction article:nth-child(2) {
|
||||
width: 50%;
|
||||
transform: rotate(3deg);
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.introduction article:nth-child(3) {
|
||||
width: 50%;
|
||||
transform: rotate(-2deg);
|
||||
align-self: flex-start;
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
|
||||
.animation-introduction{
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
z-index: -2;
|
||||
}
|
||||
|
||||
.animation-introduction ul{
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
/*overflow: hidden; ZAPNOUT KDYŽ NECHCEŠ ANIMACI PŘECHÁZET DO OSTATNÍCH DIVŮ*/
|
||||
}
|
||||
|
||||
.animation-introduction ul li{
|
||||
position: absolute;
|
||||
display: block;
|
||||
list-style: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: rgba(255, 255, 255, 35%);
|
||||
animation: animate 4s linear infinite;
|
||||
bottom: -150px;
|
||||
|
||||
}
|
||||
|
||||
.animation-introduction ul li:nth-child(1){
|
||||
left: 25%;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
animation-delay: 0s;
|
||||
}
|
||||
|
||||
|
||||
.animation-introduction ul li:nth-child(2){
|
||||
left: 10%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
animation-delay: 2s;
|
||||
animation-duration: 12s;
|
||||
}
|
||||
|
||||
.animation-introduction ul li:nth-child(3){
|
||||
left: 70%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
animation-delay: 4s;
|
||||
}
|
||||
|
||||
.animation-introduction ul li:nth-child(4){
|
||||
left: 40%;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
animation-delay: 0s;
|
||||
animation-duration: 18s;
|
||||
}
|
||||
|
||||
.animation-introduction ul li:nth-child(5){
|
||||
left: 65%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
animation-delay: 0s;
|
||||
}
|
||||
|
||||
.animation-introduction ul li:nth-child(6){
|
||||
left: 75%;
|
||||
width: 110px;
|
||||
height: 110px;
|
||||
animation-delay: 3s;
|
||||
}
|
||||
|
||||
.animation-introduction ul li:nth-child(7){
|
||||
left: 35%;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
animation-delay: 7s;
|
||||
}
|
||||
|
||||
.animation-introduction ul li:nth-child(8){
|
||||
left: 50%;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
animation-delay: 15s;
|
||||
animation-duration: 45s;
|
||||
}
|
||||
|
||||
.animation-introduction ul li:nth-child(9){
|
||||
left: 20%;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
animation-delay: 2s;
|
||||
animation-duration: 35s;
|
||||
}
|
||||
|
||||
.animation-introduction ul li:nth-child(10){
|
||||
left: 85%;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
animation-delay: 0s;
|
||||
animation-duration: 11s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@keyframes animate {
|
||||
|
||||
0%{
|
||||
transform: translateY(0) rotate(0deg);
|
||||
opacity: 1;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
100%{
|
||||
transform: translateY(-1000px) rotate(720deg);
|
||||
opacity: 0;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@media only screen and (max-width: 990px) {
|
||||
.animation-introduction ul li:nth-child(6){
|
||||
left: 67%;
|
||||
}
|
||||
.animation-introduction ul li:nth-child(10) {
|
||||
left: 60%;
|
||||
}
|
||||
.introduction {
|
||||
margin: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.introduction article {
|
||||
width: auto !important;
|
||||
transform: none !important;
|
||||
align-self: none !important;
|
||||
}
|
||||
}
|
||||
2
frontend/src/pages/home/jquery-3.7.1.js
vendored
Normal file
2
frontend/src/pages/home/jquery-3.7.1.js
vendored
Normal file
File diff suppressed because one or more lines are too long
17
frontend/src/pages/home/nav.js
Normal file
17
frontend/src/pages/home/nav.js
Normal file
@@ -0,0 +1,17 @@
|
||||
$(document).ready(function() {
|
||||
const $stickyElm = $('nav');
|
||||
const stickyOffset = $stickyElm.offset().top;
|
||||
|
||||
$(window).on('scroll', function() {
|
||||
|
||||
const isSticky = $(window).scrollTop() > stickyOffset;
|
||||
//console.log("sticky: " + isSticky);
|
||||
|
||||
$stickyElm.toggleClass('isSticky-nav', isSticky);
|
||||
});
|
||||
|
||||
$('#toggle-nav').click(function () {
|
||||
$('nav ul').toggleClass('nav-open');
|
||||
$('#toggle-nav').toggleClass('toggle-nav-rotated');
|
||||
});
|
||||
});
|
||||
4
frontend/src/pages/hosting/HostingSecurityPage.tsx
Normal file
4
frontend/src/pages/hosting/HostingSecurityPage.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
import HostingSecuritySection from "../../components/hosting/HostingSecuritySection";
|
||||
export default function HostingSecurityPage(){
|
||||
return <HostingSecuritySection />;
|
||||
}
|
||||
4
frontend/src/pages/portfolio/PortfolioPage.tsx
Normal file
4
frontend/src/pages/portfolio/PortfolioPage.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
import PortfolioGrid from "../../components/portfolio/PortfolioGrid";
|
||||
export default function PortfolioPage(){
|
||||
return <PortfolioGrid />;
|
||||
}
|
||||
4
frontend/src/pages/skills/SkillsPage.tsx
Normal file
4
frontend/src/pages/skills/SkillsPage.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
import SkillsSection from "../../components/skills/SkillsSection";
|
||||
export default function SkillsPage(){
|
||||
return <SkillsSection />;
|
||||
}
|
||||
Reference in New Issue
Block a user