Compare commits
1 commit
master
...
renovate/c
Author | SHA1 | Date | |
---|---|---|---|
e0bc3bad48 |
15 changed files with 110 additions and 186 deletions
|
@ -3,7 +3,7 @@
|
|||
"name": "Czech Quests",
|
||||
"started": "July 2024",
|
||||
"ended": "today",
|
||||
"description": "Custom project, where I make an addon, for World of Warcraft game, to show Czech translation for in-game quests. The translations are made via AI (DeepL and GPT). Texts are formated for player and quest giver gender. Also it keeps original names of other NPCs, items or places. I also create a web application where i am able to manage translations",
|
||||
"description": "Custom project, where I make an addon for World of Warcraft game, which show Czech translation for in-game quests. The translations are made via AI (DeepL and GPT). Text is formated for player and quest giver gender. Keep original names of other NPCs, items or places. I also create a web application where i am able to manage translations",
|
||||
"tags": [
|
||||
"Typescript",
|
||||
"React",
|
||||
|
@ -63,7 +63,7 @@
|
|||
"tags": [
|
||||
"Typescript",
|
||||
"React",
|
||||
"GraphQL",
|
||||
"Graphql",
|
||||
"REST",
|
||||
"Mui",
|
||||
"Azure DevOps"
|
||||
|
|
3
renovate.json
Normal file
3
renovate.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import {Kbd} from "../../components/Kbd";
|
||||
import {Card} from "../../components/Card";
|
||||
|
||||
export const AboutMe = () => {
|
||||
const age = Math.floor(
|
||||
|
@ -13,9 +13,9 @@ export const AboutMe = () => {
|
|||
<>
|
||||
<section className="leading-snug text-slate-500 dark:text-slate-400">
|
||||
<p>
|
||||
Hello, my name is Roman Jaroš. I am {age}. <b>{work} years working as software engineer.</b>
|
||||
👋, my name is Roman Jaroš. I am {age}. {work} years working as software engineer.
|
||||
I have been programming since I was 15 years old and still love to learn new technologies.
|
||||
Outside of the programming world, I maintain servers with around 80 docker containers.
|
||||
Outside of the programming world, I maintain servers with about 120 docker containers.
|
||||
Outside of the IT world, I enjoy reading self improvement books, meditating,
|
||||
alternative medicine and model painting or playing video games.
|
||||
</p>
|
||||
|
@ -24,11 +24,9 @@ export const AboutMe = () => {
|
|||
<b>I am contractor and currently only for full remote jobs.</b>
|
||||
</p>
|
||||
<br />
|
||||
<div className="flex flex-row flex-wrap gap-2">
|
||||
<Kbd>#uidesign</Kbd>
|
||||
<Kbd>#webdevelopment</Kbd>
|
||||
<Kbd>#automationtesting</Kbd>
|
||||
<Kbd>#devops</Kbd>
|
||||
<div className="flex justify-center flex-col md:flex-row gap-2">
|
||||
<Card title="Web development" description="I offer my experience in web development." />
|
||||
<Card title="UI/UX Design" description="I offer design web application in figma or penpot." />
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {GithubIcon, LinkedinIcon, MailIcon, ScrollTextIcon} from "lucide-react";
|
||||
import {GithubIcon, InfoIcon, LinkedinIcon, MailIcon} from "lucide-react";
|
||||
|
||||
export const Contact = () => {
|
||||
return (
|
||||
|
@ -8,19 +8,12 @@ export const Contact = () => {
|
|||
<a
|
||||
type="button"
|
||||
href="mailto:sales@romanjaros.dev"
|
||||
className="flex gap-2 focus:outline-none font-medium rounded-lg px-5 py-2.5 me-2 mb-2 text-yellow-600 dark:bg-transparent">
|
||||
<MailIcon/> Hire me
|
||||
</a>
|
||||
<a
|
||||
type="button"
|
||||
target="_blank"
|
||||
href="https://cloud.romanjaros.cz/s/HySLxskToRMxM6n"
|
||||
className="flex gap-2 focus:outline-none font-medium rounded-lg px-5 py-2.5 me-2 mb-2 text-pink-600 dark:bg-transparent">
|
||||
<ScrollTextIcon/> Download CV
|
||||
className="flex gap-2 focus:outline-none text-white bg-yellow-400 hover:bg-yellow-500 focus:ring-4 focus:ring-yellow-300 font-medium rounded-lg px-5 py-2.5 me-2 mb-2 dark:text-slate-600 dark:focus:ring-yellow-900">
|
||||
<MailIcon/> Send e-mail
|
||||
</a>
|
||||
</p>
|
||||
<p className="flex justify-center gap-4 my-8">
|
||||
<a href="mailto:hello@romanjaros.dev"><MailIcon/></a>
|
||||
<a href="mailto:info@romanjaros.dev"><InfoIcon/></a>
|
||||
<a
|
||||
target="_blank"
|
||||
href="http://linkedin.com/in/roman-jaroš-16a687139"
|
||||
|
|
|
@ -1,55 +1,54 @@
|
|||
"use client"
|
||||
|
||||
import {MapPinIcon, MoveRightIcon} from "lucide-react";
|
||||
import {notNil} from "../../utils/notNil";
|
||||
import {useJobs} from "../context/useJobs";
|
||||
import {notNil} from "../../utils";
|
||||
import {Job} from "../type";
|
||||
import {FC} from "react";
|
||||
|
||||
export const Jobs = () => {
|
||||
const {jobs, filter} = useJobs()
|
||||
type JobsProps = {
|
||||
jobs: Job[] | undefined
|
||||
}
|
||||
|
||||
export const Jobs: FC<JobsProps> = ({jobs}) => {
|
||||
return (
|
||||
<>
|
||||
<section className="px-4">
|
||||
<ol className="relative border-s border-gray-200 dark:border-gray-700">
|
||||
{jobs
|
||||
?.filter(notNil)
|
||||
.filter(({tags}) => filter != undefined ? tags.includes(filter) : true)
|
||||
.map(({name, started, ended, description, tags, link}, index) => (
|
||||
<li key={index} className="mb-10 ms-6">
|
||||
{jobs?.filter(notNil).map(({name, started, ended, description, tags, link}, index) => (
|
||||
<li key={index} className="mb-10 ms-6">
|
||||
<span
|
||||
className="absolute flex items-center justify-center w-6 h-6 bg-white rounded-full -start-3 ring-8 ring-white dark:ring-slate-800 dark:bg-slate-800">
|
||||
<MapPinIcon className="text-black dark:text-white"/>
|
||||
</span>
|
||||
<h3 className="flex items-center gap-2 mb-1 text-lg font-semibold text-gray-900 dark:text-white">
|
||||
{name}
|
||||
</h3>
|
||||
<time className="block mb-2 text-sm font-normal leading-none text-gray-400 dark:text-gray-500">
|
||||
{started} - {ended}
|
||||
</time>
|
||||
<p className="mb-4 text-base font-normal text-gray-500 dark:text-gray-400">
|
||||
{description}
|
||||
<h3 className="flex items-center gap-2 mb-1 text-lg font-semibold text-gray-900 dark:text-white">
|
||||
{name}
|
||||
</h3>
|
||||
<time className="block mb-2 text-sm font-normal leading-none text-gray-400 dark:text-gray-500">
|
||||
{started} - {ended}
|
||||
</time>
|
||||
<p className="mb-4 text-base font-normal text-gray-500 dark:text-gray-400">
|
||||
{description}
|
||||
</p>
|
||||
{link != undefined &&
|
||||
<p>
|
||||
<a href={link}
|
||||
target="_blank"
|
||||
className="inline-flex gap-2 mb-4 items-center px-4 py-2 text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-lg hover:bg-gray-100 hover:text-blue-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700">
|
||||
Visit website
|
||||
<MoveRightIcon/>
|
||||
</a>
|
||||
</p>
|
||||
{link != undefined &&
|
||||
<p>
|
||||
<a href={link}
|
||||
target="_blank"
|
||||
className="inline-flex gap-2 mb-4 items-center px-4 py-2 text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-lg hover:bg-gray-100 hover:text-blue-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700">
|
||||
Visit website
|
||||
<MoveRightIcon/>
|
||||
</a>
|
||||
</p>
|
||||
}
|
||||
<p className="flex flex-wrap gap-2">
|
||||
{tags.sort().map((name) => (
|
||||
<span
|
||||
key={name}
|
||||
className="bg-purple-100 text-purple-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded-full dark:bg-purple-900 dark:text-purple-300"
|
||||
>
|
||||
}
|
||||
<p className="flex flex-wrap gap-2">
|
||||
{tags.sort().map((name) => (
|
||||
<span
|
||||
key={name}
|
||||
className="bg-purple-100 text-purple-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded-full dark:bg-purple-900 dark:text-purple-300"
|
||||
>
|
||||
{name}
|
||||
</span>
|
||||
))}
|
||||
</p>
|
||||
</li>
|
||||
))}
|
||||
))}
|
||||
</p>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
</section>
|
||||
</>
|
||||
|
|
|
@ -1,53 +1,40 @@
|
|||
"use client"
|
||||
|
||||
import {notNil} from "../../utils/notNil";
|
||||
import {notNil} from "../../utils";
|
||||
import {StarIcon} from "lucide-react";
|
||||
import {FC} from "react";
|
||||
import {useJobs} from "../context/useJobs";
|
||||
import {Job} from "../type";
|
||||
|
||||
export const Skills: FC = () => {
|
||||
const {jobs, filterJobs, filter} = useJobs()
|
||||
const skills = new Set(jobs.filter(notNil).flatMap(({tags}) => tags).sort());
|
||||
type SkillsProps = {
|
||||
jobs: Job[] | undefined
|
||||
}
|
||||
|
||||
export const Skills: FC<SkillsProps> = ({jobs}) => {
|
||||
|
||||
const skills = new Set(jobs?.filter(notNil).flatMap(({tags}) => tags).sort());
|
||||
|
||||
return (
|
||||
<>
|
||||
<section>
|
||||
<div className="flex items-center my-2">
|
||||
<p className="mr-2 w-24 ms-1 text-sm font-medium text-gray-500 dark:text-gray-400">Czech, native</p>
|
||||
<StarIcon fill="#fde047" className="w-4 h-4 ms-1 text-yellow-300"/>
|
||||
<StarIcon fill="#fde047" className="w-4 h-4 ms-1 text-yellow-300"/>
|
||||
<StarIcon fill="#fde047" className="w-4 h-4 ms-1 text-yellow-300"/>
|
||||
<p className="mr-2 w-12 ms-1 text-sm font-medium text-gray-500 dark:text-gray-400">Czech</p>
|
||||
<StarIcon fill="#fde047" className="w-4 h-4 ms-1 text-yellow-300"/>
|
||||
<StarIcon fill="#fde047" className="w-4 h-4 ms-1 text-yellow-300"/>
|
||||
<StarIcon fill="#fde047" className="w-4 h-4 ms-1 text-yellow-300"/>
|
||||
</div>
|
||||
<div className="flex items-center my-2">
|
||||
<p className="mr-2 w-24 ms-1 text-sm font-medium text-gray-500 dark:text-gray-400">English, B1</p>
|
||||
<p className="mr-2 w-12 ms-1 text-sm font-medium text-gray-500 dark:text-gray-400">English </p>
|
||||
<StarIcon fill="#fde047" className="w-4 h-4 ms-1 text-yellow-300"/>
|
||||
<StarIcon fill="#fde047" className="w-4 h-4 ms-1 text-yellow-300"/>
|
||||
<StarIcon fill="#fde047" className="w-4 h-4 ms-1 text-yellow-300"/>
|
||||
<StarIcon className="w-4 h-4 ms-1 text-gray-300 dark:text-gray-500"/>
|
||||
<StarIcon className="w-4 h-4 ms-1 text-gray-300 dark:text-gray-500"/>
|
||||
<StarIcon className="w-4 h-4 ms-1 text-gray-300 dark:text-gray-500"/>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{Array.from(skills)?.map((name) => (
|
||||
<span
|
||||
key={name}
|
||||
onClick={() => filterJobs(name)}
|
||||
className={[
|
||||
"cursor-pointer text-xs font-medium me-2 px-2.5 py-0.5 rounded-full select-none",
|
||||
filter === name
|
||||
? "bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300"
|
||||
: "bg-indigo-100 text-indigo-800 dark:bg-indigo-900 dark:text-indigo-300"
|
||||
].join(" ")}>
|
||||
{name}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<p className="mt-5 text-xs text-gray-400">Psss, you can click on pill to filter.</p>
|
||||
<section className="flex flex-wrap gap-2">
|
||||
{Array.from(skills)?.map((name) => (
|
||||
<span
|
||||
key={name}
|
||||
className="bg-indigo-100 text-indigo-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded-full dark:bg-indigo-900 dark:text-indigo-300">
|
||||
{name}
|
||||
</span>
|
||||
))}
|
||||
</section>
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
export const Title = () => {
|
||||
return (
|
||||
<div className="flex justify-center text-4xl sm:text-6xl my-8 text-slate-400">
|
||||
<div className="flex justify-center text-4xl sm:text-6xl my-8">
|
||||
<span className="text-gray-600 dark:text-white"><</span>
|
||||
<h1>
|
||||
Roman Jaroš
|
||||
<span className="text-gray-600 dark:text-white">Just</span>
|
||||
<span className="text-orange-800 dark:text-orange-400">Roman</span>
|
||||
</h1>
|
||||
<span className="text-orange-800 dark:text-orange-400"> /></span>
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
"use client"
|
||||
|
||||
import {createContext, ReactNode, FC, useState, Dispatch, SetStateAction} from "react";
|
||||
import {Job} from "../type";
|
||||
|
||||
type JobsContextPayload = {
|
||||
data: Job[]
|
||||
filter?: string
|
||||
setFilter: Dispatch<SetStateAction<string | undefined>>
|
||||
} | undefined
|
||||
|
||||
export const JobsContext = createContext<JobsContextPayload>(undefined);
|
||||
|
||||
type JobsContextProviderProps = {
|
||||
jobs: Job[]
|
||||
children: ReactNode[]
|
||||
}
|
||||
|
||||
export const JobsProvider: FC<JobsContextProviderProps> = ({children, jobs: data}) => {
|
||||
const [filter, setFilter] = useState<string>()
|
||||
|
||||
return (
|
||||
<JobsContext value={{data, filter, setFilter}}>{children}</JobsContext>
|
||||
)
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
"use client"
|
||||
|
||||
import {useContext} from "react";
|
||||
import {JobsContext} from "./JobsContext";
|
||||
|
||||
export const useJobs = () => {
|
||||
const context = useContext(JobsContext)
|
||||
if (!context) {
|
||||
throw new Error('useJobs must be used within a JobsProvider!');
|
||||
}
|
||||
|
||||
const handleFilterJob = (name: string) => {
|
||||
if (name === context.filter) {
|
||||
context.setFilter(undefined)
|
||||
} else {
|
||||
context.setFilter(name)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
jobs: context.data,
|
||||
filter: context?.filter,
|
||||
filterJobs: handleFilterJob,
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
import {Job} from "./type";
|
||||
|
||||
export const fetchJobs = async (): Promise<Job[] | undefined> => {
|
||||
const res = await fetch(`${process.env.NEXT_PUBLIC_SITE_URL}/jobs.json`, {cache: "no-store"})
|
||||
return await res?.json();
|
||||
}
|
|
@ -8,16 +8,13 @@ export default function AppLayout({
|
|||
}) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charSet="utf-8"/>
|
||||
<title>Roman Jaroš</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<script defer src="https://wa.romanjaros.dev/script.js"
|
||||
data-website-id="27410757-9ce4-4dad-a0ad-a1e2a0303495"></script>
|
||||
</head>
|
||||
<body className="bg-white dark:bg-slate-800">
|
||||
<main>{children}</main>
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
<head>
|
||||
<meta charSet="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
<body className="bg-white dark:bg-slate-800">
|
||||
<main>{children}</main>
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
|
@ -4,24 +4,21 @@ import {Contact} from "./components/Contact";
|
|||
import {Footer} from "./components/Footer";
|
||||
import {Jobs} from "./components/Jobs";
|
||||
import {Skills} from "./components/Skills";
|
||||
import {fetchJobs} from "./fetch";
|
||||
import {JobsProvider} from "./context/JobsContext";
|
||||
import {use} from "react";
|
||||
import {Job} from "./type";
|
||||
|
||||
export default function Page() {
|
||||
export default async function Page() {
|
||||
|
||||
const jobs = use(fetchJobs())
|
||||
const res = await fetch(`${process.env.NEXT_PUBLIC_SITE_URL}/jobs.json`, {cache: "no-store"})
|
||||
const data: Job[] | undefined = await res?.json();
|
||||
|
||||
return (
|
||||
<>
|
||||
<div style={{maxWidth: 1000}} className="flex flex-col gap-y-16 md:w-4/5 mx-auto p-4 mb-20">
|
||||
<div className="flex flex-col gap-y-16 md:w-3/4 lg:w-2/3 mx-auto p-4 mb-20">
|
||||
<Title />
|
||||
<AboutMe />
|
||||
<Contact />
|
||||
<JobsProvider jobs={jobs ?? []}>
|
||||
<Skills />
|
||||
<Jobs />
|
||||
</JobsProvider>
|
||||
<Skills jobs={data} />
|
||||
<Jobs jobs={data} />
|
||||
<Footer />
|
||||
</div>
|
||||
</>
|
||||
|
|
17
src/components/Card.tsx
Normal file
17
src/components/Card.tsx
Normal file
|
@ -0,0 +1,17 @@
|
|||
import {FC} from "react";
|
||||
|
||||
type CardProps = {
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export const Card: FC<CardProps> = ({title, description}) => {
|
||||
return (
|
||||
<div
|
||||
className="w-full md:w-80 block p-6 bg-white border border-gray-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700"
|
||||
>
|
||||
<h5 className="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white">{title}</h5>
|
||||
<p className="font-normal text-gray-700 dark:text-gray-400">{description}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
import {FC} from "react";
|
||||
|
||||
type KbdProps = {
|
||||
children: string
|
||||
}
|
||||
|
||||
export const Kbd: FC<KbdProps> = ({children}) => {
|
||||
return (
|
||||
<kbd
|
||||
className="px-2 py-1.5 text-xs font-semibold text-gray-800 bg-gray-100 border border-gray-200 rounded-lg dark:bg-gray-600 dark:text-gray-100 dark:border-gray-500">
|
||||
{children}
|
||||
</kbd>
|
||||
)
|
||||
}
|
Loading…
Add table
Reference in a new issue