- Update project

* - update port for developmnet mode

* - fix style for source code

* - remove default import - defineProps

* - add robots.txt

* - update vite config

* - prepared .env files - production, development

* - update pages

* - fix development port

* - update skills

* - update body style

* - update links

* - update index header
This commit is contained in:
Kamil Niemczycki 2023-07-30 13:33:56 +02:00
parent cd66fd6ffa
commit 2c463543b7
Signed by: kamilniemczycki
GPG Key ID: 04D4E2012F969213
19 changed files with 205 additions and 129 deletions

4
.env.development Normal file
View File

@ -0,0 +1,4 @@
VITE_APP_NAME="Kamil Niemczycki CV"
VITE_CV_URL="http://localhost:5173"
VITE_API_URL="http://localhost"
VITE_SOURCE_CODE="https://github.com/kamilniemczycki/kamilcraft-cv"

View File

@ -1,4 +1,5 @@
VITE_APP_NAME="Kamil Niemczycki CV" VITE_APP_NAME="Kamil Niemczycki CV"
VITE_CV_URL="http://localhost:5173" VITE_CV_URL="http://localhost:5173"
VITE_PORT=5173
VITE_API_URL="http://localhost" VITE_API_URL="http://localhost"
VITE_SOURCE_CODE="https://github.com/kamilniemczycki" VITE_SOURCE_CODE="https://github.com/kamilniemczycki"

View File

@ -6,8 +6,14 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" />
<title>Kamil Niemczycki CV</title> <title>Kamil Niemczycki CV</title>
<meta property="og:site_name" content="cv.kamilcraft.com" />
<meta property="og:title" content="Kamil Niemczycki CV" />
<meta property="og:type" content="website" />
<meta property="og:image" content="https://cv.kamilcraft.com/me.webp" />
<meta property="og:description" content="Ambitny i nastawiony na cel, gotowy do podjęcia wyzwań absolwent informatyki, który szuka pierwszej pracy jako inżynier oprogramowania." />
<meta name="description" content="Ambitny i nastawiony na cel, gotowy do podjęcia wyzwań absolwent informatyki, który szuka pierwszej pracy jako inżynier oprogramowania." />
</head> </head>
<body> <body class="min-h-screen">
<div id="cv"></div> <div id="cv"></div>
<script type="module" src="/src/main.js"></script> <script type="module" src="/src/main.js"></script>
</body> </body>

2
public/robots.txt Normal file
View File

@ -0,0 +1,2 @@
User-agent: *
Allow: /

View File

@ -1,4 +1,11 @@
<script setup> <script setup>
import { computed } from 'vue';
import { useRoute } from 'vue-router';
const route = useRoute();
const isErrorPage = computed(() => route.name === 'NotFound');
function print() { function print() {
window.print(); window.print();
} }
@ -17,10 +24,10 @@ function print() {
</style> </style>
<template> <template>
<div class="print-section"> <div v-if="!isErrorPage" class="print-section">
<button class="px-4 py-2 w-full sm:w-80 font-semibold text-sm bg-orange-300 text-orange-700 rounded-full shadow-sm" @click="print">Drukuj CV</button> <button class="px-4 py-2 w-full sm:w-80 font-semibold text-sm bg-orange-300 text-orange-700 rounded-full shadow-sm" @click="print">Drukuj CV</button>
</div> </div>
<main class="main bg-blob"> <main class="main bg-white">
<router-view /> <router-view />
</main> </main>
</template> </template>

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { defineProps, computed, ref } from 'vue'; import { computed, ref } from 'vue';
import Education from './Body/Education.vue'; import Education from './Body/Education.vue';
import MajorAchivments from './Body/MajorAchivments.vue'; import MajorAchivments from './Body/MajorAchivments.vue';
import Skills from './Side/Skills.vue'; import Skills from './Side/Skills.vue';
@ -8,16 +8,16 @@ import Links from './Side/Links.vue';
const props = defineProps({ const props = defineProps({
token: { token: {
type: String,
default: null, default: null,
type: [String, null],
}, },
rodo: { rodo: {
type: String,
default: null, default: null,
type: [String, null],
}, },
loading: { loading: {
required: true,
type: Boolean, type: Boolean,
default: false,
}, },
}); });
@ -33,7 +33,7 @@ const qrCodeLink = computed(() => {
</script> </script>
<template> <template>
<div class="flex flex-col-reverse print:flex-row print:gap-2 md:flex-row md:gap-2"> <div class="flex flex-col-reverse print:flex-row print:gap-1 md:flex-row md:gap-1">
<div class="relative print:w-2/3 print:flex-shrink-0 md:w-2/3 pb-3 md:flex-shrink-0 pt-1 px-4"> <div class="relative print:w-2/3 print:flex-shrink-0 md:w-2/3 pb-3 md:flex-shrink-0 pt-1 px-4">
<MajorAchivments /> <MajorAchivments />
<Education /> <Education />
@ -59,11 +59,11 @@ const qrCodeLink = computed(() => {
<Skills /> <Skills />
<Certificates /> <Certificates />
<Links class="pb-8 md:pb-0" /> <Links class="pb-8 md:pb-0" />
<div class="hidden print:block print:absolute bottom-7 right-0 w-full"> <div class="hidden print:block print:absolute bottom-9 right-0 w-full">
<a :href="qrCodeLink" title="Link do CV w wersji przeglądarkowej" target="_blink"> <a :href="qrCodeLink" title="Link do CV w wersji przeglądarkowej" target="_blink">
<QRCode <QRCode
render-as="svg" render-as="svg"
class="mx-auto mb-2" class="mx-auto mb-1"
:value="qrCodeLink" :value="qrCodeLink"
:size="100" :size="100"
level="L" level="L"
@ -71,7 +71,7 @@ const qrCodeLink = computed(() => {
</a> </a>
<div class="text-center">CV w wersji online</div> <div class="text-center">CV w wersji online</div>
</div> </div>
<div class="absolute bottom-1.5 right-0 w-full px-5 text-right md:text-center text-xs"> <div class="absolute bottom-1.5 right-0 w-full px-5 text-center text-xs">
<a :href="sourceCode" target="_blank"><FontAwesomeIcon class="mr-1" :icon="['fab', 'github']"/>Kod źródłowy CV</a> <a :href="sourceCode" target="_blank"><FontAwesomeIcon class="mr-1" :icon="['fab', 'github']"/>Kod źródłowy CV</a>
</div> </div>
</div> </div>

View File

@ -1,5 +1,4 @@
<script setup> <script setup>
import { defineProps } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import Mission from './Header/Mission.vue'; import Mission from './Header/Mission.vue';
import ContactList from './Header/ContactList.vue'; import ContactList from './Header/ContactList.vue';
@ -7,21 +6,35 @@ import ContactList from './Header/ContactList.vue';
defineProps({ defineProps({
loading: { loading: {
tyle: Boolean, tyle: Boolean,
default: false,
}, },
email: { email: {
type: String, type: String,
default: 'contact@kamilcraft.com',
}, },
tel: { tel: {
type: Object, type: Object,
default: {
hasPhoneNumber: false,
phoneNumber: '',
formattedPhoneNumber: '',
},
}, },
locations: { locations: {
type: Array, type: Array,
default: [
'Wrocław',
'Legnica',
'Remote',
],
}, },
position: { position: {
type: [String, null], type: String,
default: null,
}, },
mission: { mission: {
type: Array, type: Array,
default: [],
}, },
}); });
</script> </script>
@ -51,7 +64,7 @@ defineProps({
</style> </style>
<template> <template>
<div class="flex justify-between mx-1 mt-1 print:border-b-2 md:border-b-2"> <div class="bg-blob flex justify-between mx-1 mt-1 print:border-b-2 md:border-b-2">
<div class="flex-shrink-0 w-32 h-32"> <div class="flex-shrink-0 w-32 h-32">
<img class="w-full h-full" alt="Profilowe" src="/me.webp" /> <img class="w-full h-full" alt="Profilowe" src="/me.webp" />
</div> </div>

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { defineProps, computed } from 'vue'; import { computed } from 'vue';
const props = defineProps({ const props = defineProps({
loading: { loading: {

View File

@ -1,6 +1,4 @@
<script setup> <script setup>
import { defineProps } from 'vue';
defineProps({ defineProps({
loading: { loading: {
type: Boolean, type: Boolean,

View File

@ -36,7 +36,7 @@ const otherSkills = ref(importOtherSkills);
<template> <template>
<div class="px-4 py-3"> <div class="px-4 py-3">
<h2 class="text-xl text-[#E57D4C] pb-2">Główne umiejętności</h2> <h2 class="text-xl text-[#E57D4C] pb-2">Główne umiejętności</h2>
<ul class="flex flex-row flex-wrap justify-center align-baseline gap-4 text-xl"> <ul class="grid grid-cols-4 justify-between align-baseline gap-2 text-xl">
<li <li
v-for="(skill, key) in skills" v-for="(skill, key) in skills"
:key="key" :key="key"
@ -48,7 +48,7 @@ const otherSkills = ref(importOtherSkills);
</ul> </ul>
<div class="mt-3"> <div class="mt-3">
<h3 class="text-lg text-[#E57D4C] pb-1">Dodatkowo</h3> <h3 class="text-lg text-[#E57D4C] pb-1">Dodatkowo</h3>
<ul class="flex flex-row flex-wrap align-baseline gap-1"> <ul class="flex flex-row flex-wrap align-baseline gap-0.5">
<li v-for="(skill, key) in otherSkills" :key="key" class="border border-slate-200 rounded-md px-1.5 py-0.5 bg-white">{{ skill }}</li> <li v-for="(skill, key) in otherSkills" :key="key" class="border border-slate-200 rounded-md px-1.5 py-0.5 bg-white">{{ skill }}</li>
</ul> </ul>
</div> </div>

View File

@ -13,6 +13,13 @@ const links = [
title: 'Mój LinkedIn', title: 'Mój LinkedIn',
text: '/in/kamilniemczycki', text: '/in/kamilniemczycki',
}, },
{
slug: 'gitea',
faIcon: ['fas', 'arrow-up-right-from-square'],
url: 'https://git.kamilcraft.com/kamilniemczycki',
title: 'Prywatny serwer git',
text: 'git.kamilcraft.com',
},
{ {
slug: 'github', slug: 'github',
faIcon: ['fab', 'github'], faIcon: ['fab', 'github'],

View File

@ -1,52 +1,52 @@
const skills = [ const skills = [
{
class: 'linux',
faIcon: ['fab', 'linux'],
title: 'Ikona Linux',
text: 'GNU/Linux',
},
{
class: 'git',
faIcon: ['fab', 'git'],
title: 'Ikona Git',
text: 'Git',
},
{ {
class: 'php', class: 'php',
faIcon: ['fab', 'php'], faIcon: ['fab', 'php'],
title: 'PHP Icon', title: 'Ikona PHP',
text: 'PHP 7/8', text: 'PHP 7/8',
}, },
{ {
class: 'laravel', class: 'laravel',
faIcon: ['fab', 'laravel'], faIcon: ['fab', 'laravel'],
title: 'Laravel Icon', title: 'Ikona Laravel',
text: 'Laravel', text: 'Laravel',
}, },
{ {
class: 'js', class: 'js',
faIcon: ['fab', 'js'], faIcon: ['fab', 'js'],
title: 'JavaScript Icon', title: 'Ikona JavaScript',
text: 'JavaScript', text: 'JavaScript',
}, },
{ {
class: 'vuejs', class: 'vuejs',
faIcon: ['fab', 'vuejs'], faIcon: ['fab', 'vuejs'],
title: 'Vue.js Icon', title: 'Ikona Vue.js',
text: 'Vue.js', text: 'Vue.js',
}, },
{ {
class: 'html5', class: 'html5',
faIcon: ['fab', 'html5'], faIcon: ['fab', 'html5'],
title: 'HTML 5 Icon', title: 'Ikona HTML 5',
text: 'HTML', text: 'HTML',
}, },
{ {
class: 'css3-alt', class: 'css3-alt',
faIcon: ['fab', 'css3-alt'], faIcon: ['fab', 'css3-alt'],
title: 'CSS 3 Icon', title: 'Ikona CSS 3',
text: 'CSS', text: 'CSS',
}, },
{
class: 'git',
faIcon: ['fab', 'git'],
title: 'Git Icon',
text: 'Git',
},
{
class: 'linux',
faIcon: ['fab', 'linux'],
title: 'Linux Icon',
text: 'GNU/Linux',
},
]; ];
const otherSkills = [ const otherSkills = [
@ -63,6 +63,7 @@ const otherSkills = [
'WSL', 'WSL',
'LXC/LXD', 'LXC/LXD',
'REST', 'REST',
'RWD',
'i więcej...', 'i więcej...',
]; ];

View File

@ -1,16 +1,26 @@
import { createRouter, createWebHistory } from 'vue-router'; import { createRouter, createWebHistory } from 'vue-router';
import Home from './views/Home.vue';
const Home = () => import('./views/Home.vue');
const Show = () => import('./views/Show.vue');
const NotFound = () => import('./views/NotFound.vue');
export default createRouter({ export default createRouter({
history: createWebHistory(), history: createWebHistory(),
routes: [ routes: [
{ {
path: '/', path: '/',
name: 'Home',
component: Home, component: Home,
}, },
{ {
path: '/show/:token', path: '/show/:token',
component: Home, name: 'Show',
} component: Show,
},
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: NotFound
},
], ],
}); });

View File

@ -13,7 +13,6 @@
.bg-blob { .bg-blob {
background-image: var(--theme-bg-blob); background-image: var(--theme-bg-blob);
background-color: white;
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: 170px; background-size: 170px;
background-position: -25px -22px; background-position: -25px -22px;

View File

@ -1,87 +1,12 @@
<script setup> <script setup>
import { onMounted, ref, reactive } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import Header from '../components/Header.vue'; import Header from '../components/Header.vue';
import Body from '../components/Body.vue'; import Body from '../components/Body.vue';
const loading = ref(false);
const token = ref(null);
const email = ref('contact@kamilcraft.com');
const tel = reactive({
hasPhoneNumber: false,
phoneNumber: '',
formattedPhoneNumber: '',
});
const locations = reactive([
'Wrocław',
'Legnica',
'Remote',
]);
const position = ref(null);
const mission = reactive([]);
const rodo = ref(null);
onMounted(() => {
const router = useRoute();
token.value = router.params['token'] ?? null;
if (token.value) {
loading.value = true;
const apiUrl = import.meta.env.VITE_API_URL;
fetch(`${apiUrl}/v1/cv/${token.value}`, {
method: 'GET',
cache: "no-cache",
credentials: "same-origin",
headers: {
"Content-Type": "application/json",
}
}).then(response => (response.json().then(data => ({ status: response.status, data }))))
.then(response => {
setTimeout(() => showData(response), 1000);
})
.catch(() => {
loading.value = false;
});
}
});
function showData(response) {
if (response.status === 200) {
const data = response.data;
email.value = data.email;
tel.hasPhoneNumber = true;
tel.phoneNumber = data.phone.number;
tel.formattedPhoneNumber = data.phone.formattedNumber;
while(locations.length > 0) {
locations.pop();
}
if (data.position) {
position.value = data.position;
}
data.locations?.forEach(value => {
locations.push(value);
});
data.mission?.forEach(value => {
mission.push(value);
});
if (data.rodo) {
rodo.value = data.rodo;
}
loading.value = false;
}
}
</script> </script>
<template> <template>
<div class="grid grid-rows-[max-content_max-content_auto] h-full"> <div class="grid grid-rows-[max-content_max-content_auto] h-full">
<Header :loading="loading" <Header />
:email="email" <Body />
:tel="tel"
:locations="locations"
:position="position"
:mission="mission" />
<Body :token="token" :rodo="rodo" :loading="loading" />
</div> </div>
</template> </template>

14
src/views/NotFound.vue Normal file
View File

@ -0,0 +1,14 @@
<script setup>
import { useRoute } from 'vue-router';
import Header from '../components/Header.vue';
import Body from '../components/Body.vue';
</script>
<template>
<div class="grid grid-rows-[max-content_max-content_auto] h-full px-5 py-4">
<header>
<h1 class="text-3xl mb-4">Błąd 404</h1>
</header>
<p>Strona nie została znaleziona.</p>
</div>
</template>

87
src/views/Show.vue Normal file
View File

@ -0,0 +1,87 @@
<script setup>
import { onMounted, ref, reactive } from 'vue';
import { useRoute } from 'vue-router';
import Header from '../components/Header.vue';
import Body from '../components/Body.vue';
const loading = ref(false);
const token = ref(null);
const email = ref('contact@kamilcraft.com');
const tel = reactive({
hasPhoneNumber: false,
phoneNumber: '',
formattedPhoneNumber: '',
});
const locations = reactive([
'Wrocław',
'Legnica',
'Remote',
]);
const position = ref(null);
const mission = reactive([]);
const rodo = ref(null);
onMounted(() => {
const router = useRoute();
token.value = router.params['token'] ?? null;
if (token.value) {
loading.value = true;
const apiUrl = import.meta.env.VITE_API_URL;
fetch(`${apiUrl}/v1/cv/${token.value}`, {
method: 'GET',
cache: "no-cache",
credentials: "same-origin",
headers: {
"Content-Type": "application/json",
}
}).then(response => (response.json().then(data => ({ status: response.status, data }))))
.then(response => {
setTimeout(() => showData(response), 1000);
})
.catch(() => {
loading.value = false;
});
}
});
function showData(response) {
if (response.status === 200) {
const data = response.data;
email.value = data.email;
tel.hasPhoneNumber = true;
tel.phoneNumber = data.phone.number;
tel.formattedPhoneNumber = data.phone.formattedNumber;
while(locations.length > 0) {
locations.pop();
}
if (data.position) {
position.value = data.position;
}
data.locations?.forEach(value => {
locations.push(value);
});
data.mission?.forEach(value => {
mission.push(value);
});
if (data.rodo) {
rodo.value = data.rodo;
}
loading.value = false;
}
}
</script>
<template>
<div class="grid grid-rows-[max-content_max-content_auto] h-full">
<Header :loading="loading"
:email="email"
:tel="tel"
:locations="locations"
:position="position"
:mission="mission" />
<Body :token="token" :rodo="rodo" :loading="loading" />
</div>
</template>

View File

@ -1,13 +1,15 @@
import { defineConfig, splitVendorChunkPlugin } from 'vite' import { defineConfig, loadEnv, splitVendorChunkPlugin } from 'vite';
import vue from '@vitejs/plugin-vue' import vue from '@vitejs/plugin-vue';
// https://vitejs.dev/config/ export default defineConfig((mode) => {
export default defineConfig({ const env = loadEnv(mode, process.cwd(), "");
return {
server: {
port: parseInt(env.VITE_PORT ?? 5173),
},
plugins: [ vue(), splitVendorChunkPlugin() ], plugins: [ vue(), splitVendorChunkPlugin() ],
resolve: { resolve: {
vue: 'vue/dist/vue.esm-bundler.js', vue: 'vue/dist/vue.esm-bundler.js',
}, },
build: { };
chunkSizeWarningLimit: 2048,
},
}); });