Skip to content

Commit 2c9deaf

Browse files
committed
feat: improve profile data and dynamic project filters
1 parent 38a571d commit 2c9deaf

File tree

18 files changed

+158
-176
lines changed

18 files changed

+158
-176
lines changed

app/app.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ useHead({
2323
],
2424
link: [
2525
{ rel: "canonical", href: "https://patrickluz.dev" },
26-
{ rel: "preload", as: "image", href: "/logo.jpeg" },
26+
{ rel: "preload", as: "image", href: "/logo.webp" },
2727
],
2828
});
29-
</script>
29+
</script>

app/components/header/components/HeaderProfile.vue

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
<template>
22
<div class="flex items-center gap-2 md:gap-3">
33
<NuxtLink to="/">
4-
<div class="w-8 h-8 md:w-10 md:h-10 rounded-full overflow-hidden ring-1 ring-stone-200">
4+
<div
5+
class="w-8 h-8 md:w-10 md:h-10 rounded-full overflow-hidden ring-1 ring-stone-200"
6+
>
57
<img
6-
src="/logo.jpeg"
8+
src="/logo.webp"
79
alt="Patrick Luz"
810
class="w-full h-full object-cover"
911
/>

app/components/sidebar/Sidebar.vue

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
<!-- Avatar -->
55
<div class="mb-8 pl-1">
66
<NuxtLink to="/" class="block">
7-
<div class="w-[72px] h-[72px] rounded-full overflow-hidden ring-2 ring-stone-200 ring-offset-2">
7+
<div
8+
class="w-[72px] h-[72px] rounded-full overflow-hidden ring-2 ring-stone-200 ring-offset-2"
9+
>
810
<img
9-
src="/logo.jpeg"
11+
src="/logo.webp"
1012
alt="Patrick Luz"
1113
class="w-full h-full object-cover"
1214
/>
@@ -24,31 +26,30 @@
2426
:class="[
2527
isActiveRoute(item.to)
2628
? 'text-stone-900 font-medium before:absolute before:left-[-12px] before:top-1/2 before:-translate-y-1/2 before:w-[3px] before:h-5 before:bg-stone-400 before:rounded-full'
27-
: 'text-stone-500 hover:text-stone-900'
29+
: 'text-stone-500 hover:text-stone-900',
2830
]"
2931
>
3032
{{ item.label }}
3133
</NuxtLink>
3234
</nav>
33-
3435
</div>
3536
</aside>
3637
</template>
3738

3839
<script setup lang="ts">
39-
const route = useRoute()
40+
const route = useRoute();
4041
4142
const menuItems = [
42-
{ label: 'Home', to: '/' },
43-
{ label: 'Sobre mim', to: '/sobre' },
44-
{ label: 'Meus projetos', to: '/projetos' },
45-
{ label: 'Contato', to: '/contato' }
46-
]
43+
{ label: "Home", to: "/" },
44+
{ label: "Sobre mim", to: "/sobre" },
45+
{ label: "Meus projetos", to: "/projetos" },
46+
{ label: "Contato", to: "/contato" },
47+
];
4748
4849
const isActiveRoute = (path: string) => {
49-
if (path === '/') {
50-
return route.path === '/'
50+
if (path === "/") {
51+
return route.path === "/";
5152
}
52-
return route.path.startsWith(path)
53-
}
53+
return route.path.startsWith(path);
54+
};
5455
</script>

app/composables/useProfile.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,53 @@ import {
55
testimonials,
66
} from "~/data";
77

8+
const monthMap: Record<string, number> = {
9+
jan: 0, fev: 1, mar: 2, abr: 3, mai: 4, jun: 5,
10+
jul: 6, ago: 7, set: 8, out: 9, nov: 10, dez: 11,
11+
};
12+
13+
function parsePeriod(period: string): { start: Date; end: Date } {
14+
const parts = period.split(" - ");
15+
const startStr = parts[0] ?? "";
16+
const endStr = parts[1] ?? "";
17+
18+
const startParts = startStr.split("/");
19+
const startMonth = startParts[0] ?? "";
20+
const startYear = startParts[1] ?? "";
21+
const start = new Date(parseInt(startYear), monthMap[startMonth] ?? 0);
22+
23+
let end: Date;
24+
if (endStr === "atual") {
25+
end = new Date();
26+
} else {
27+
const endParts = endStr.split("/");
28+
const endMonth = endParts[0] ?? "";
29+
const endYear = endParts[1] ?? "";
30+
end = new Date(parseInt(endYear), monthMap[endMonth] ?? 0);
31+
}
32+
33+
return { start, end };
34+
}
35+
36+
function calculateDuration(period: string): string {
37+
const { start, end } = parsePeriod(period);
38+
39+
let months = (end.getFullYear() - start.getFullYear()) * 12;
40+
months += end.getMonth() - start.getMonth();
41+
months += 1;
42+
43+
const years = Math.floor(months / 12);
44+
const remainingMonths = months % 12;
45+
46+
if (years === 0) {
47+
return `${remainingMonths} ${remainingMonths === 1 ? "mês" : "meses"}`;
48+
} else if (remainingMonths === 0) {
49+
return `${years} ${years === 1 ? "ano" : "anos"}`;
50+
} else {
51+
return `${years} ${years === 1 ? "ano" : "anos"} e ${remainingMonths} ${remainingMonths === 1 ? "mês" : "meses"}`;
52+
}
53+
}
54+
855
interface ExperienceWithError {
956
title: string;
1057
company: string;
@@ -43,5 +90,6 @@ export const useProfile = () => {
4390
education,
4491
skillCategories,
4592
testimonials,
93+
calculateDuration,
4694
};
4795
};

app/composables/useProjects.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ export const useProjects = () => {
66
const selectedTypes = ref<ProjectType[]>([]);
77
const selectedTechs = ref<string[]>([]);
88

9+
const availableTypes = computed(() => {
10+
const types = new Set(projects.map((p) => p.type));
11+
return projectTypes.filter((t) => types.has(t.value));
12+
});
13+
914
const availableTechs = computed(() => {
1015
const techs = new Set<string>();
1116
projects.forEach((p) => p.stack.forEach((t) => techs.add(t)));
@@ -90,7 +95,7 @@ export const useProjects = () => {
9095

9196
return {
9297
projects,
93-
projectTypes,
98+
availableTypes,
9499
availableTechs,
95100
searchQuery,
96101
selectedTypes,

app/data/content.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,14 @@ export const servicesDetailed: Service[] = [
3535
];
3636

3737
export const metricsHome: Metric[] = [
38-
{ value: "+5", label: "Anos de exp." },
38+
{ value: "+6", label: "Anos de exp." },
3939
{ value: "6", label: "Empresas" },
4040
{ value: "24h", label: "Tempo de resposta" },
4141
{ value: "+100M", label: "Usuários" },
4242
];
4343

4444
export const metricsAbout: Metric[] = [
45-
{ value: "+5", label: "Anos de exp." },
45+
{ value: "+6", label: "Anos de exp." },
4646
{ value: "6", label: "Empresas" },
4747
{ value: "24h", label: "Tempo de resposta" },
4848
{ value: "+100M", label: "Usuários" },
@@ -52,7 +52,7 @@ export const faqs: FAQ[] = [
5252
{
5353
question: "Quanto custa um projeto?",
5454
answer:
55-
"O valor depende da complexidade e escopo do projeto. Após entender suas necessidades, envio uma proposta detalhada com valores e prazos. Projetos simples começam a partir de R$ 3.000.",
55+
"O valor depende da complexidade e escopo do projeto. Após entender suas necessidades, envio uma proposta detalhada com valores e prazos.",
5656
},
5757
{
5858
question: "Qual o prazo de entrega?",

app/data/profile.ts

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Experience, Education, SkillCategory, Testimonial } from "./types";
1+
import type { Education, Experience, SkillCategory, Testimonial } from "./types";
22

33
export const experiences: Experience[] = [
44
{
@@ -75,24 +75,26 @@ export const skillCategories: SkillCategory[] = [
7575
{ name: "Go", icon: "logos:go" },
7676
{ name: "Java", icon: "logos:java" },
7777
{ name: "Node.js", icon: "logos:nodejs-icon" },
78+
{ name: "Python", icon: "logos:python" },
7879
{ name: "PHP", icon: "logos:php" },
7980
],
8081
},
8182
{
8283
name: "Frontend",
8384
skills: [
8485
{ name: "React", icon: "logos:react" },
86+
{ name: "Next.js", icon: "logos:nextjs-icon" },
8587
{ name: "Vue.js", icon: "logos:vue" },
86-
{ name: "JavaScript", icon: "logos:javascript" },
87-
{ name: "HTML5", icon: "logos:html-5" },
88-
{ name: "CSS3", icon: "logos:css-3" },
89-
{ name: "Sass", icon: "logos:sass" },
88+
{ name: "Nuxt", icon: "logos:nuxt-icon" },
89+
{ name: "TypeScript", icon: "logos:typescript-icon" },
90+
{ name: "Tailwind", icon: "logos:tailwindcss-icon" },
9091
],
9192
},
9293
{
9394
name: "Banco de Dados",
9495
skills: [
9596
{ name: "PostgreSQL", icon: "logos:postgresql" },
97+
{ name: "MongoDB", icon: "logos:mongodb-icon" },
9698
{ name: "DynamoDB", icon: "logos:aws-dynamodb" },
9799
{ name: "Elasticsearch", icon: "logos:elasticsearch" },
98100
],
@@ -105,26 +107,30 @@ export const skillCategories: SkillCategory[] = [
105107
{ name: "Docker", icon: "logos:docker-icon" },
106108
{ name: "Kubernetes", icon: "logos:kubernetes" },
107109
{ name: "Terraform", icon: "logos:terraform-icon" },
108-
{ name: "Jenkins", icon: "logos:jenkins" },
110+
{ name: "Prometheus", icon: "logos:prometheus" },
109111
{ name: "Grafana", icon: "logos:grafana" },
110-
{ name: "Datadog", icon: "logos:datadog" },
111112
{ name: "Git", icon: "logos:git-icon" },
113+
],
114+
},
115+
{
116+
name: "Ferramentas",
117+
skills: [
118+
{ name: "Figma", icon: "logos:figma" },
119+
{ name: "n8n", icon: "simple-icons:n8n" },
112120
{ name: "Linux", icon: "logos:linux-tux" },
121+
{ name: "Datadog", icon: "logos:datadog" },
122+
{ name: "Jenkins", icon: "logos:jenkins" },
113123
],
114124
},
115125
];
116126

117127
export const testimonials: Testimonial[] = [
118128
{
119-
name: "João Silva",
120-
initials: "JS",
121-
role: "Tech Lead @ Hurst Capital",
122-
text: "Patrick é um desenvolvedor excepcional. Entrega código de alta qualidade e sempre busca as melhores soluções para os problemas.",
123-
},
124-
{
125-
name: "Maria Santos",
126-
initials: "MS",
127-
role: "Product Manager @ Inter",
128-
text: "Trabalhar com o Patrick foi ótimo. Muito proativo, entende o negócio e traduz requisitos em soluções técnicas eficientes.",
129+
name: "Ângelo Kusmann Cavalet",
130+
initials: "AK",
131+
role: "Tech Lead @ IXCSoft",
132+
text: "Patrick é uma pessoa carismática, a comunicação com ele acontece de forma descontraída mas também produtiva. Resolve de forma ágil e competente os problemas designados, buscando sempre as melhores soluções. Gosta de um código bem escrito e estruturado, e encara o desconhecido como oportunidade de aprender.",
133+
image: "https://media.licdn.com/dms/image/v2/D4D03AQH1pjyWVjQm2A/profile-displayphoto-shrink_100_100/profile-displayphoto-shrink_100_100/0/1673991544727?e=1765411200&v=beta&t=_mi28hBz5IEvFbgEk797m0n3Jt5QjUigoRRxdskU288",
134+
link: "https://www.linkedin.com/in/angelocavallet",
129135
},
130136
];

app/data/projects.ts

Lines changed: 7 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,118 +1,21 @@
11
import type { Project } from "./types";
22

33
export const projects: Project[] = [
4-
{
5-
id: "1",
6-
title: "Sistema de Gestão",
7-
description:
8-
"Sistema completo para gestão empresarial com dashboard, relatórios e controle de estoque.",
9-
image: "/projects/gestao.png",
10-
stack: ["Vue.js", "Node.js", "PostgreSQL"],
11-
status: "completed",
12-
year: 2024,
13-
type: "web",
14-
category: "featured",
15-
links: {
16-
demo: "https://exemplo.com",
17-
github: "https://github.com/plfrancisco/gestao",
18-
},
19-
},
20-
{
21-
id: "2",
22-
title: "E-commerce Platform",
23-
description:
24-
"Loja virtual completa com carrinho, pagamentos Stripe e painel administrativo.",
25-
image: "/projects/ecommerce.png",
26-
stack: ["React", "Go", "PostgreSQL", "Stripe"],
27-
status: "completed",
28-
year: 2024,
29-
type: "web",
30-
category: "featured",
31-
links: {
32-
demo: "https://exemplo.com",
33-
},
34-
},
35-
{
36-
id: "3",
37-
title: "Boilerplate Nuxt",
38-
description:
39-
"Template Nuxt 3 com Tailwind, shadcn/ui, autenticação e estrutura pronta para produção.",
40-
stack: ["Nuxt", "Tailwind", "TypeScript"],
41-
status: "completed",
42-
year: 2024,
43-
type: "tool",
44-
category: "personal",
45-
links: {
46-
github: "https://github.com/plfrancisco/nuxt-boilerplate",
47-
},
48-
},
494
{
505
id: "4",
516
title: "Patrick.dev",
527
description: "Meu site pessoal. O que você está vendo agora.",
53-
stack: ["Nuxt", "Tailwind", "Supabase"],
8+
stack: ["Nuxt", "Tailwind", "TypeScript"],
549
status: "development",
55-
year: 2024,
10+
year: 2025,
5611
type: "web",
57-
category: "personal",
58-
links: {
59-
github: "https://github.com/plfrancisco/my-site-vue",
60-
},
61-
},
62-
{
63-
id: "5",
64-
title: "Vue Component Library",
65-
description:
66-
"Biblioteca de componentes Vue 3 com acessibilidade e customização via CSS variables.",
67-
stack: ["Vue.js", "TypeScript", "Vite"],
68-
status: "development",
69-
year: 2024,
70-
type: "tool",
71-
category: "opensource",
72-
links: {
73-
github: "https://github.com/plfrancisco/vue-components",
74-
demo: "https://components.patrick.dev",
75-
},
76-
},
77-
{
78-
id: "6",
79-
title: "App de Delivery",
80-
description:
81-
"Aplicativo para delivery de comida com rastreamento em tempo real e notificações push.",
82-
image: "/projects/delivery.png",
83-
stack: ["React Native", "Node.js", "Firebase"],
84-
status: "completed",
85-
year: 2023,
86-
type: "mobile",
87-
category: "client",
88-
links: {},
89-
},
90-
{
91-
id: "7",
92-
title: "API de Pagamentos",
93-
description:
94-
"Microserviço de processamento de pagamentos com integração multi-gateway.",
95-
stack: ["Go", "gRPC", "Redis", "PostgreSQL"],
96-
status: "completed",
97-
year: 2023,
98-
type: "api",
99-
category: "client",
100-
links: {},
101-
},
102-
{
103-
id: "8",
104-
title: "CLI Task Manager",
105-
description:
106-
"Gerenciador de tarefas via terminal com sync para múltiplos dispositivos.",
107-
stack: ["Go", "SQLite", "Bubble Tea"],
108-
status: "development",
109-
year: 2024,
110-
type: "tool",
111-
category: "experiment",
12+
category: "featured",
13+
image: "/projects/my-site.webp",
11214
links: {
113-
github: "https://github.com/plfrancisco/task-cli",
15+
github: "https://github.com/patrickluzdev/my-site",
16+
demo: "https://patrickluz.dev/"
11417
},
115-
},
18+
}
11619
];
11720

11821
export const projectTypes = [

0 commit comments

Comments
 (0)