Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,36 @@
</a>

<p></p>

## Updating Portfolio Content

The content of this portfolio (About Me, Experience, Projects) is managed through a central JSON file located at `src/data/content.json`.

To update your portfolio information:

1. **Open `src/data/content.json`**.
2. **Modify the data**:
* **`aboutMe`**:
* `personalImageAlt`: Alt text for your main image.
* `paragraphs`: An array of strings. Each string is a paragraph in the "About Me" section. You can use HTML tags like `<strong>` or `<em>` for emphasis.
* **`experience`**: An array of experience objects. Each object has:
* `date`: Date or period of the experience.
* `title`: Your job title.
* `company`: Company name.
* `description`: Description of your role and achievements.
* `link` (optional): A URL for more details.
* **`projects`**:
* `tags`: An object defining available tags for projects. Each key (e.g., "NEXT") is a tag identifier used in `project.items`. It contains:
* `name`: Display name of the tag (e.g., "Next.js").
* `class`: Tailwind CSS classes for styling the tag badge.
* `icon`: A string key (e.g., "NextJS") that maps to an icon component in `src/components/Projects.astro`. Ensure the corresponding icon component (e.g., `NextJS.astro`) exists in `src/components/icons/` and is mapped in the `ICONS` object within `src/components/Projects.astro`.
* `items`: An array of project objects. Each object has:
* `title`: Project title.
* `description`: Project description.
* `link` (optional): Live preview URL.
* `github` (optional): GitHub repository URL.
* `image`: Path to the project image (usually in `public/projects/`).
* `tags`: An array of string keys (e.g., `["NEXT", "TAILWIND"]`) that correspond to keys in `projects.tags`.
3. **Save the file**. The website will automatically reflect the changes when you rebuild or if your development server is running with HMR (Hot Module Replacement).

Make sure the JSON structure remains valid after your changes.
28 changes: 5 additions & 23 deletions src/components/AboutMe.astro
Original file line number Diff line number Diff line change
@@ -1,33 +1,15 @@
---
const personalImageAlt = "Miguel Ángel"
import contentData from '@/data/content.json';
const { personalImageAlt, paragraphs } = contentData.aboutMe;
---

<article class="flex flex-col items-center justify-center gap-8 text-gray-700 dark:text-gray-300 md:flex-row">
<div
class="[&>p]:mb-4 [&>p>strong]:text-yellow-500 dark:[&>p>strong]:text-yellow-100 [&>p>strong]:font-normal [&>p>strong]:font-mono text-pretty order-2 md:order-1"
>
<p>
Me llamo Miguel Ángel pero mis amigos me llaman midu. Empecé en la
programación con un Amstrad, tenía 10 años. Actualmente estoy <strong
>liderando equipos de desarrollo en multinacionales</strong
>.
</p>

<p>
Algunos de mis éxitos incluyen <strong
>colaborar con Mozilla para el desarrollo de las primeras apps en su
sistema FirefoxOS</strong
>. Aunque hoy está desaparecido fue un gran avance en el mundo del
desarrollo web.
</p>

<p>
Como creador de contenido, <strong
>cuento con el canal de habla hispana más visto del mundo en la
categoría de <em class="italic">Software & Game Development</em> en Twitch</strong
>. Mi objetivo es mejorar la empleabilidad de la comunidad hispana y el
acceso a contenido de calidad.
</p>
{paragraphs.map((paragraph: any) => (
<p set:html={paragraph}></p>
))}
</div>

<img width="200" height="200" src="/me.png" alt={personalImageAlt} class="order-1 object-cover w-64 h-full p-1 md:order-2 rotate-3 lg:p-2 lg:w-64 aspect-square rounded-2xl bg-black/20 dark:bg-yellow-500/5 ring-1 ring-black/70 dark:ring-white/20 " style="object-position: 50% 50%">
Expand Down
22 changes: 3 additions & 19 deletions src/components/Experience.astro
Original file line number Diff line number Diff line change
@@ -1,27 +1,11 @@
---
import ExperienceItem from "./ExperienceItem.astro"
const EXPERIENCE = [
{
date: "Actualmente...",
title: "Creador de Contenido",
company: "Twitch",
description:
"Divulgo sobre programación y desarrollo web en diferentes plataformas. Galardonado como mejor creador de contenido de habla no-inglesa en 2022 y mejor comunidad en 2023 por GitHub.",
link: "https://twitch.tv/midudev",
},
{
date: "Septiembre 2022",
title: "Principal Frontend Engineer",
company: "Adevinta Spain",
description:
"Responsable de la plataforma, componentes y utilidades para la creación y desarrollo de aplicaciones web. Mejora de un 30% en la entrega de software. Implantación de medidas de integración continua y despliegue con A/B testing en más de 15 equipos.",
},
]
import ExperienceItem from "./ExperienceItem.astro";
import contentData from '@/data/content.json';
---

<ol class="relative mt-16">
{
EXPERIENCE.map((experience) => (
contentData.experience.map((experience: any) => (
<li class="">
<ExperienceItem {...experience} />
</li>
Expand Down
91 changes: 37 additions & 54 deletions src/components/Projects.astro
Original file line number Diff line number Diff line change
@@ -1,82 +1,65 @@
---
import GitHub from "./icons/GitHub.astro"
import NextJS from "./icons/NextJS.astro"
import Tailwind from "./icons/Tailwind.astro"
import Link from "./icons/Link.astro"
import LinkButton from "./LinkButton.astro"
import GitHub from "./icons/GitHub.astro";
import Link from "./icons/Link.astro";
import LinkButton from "./LinkButton.astro";
import contentData from '@/data/content.json';

const TAGS = {
NEXT: {
name: "Next.js",
class: "bg-black text-white",
icon: NextJS,
},
TAILWIND: {
name: "Tailwind CSS",
class: "bg-[#003159] text-white",
icon: Tailwind,
},
}
const PROJECTS = [
{
title: "SVGL - A beautiful library with SVG logos",
description:
"Biblioteca de logos SVG de las marcas más populares. +10k visitas al mes. +2K svgs descargados. Creado desde cero con Next.js, React y Tailwind CSS.",
link: "https://svgl.vercel.app/",
github: "https://github.com/pheralb/svgl",
image: "/projects/svgl.webp",
tags: [TAGS.NEXT, TAGS.TAILWIND],
},
{
title: "AdventJS - Retos de programación con JavaScript y TypeScript",
description:
"Plataforma gratuita con retos de programación. Más de 1 millón de visitas en un mes. +50K retos completados. Creada desde cero con Next.js, React y Tailwind CSS.",
link: "https://adventjs.dev",
image: "/projects/adventjs.webp",
tags: [TAGS.NEXT, TAGS.TAILWIND],
},
]
// Import required icons
import NextJS from "./icons/NextJS.astro";
import Tailwind from "./icons/Tailwind.astro";
// Potentially import other icons if your JSON defines them

// Map icon strings from JSON to actual components
const ICONS = {
NextJS: NextJS,
Tailwind: Tailwind,
// Add other icons here corresponding to content.json
};

const { tags: projectTagsData, items: projectItems } = contentData.projects;
---

<div class="flex flex-col gap-y-16">
{
PROJECTS.map(({ image, title, description, tags, link, github }) => (
projectItems.map((project: any) => (
<article class="flex flex-col space-x-0 space-y-8 group md:flex-row md:space-x-8 md:space-y-0">
<div class="w-full md:w-1/2">
<div class="relative flex flex-col items-center col-span-6 row-span-5 gap-8 transition duration-500 ease-in-out transform shadow-xl overflow-clip rounded-xl sm:rounded-xl md:group-hover:-translate-y-1 md:group-hover:shadow-2xl lg:border lg:border-gray-800 lg:hover:border-gray-700 lg:hover:bg-gray-800/50">
<img alt="Recién llegado vs 5 años en Nueva Zelanda" class="object-cover object-top w-full h-56 transition duration-500 sm:h-full md:scale-110 md:group-hover:scale-105" loading="lazy" src={image} />
<img alt={project.title} class="object-cover object-top w-full h-56 transition duration-500 sm:h-full md:scale-110 md:group-hover:scale-105" loading="lazy" src={project.image} />
</div>
</div>

<div class="w-full md:w-1/2 md:max-w-lg">
<h3 class="text-2xl font-bold text-gray-800 dark:text-gray-100">
{title}
{project.title}
</h3>
<div class="flex flex-wrap mt-2">
<ul class="flex flex-row mb-2 gap-x-2">
{tags.map((tag) => (
<li>
<span
class={`flex gap-x-2 rounded-full text-xs ${tag.class} py-1 px-2 `}
>
<tag.icon class="size-4" />
{tag.name}
</span>
</li>
))}
{project.tags.map((tagKey: keyof typeof projectTagsData) => {
const tagDetails = projectTagsData[tagKey];
const IconComponent = ICONS[tagDetails.icon as keyof typeof ICONS];
return (
<li>
<span class={`flex gap-x-2 rounded-full text-xs ${tagDetails.class} py-1 px-2 `}>
{IconComponent && <IconComponent class="size-4" />}
{tagDetails.name}
</span>
</li>
);
})}
</ul>

<div class="mt-2 text-gray-700 dark:text-gray-400">{description}</div>
<div class="mt-2 text-gray-700 dark:text-gray-400">{project.description}</div>
<footer class="flex items-end justify-start mt-4 gap-x-4">
{github && (
<LinkButton href={github}>
{project.github && (
<LinkButton href={project.github}>
<GitHub class="size-6" />
Code

</LinkButton>
)}
{link && (
<LinkButton href={link}>
{project.link && (
<LinkButton href={project.link}>
<Link class="size-4" />
Preview
</LinkButton>
Expand Down
56 changes: 56 additions & 0 deletions src/data/content.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"aboutMe": {
"personalImageAlt": "Miguel Ángel",
"paragraphs": [
"Me llamo Miguel Ángel pero mis amigos me llaman midu. Empecé en la programación con un Amstrad, tenía 10 años. Actualmente estoy <strong>liderando equipos de desarrollo en multinacionales</strong>.",
"Algunos de mis éxitos incluyen <strong>colaborar con Mozilla para el desarrollo de las primeras apps en su sistema FirefoxOS</strong>. Aunque hoy está desaparecido fue un gran avance en el mundo del desarrollo web.",
"Como creador de contenido, <strong>cuento con el canal de habla hispana más visto del mundo en la categoría de <em>Software & Game Development</em> en Twitch</strong>. Mi objetivo es mejorar la empleabilidad de la comunidad hispana y el acceso a contenido de calidad."
]
},
"experience": [
{
"date": "Actualmente...",
"title": "Creador de Contenido",
"company": "Twitch",
"description": "Divulgo sobre programación y desarrollo web en diferentes plataformas. Galardonado como mejor creador de contenido de habla no-inglesa en 2022 y mejor comunidad en 2023 por GitHub.",
"link": "https://twitch.tv/midudev"
},
{
"date": "Septiembre 2022",
"title": "Principal Frontend Engineer",
"company": "Adevinta Spain",
"description": "Responsable de la plataforma, componentes y utilidades para la creación y desarrollo de aplicaciones web. Mejora de un 30% en la entrega de software. Implantación de medidas de integración continua y despliegue con A/B testing en más de 15 equipos."
}
],
"projects": {
"tags": {
"NEXT": {
"name": "Next.js",
"class": "bg-black text-white",
"icon": "NextJS"
},
"TAILWIND": {
"name": "Tailwind CSS",
"class": "bg-[#003159] text-white",
"icon": "Tailwind"
}
},
"items": [
{
"title": "SVGL - A beautiful library with SVG logos",
"description": "Biblioteca de logos SVG de las marcas más populares. +10k visitas al mes. +2K svgs descargados. Creado desde cero con Next.js, React y Tailwind CSS.",
"link": "https://svgl.vercel.app/",
"github": "https://github.com/pheralb/svgl",
"image": "/projects/svgl.webp",
"tags": ["NEXT", "TAILWIND"]
},
{
"title": "AdventJS - Retos de programación con JavaScript y TypeScript",
"description": "Plataforma gratuita con retos de programación. Más de 1 millón de visitas en un mes. +50K retos completados. Creada desde cero con Next.js, React y Tailwind CSS.",
"link": "https://adventjs.dev",
"image": "/projects/adventjs.webp",
"tags": ["NEXT", "TAILWIND"]
}
]
}
}
5 changes: 5 additions & 0 deletions src/env.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
/// <reference path="../.astro/types.d.ts" />
/// <reference types="astro/client" />

declare module "*.json" {
const value: any;
export default value;
}
4 changes: 3 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
"@/*": [
"src/*"
]
}
},
"resolveJsonModule": true,
"esModuleInterop": true
}
}