Add portafolio page
This commit is contained in:
parent
0c10c3fa77
commit
318a647147
8 changed files with 166 additions and 23 deletions
|
|
@ -57,6 +57,16 @@ ul {
|
|||
margin: 0;
|
||||
}
|
||||
|
||||
.list-unstyle {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
img,
|
||||
video {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
li:not(:last-child) {
|
||||
margin-bottom: var(--prj-spacing-1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,7 @@
|
|||
---
|
||||
export interface Props {
|
||||
title: string;
|
||||
body: string;
|
||||
href: string;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
const { href, title, body } = Astro.props;
|
||||
---
|
||||
|
||||
<div class="card vstack">
|
||||
|
|
|
|||
44
src/components/MediaGallery/Gallery.tsx
Normal file
44
src/components/MediaGallery/Gallery.tsx
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import React from 'react';
|
||||
import { MediaType, type Media } from './types';
|
||||
|
||||
interface Props {
|
||||
items: Media[];
|
||||
}
|
||||
|
||||
// TODO: transform this component in a lightbox
|
||||
export default function Gallery({ items }: Props): JSX.Element {
|
||||
const renderItem = (item: Media): JSX.Element => {
|
||||
// get the file id from the "share" link of google drive
|
||||
const googleFileId = item.url.split('/').at(5);
|
||||
let url;
|
||||
switch (item.type) {
|
||||
case MediaType.Image:
|
||||
url =
|
||||
googleFileId !== undefined
|
||||
? `https://drive.google.com/uc?export=preview&id=${googleFileId}`
|
||||
: item.url;
|
||||
return <img src={url} alt={item.alt} />;
|
||||
case MediaType.Video:
|
||||
url =
|
||||
googleFileId !== undefined
|
||||
? `https://drive.google.com/file/d/${googleFileId}/preview`
|
||||
: item.url;
|
||||
return (
|
||||
<iframe src={url} width="600" height="500" allow="autoplay"></iframe>
|
||||
);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
||||
return (
|
||||
<ul className="list-unstyle">
|
||||
{items.map((item, idx) => (
|
||||
<li key={idx}>{renderItem(item)}</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
11
src/components/MediaGallery/types.ts
Normal file
11
src/components/MediaGallery/types.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
export enum MediaType {
|
||||
Image = 'image',
|
||||
Video = 'video',
|
||||
}
|
||||
|
||||
export interface Media {
|
||||
type: MediaType;
|
||||
url: string;
|
||||
alt: string;
|
||||
mime?: string;
|
||||
}
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 6b406948b70fa4c56333c0afc628e9b6618aacdc
|
||||
Subproject commit e52081e1e2d2acaf810e9902a009aaa372313ae6
|
||||
|
|
@ -5,7 +5,15 @@ import Card from '../components/Card.astro';
|
|||
import { Image } from 'astro:assets';
|
||||
|
||||
import portrait from '../assets/images/portrait.jpg';
|
||||
const games = await getCollection('games', (_, idx) => idx < 3);
|
||||
const games = await getCollection('games');
|
||||
|
||||
const blog = await getCollection('blog', ({ data }) =>
|
||||
import.meta.env.PROD ? data.draft !== true : true,
|
||||
);
|
||||
// TODO: show the pinned ones, not the recents
|
||||
const portafolio = await getCollection('portafolio', ({ data }) =>
|
||||
import.meta.env.PROD ? data.draft !== true : true,
|
||||
);
|
||||
---
|
||||
|
||||
<Layout title="aleidk">
|
||||
|
|
@ -34,29 +42,29 @@ const games = await getCollection('games', (_, idx) => idx < 3);
|
|||
<Card>
|
||||
<h3 slot="title">Portafolio</h3>
|
||||
<ul>
|
||||
<li><a href="">This is a project X</a></li>
|
||||
<li><a href="">This is a project Y</a></li>
|
||||
<li><a href="">This is a project Z</a></li>
|
||||
{
|
||||
portafolio.slice(0, 3).map((item) => (
|
||||
<li>
|
||||
<a href={`portafolio/${item.slug}`}>{item.data.title}</a>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
<div slot="footer" class="text-end">
|
||||
<a href="">See more...</a>
|
||||
<a href="/portafolio">See more...</a>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<h3 slot="title">Blog</h3>
|
||||
<ul>
|
||||
<li><a href="">How I do X thing because I wanted</a></li>
|
||||
{
|
||||
blog.slice(0, 3).map((item) => (
|
||||
<li>
|
||||
<a href=""
|
||||
>The day I discover something marvelous and everything was better</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href=""
|
||||
>This is a clickbait title trying to make you enter this article</a
|
||||
>
|
||||
<a href={`blog/${item.slug}`}>{item.data.title}</a>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
<div slot="footer" class="text-end">
|
||||
<a href="/blog">See more...</a>
|
||||
|
|
@ -67,7 +75,7 @@ const games = await getCollection('games', (_, idx) => idx < 3);
|
|||
<h3 slot="title">Games</h3>
|
||||
<ul>
|
||||
{
|
||||
games.map((item) => (
|
||||
games.slice(0, 3).map((item) => (
|
||||
<li>
|
||||
<a href={`games/${item.slug}`}>{item.data.title}</a>
|
||||
</li>
|
||||
|
|
|
|||
31
src/pages/portafolio/[...slug].astro
Normal file
31
src/pages/portafolio/[...slug].astro
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
import type { InferGetStaticPropsType, GetStaticPaths } from 'astro';
|
||||
import { getCollection } from 'astro:content';
|
||||
import Layout from '@layouts/Layout.astro';
|
||||
import Toc from '@components/Toc/Toc';
|
||||
import Gallery from '@components/MediaGallery/Gallery';
|
||||
|
||||
export const getStaticPaths = (async () => {
|
||||
const entries = await getCollection('portafolio');
|
||||
|
||||
return entries.map((entry) => ({
|
||||
params: { slug: entry.slug },
|
||||
props: entry,
|
||||
}));
|
||||
}) satisfies GetStaticPaths;
|
||||
|
||||
type Props = InferGetStaticPropsType<typeof getStaticPaths>;
|
||||
|
||||
const entry = Astro.props;
|
||||
const { Content, headings } = await entry.render();
|
||||
---
|
||||
|
||||
<Layout title={entry.data.title}>
|
||||
<h1>{entry.data.title}</h1>
|
||||
|
||||
<Gallery items={entry.data.media} />
|
||||
|
||||
<Toc headings={headings} />
|
||||
|
||||
<Content />
|
||||
</Layout>
|
||||
43
src/pages/portafolio/index.astro
Normal file
43
src/pages/portafolio/index.astro
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
---
|
||||
import { getCollection } from 'astro:content';
|
||||
import Layout from '@layouts/Layout.astro';
|
||||
import Table from '@components/Table';
|
||||
import { HeaderType, type Header } from '@components/Table/types';
|
||||
|
||||
const rawEntries = await getCollection('portafolio', ({ data }) => {
|
||||
return import.meta.env.PROD ? data.draft !== true : true;
|
||||
});
|
||||
|
||||
const entries = rawEntries.map((item, idx) => ({
|
||||
...item.data,
|
||||
id: idx + 1,
|
||||
slug: item.slug,
|
||||
}));
|
||||
|
||||
const headers: Header[] = [
|
||||
{
|
||||
key: 'id',
|
||||
header: 'index',
|
||||
type: HeaderType.Index,
|
||||
},
|
||||
{
|
||||
key: 'title',
|
||||
header: 'Title',
|
||||
formatter: (data) => `<a href="portafolio/${data.slug}">${data.title}</a>`,
|
||||
type: HeaderType.String,
|
||||
},
|
||||
{
|
||||
key: 'technologies',
|
||||
header: 'Technologies',
|
||||
type: HeaderType.Multiple,
|
||||
},
|
||||
];
|
||||
---
|
||||
|
||||
<Layout title="List of blog entries">
|
||||
<h1>Blog's entries</h1>
|
||||
|
||||
<section>
|
||||
<Table client:load data={entries} headers={headers} />
|
||||
</section>
|
||||
</Layout>
|
||||
Loading…
Add table
Add a link
Reference in a new issue