Merge pull request #530 from m-tartari/feat/business-card
✨ feat(card): add Business Card page
This commit is contained in:
@@ -1 +1,7 @@
|
||||
VITE_GA_MEASUREMENT_ID=G-XXXXXXXXXX
|
||||
VITE_BC_NAME=XXX XXXXXXX
|
||||
VITE_BC_POSITION=XXXXXXXXXX
|
||||
VITE_BC_VAT=ITXXXXXXXXXX
|
||||
VITE_BC_PHONE=+39XXXXXXXXXX
|
||||
VITE_BC_EMAIL=XXXX@XXXX.XX
|
||||
VITE_BC_LINKEDIN=XXXXXXXXXX
|
||||
|
||||
@@ -37,8 +37,27 @@ jobs:
|
||||
node-version: 'lts/*'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Set VITE_GA_MEASUREMENT_ID
|
||||
run: echo "VITE_GA_MEASUREMENT_ID=${{ secrets.VITE_GA_MEASUREMENT_ID }}" > .env
|
||||
- name: Set environment variables
|
||||
run: |
|
||||
echo "VITE_GA_MEASUREMENT_ID=${{ secrets.VITE_GA_MEASUREMENT_ID }}" > .env
|
||||
if [[ -n "${{ vars.VITE_BC_NAME }}" ]]; then
|
||||
echo "VITE_BC_NAME=${{ vars.VITE_BC_NAME }}" >> .env
|
||||
fi
|
||||
if [[ -n "${{ vars.VITE_BC_POSITION }}" ]]; then
|
||||
echo "VITE_BC_POSITION=${{ vars.VITE_BC_POSITION }}" >> .env
|
||||
fi
|
||||
if [[ -n "${{ vars.VITE_BC_VAT }}" ]]; then
|
||||
echo "VITE_BC_VAT=${{ vars.VITE_BC_VAT }}" >> .env
|
||||
fi
|
||||
if [[ -n "${{ vars.VITE_BC_LINKEDIN }}" ]]; then
|
||||
echo "VITE_BC_LINKEDIN=${{ vars.VITE_BC_LINKEDIN }}" >> .env
|
||||
fi
|
||||
if [[ -n "${{ secrets.VITE_BC_PHONE }}" ]]; then
|
||||
echo "VITE_BC_PHONE=${{ secrets.VITE_BC_PHONE }}" >> .env
|
||||
fi
|
||||
if [[ -n "${{ secrets.VITE_BC_EMAIL }}" ]]; then
|
||||
echo "VITE_BC_EMAIL=${{ secrets.VITE_BC_EMAIL }}" >> .env
|
||||
fi
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci --omit=dev
|
||||
|
||||
@@ -0,0 +1,351 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" width="792" height="792" viewBox="0 0 792 792">
|
||||
<style>
|
||||
#body { fill: #e44c65; }
|
||||
#bg { fill: #292323; }
|
||||
#eye { fill: #292323; }
|
||||
#opaque_bg { opacity: 1; fill: #9c9999; }
|
||||
#opaque_body { opacity: 1; fill: #71706e; }
|
||||
#qr { fill: #000000; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
#bg { fill: #ffffff; }
|
||||
#eye { fill: #ffffff; }
|
||||
#opaque_bg { opacity: 0.15; fill: #ffffff; }
|
||||
#opaque_body { opacity: 0.3; fill: #ffffff; }
|
||||
#qr { fill: #ffffff; }
|
||||
}
|
||||
</style>
|
||||
<g id="qr">
|
||||
<circle cx="3" cy="3" r="3" transform="translate(240,48) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(336,48) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(360,48) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(384,48) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(432,48) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(504,48) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(528,48) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(240,72) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(312,72) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(336,72) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(432,72) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(456,72) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(528,72) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(240,96) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(336,96) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(384,96) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(408,96) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(528,96) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(288,120) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(336,120) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(360,120) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(408,120) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(432,120) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(456,120) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(480,120) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(528,120) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(312,144) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(336,144) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(360,144) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(384,144) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(408,144) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(432,144) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(480,144) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(504,144) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(528,144) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(240,168) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(336,168) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(360,168) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(408,168) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(432,168) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(528,168) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(240,192) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(288,192) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(336,192) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(384,192) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(432,192) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(480,192) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(528,192) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(240,216) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(336,216) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(384,216) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(408,216) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(432,216) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(504,216) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(528,216) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(96,240) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(120,240) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(144,240) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(192,240) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(240,240) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(264,240) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(288,240) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(312,240) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(480,240) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(552,240) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(576,240) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(600,240) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(672,240) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(696,240) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(720,240) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(120,264) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(216,264) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(240,264) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(264,264) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(360,264) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(480,264) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(504,264) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(528,264) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(552,264) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(576,264) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(600,264) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(624,264) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(648,264) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(672,264) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(720,264) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(72,288) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(96,288) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(144,288) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(192,288) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(216,288) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(240,288) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(264,288) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(552,288) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(576,288) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(624,288) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(648,288) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(48,312) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(72,312) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(120,312) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(240,312) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(264,312) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(504,312) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(528,312) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(552,312) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(576,312) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(600,312) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(624,312) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(648,312) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(696,312) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(120,336) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(144,336) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(168,336) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(192,336) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(216,336) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(264,336) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(504,336) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(528,336) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(552,336) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(600,336) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(648,336) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(672,336) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(696,336) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(720,336) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(96,360) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(144,360) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(216,360) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(240,360) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(264,360) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(504,360) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(528,360) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(552,360) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(576,360) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(624,360) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(648,360) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(696,360) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(720,360) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(72,384) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(144,384) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(192,384) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(216,384) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(240,384) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(264,384) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(528,384) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(552,384) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(600,384) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(696,384) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(72,408) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(120,408) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(144,408) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(216,408) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(264,408) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(504,408) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(528,408) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(624,408) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(648,408) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(48,432) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(120,432) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(144,432) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(192,432) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(216,432) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(600,432) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(648,432) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(672,432) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(720,432) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(48,456) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(72,456) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(120,456) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(240,456) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(264,456) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(504,456) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(576,456) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(600,456) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(624,456) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(648,456) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(696,456) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(720,456) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(48,480) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(96,480) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(144,480) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(168,480) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(192,480) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(216,480) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(240,480) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(528,480) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(576,480) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(48,504) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(96,504) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(120,504) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(144,504) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(168,504) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(240,504) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(264,504) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(312,504) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(360,504) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(384,504) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(432,504) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(480,504) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(576,504) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(624,504) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(648,504) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(696,504) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(48,528) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(120,528) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(144,528) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(192,528) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(240,528) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(264,528) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(312,528) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(408,528) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(480,528) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(504,528) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(528,528) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(552,528) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(576,528) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(600,528) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(624,528) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(672,528) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(696,528) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(720,528) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(240,552) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(312,552) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(336,552) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(384,552) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(408,552) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(480,552) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(504,552) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(528,552) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(624,552) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(648,552) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(696,552) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(720,552) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(264,576) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(312,576) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(336,576) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(360,576) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(384,576) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(408,576) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(504,576) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(528,576) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(576,576) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(624,576) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(264,600) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(312,600) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(360,600) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(384,600) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(408,600) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(504,600) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(528,600) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(624,600) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(648,600) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(696,600) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(720,600) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(240,624) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(288,624) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(336,624) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(360,624) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(456,624) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(480,624) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(528,624) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(552,624) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(576,624) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(600,624) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(624,624) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(648,624) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(672,624) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(720,624) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(240,648) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(264,648) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(336,648) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(408,648) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(480,648) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(504,648) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(528,648) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(552,648) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(600,648) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(240,672) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(336,672) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(360,672) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(408,672) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(456,672) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(504,672) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(528,672) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(552,672) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(576,672) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(600,672) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(624,672) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(648,672) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(696,672) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(288,696) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(360,696) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(408,696) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(456,696) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(480,696) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(528,696) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(552,696) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(624,696) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(648,696) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(696,696) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(264,720) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(288,720) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(336,720) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(360,720) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(384,720) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(408,720) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(504,720) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(552,720) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(576,720) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(624,720) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(648,720) scale(4.12)"/>
|
||||
<circle cx="3" cy="3" r="3" transform="translate(672,720) scale(4.12)"/>
|
||||
<path d="M4.5,14h5.1C12,14,14,12,14,9.6V4.5C14,2,12,0,9.5,0H4.4C2,0,0,2,0,4.4v5.1C0,12,2,14,4.5,14z M12,4.8v4.4 c0,1.5-1.3,2.8-2.8,2.8H4.8C3.2,12,2,10.8,2,9.2V4.8C2,3.3,3.3,2,4.8,2h4.4C10.8,2,12,3.2,12,4.8z" transform="translate(48,48) scale(12)"/>
|
||||
<path d="M4.5,14h5.1C12,14,14,12,14,9.6V4.5C14,2,12,0,9.5,0H4.4C2,0,0,2,0,4.4v5.1C0,12,2,14,4.5,14z M12,4.8v4.4 c0,1.5-1.3,2.8-2.8,2.8H4.8C3.2,12,2,10.8,2,9.2V4.8C2,3.3,3.3,2,4.8,2h4.4C10.8,2,12,3.2,12,4.8z" transform="translate(576,48) scale(12)"/>
|
||||
<path d="M4.5,14h5.1C12,14,14,12,14,9.6V4.5C14,2,12,0,9.5,0H4.4C2,0,0,2,0,4.4v5.1C0,12,2,14,4.5,14z M12,4.8v4.4 c0,1.5-1.3,2.8-2.8,2.8H4.8C3.2,12,2,10.8,2,9.2V4.8C2,3.3,3.3,2,4.8,2h4.4C10.8,2,12,3.2,12,4.8z" transform="translate(48,576) scale(12)"/>
|
||||
<rect width="6" height="6" transform="translate(96,96) scale(12)"/>
|
||||
<rect width="6" height="6" transform="translate(624,96) scale(12)"/>
|
||||
<rect width="6" height="6" transform="translate(96,624) scale(12)"/>
|
||||
</g>
|
||||
|
||||
<g id="logo" display="inline" transform="matrix(14,0,0,13.5,640,802)">
|
||||
<path id="body" stroke-width="0.139944" fill-rule="evenodd"
|
||||
d="m -17.780346,-23.927296 c -0.740502,-0.204654 -1.19858,-0.50646 -1.708866,-0.912419 -0.195581,-0.155595 -1.180741,-0.921079 -1.587656,-0.186959 -1.282534,-1.269981 -0.904624,-3.38856 -0.223378,-4.775114 0.302528,-0.380694 0.649272,-0.729003 0.96505,-1.091677 -0.228696,-0.825519 -0.351618,-3.291542 1.987329,-3.895113 0.972192,0.0552 1.717975,1.013344 1.712946,2.177118 -0.0086,1.994014 -3.185616,2.50384 -3.338799,5.024277 -0.124698,2.051749 0.780153,3.095667 2.64463,3.740202 0,0 -0.278155,-0.03246 -0.451256,-0.0803 z" />
|
||||
<path id="opaque_bg" stroke-width="0.139944"
|
||||
d="m -16.525233,-25.802896 c -0.431571,-0.155018 -0.651471,-0.401725 -0.807251,-0.620025 -0.936017,1.334687 -3.339164,-1.536579 0.55272,-2.4916 -1.12997,0.424632 -0.813709,1.899893 -0.738003,1.996262 0.04362,-0.672993 0.763138,-1.09084 1.517398,-1.059535 0.21776,-0.0046 0.344574,0.03097 0.535276,0.123046 0.175909,0.101143 0.338175,0.225574 0.447391,0.376562 -0.653075,-0.542532 -1.44499,0.429431 -0.991579,0.870799 0.414816,0.434889 0.131767,0.811205 -0.0068,0.843304 -0.177973,0.0645 -0.331948,0.0028 -0.509175,-0.03881 z m 1.507219,-1.675067 c 0.24031,-0.425117 0.456162,-0.777983 1.455832,-0.921665 0.126769,-0.904263 0.619657,-1.16869 1.367029,-1.300232 -0.466802,1.263568 -1.425461,2.318603 -2.747131,2.489602 -0.140204,-1.5e-5 -0.147425,-0.148586 -0.07573,-0.267705 z m -7.812181,-1.920908 c 0.310588,0.01968 0.800422,0.21683 0.902047,0.318455 l -0.177713,0.294168 c -0.08445,-0.336394 -0.444015,-0.466598 -0.724334,-0.612623 z m 0.364831,-0.550187 c -0.119723,-0.286303 -0.161624,-0.50318 -0.612134,-0.641661 l -0.07274,-0.394547 c 1.059661,-0.06702 1.540798,0.430742 1.805881,1.19285 -0.172096,0.180593 -0.335411,0.362047 -0.488409,0.565322 -0.156151,-0.263454 -0.277954,-0.541222 -0.632608,-0.721964 z m 8.452337,-3.056927 -0.498571,0.100443 -0.174332,-0.482029 0.537781,-0.08845 0.217689,-0.247478 0.285717,-0.48926 -0.271865,-0.260991 -0.152046,0.04815 -0.116219,-0.07262 -0.114406,-0.04673 -0.518151,-0.102346 c -0.08553,0.197486 -0.165289,0.399386 -0.26161,0.588614 -0.106002,-0.1626 -0.226174,-0.300579 -0.394125,-0.433313 l 0.06694,-0.06811 0.216195,-0.523535 0.117247,-0.06813 0.943714,0.316629 0.315949,-0.04145 0.04256,0.109617 0.641359,0.494004 0.155872,-0.05923 c 0.155937,-0.04127 0.305005,-0.04907 0.454811,-0.06045 0.08197,0.123963 0.150547,0.250758 0.212858,0.378877 l -0.267391,0.01312 -0.401582,0.09235 -0.283086,0.19474 -0.286495,0.445961 0.05465,0.0746 0.112217,0.02108 0.139213,0.01356 0.05593,0.161827 0.03338,0.002 0.0054,0.113294 -0.05456,0.02166 c -0.150894,-0.04082 -0.198671,-0.03397 -0.310541,-0.01966 l -0.04879,0.07945 -0.14554,4.2e-4 z m -0.1817,-3.910579 c -18.3142,-12.855079 -18.3142,-12.855079 0,0 z m -1.386806,0.559437 -0.06544,-0.635205 -0.05468,-0.430235 -0.110769,-0.224401 v -0.05738 -0.05738 l 0.04628,0.0013 0.04628,0.0013 0.11108,0.0953 0.111081,0.0953 0.09785,0.232557 0.07423,0.171476 0.07423,0.171474 0.07035,0.119051 -0.0053,0.255837 0.05775,0.136356 0.429522,-0.104527 0.03372,-0.05754 0.113067,-0.101677 0.159348,0.139552 0.09473,0.0353 0.145295,0.173067 -0.116613,0.07124 -0.329849,0.04556 -0.530147,0.154936 -0.520678,0.528666 -0.122723,0.368287 -0.0016,0.557746 c -0.232462,-0.140161 -0.472491,-0.250597 -0.729758,-0.313503 l 0.02666,-0.03033 0.306126,-0.688346 0.0351,-0.443729 0.357901,0.05307 z" />
|
||||
<path id="opaque_body" stroke-width="0.0370268"
|
||||
d="m -21.300795,-29.801442 -0.05705,0.02232 c -0.140072,0.147509 -0.325447,0.352966 -0.472774,0.548961 -0.0805,0.131068 -0.183127,0.280436 -0.274805,0.444323 -0.219791,0.392905 -0.37501,0.892737 -0.409556,1.261252 0.04055,2.092768 1.064959,2.671403 1.364081,2.769177 l 0.07391,-0.271495 c -1.044292,-1.02787 -1.107279,-2.821337 -0.223803,-4.774537 z" />
|
||||
<path id="bg" stroke-width="0.139944"
|
||||
d="m -24.00005,-28.198155 c 0.540066,-0.206305 1.208639,0.07327 1.486003,0.673362 0.205627,1.102615 0.225643,2.227548 1.363032,2.76993 0.138024,-1.020193 1.282004,-0.476272 1.713084,-0.0611 0.534767,0.463498 1.179757,0.767742 1.866736,0.92638 1.368373,0.310783 4.967204,-0.235146 4.929394,-2.967739 0.387504,-0.303388 0.263813,-0.573515 0.3116,-0.976043 0.08838,-0.74445 0.81786,-0.856106 1.371884,-0.823218 -0.05805,7.015113 -12.334646,7.955045 -13.041733,0.458431 z m 9.241584,2.072657 c 0.145408,0.01363 0.277649,0.0075 0.410757,0.0048 -0.236519,0.426152 -0.611821,0.514387 -0.83621,0.42115 0.177779,-0.08335 0.310055,-0.257038 0.425453,-0.425887 z m -2.288667,-2.69431 c 1.4131,-0.496237 3.099837,-1.639898 2.534422,-4.085356 0.172719,-0.03853 0.338184,-0.0654 0.501048,-0.104119 0.09799,0.0769 0.203151,0.142718 0.308292,0.20909 h 0.145209 l 0.04894,-0.07918 c 0.12455,-0.02144 0.223126,-0.0087 0.310206,0.01908 l 0.05531,-0.02046 -0.0056,-0.113795 -0.03283,-0.0017 -0.056,-0.1619 -0.140933,-0.0138 -0.110407,-0.02109 -0.05485,-0.07572 c 0.08495,-0.162161 0.192391,-0.297126 0.28599,-0.446762 l 0.282196,-0.193858 0.404567,-0.08728 0.265134,-0.01578 c 0.646961,1.400723 0.665782,2.929648 0.111935,4.360422 -0.753547,0.03106 -1.338809,0.51509 -1.315785,1.258657 -0.480916,0.06166 -0.702315,0.09418 -0.866398,0.477373 -0.30325,0.05951 -0.456368,0.239528 -0.641838,0.458972 -0.822412,-0.913337 -2.375228,-0.507085 -2.502888,0.53881 -0.336543,-0.781733 -0.08326,-1.51134 0.474284,-1.901619 z m -6.088533,-2.157259 c -0.539058,-4.408364 3.611971,-7.390752 7.481325,-6.067722 l 0.07276,0.68782 -0.199,0.26325 -0.355579,-0.04717 -0.03491,0.434089 -0.302477,0.69199 -0.01597,0.01597 -0.01597,0.01597 c -2.77893,-0.220435 -4.406959,1.610741 -3.816223,4.117026 -0.348573,0.335047 -0.66657,0.695942 -0.977997,1.064826 l -0.07886,0.03053 c -0.33194,-1.049822 -0.814293,-1.165861 -1.757096,-1.206577 z m 8.056285,-3.080196 c 0.09707,-0.191174 0.179226,-0.388539 0.259856,-0.58697 l 0.519682,0.09902 0.114844,0.04595 0.116541,0.0737 0.151818,-0.04819 c 0.09857,0.06769 0.195057,0.172097 0.272691,0.261321 -0.09342,0.164788 -0.190446,0.32753 -0.286229,0.490961 l -0.216367,0.246427 c -0.181907,0.02133 -0.362188,0.05029 -0.541827,0.08539 -0.09464,-0.241763 -0.250696,-0.450895 -0.391009,-0.667616 z m 1.611498,-0.463995 -0.301659,-0.245321 -0.02144,-0.05587 -0.02144,-0.05587 -0.15764,0.02317 -0.15764,0.02317 -0.333241,-0.115178 -0.333242,-0.115179 -0.140429,-0.04334 -0.140431,-0.04334 -0.05631,0.03584 -0.05631,0.03584 -0.108156,0.256765 -0.108157,0.256765 -0.03083,0.03083 -0.03847,0.04152 c -0.09574,-0.06777 -0.198667,-0.122732 -0.301498,-0.178577 l -2.8e-4,-0.280362 -2.8e-4,-0.280362 0.0621,-0.182473 0.0621,-0.182473 0.261884,-0.265179 0.261884,-0.265178 0.263097,-0.07729 0.263097,-0.07729 0.166011,-0.02276 0.166011,-0.02275 c 0.571618,0.329796 1.362626,1.116576 1.743422,1.939135 -0.151971,-9.66e-4 -0.297574,0.02821 -0.445347,0.05846 l -0.160683,0.06398 z m -0.456181,-1.015239 c -10.824586,-2.370009 -10.824586,-2.370009 0,0 z m -1.258899,-1.083518 v -0.253131 l -0.03282,-0.05983 c 0.150432,0.06825 0.40955,0.206592 0.549691,0.291502 l -0.03426,0.05543 c -0.140835,0.05311 -0.287215,0.06142 -0.427026,0.107752 m 0.424578,0.858015 c 0,0.533333 0.8,0.533333 0.8,0 0,-0.533333 -0.8,-0.533333 -0.8,0 m 0.157809,-0.05896 c 0,0.266667 0.4,0.266667 0.4,0 0,-0.266667 -0.4,-0.266667 -0.4,0 z" />
|
||||
<circle id="eye" fill="#292323" stroke-width="0.0370268" fill-opacity="1" cx="-18.741674" cy="-32.929714"
|
||||
r="0.69050175" />
|
||||
</g>
|
||||
<image xlink:href="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+Cjxzdmcgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDI0IDI0IiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zOnNlcmlmPSJodHRwOi8vd3d3LnNlcmlmLmNvbS8iIHN0eWxlPSJmaWxsLXJ1bGU6ZXZlbm9kZDtjbGlwLXJ1bGU6ZXZlbm9kZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MjsiPgo8L3N2Zz4K" width="216" height="216" x="288" y="288"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 32 KiB |
@@ -10,6 +10,7 @@ const ProjectsPage = React.lazy(() => import('components/pages/ProjectsPage/inde
|
||||
const CurriculumPage = React.lazy(() => import('components/pages/CurriculumPage/index.js'))
|
||||
const ContactPage = React.lazy(() => import('components/pages/ContactPage/index.js'))
|
||||
const PrivacyPage = React.lazy(() => import('components/pages/PrivacyPage/index.js'))
|
||||
const BusinessCardPage = React.lazy(() => import('components/pages/BusinessCardPage/index.js'))
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
@@ -57,6 +58,14 @@ const router = createBrowserRouter([
|
||||
</React.Suspense>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'card',
|
||||
element: (
|
||||
<React.Suspense fallback={<Loading />}>
|
||||
<BusinessCardPage />
|
||||
</React.Suspense>
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
@@ -62,6 +62,9 @@ const CookieConsent = () => {
|
||||
mb: theme.spacing(1),
|
||||
p: theme.spacing(3),
|
||||
zIndex: 9999,
|
||||
'@media print': {
|
||||
display: 'none',
|
||||
},
|
||||
})}>
|
||||
<Box>
|
||||
<SectionSubheader id="cookie-consent-dialog-title" component="h2" variant="h6">
|
||||
|
||||
@@ -32,10 +32,16 @@ const CurriculumItem = (props: CurriculumEntryProps) => {
|
||||
// '& .MuiAccordionSummary-expandIconWrapper .MuiSvgIcon-root': { color: 'primary.dark' }, // use summary hover icon color
|
||||
},
|
||||
'&.Mui-expanded': {
|
||||
bgcolor: 'background.paper', // use summary hover background
|
||||
borderBottom: '1px solid',
|
||||
borderTop: '1px solid',
|
||||
bgcolor: 'background.paper', // use summary hover background
|
||||
elevation: 1,
|
||||
'@media print': {
|
||||
bgcolor: 'background.default',
|
||||
borderBottom: 0,
|
||||
borderTop: 0,
|
||||
elevation: 0,
|
||||
},
|
||||
// '& .MuiAccordionSummary-expandIconWrapper .MuiSvgIcon-root': { color: 'primary.dark' }, // use summary hover icon color
|
||||
},
|
||||
}}>
|
||||
@@ -48,6 +54,9 @@ const CurriculumItem = (props: CurriculumEntryProps) => {
|
||||
},
|
||||
'& .MuiAccordionSummary-expandIconWrapper.Mui-expanded': {
|
||||
transform: 'rotate(0deg)',
|
||||
'@media print': {
|
||||
transform: 'rotate(-90deg)',
|
||||
},
|
||||
},
|
||||
'& .MuiAccordionSummary-content': {
|
||||
marginLeft: 2,
|
||||
|
||||
@@ -14,7 +14,17 @@ type FooterProps = {
|
||||
const Footer: React.FC<FooterProps> = ({ showCookieConsent = true }) => {
|
||||
const year = new Date().getFullYear()
|
||||
return (
|
||||
<Box sx={{ bgcolor: 'background.paper', p: 6, display: 'flex', alignItems: 'center', flexDirection: 'column' }} component="footer">
|
||||
<Box
|
||||
id="footer"
|
||||
component="footer"
|
||||
sx={{
|
||||
bgcolor: 'background.paper',
|
||||
py: 6,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'column',
|
||||
'@media print': { py: 3 },
|
||||
}}>
|
||||
<Stack direction="row" gap={2}>
|
||||
<LinkedInIconLink fontSize="large" href="https://www.linkedin.com/in/m-tartari/" />
|
||||
<GitHubIconLink fontSize="large" href="https://www.github.com/m-tartari/" />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react'
|
||||
import { ParallaxBanner, ParallaxProvider } from 'react-scroll-parallax'
|
||||
import { Box, Grid, IconButton, Typography, useTheme } from '@mui/material'
|
||||
import { Box, Grid, IconButton, Typography } from '@mui/material'
|
||||
import { ExpandMore } from '@mui/icons-material'
|
||||
|
||||
import { useViewport } from 'components/utils/viewport/index.js'
|
||||
@@ -125,79 +125,91 @@ const Description = (props: Props) => {
|
||||
}
|
||||
}
|
||||
|
||||
const DesktopComponent = React.forwardRef<HTMLSelectElement, Props>((props, ref) => {
|
||||
return (
|
||||
<section ref={ref}>
|
||||
<ParallaxProvider>
|
||||
<ParallaxBanner
|
||||
layers={[
|
||||
{
|
||||
// translateY: [0, 30],
|
||||
// scale: [1, 0.5, 'easeOutCubic'],
|
||||
// shouldAlwaysCompleteAnimation: true,
|
||||
image: props.image,
|
||||
expanded: false,
|
||||
speed: -20,
|
||||
},
|
||||
]}
|
||||
style={{
|
||||
// width: '150%',
|
||||
// marginLeft: '-25%',
|
||||
minHeight: '100vh',
|
||||
position: 'relative',
|
||||
const ProjectSpotlight = React.forwardRef<HTMLSelectElement, Props>((props, ref) => {
|
||||
const { width } = useViewport()
|
||||
|
||||
height: '100vh',
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
<Description {...props} />
|
||||
{props.scrollTarget && (
|
||||
<IconButton
|
||||
size="large"
|
||||
onClick={() => (props.scrollTarget as React.RefObject<HTMLSelectElement | null>).current?.scrollIntoView()}
|
||||
sx={{ position: 'absolute', p: 1, bottom: 2, left: '50%', transform: 'translate(-50%, 0)' }}>
|
||||
<ExpandMore fontSize="large" />
|
||||
</IconButton>
|
||||
)}
|
||||
</ParallaxBanner>
|
||||
</ParallaxProvider>
|
||||
</section>
|
||||
)
|
||||
})
|
||||
|
||||
const MobileComponent = React.forwardRef<HTMLSelectElement, Props>((props, ref) => {
|
||||
return (
|
||||
<Box component="section">
|
||||
<Box
|
||||
ref={ref}
|
||||
component="img"
|
||||
sx={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
maxHeight: '60vh',
|
||||
}}
|
||||
src={props.mobileImage ?? props.image}
|
||||
/>
|
||||
<Box ref={ref}>
|
||||
<Box
|
||||
component="section"
|
||||
sx={theme => ({
|
||||
mt: theme.spacing(-1.05),
|
||||
borderTopStyle: 'solid',
|
||||
borderTopWidth: theme.spacing(0.5),
|
||||
borderTopColor: theme.palette.primary.main,
|
||||
p: `${theme.spacing(7)} ${theme.spacing(5)} ${theme.spacing(5.5)} ${theme.spacing(5)}`,
|
||||
display: width < theme.breakpoints.values.md ? 'none' : 'inline',
|
||||
'@media print': { display: 'none' },
|
||||
})}>
|
||||
<Typography variant="h4">{props.title}</Typography>
|
||||
{props.caption}
|
||||
<Typography pt={1}>{props.children}</Typography>
|
||||
<ParallaxProvider>
|
||||
<ParallaxBanner
|
||||
layers={[
|
||||
{
|
||||
// translateY: [0, 30],
|
||||
// scale: [1, 0.5, 'easeOutCubic'],
|
||||
// shouldAlwaysCompleteAnimation: true,
|
||||
image: props.image,
|
||||
expanded: false,
|
||||
speed: -20,
|
||||
},
|
||||
]}
|
||||
style={{
|
||||
// width: '150%',
|
||||
// marginLeft: '-25%',
|
||||
minHeight: '100vh',
|
||||
position: 'relative',
|
||||
|
||||
height: '100vh',
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
<Description {...props} />
|
||||
{props.scrollTarget && (
|
||||
<IconButton
|
||||
size="large"
|
||||
onClick={() => (props.scrollTarget as React.RefObject<HTMLSelectElement | null>).current?.scrollIntoView()}
|
||||
sx={{ position: 'absolute', p: 1, bottom: 2, left: '50%', transform: 'translate(-50%, 0)' }}>
|
||||
<ExpandMore fontSize="large" />
|
||||
</IconButton>
|
||||
)}
|
||||
</ParallaxBanner>
|
||||
</ParallaxProvider>
|
||||
</Box>
|
||||
<Box
|
||||
component="section"
|
||||
sx={theme => ({
|
||||
display: width >= theme.breakpoints.values.md ? 'none' : 'inline',
|
||||
'@media print': {
|
||||
display: 'inline',
|
||||
height: '100%',
|
||||
},
|
||||
})}>
|
||||
<Box
|
||||
component="img"
|
||||
sx={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
maxHeight: '60vh',
|
||||
'@media print': {
|
||||
maxHeight: '100%',
|
||||
WebkitPrintColorAdjust: 'exact !important',
|
||||
printColorAdjust: 'exact !important',
|
||||
},
|
||||
}}
|
||||
src={props.mobileImage ?? props.image}
|
||||
/>
|
||||
<Box
|
||||
sx={theme => ({
|
||||
mt: theme.spacing(-1.05),
|
||||
borderTopStyle: 'solid',
|
||||
borderTopWidth: theme.spacing(0.5),
|
||||
borderTopColor: theme.palette.primary.main,
|
||||
p: `${theme.spacing(7)} ${theme.spacing(5)} ${theme.spacing(5.5)} ${theme.spacing(5)}`,
|
||||
'@media print': {
|
||||
backgroundColor: 'background.default',
|
||||
},
|
||||
})}>
|
||||
<Typography variant="h4">{props.title}</Typography>
|
||||
{props.caption}
|
||||
<Typography pt={1}>{props.children}</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
})
|
||||
|
||||
const ProjectSpotlight = React.forwardRef<HTMLSelectElement, Props>((props, ref) => {
|
||||
const { width } = useViewport()
|
||||
const theme = useTheme()
|
||||
|
||||
return width < theme.breakpoints.values.md ? <MobileComponent {...props} ref={ref} /> : <DesktopComponent {...props} ref={ref} />
|
||||
})
|
||||
|
||||
export default ProjectSpotlight
|
||||
|
||||
@@ -15,22 +15,30 @@ import {
|
||||
Stack,
|
||||
Toolbar,
|
||||
Typography,
|
||||
TypographyProps,
|
||||
styled,
|
||||
useScrollTrigger,
|
||||
useTheme,
|
||||
} from '@mui/material'
|
||||
import MenuIcon from '@mui/icons-material/Menu'
|
||||
|
||||
const AppBarButton = styled(Button<typeof RouterLink>)(({ theme }) => ({
|
||||
textTransform: 'none',
|
||||
color: theme.palette.mode === 'dark' ? 'inherit' : 'text.secondary',
|
||||
':hover': {
|
||||
backgroundColor: 'transparent',
|
||||
...(theme.palette.mode === 'dark' && {
|
||||
const TypographyLink = styled(Typography)(({ theme }) => ({
|
||||
role: 'link',
|
||||
textDecoration: 'none',
|
||||
...(theme.palette.mode === 'dark' && {
|
||||
color: 'inherit',
|
||||
':hover': {
|
||||
color: theme.palette.primary.main,
|
||||
}),
|
||||
},
|
||||
},
|
||||
}),
|
||||
...(theme.palette.mode === 'light' && {
|
||||
color: theme.palette.text.primary,
|
||||
':hover': {
|
||||
color: theme.palette.primary.dark,
|
||||
},
|
||||
}),
|
||||
}))
|
||||
const AppBarLink = (props: TypographyProps & { to: string }) => <TypographyLink component={RouterLink} variant="h6" {...props} />
|
||||
const DrawerButton = styled(ListItemButton<typeof RouterLink>)(({ theme }) => ({
|
||||
textTransform: 'none',
|
||||
color: 'inherit',
|
||||
@@ -102,6 +110,7 @@ const CustomToolbar: React.FC<CustomToolbarProps> = ({ drawerWidth = 240, window
|
||||
{...props}
|
||||
sx={{
|
||||
backgroundColor: trigger ? 'background.paper' : 'transparent',
|
||||
'@media print': { backgroundColor: 'background.default' },
|
||||
...(theme.palette.mode === 'light' && {
|
||||
backgroundColor: 'hsla(0, 0%, 100%, 0.75)',
|
||||
backdropFilter: 'blur(8px)',
|
||||
@@ -115,6 +124,7 @@ const CustomToolbar: React.FC<CustomToolbarProps> = ({ drawerWidth = 240, window
|
||||
<Toolbar
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
// maxWidth: '100%',
|
||||
// [theme.breakpoints.up('md')]: { width: theme => theme.breakpoints.values.lg, mx: 'auto' },
|
||||
}}>
|
||||
@@ -123,58 +133,47 @@ const CustomToolbar: React.FC<CustomToolbarProps> = ({ drawerWidth = 240, window
|
||||
aria-label="open drawer"
|
||||
edge="start"
|
||||
onClick={handleDrawerToggle}
|
||||
sx={{ mr: 2, display: { md: 'none' }, position: 'absolute', backgroundColor: 'primary' }}>
|
||||
sx={{
|
||||
mr: 2,
|
||||
display: { md: 'none' },
|
||||
position: 'absolute',
|
||||
backgroundColor: 'primary',
|
||||
'@media print': { display: 'none' },
|
||||
}}>
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
<AppBarButton
|
||||
component={RouterLink}
|
||||
|
||||
<AppBarLink
|
||||
to="/"
|
||||
disableRipple
|
||||
sx={theme => ({
|
||||
color: theme.palette.text.primary,
|
||||
[theme.breakpoints.down('md')]: { mx: 'auto' },
|
||||
[theme.breakpoints.up('md')]: { mr: 'auto' },
|
||||
})}>
|
||||
<Typography variant="h6">Michele Tartari</Typography>
|
||||
</AppBarButton>
|
||||
<Stack component="div" direction="row" spacing={2} sx={{ display: { xs: 'none', md: 'flex' } }}>
|
||||
<AppBarButton component={RouterLink} to="/" disableRipple>
|
||||
<Typography variant="h6">Home</Typography>
|
||||
</AppBarButton>
|
||||
<AppBarButton component={RouterLink} to="/projects" disableRipple>
|
||||
<Typography variant="h6">Projects</Typography>
|
||||
</AppBarButton>
|
||||
<AppBarButton component={RouterLink} to="/curriculum" disableRipple>
|
||||
<Typography variant="h6">Curriculum</Typography>
|
||||
</AppBarButton>
|
||||
<Button
|
||||
component={RouterLink}
|
||||
to="/contacts"
|
||||
color="primary"
|
||||
variant="contained"
|
||||
sx={{
|
||||
textTransform: 'none',
|
||||
}}>
|
||||
Michele Tartari
|
||||
</AppBarLink>
|
||||
<Stack component="div" direction="row" spacing={2} sx={{ display: { xs: 'none', md: 'flex', alignItems: 'center' } }}>
|
||||
<AppBarLink to="/">Home</AppBarLink>
|
||||
<AppBarLink to="/projects">Projects</AppBarLink>
|
||||
<AppBarLink to="/curriculum">Curriculum</AppBarLink>
|
||||
<Button component={RouterLink} to="/contacts" color="primary" variant="contained" sx={{ textTransform: 'none' }}>
|
||||
<Typography variant="h6">Hire me</Typography>
|
||||
</Button>
|
||||
</Stack>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
<nav>
|
||||
<Drawer
|
||||
variant="temporary"
|
||||
open={mobileOpen}
|
||||
onClose={handleDrawerToggle}
|
||||
ModalProps={{
|
||||
keepMounted: true, // Better open performance on mobile.
|
||||
}}
|
||||
sx={{
|
||||
display: { xs: 'block', md: 'none' },
|
||||
'& .MuiDrawer-paper': { boxSizing: 'border-box', width: drawerWidth },
|
||||
}}>
|
||||
{drawer}
|
||||
</Drawer>
|
||||
</nav>
|
||||
<Drawer
|
||||
variant="temporary"
|
||||
open={mobileOpen}
|
||||
onClose={handleDrawerToggle}
|
||||
ModalProps={{
|
||||
keepMounted: true, // Better open performance on mobile.
|
||||
}}
|
||||
sx={{
|
||||
display: { xs: 'block', md: 'none' },
|
||||
'& .MuiDrawer-paper': { boxSizing: 'border-box', width: drawerWidth },
|
||||
}}>
|
||||
{drawer}
|
||||
</Drawer>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
import { screen } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
import { useAnalytics } from 'use-analytics'
|
||||
|
||||
import { renderWithRouter as render } from 'components/utils/tests/index.js'
|
||||
import BusinessCardPage from './index.js'
|
||||
|
||||
vi.mock('./envVars.js', () => ({
|
||||
NAME: 'Test Name',
|
||||
POSITION: 'Test Position',
|
||||
VAT: 'Test VAT',
|
||||
PHONE: '+123456789',
|
||||
EMAIL: 'test@example.com',
|
||||
LINKEDIN: 'testlinkedin',
|
||||
}))
|
||||
|
||||
// Mock window.print
|
||||
const mockPrint = vi.fn()
|
||||
window.print = mockPrint
|
||||
|
||||
// Mock the analytics module
|
||||
vi.mock('use-analytics', () => ({
|
||||
useAnalytics: () => ({ plugins: { enable: vi.fn(), disable: vi.fn() } }),
|
||||
}))
|
||||
|
||||
describe('BusinessCardPage', () => {
|
||||
beforeEach(() => {
|
||||
mockPrint.mockClear()
|
||||
})
|
||||
|
||||
it('renders correctly with environment variables', () => {
|
||||
render(<BusinessCardPage />)
|
||||
|
||||
// Name appears in uppercase in the first card
|
||||
expect(screen.getAllByText('Test Name')).toHaveLength(2)
|
||||
expect(screen.getByText('Test Position')).toBeInTheDocument()
|
||||
|
||||
// Check if values are rendered correctly in the second card
|
||||
expect(screen.getByText('Test VAT')).toBeInTheDocument()
|
||||
expect(screen.getByText('+123456789')).toBeInTheDocument()
|
||||
expect(screen.getByText('test@example.com')).toBeInTheDocument()
|
||||
expect(screen.getByText('www.m-tartari.eu')).toBeInTheDocument()
|
||||
expect(screen.getByText('testlinkedin')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('has correct links with proper href attributes', () => {
|
||||
render(<BusinessCardPage />)
|
||||
|
||||
// Check phone link
|
||||
const phoneLink = screen.getByText('+123456789').closest('a')
|
||||
expect(phoneLink).toHaveAttribute('href', 'tel:+123456789')
|
||||
|
||||
// Check email link
|
||||
const emailLink = screen.getByText('test@example.com').closest('a')
|
||||
expect(emailLink).toHaveAttribute('href', 'mailto:test@example.com')
|
||||
|
||||
// Check website link
|
||||
const websiteLink = screen.getByText('www.m-tartari.eu').closest('a')
|
||||
expect(websiteLink).toHaveAttribute('href', 'https://www.m-tartari.eu')
|
||||
|
||||
// Check LinkedIn link
|
||||
const linkedinLink = screen.getByText('testlinkedin').closest('a')
|
||||
expect(linkedinLink).toHaveAttribute('href', 'https://www.linkedin.com/in/testlinkedin/')
|
||||
})
|
||||
|
||||
it('calls window.print when print button is clicked', async () => {
|
||||
const user = userEvent.setup()
|
||||
render(<BusinessCardPage />)
|
||||
|
||||
await user.click(screen.getByText('Print Business Card'))
|
||||
|
||||
expect(mockPrint).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('renders correct number of business cards', () => {
|
||||
render(<BusinessCardPage />)
|
||||
|
||||
const businessCards = screen.getAllByRole('img', { name: /logo|qr code/i })
|
||||
expect(businessCards).toHaveLength(2)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,7 @@
|
||||
/* istanbul ignore file */
|
||||
export const NAME = import.meta.env.VITE_BC_NAME || '[FULL NAME]'
|
||||
export const POSITION = import.meta.env.VITE_BC_POSITION || '[POSITION]'
|
||||
export const VAT = import.meta.env.VITE_BC_VAT || '[VAT NUMBER]'
|
||||
export const PHONE = import.meta.env.VITE_BC_PHONE || '[Redacted for privacy]'
|
||||
export const EMAIL = import.meta.env.VITE_BC_EMAIL || '[Redacted for privacy]'
|
||||
export const LINKEDIN = import.meta.env.VITE_BC_LINKEDIN || '[Redacted for privacy]'
|
||||
@@ -0,0 +1,31 @@
|
||||
/* Print styles for business card */
|
||||
@media print {
|
||||
body * {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#main,
|
||||
#page-content {
|
||||
margin-bottom: 0;
|
||||
margin-top: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
#footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.print-business-card,
|
||||
.print-business-card * {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.print-business-card {
|
||||
width: 85mm;
|
||||
height: 55mm;
|
||||
}
|
||||
|
||||
@page {
|
||||
size: 85mm 55mm;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
import { Box, Typography, Stack, Button, Paper, Link, TypographyProps, SvgIcon, PaperProps, ThemeProvider, createTheme } from '@mui/material'
|
||||
import { Email, LinkedIn, Print, PhoneInTalk, Public } from '@mui/icons-material'
|
||||
|
||||
import { Page } from 'components/templates/index.js'
|
||||
import typography from 'style/typography/index.js'
|
||||
import palette from 'style/palette/index.js'
|
||||
import * as env from './envVars.js'
|
||||
import './index.css'
|
||||
|
||||
const TypographyLink = ({ icon: Icon, children, ...props }: TypographyProps<typeof Link, { icon: typeof SvgIcon }>) => (
|
||||
<Box
|
||||
component={Link}
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
underline="hover"
|
||||
color="text.secondary"
|
||||
{...props}
|
||||
sx={{ display: 'flex', alignItems: 'center', gap: 0.5, ...props.sx }}>
|
||||
<Icon
|
||||
sx={{
|
||||
color: 'text.primary',
|
||||
fontSize: '0.7rem',
|
||||
}}
|
||||
/>
|
||||
<Typography variant="caption" color="text.secondary" fontSize="0.55rem">
|
||||
{children}
|
||||
</Typography>
|
||||
</Box>
|
||||
)
|
||||
|
||||
const theme = {
|
||||
light: createTheme({
|
||||
typography: typography,
|
||||
palette: palette.light,
|
||||
}),
|
||||
dark: createTheme({
|
||||
typography: typography,
|
||||
palette: palette.dark,
|
||||
}),
|
||||
}
|
||||
|
||||
type BusinessCardProps = {
|
||||
mode?: 'light' | 'dark'
|
||||
src: string
|
||||
alt?: string
|
||||
} & PaperProps
|
||||
const BusinessCard = ({ mode = 'dark', src, alt, sx, ...props }: BusinessCardProps) => (
|
||||
<ThemeProvider theme={theme[mode]}>
|
||||
<Paper
|
||||
className="print-business-card"
|
||||
elevation={1}
|
||||
sx={{
|
||||
colorScheme: `${mode} !important`,
|
||||
backgroundColor: 'background.default',
|
||||
width: '85mm',
|
||||
height: '55mm',
|
||||
padding: '6mm',
|
||||
borderRadius: 2,
|
||||
borderWidth: 1,
|
||||
borderStyle: 'solid',
|
||||
borderColor: mode === 'light' ? 'background.default' : 'white',
|
||||
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 1.5,
|
||||
|
||||
'@media print': {
|
||||
backgroundColor: mode === 'light' ? 'white' : 'background.default',
|
||||
WebkitPrintColorAdjust: 'exact !important',
|
||||
printColorAdjust: 'exact !important',
|
||||
elevation: 0,
|
||||
borderRadius: 0,
|
||||
borderColor: 'transparent',
|
||||
gap: 1.5,
|
||||
},
|
||||
...sx,
|
||||
}}
|
||||
{...props}>
|
||||
<Box
|
||||
component="img"
|
||||
src={src}
|
||||
alt={alt}
|
||||
sx={{
|
||||
width: '30%',
|
||||
height: 'fit-content',
|
||||
objectFit: 'cover',
|
||||
// borderRadius: '50%',
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Right side with content */}
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
}}>
|
||||
{props.children}
|
||||
</Box>
|
||||
</Paper>
|
||||
</ThemeProvider>
|
||||
)
|
||||
|
||||
/**
|
||||
* BusinessCardPage component - A digital business card
|
||||
* Designed to be printable as a standard business card (3.5 x 2 inches)
|
||||
*/
|
||||
const BusinessCardPage = () => {
|
||||
return (
|
||||
<Page>
|
||||
<Stack
|
||||
sx={{
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
pt: 4,
|
||||
gap: 2,
|
||||
'@media print': {
|
||||
pt: 0,
|
||||
gap: 0,
|
||||
},
|
||||
}}>
|
||||
<BusinessCard src="/fav-icon.svg" alt="logo">
|
||||
<Typography variant="h5" component="h1" fontFamily="Oswald, sans-serif" textTransform="uppercase" fontWeight={700}>
|
||||
{env.NAME}
|
||||
</Typography>
|
||||
<Typography variant="subtitle2" color="text.secondary" sx={{ fontSize: '0.55rem', mb: 0.5 }}>
|
||||
{env.POSITION}
|
||||
</Typography>
|
||||
</BusinessCard>
|
||||
|
||||
<BusinessCard src="/images/qr.svg" alt="Qr Code" mode="light">
|
||||
<Stack
|
||||
sx={{
|
||||
mt: 0.5,
|
||||
borderLeft: '1px solid',
|
||||
borderColor: 'divider',
|
||||
pl: 2,
|
||||
}}>
|
||||
<Typography
|
||||
variant="subtitle2"
|
||||
color="text.secondary"
|
||||
fontSize="0.55rem"
|
||||
fontFamily="Oswald, sans-serif"
|
||||
textTransform="uppercase"
|
||||
fontWeight={700}>
|
||||
{env.NAME}
|
||||
</Typography>
|
||||
<Typography variant="caption" color="text.secondary" fontSize="0.55rem" mb={0.5}>
|
||||
{env.VAT}
|
||||
</Typography>
|
||||
<TypographyLink icon={PhoneInTalk} href={`tel:${env.PHONE.replace(/[^\d+]/g, '').replace(/^\+?/, '+')}`}>
|
||||
{env.PHONE}
|
||||
</TypographyLink>
|
||||
<TypographyLink icon={Email} href={`mailto:${env.EMAIL}`}>
|
||||
{env.EMAIL}
|
||||
</TypographyLink>
|
||||
<TypographyLink icon={Public} href="https://www.m-tartari.eu">
|
||||
www.m-tartari.eu
|
||||
</TypographyLink>
|
||||
<TypographyLink icon={LinkedIn} href={`https://www.linkedin.com/in/${env.LINKEDIN}/`}>
|
||||
{env.LINKEDIN}
|
||||
</TypographyLink>
|
||||
</Stack>
|
||||
</BusinessCard>
|
||||
</Stack>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
ariaLabel: 'Print Business Card',
|
||||
my: 7,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
'@media print': {
|
||||
display: 'none',
|
||||
},
|
||||
}}>
|
||||
<Button variant="contained" startIcon={<Print />} onClick={() => window.print()}>
|
||||
Print Business Card
|
||||
</Button>
|
||||
</Box>
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
|
||||
export default BusinessCardPage
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Box, Divider, Link, Stack, Table, TableCell, TableRow, Typography } from '@mui/material'
|
||||
import { Box, Divider, Link, Stack, Table, TableBody, TableCell, TableRow, Typography } from '@mui/material'
|
||||
|
||||
import { Award, ExternalIconLink, Extracurricular, Publication, SectionHeader, SectionSubheader } from 'components/atoms/index.js'
|
||||
import { CurriculumItem } from 'components/molecules/index.js'
|
||||
@@ -12,20 +12,18 @@ const Curriculum = () => {
|
||||
|
||||
{/* id="selfemployed-software" */}
|
||||
<CurriculumItem title="Software Engineering Consultant" dates="04/24 - Today" company="Self-Employed" location="Remote">
|
||||
<Typography>
|
||||
<ul style={{ paddingLeft: '1em', marginTop: 0 }}>
|
||||
<li>
|
||||
Design and development of customized graphical interfaces and software components to facilitate the use of robots and industrial
|
||||
machines.
|
||||
</li>
|
||||
<li>Production and maintenance of revisions of the documentation.</li>
|
||||
<li>Providing technical assistance for R&D activities.</li>
|
||||
</ul>
|
||||
<Typography component="ul" sx={{ paddingLeft: '1em', marginTop: 0 }}>
|
||||
<li>
|
||||
Design and development of customized graphical interfaces and software components to facilitate the use of robots and industrial
|
||||
machines.
|
||||
</li>
|
||||
<li>Production and maintenance of revisions of the documentation.</li>
|
||||
<li>Providing technical assistance for R&D activities.</li>
|
||||
</Typography>
|
||||
</CurriculumItem>
|
||||
{/* id="MinHub-lead" */}
|
||||
<CurriculumItem title="Business Partner & Technical Lead" dates="03/23 - 12/24" company="MindHub Ferrara" location="Ferrara, Italy">
|
||||
<Typography>
|
||||
<Typography component="div">
|
||||
Co-founded{' '}
|
||||
<Link
|
||||
target="_blank"
|
||||
@@ -49,7 +47,7 @@ const Curriculum = () => {
|
||||
</CurriculumItem>
|
||||
{/* id="IIT-software" */}
|
||||
<CurriculumItem title="Software Engineer" dates="06/21 - 04/24" company="Istituto Italiano di Tecnologia" location="Remote">
|
||||
<Typography>
|
||||
<Typography component="div">
|
||||
Worked in{' '}
|
||||
<Link target="_blank" rel="noopener noreferrer" href="https://web.archive.org/web/20220706131402/https://alberobotics.it/">
|
||||
Alberobotics
|
||||
@@ -76,7 +74,7 @@ const Curriculum = () => {
|
||||
</CurriculumItem>
|
||||
{/* id="IIT-embedded" */}
|
||||
<CurriculumItem title="Embedded Engineer" dates="04/20 - 06/21" company="Istituto Italiano di Tecnologia" location="Genoa, Italy">
|
||||
<Typography>
|
||||
<Typography component="div">
|
||||
Worked in{' '}
|
||||
<Link target="_blank" rel="noopener noreferrer" href="https://web.archive.org/web/20220706131402/https://alberobotics.it/">
|
||||
Alberobotics
|
||||
@@ -104,7 +102,7 @@ const Curriculum = () => {
|
||||
dates="01/19 - 06/19"
|
||||
company="Warsaw University of Technology - Division of Theory of Machines and Robots"
|
||||
location="Warsaw, Poland">
|
||||
<Typography>
|
||||
<Typography component="div">
|
||||
My main duties included:
|
||||
<ul style={{ paddingLeft: '1em', marginTop: 0 }}>
|
||||
<li>Developing a voice interface and some user detection strategies for low-power robotic systems using ROS.</li>
|
||||
@@ -115,7 +113,7 @@ const Curriculum = () => {
|
||||
</CurriculumItem>
|
||||
{/* id="WUT-intern" */}
|
||||
<CurriculumItem title="Researcher - Internship" dates="01/17 - 02/17" company="LAR - University of Bologna" location="Bologna, Italy">
|
||||
<Typography>
|
||||
<Typography component="div">
|
||||
My main duties included:
|
||||
<ul style={{ paddingLeft: '1em', marginTop: 0 }}>
|
||||
<li>Developing a setup to control a shape memory alloy material using an embedded PC (a Beagle Bone Black).</li>
|
||||
@@ -157,26 +155,24 @@ const Curriculum = () => {
|
||||
// (grade: 5/5,summa cum laude)
|
||||
location="Warsaw, Poland"
|
||||
dates="09/18 - 06/19">
|
||||
<Typography>
|
||||
<ul style={{ paddingLeft: '1em', marginTop: 0 }}>
|
||||
<li>Consortium scholarship issued for EMARO+ students based on academic merits and offered to top applicants only.</li>
|
||||
<li>Relevant courses: Research Methodology, Bio-Robotics, Dynamics of Multi-Body Systems, Advanced Mechanical Design.</li>
|
||||
<li>Other relevant courses: 2 exams of Polish language and 1 of Polish culture.</li>
|
||||
<li>
|
||||
Thesis:{' '}
|
||||
<Link
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://drive.google.com/drive/folders/1Y7TJJN8z1llCkSTnpVHMELD3PqsX8VF9?usp=sharing">
|
||||
Voice Controlled Mobile Robot for Indoor Applications
|
||||
</Link>{' '}
|
||||
(research publication available{' '}
|
||||
<Link target="_blank" rel="noopener noreferrer" href="https://doi.org/10.1007/978-3-030-58380-4_7">
|
||||
<em>here</em>
|
||||
</Link>
|
||||
).
|
||||
</li>
|
||||
</ul>
|
||||
<Typography component="ul" sx={{ paddingLeft: '1em', marginTop: 0 }}>
|
||||
<li>Consortium scholarship issued for EMARO+ students based on academic merits and offered to top applicants only.</li>
|
||||
<li>Relevant courses: Research Methodology, Bio-Robotics, Dynamics of Multi-Body Systems, Advanced Mechanical Design.</li>
|
||||
<li>Other relevant courses: 2 exams of Polish language and 1 of Polish culture.</li>
|
||||
<li>
|
||||
Thesis:{' '}
|
||||
<Link
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://drive.google.com/drive/folders/1Y7TJJN8z1llCkSTnpVHMELD3PqsX8VF9?usp=sharing">
|
||||
Voice Controlled Mobile Robot for Indoor Applications
|
||||
</Link>{' '}
|
||||
(research publication available{' '}
|
||||
<Link target="_blank" rel="noopener noreferrer" href="https://doi.org/10.1007/978-3-030-58380-4_7">
|
||||
<em>here</em>
|
||||
</Link>
|
||||
).
|
||||
</li>
|
||||
</Typography>
|
||||
</CurriculumItem>
|
||||
{/* id="ECN" */}
|
||||
@@ -185,25 +181,23 @@ const Curriculum = () => {
|
||||
company="Ecole Centrale de Nantes"
|
||||
location="Nantes, France"
|
||||
dates="09/17 - 06/18">
|
||||
<Typography>
|
||||
<ul style={{ paddingLeft: '1em', marginTop: 0 }}>
|
||||
<li>Consortium scholarship issued for EMARO+ students based on academic merits and offered to top applicants only.</li>
|
||||
<li>
|
||||
Relevant courses: Mobile Robotics, Non-linear Control Theory, Software Architecture for Robotics, Artificial Intelligence, Computer
|
||||
Vision, Real Time Systems, Mechanical Design Methods in Robotics, Optimization Techniques.
|
||||
</li>
|
||||
<li>Other relevant courses: 2 exams of French language.</li>
|
||||
<li>
|
||||
Project:{' '}
|
||||
<Link
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://drive.google.com/file/d/1wBfvAzy9N8uChNxNj9Kd_hcvI0Qq3uQj/view?usp=sharing">
|
||||
Recharging electric cars with a mobile robot
|
||||
</Link>
|
||||
.
|
||||
</li>
|
||||
</ul>
|
||||
<Typography component="ul" sx={{ paddingLeft: '1em', marginTop: 0 }}>
|
||||
<li>Consortium scholarship issued for EMARO+ students based on academic merits and offered to top applicants only.</li>
|
||||
<li>
|
||||
Relevant courses: Mobile Robotics, Non-linear Control Theory, Software Architecture for Robotics, Artificial Intelligence, Computer
|
||||
Vision, Real Time Systems, Mechanical Design Methods in Robotics, Optimization Techniques.
|
||||
</li>
|
||||
<li>Other relevant courses: 2 exams of French language.</li>
|
||||
<li>
|
||||
Project:{' '}
|
||||
<Link
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://drive.google.com/file/d/1wBfvAzy9N8uChNxNj9Kd_hcvI0Qq3uQj/view?usp=sharing">
|
||||
Recharging electric cars with a mobile robot
|
||||
</Link>
|
||||
.
|
||||
</li>
|
||||
</Typography>
|
||||
</CurriculumItem>
|
||||
|
||||
@@ -227,18 +221,16 @@ const Curriculum = () => {
|
||||
// (grade: 5/5)
|
||||
location="Shanghai, China"
|
||||
dates="09/14 - 06/17">
|
||||
<Typography>
|
||||
<ul style={{ paddingLeft: '1em', marginTop: 0 }}>
|
||||
<li>Relevant courses: Automation Controls, Electrical Circuit, Logical Design of Digital Systems.</li>
|
||||
<li>Other relevant courses: 2 Exams about Chinese Language and 3 Exams about Chinese Culture.</li>
|
||||
<li>
|
||||
Thesis:{' '}
|
||||
<Link target="_blank" rel="noopener noreferrer" href="https://drive.google.com/file/d/0B1z42QMR-0UtYzBYSVhRV3ZPWk0/view?usp=sharing">
|
||||
Solar Panel Fault Detection Method Based on Infrared Image Processing
|
||||
</Link>
|
||||
.
|
||||
</li>
|
||||
</ul>
|
||||
<Typography component="ul" sx={{ paddingLeft: '1em', marginTop: 0 }}>
|
||||
<li>Relevant courses: Automation Controls, Electrical Circuit, Logical Design of Digital Systems.</li>
|
||||
<li>Other relevant courses: 2 Exams about Chinese Language and 3 Exams about Chinese Culture.</li>
|
||||
<li>
|
||||
Thesis:{' '}
|
||||
<Link target="_blank" rel="noopener noreferrer" href="https://drive.google.com/file/d/0B1z42QMR-0UtYzBYSVhRV3ZPWk0/view?usp=sharing">
|
||||
Solar Panel Fault Detection Method Based on Infrared Image Processing
|
||||
</Link>
|
||||
.
|
||||
</li>
|
||||
</Typography>
|
||||
</CurriculumItem>
|
||||
{/* id="UniBo" */}
|
||||
@@ -248,19 +240,17 @@ const Curriculum = () => {
|
||||
location="Bologna, Italy"
|
||||
//(grade: 102/110)
|
||||
dates="09/13 - 12/16">
|
||||
<Typography>
|
||||
<ul style={{ paddingLeft: '1em', marginTop: 0 }}>
|
||||
<li>Scholarship issued for Almatong Project student based on academic merits and offered to top applicants only.</li>
|
||||
<li>Relevant courses: Foundation of Industrial Robotics, Automatic Machines, Computer Architectures.</li>
|
||||
<li>Other relevant courses: 2 exams of French language.</li>
|
||||
<li>
|
||||
Thesis:{' '}
|
||||
<Link target="_blank" rel="noopener noreferrer" href="https://drive.google.com/file/d/0B1z42QMR-0UtSkxRVzJPTk1IX0k/view?usp=sharing">
|
||||
Design of a Testing Machine for Flexure Hinges
|
||||
</Link>
|
||||
.
|
||||
</li>
|
||||
</ul>
|
||||
<Typography component="ul" sx={{ paddingLeft: '1em', marginTop: 0 }}>
|
||||
<li>Scholarship issued for Almatong Project student based on academic merits and offered to top applicants only.</li>
|
||||
<li>Relevant courses: Foundation of Industrial Robotics, Automatic Machines, Computer Architectures.</li>
|
||||
<li>Other relevant courses: 2 exams of French language.</li>
|
||||
<li>
|
||||
Thesis:{' '}
|
||||
<Link target="_blank" rel="noopener noreferrer" href="https://drive.google.com/file/d/0B1z42QMR-0UtSkxRVzJPTk1IX0k/view?usp=sharing">
|
||||
Design of a Testing Machine for Flexure Hinges
|
||||
</Link>
|
||||
.
|
||||
</li>
|
||||
</Typography>
|
||||
</CurriculumItem>
|
||||
|
||||
@@ -305,50 +295,52 @@ const Curriculum = () => {
|
||||
<Divider sx={{ mt: 4, mb: 4 }} />
|
||||
<SectionHeader>Skills</SectionHeader>
|
||||
<Table>
|
||||
<TableRow>
|
||||
<TableCell width="15%">
|
||||
<Typography variant="h6" component="b" fontWeight={600}>
|
||||
Languages
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell width="85%">Italian (Native Speaker), English (Proficient, IELTS 7.5), French (Good), Chinese (Basic, HSK3)</TableCell>
|
||||
</TableRow>
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell width="15%">
|
||||
<Typography variant="h6" component="b" fontWeight={600}>
|
||||
Languages
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell width="85%">Italian (Native Speaker), English (Proficient, IELTS 7.5), French (Good), Chinese (Basic, HSK3)</TableCell>
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
<TableCell rowSpan={4} width="15%" id="digital_skills">
|
||||
<Typography variant="h6" component="b" fontWeight={600}>
|
||||
Digital
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell width="85%">
|
||||
<b>Languages:</b> TypeScript/Javascript (Proficient), Python (Proficient), Bash (Good), C++ (Good), C (Fair)
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<b>Softwares:</b> git (Proficient), Matlab (Good), LaTeX (Good), CATIA (Fair)
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<b>Middleware:</b> Robotic Operative System (ROS, Good)
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<b>OS Used:</b> Linux, Microsoft
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow sx={{ '&:last-child td': { borderBottom: 0 } }}>
|
||||
<TableCell width="15%">
|
||||
<Typography variant="h6" component="b" fontWeight={600}>
|
||||
Softskills
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell width="85%">
|
||||
Adaptability, Communication, Creativity, Critical Thinking, Problem-Solving, Self-Organization, Teamworking
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell rowSpan={4} width="15%" id="digital_skills">
|
||||
<Typography variant="h6" component="b" fontWeight={600}>
|
||||
Digital
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell width="85%">
|
||||
<b>Languages:</b> TypeScript/Javascript (Proficient), Python (Proficient), Bash (Good), C++ (Good), C (Fair)
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<b>Softwares:</b> git (Proficient), Matlab (Good), LaTeX (Good), CATIA (Fair)
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<b>Middleware:</b> Robotic Operative System (ROS, Good)
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<b>OS Used:</b> Linux, Microsoft
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow sx={{ '&:last-child td': { borderBottom: 0 } }}>
|
||||
<TableCell width="15%">
|
||||
<Typography variant="h6" component="b" fontWeight={600}>
|
||||
Softskills
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell width="85%">
|
||||
Adaptability, Communication, Creativity, Critical Thinking, Problem-Solving, Self-Organization, Teamworking
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
{/* Accomplishments */}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { screen, cleanup, render } from '@testing-library/react'
|
||||
import { screen, render } from '@testing-library/react'
|
||||
import { RouterProvider, createMemoryRouter } from 'react-router-dom'
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
import { useAnalytics } from 'use-analytics'
|
||||
@@ -10,19 +10,14 @@ vi.mock('use-analytics', () => ({
|
||||
useAnalytics: () => ({ plugins: { enable: vi.fn(), disable: vi.fn() } }),
|
||||
}))
|
||||
|
||||
// Tests
|
||||
describe('Renders ErrorPage correctly', async () => {
|
||||
/**
|
||||
* Resets all renders after each test
|
||||
*/
|
||||
afterEach(() => {
|
||||
cleanup()
|
||||
})
|
||||
// Suppress console.error messages during tests
|
||||
const originalConsoleError = console.error
|
||||
console.error = vi.fn()
|
||||
|
||||
/**
|
||||
* Passes - shows title correctly
|
||||
*/
|
||||
describe('Renders ErrorPage correctly', async () => {
|
||||
it('Should render the error page correctly', () => {
|
||||
console.error = vi.fn()
|
||||
|
||||
const badRoute = '/some/bad/route'
|
||||
const router = createMemoryRouter([{ path: '/', element: <div />, errorElement: <ErrorPage /> }], { initialEntries: [badRoute] })
|
||||
|
||||
@@ -33,3 +28,5 @@ describe('Renders ErrorPage correctly', async () => {
|
||||
expect(screen.getByText(`404 Error: No route matches URL "${badRoute}"`)).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
console.error = originalConsoleError
|
||||
|
||||
@@ -18,25 +18,10 @@ describe('Main page', () => {
|
||||
// Make sure the page title is correct
|
||||
expect(screen.getByText('Robotics Engineer')).toBeInTheDocument()
|
||||
|
||||
// Make sure the project spotlights are present
|
||||
expect(screen.getByText('LINFA: An Intuitive Block-Programming Interface for Reconfigurable Robots')).toBeInTheDocument()
|
||||
expect(screen.getByText("Revamping HHCM's Modular: Swift Generation of Modular Robot Models")).toBeInTheDocument()
|
||||
expect(screen.getByText('Automated Actuators and Robotic Modules Calibration')).toBeInTheDocument()
|
||||
expect(screen.getByText('Voice Controlled Mobile Robot for Indoor Applications')).toBeInTheDocument()
|
||||
})
|
||||
it('Should render the page correctly on mobile', async () => {
|
||||
// Set the window size to mobile (Galaxy Note 20 Android 11)
|
||||
window.innerWidth = 412
|
||||
window.innerHeight = 915
|
||||
await render(<MainPage />)
|
||||
|
||||
// Make sure the page title is correct
|
||||
expect(screen.getByText('Robotics Engineer')).toBeInTheDocument()
|
||||
|
||||
// Make sure the project spotlights are present
|
||||
expect(screen.getByText('LINFA: An Intuitive Block-Programming Interface for Reconfigurable Robots')).toBeInTheDocument()
|
||||
expect(screen.getByText("Revamping HHCM's Modular: Swift Generation of Modular Robot Models")).toBeInTheDocument()
|
||||
expect(screen.getByText('Automated Actuators and Robotic Modules Calibration')).toBeInTheDocument()
|
||||
expect(screen.getByText('Voice Controlled Mobile Robot for Indoor Applications')).toBeInTheDocument()
|
||||
// Make sure the project spotlights are present for both desktop and mobile
|
||||
expect(screen.getAllByText('LINFA: An Intuitive Block-Programming Interface for Reconfigurable Robots')).toHaveLength(2)
|
||||
expect(screen.getAllByText("Revamping HHCM's Modular: Swift Generation of Modular Robot Models")).toHaveLength(2)
|
||||
expect(screen.getAllByText('Automated Actuators and Robotic Modules Calibration')).toHaveLength(2)
|
||||
expect(screen.getAllByText('Voice Controlled Mobile Robot for Indoor Applications')).toHaveLength(2)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2,5 +2,6 @@ export { default as MainPage } from './MainPage'
|
||||
export { default as ContactPage } from './ContactPage'
|
||||
export { default as CurriculumPage } from './CurriculumPage'
|
||||
export { default as ProjectsPage } from './ProjectsPage'
|
||||
export { default as BusinessCardPage } from './BusinessCardPage'
|
||||
|
||||
export { default as ErrorPage } from './ErrorPage'
|
||||
|
||||
@@ -19,11 +19,17 @@ const Page: React.FC<PageProps> = ({ title, children, showCookieConsent, ...prop
|
||||
}, [title])
|
||||
|
||||
return (
|
||||
<Box sx={{ flexGrow: 1 }} aria-label="page">
|
||||
<Box
|
||||
aria-label="page"
|
||||
sx={{
|
||||
'@media print': { backgroundColor: 'background.default' },
|
||||
flexGrow: 1,
|
||||
}}>
|
||||
<Toolbar />
|
||||
{title && (
|
||||
<>
|
||||
<Typography
|
||||
id="page-title"
|
||||
variant="h1"
|
||||
aria-label="page-title"
|
||||
textAlign="center"
|
||||
@@ -53,8 +59,8 @@ const Page: React.FC<PageProps> = ({ title, children, showCookieConsent, ...prop
|
||||
</>
|
||||
)}
|
||||
<Container
|
||||
id="page-content"
|
||||
component="main"
|
||||
maxWidth={false}
|
||||
sx={{
|
||||
mt: { xs: 8, md: 12 },
|
||||
mb: 4,
|
||||
|
||||
@@ -14,6 +14,11 @@ afterEach(() => {
|
||||
cleanup()
|
||||
})
|
||||
|
||||
/**
|
||||
* Mocks the window.scrollTo method (called on page change)
|
||||
*/
|
||||
vi.stubGlobal('scrollTo', vi.fn())
|
||||
|
||||
/**
|
||||
* Mocks the ResizeObserver API (used by ParallaxProvider)
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { PaletteOptions } from '@mui/material'
|
||||
|
||||
export const dark: PaletteOptions = {
|
||||
mode: 'dark',
|
||||
primary: {
|
||||
main: '#e44c65', // Rouge Red (similar)
|
||||
// main:'#df4661', // Pantone PMS 198 C
|
||||
},
|
||||
secondary: {
|
||||
main: '#3f51b5', // Early Spring Night (similar)
|
||||
// main: '#3a5dae', // Pantone 7455 C Color
|
||||
},
|
||||
background: {
|
||||
default: '#101018',
|
||||
paper: '#1b1d27',
|
||||
},
|
||||
}
|
||||
export const light: PaletteOptions = {
|
||||
primary: {
|
||||
main: '#AC1D1B', // Casual Red
|
||||
// main: '#AF231C' // Pantone 2350 C
|
||||
// main: '#AB2328' // Pantone 7621 C
|
||||
},
|
||||
secondary: {
|
||||
main: '#006269', // Pantone 7715 C
|
||||
},
|
||||
background: { default: '#fafafa', paper: '#eeeeee' }, // Light background
|
||||
text: { secondary: '#666' }, // Dark text for readability
|
||||
}
|
||||
|
||||
export default { dark, light }
|
||||
+16
-50
@@ -1,58 +1,24 @@
|
||||
/* istanbul ignore file */
|
||||
import { createTheme, responsiveFontSizes } from '@mui/material/styles'
|
||||
// A custom theme for this app
|
||||
let theme = createTheme({
|
||||
typography: {
|
||||
fontFamily: 'Inter, sans-serif', // Base font for headings
|
||||
|
||||
h1: {
|
||||
fontFamily: 'Oswald, sans-serif',
|
||||
textTransform: 'uppercase',
|
||||
fontWeight: 700,
|
||||
},
|
||||
h2: {
|
||||
fontFamily: 'Oswald, sans-serif',
|
||||
textTransform: 'uppercase',
|
||||
fontWeight: 700,
|
||||
},
|
||||
h3: {
|
||||
fontFamily: 'Oswald, sans-serif',
|
||||
fontWeight: 700,
|
||||
},
|
||||
h4: {
|
||||
fontFamily: 'Oswald, sans-serif',
|
||||
fontWeight: 700,
|
||||
},
|
||||
},
|
||||
import typography from './typography/index.js'
|
||||
import palette from './palette/index.js'
|
||||
|
||||
let theme = createTheme({
|
||||
typography: typography,
|
||||
colorSchemes: {
|
||||
dark: {
|
||||
palette: {
|
||||
primary: {
|
||||
main: '#e44c65', // Rouge Red (similar)
|
||||
// main:'#df4661', // Pantone PMS 198 C
|
||||
light: { palette: palette.light },
|
||||
dark: { palette: palette.dark },
|
||||
},
|
||||
components: {
|
||||
MuiCollapse: {
|
||||
styleOverrides: {
|
||||
hidden: {
|
||||
'@media print': {
|
||||
visibility: 'visible',
|
||||
height: 'auto',
|
||||
},
|
||||
},
|
||||
secondary: {
|
||||
main: '#3f51b5', // Early Spring Night (similar)
|
||||
// main: '#3a5dae', // Pantone 7455 C Color
|
||||
},
|
||||
background: {
|
||||
default: '#101018',
|
||||
paper: '#1b1d27',
|
||||
},
|
||||
},
|
||||
},
|
||||
light: {
|
||||
palette: {
|
||||
primary: {
|
||||
main: '#AC1D1B', // Casual Red
|
||||
// main: '#AF231C' // Pantone 2350 C
|
||||
// main: '#AB2328' // Pantone 7621 C
|
||||
},
|
||||
secondary: {
|
||||
main: '#006269', // Pantone 7715 C
|
||||
},
|
||||
background: { default: '#fafafa', paper: '#eeeeee' }, // Light background
|
||||
text: { secondary: '#666' }, // Dark text for readability
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { TypographyVariantsOptions } from '@mui/material'
|
||||
|
||||
const typography: TypographyVariantsOptions = {
|
||||
fontFamily: 'Inter, sans-serif', // Base font for headings
|
||||
|
||||
h1: {
|
||||
fontFamily: 'Oswald, sans-serif',
|
||||
textTransform: 'uppercase',
|
||||
fontWeight: 700,
|
||||
},
|
||||
h2: {
|
||||
fontFamily: 'Oswald, sans-serif',
|
||||
textTransform: 'uppercase',
|
||||
fontWeight: 700,
|
||||
},
|
||||
h3: {
|
||||
fontFamily: 'Oswald, sans-serif',
|
||||
fontWeight: 700,
|
||||
},
|
||||
h4: {
|
||||
fontFamily: 'Oswald, sans-serif',
|
||||
fontWeight: 700,
|
||||
},
|
||||
}
|
||||
|
||||
export default typography
|
||||
Vendored
+6
@@ -2,6 +2,12 @@
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_GA_MEASUREMENT_ID: string
|
||||
readonly VITE_BC_NAME: string
|
||||
readonly VITE_BC_POSITION: string
|
||||
readonly VITE_BC_VAT: string
|
||||
readonly VITE_BC_PHONE: string
|
||||
readonly VITE_BC_EMAIL: string
|
||||
readonly VITE_BC_LINKEDIN: string
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
|
||||
Reference in New Issue
Block a user