Compare commits

..

3 Commits

@ -3,8 +3,13 @@ import preact from '@astrojs/preact'
import mdx from '@astrojs/mdx'
import yaml from '@rollup/plugin-yaml'
// https://astro.build/config
export default defineConfig({
vite: {
plugins: [yaml()],
},
server: {
port: 3000,
},
@ -14,5 +19,5 @@ export default defineConfig({
},
},
integrations: [preact(), mdx()],
output: 'static'
output: 'static',
})

Binary file not shown.

@ -11,8 +11,8 @@
},
"dependencies": {
"@astrojs/check": "^0.9.4",
"@astrojs/node": "^8.3.4",
"@astrojs/preact": "^3.5.3",
"@astrojs/node": "9.0.0",
"@astrojs/preact": "4.0.0",
"@fontsource-variable/material-symbols-outlined": "^5.1.1",
"@fontsource/iosevka": "^5.0.11",
"@fontsource/mononoki": "^5.0.11",
@ -20,9 +20,10 @@
"@fontsource/source-code-pro": "^5.0.16",
"@fontsource/source-sans-pro": "^5.0.8",
"@fontsource/space-mono": "^5.0.20",
"@phosphor-icons/core": "^2.1.1",
"@preact/signals": "^1.3.0",
"@types/jsdom": "^21.1.7",
"astro": "^4.15.11",
"astro": "5.1.0",
"fuse.js": "^7.0.0",
"katex": "^0.16.9",
"lucide-static": "^0.468.0",
@ -30,7 +31,8 @@
"typescript": "^5.3.3"
},
"devDependencies": {
"@astrojs/mdx": "^3.1.7",
"@astrojs/mdx": "4.0.2",
"@rollup/plugin-yaml": "^4.1.2",
"@types/katex": "^0.16.7",
"jsdom": "^24.1.1",
"linkedom": "^0.18.4",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

@ -1,6 +1,7 @@
import { type ComponentChildren } from 'preact'
import { useState, useRef, useEffect } from 'preact/hooks'
import { clsx, isMobile } from './lib/util'
import { PhosphorIcon } from './Icon'
export const ComboBox = ({
value,
@ -40,7 +41,8 @@ export const ComboBox = ({
>
<div class="selected" onClick={() => setOpen(!open)}>
<div class="content">{children[value]}</div>
<span class="material-symbols-outlined">expand_more</span>
{/* <span class="material-symbols-outlined">expand_more</span> */}
<PhosphorIcon name="caret-down" />
</div>
{open && (
<div

@ -0,0 +1,21 @@
const icons = Object.fromEntries(
Object.entries(
import.meta.glob<{ default: ImageMetadata }>(`node_modules/@phosphor-icons/core/assets/light/*.svg`, {
eager: true,
})
).map(([path, module]) => [path.split('/').pop()!.split('.')[0].replace('-light', ''), module])
)
type Props = {
name: string
}
export const PhosphorIcon = ({ name }: Props) => {
const icon = icons[name]
if (!icon) {
throw new Error(`Icon "${name}" not found`)
}
return <img class="phosphor-icon" src={icon.default.src} alt={name} />
}

@ -3,6 +3,7 @@ import Fuse from 'fuse.js'
import { useEffect } from 'preact/hooks'
import { ShowMore } from './Paginate'
import { ComboBox } from './ComboBox'
import { PhosphorIcon } from './Icon'
type User = {
uid: string
@ -11,15 +12,15 @@ type User = {
const FILTERS = {
utenti: {
icon: 'person',
icon: 'user',
label: 'Utenti',
},
macchinisti: {
icon: 'construction',
icon: 'wrench',
label: 'Macchinisti',
},
rappstud: {
icon: 'account_balance',
icon: 'bank',
label: 'Rappresentanti',
},
}
@ -105,7 +106,9 @@ export const UtentiPage = () => {
Object.entries(FILTERS).map(([k, v]) => [
k,
<>
<span class="material-symbols-outlined">{v.icon}</span> {v.label}
{/* <span class="material-symbols-outlined">{v.icon}</span> {v.label} */}
<PhosphorIcon name={v.icon} />
{v.label}
</>,
])
)}
@ -117,7 +120,8 @@ export const UtentiPage = () => {
onInput={e => ($searchText.value = e.currentTarget.value)}
value={$searchText.value}
/>
<span class="material-symbols-outlined">search</span>
{/* <span class="material-symbols-outlined">search</span> */}
<PhosphorIcon name="magnifying-glass" />
</div>
</div>
<div class="search-results">
@ -126,13 +130,22 @@ export const UtentiPage = () => {
{poissonUser => (
<div class="search-result">
<div class="icon">
<span class="material-symbols-outlined">
{/* <span class="material-symbols-outlined">
{RAPPSTUD.includes(poissonUser.uid)
? 'account_balance'
: MACCHINISTI.includes(poissonUser.uid)
? 'construction'
: 'person'}
</span>
</span> */}
<PhosphorIcon
name={
RAPPSTUD.includes(poissonUser.uid)
? 'bank'
: MACCHINISTI.includes(poissonUser.uid)
? 'wrench'
: 'user'
}
/>
</div>
<div class="text">{poissonUser.gecos}</div>
<div class="right">
@ -140,7 +153,8 @@ export const UtentiPage = () => {
href={`https://poisson.phc.dm.unipi.it/~${poissonUser.uid}`}
target="_blank"
>
<span class="material-symbols-outlined">open_in_new</span>
{/* <span class="material-symbols-outlined">open_in_new</span> */}
<PhosphorIcon name="arrow-square-out" />
</a>
</div>
</div>

@ -1,20 +1,18 @@
---
import iconGithub from 'lucide-static/icons/github.svg'
import iconLinkedin from 'lucide-static/icons/linkedin.svg'
import iconGlobe from 'lucide-static/icons/globe.svg'
import iconMail from 'lucide-static/icons/mail.svg'
import PhosphorIcon from './PhosphorIcon.astro'
const ICONS_MAP = {
github: iconGithub,
linkedin: iconLinkedin,
website: iconGlobe,
mail: iconMail,
const ICONS_MAP: Record<string, string> = {
github: 'github-logo',
linkedin: 'linkedin-logo',
website: 'globe',
mail: 'mailbox',
}
type Props = {
image: ImageMetadata
fullName: string
date: string
entranceDate: number
exitDate?: number
description: string
social?: {
github?: string
@ -24,21 +22,20 @@ type Props = {
}
}
const { image, fullName, date, description, social } = Astro.props
const { image, fullName, entranceDate, exitDate, description, social } = Astro.props
---
<div class="bubble">
<img src={image.src} alt={fullName.toLowerCase()} />
<div class="title">{fullName}</div>
<div class="date">{date}</div>
<div class="date">{entranceDate}&mdash;{exitDate ?? 'Presente'}</div>
<div class="description">{description}</div>
{
social && (
<div class="social">
{Object.entries(social).map(([key, value]) => (
<a href={value} target="_blank" rel="noopener noreferrer">
{/* @ts-ignore */}
<img src={ICONS_MAP[key].src} alt={key} />
<PhosphorIcon name={ICONS_MAP[key] ?? 'question-mark'} />
</a>
))}
</div>

@ -1,7 +1,4 @@
---
import type { AstroBuiltinProps } from 'astro'
import type { AstroComponentFactory } from 'astro/runtime/server/index.js'
type Props = {
large?: boolean
style?: string

@ -1,17 +1,36 @@
---
const links = [
{ href: '/utenti', text: 'Utenti' },
// { href: '/macchinisti', text: 'Macchinisti' },
// { href: '/appunti', text: 'Appunti' },
{ href: '/notizie', text: 'Notizie' },
{ href: '/guide', text: 'Guide' },
{ href: '/storia', text: 'Storia' },
// { href: '/login', text: 'Login' },
]
---
<header>
<!-- main logo on the left -->
<a href="/" class="logo">
<img src="/images/phc-logo-2024-11@x8.png" alt="phc logo" />
</a>
<!-- hidden checkbox for mobile js-less sidebar interaction -->
<input type="checkbox" id="header-menu-toggle" />
<!-- desktop navbar links -->
<div class="links desktop-only">
<a role="button" href="/utenti">Utenti</a>
<a role="button" href="/macchinisti">Macchinisti</a>
<!-- <a role="button" href="/appunti">Appunti</a> -->
<a role="button" href="/notizie">Notizie</a>
<a role="button" href="/guide">Guide</a>
<a role="button" href="/storia">Storia</a>
<!-- <a class="primary" role="button" href="/login">Login</a> -->
{
links.map(link => (
<a role="button" href={link.href}>
{link.text}
</a>
))
}
</div>
<!-- sidebar menu for mobile -->
<div class="mobile-only">
<label id="header-menu-toggle-menu" role="button" class="flat icon" for="header-menu-toggle">
<span class="material-symbols-outlined">menu</span>
@ -20,15 +39,17 @@
<span class="material-symbols-outlined">close</span>
</label>
</div>
<!-- sidebar menu only visible on mobile when #header-menu-toggle is checked -->
<div class="side-menu">
<div class="links">
<a role="button" href="/utenti">Utenti</a>
<a role="button" href="/macchinisti">Macchinisti</a>
<!-- <a role="button" href="/appunti">Appunti</a> -->
<a role="button" href="/notizie">Notizie</a>
<a role="button" href="/guide">Guide</a>
<a role="button" href="/storia">Storia</a>
<!-- <a class="primary" role="button" href="/login">Login</a> -->
{
links.map(link => (
<a role="button" href={link.href}>
{link.text}
</a>
))
}
</div>
</div>
</header>

@ -0,0 +1,21 @@
---
import { Image } from 'astro:assets'
type Props = {
name: string
}
const { name } = Astro.props
const icons = Object.fromEntries(
Object.entries(
import.meta.glob<{ default: ImageMetadata }>(`node_modules/@phosphor-icons/core/assets/light/*.svg`)
).map(([path, module]) => [path.split('/').pop()!.split('.')[0].replace('-light', ''), module])
)
if (!icons[name]) {
throw new Error(`Icon "${name}" not found`)
}
---
<Image class="phosphor-icon" src={icons[name]()} alt={name} />

@ -1,47 +0,0 @@
[
{
"fullName": "Antonio De Lucreziis",
"dateIn": "2019",
"dateOut": "",
"description": "Description here",
"github": "https://github.com/adelucreziis",
"website": "",
"linkedin": ""
},
{
"fullName": "Luca Lombardo",
"dateIn": "2024",
"dateOut": "",
"description": "Algoritmi e Strutture Dati Bla Bla",
"github": "https://github.com/lukefleed",
"website": "https://lukefleex.xyz",
"linkedin": "https://www.linkedin.com/in/l-lombardo/"
},
{
"fullName": "Francesco Minnocci",
"dateIn": "2022",
"dateOut": "",
"description": "Ho una laurea(?)",
"github": "https://github.com/BachoSeven",
"website": "https://bachoseven.com",
"linkedin": ""
},
{
"fullName": "Francesco Baldino",
"dateIn": "2022",
"dateOut": "",
"description": "Bla Bla",
"github": "",
"website": "",
"linkedin": ""
},
{
"fullName": "Illya Serdyuk",
"dateIn": "2019",
"dateOut": "",
"description": "Bla Bla",
"github": "",
"website": "",
"linkedin": ""
}
]

@ -0,0 +1,37 @@
# the schema of this file in "@/files.d.ts"
- fullName: Antonio De Lucreziis
entranceDate: 2019
description: |
Appassionato di geometria computazionale, parser, teoria dei linguaggi di programmazione.
social:
github: https://github.com/aziis98
website: https://poisson.phc.dm.unipi.it/~delucreziis/
- fullName: Luca Lombardo
entranceDate: 2024
description: Algoritmi e Strutture Dati bla bla
social:
github: https://github.com/lukefleed
website: https://lukefleex.xyz
linkedin: https://www.linkedin.com/in/l-lombardo/
- fullName: Francesco Minnocci
entranceDate: 2022
description: Ho una laurea (?), bla bla tastiere meccaniche
social:
github: https://github.com/BachoSeven
website: https://bachoseven.com
- fullName: Francesco Baldino
entranceDate: 2022
description: Bla bla Star Wars
- fullName: Illya Serdyuk
entranceDate: 2020
description: Bla bla Void Linux
- fullName: Francesco Manicastri
entranceDate: 2022
exitDate: 2024
description: Bla bla Keenan Crane

@ -1,11 +0,0 @@
[
{
"fullName": "Francesco Manicastri",
"dateIn": "2022",
"dateOut": "2024",
"description": "Geometria bla bla",
"github": "",
"website": "",
"linkedin": ""
}
]

17
src/files.d.ts vendored

@ -0,0 +1,17 @@
declare module '*.yaml' {
const value: any // Add type definitions here if desired
export default value
}
declare module '@/data/macchinisti.yaml' {
type Macchinista = {
fullName: string
entranceDate: number
exitDate?: number
description: string
social: Record<string, string>
}
const value: Macchinista[]
export default value
}

@ -4,28 +4,25 @@ import Header from '../components/Header.astro'
import Footer from '../components/Footer.astro'
import Bubble from '../components/Bubble.astro'
import currentMacchinisti from '../data/current-macchinisti.json'
import pastMacchinisti from '../data/past-macchinisti.json'
import macchinisti from '@/data/macchinisti.yaml'
macchinisti.sort((a, b) => b.entranceDate - a.entranceDate)
// Import all images from assets folder
const images = import.meta.glob('@/assets/macchinisti/*.{jpg,jpeg,png,gif}', {
eager: true
})
const images = Object.fromEntries(
Object.entries(
import.meta.glob<{ default: ImageMetadata }>('@/assets/macchinisti/*', {
eager: true,
})
).map(([path, module]) => [path.split('/').pop()!.split('.')[0], module])
)
// Sort by dateIn
const sortedCurrent = currentMacchinisti.sort((a, b) => parseInt(a.dateIn) - parseInt(b.dateIn))
const sortedPast = pastMacchinisti.sort((a, b) => parseInt(a.dateIn) - parseInt(b.dateIn))
const currentMacchinisti = macchinisti.filter(macchinista => !macchinista.exitDate)
const pastMacchinisti = macchinisti.filter(macchinista => macchinista.exitDate)
// Define a fallback image metadata
const fallbackImage = { default: 'assets/macchinisti/fallback.jpg' }
// Function to get image for a macchinista
const getImage = (fullName: string) => {
const imageName = fullName.toLowerCase().replaceAll(' ', '-')
const imageModule = Object.entries(images).find(([path]) =>
path.includes(imageName)
)?.[1]
return imageModule || fallbackImage
const getMacchinistaPicture = (fullName: string) => {
const macchinistaId = fullName.toLowerCase().replace(/\s+/g, '-')
const { default: image } = images[macchinistaId] ?? images['fallback']
return image
}
---
@ -40,42 +37,39 @@ const getImage = (fullName: string) => {
</div>
<div class="bubble-array">
{sortedCurrent.map(async (macchinista) => (
{
currentMacchinisti.map(macchinista => (
<Bubble
image={(getImage(macchinista.fullName) as { default: string }).default as unknown as ImageMetadata}
image={getMacchinistaPicture(macchinista.fullName)}
fullName={macchinista.fullName}
date={`${macchinista.dateIn}—Presente`}
entranceDate={macchinista.entranceDate}
description={macchinista.description}
social={{
...(macchinista.github && { github: macchinista.github }),
...(macchinista.website && { website: macchinista.website }),
...(macchinista.linkedin && { linkedin: macchinista.linkedin })
}}
social={macchinista.social}
/>
))}
))
}
</div>
<div class="card large" style={{ '--card-base': '#6BD6E1' }}>
<div class="title">Ex Macchinisti</div>
<div class="title"><s>Deus</s> Ex Macchinisti</div>
<div class="text">
<p>Gli vogliamo molto bene, hanno fatto grandi cose!</p>
</div>
</div>
<div class="bubble-array">
{sortedPast.map(async (macchinista) => (
{
pastMacchinisti.map(macchinista => (
<Bubble
image={(getImage(macchinista.fullName) as { default: string }).default as unknown as ImageMetadata}
image={getMacchinistaPicture(macchinista.fullName)}
fullName={macchinista.fullName}
date={`${macchinista.dateIn}—${macchinista.dateOut}`}
entranceDate={macchinista.entranceDate}
exitDate={macchinista.exitDate}
description={macchinista.description}
social={{
...(macchinista.github && { github: macchinista.github }),
...(macchinista.website && { website: macchinista.website }),
...(macchinista.linkedin && { linkedin: macchinista.linkedin })
}}
social={macchinista.social}
/>
))}
))
}
</div>
</main>
<Footer />

@ -8,6 +8,16 @@
// Components - for complex parts of the UI like search bars or compound buttons
//
.phosphor-icon {
box-sizing: content-box;
width: 22px;
height: 22px;
display: grid;
place-content: center;
}
.material-symbols-outlined {
font-family: 'Material Symbols Outlined Variable';
font-weight: normal;
@ -63,7 +73,8 @@
padding-left: 0.35rem;
}
.material-symbols-outlined {
.material-symbols-outlined,
.phosphor-icon {
padding: 0 0.5rem;
}
}
@ -699,7 +710,8 @@
background: var(--filter-bg-color, #ddd);
.material-symbols-outlined {
.material-symbols-outlined,
.phosphor-icon {
padding: 0 0.35rem;
}
@ -818,4 +830,53 @@
}
}
}
.bubble-array {
display: flex;
width: 100%;
flex-wrap: wrap;
justify-content: center;
gap: 3rem 6rem;
> .bubble {
display: grid;
grid-template-rows: auto auto auto auto;
grid-auto-rows: auto;
gap: 0.5rem;
text-align: center;
justify-items: center;
align-content: start;
width: 28ch;
.date {
display: grid;
place-content: center;
font-size: 16px;
font-weight: 600;
padding: 0.25rem 0.5rem;
background: #0002;
border-radius: 0.25rem;
}
.social {
display: grid;
grid-auto-flow: column;
gap: 0.5rem;
justify-content: center;
.phosphor-icon {
width: 28px;
height: 28px;
}
}
> img {
border-radius: 100%;
border: 4px solid #333;
object-fit: cover;
width: 100%;
aspect-ratio: 1 / 1;
}
}
}
}

@ -646,51 +646,8 @@
flex-direction: column;
align-items: center;
padding: 6rem 4rem;
gap: 4rem;
}
.bubble-array {
display: flex;
width: 100%;
flex-wrap: wrap;
justify-content: center;
gap: 4rem;
> .bubble {
display: grid;
grid-template-rows: auto auto auto auto;
grid-auto-rows: auto;
gap: 0.5rem;
text-align: center;
justify-items: center;
width: 25ch;
.date {
display: grid;
place-content: center;
font-size: 16px;
font-weight: 600;
padding: 0rem 0.5rem;
background: #0002;
border-radius: 0.25rem;
}
.social {
display: grid;
grid-auto-flow: column;
gap: 0.5rem;
justify-content: center;
}
> img {
border-radius: 100%;
border: 4px solid #333;
object-fit: cover;
width: 100%;
aspect-ratio: 1 / 1;
}
}
padding: 6rem;
gap: 6rem;
}
}

Loading…
Cancel
Save