Merge branch 'main' into backend
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 77 KiB |
@@ -0,0 +1,766 @@
|
|||||||
|
import { useState, useEffect } from 'preact/hooks'
|
||||||
|
import { render } from 'preact'
|
||||||
|
|
||||||
|
// Tipi per la gestione dei dati
|
||||||
|
type TipoStudente = 'triennale' | 'magistrale'
|
||||||
|
|
||||||
|
interface Corso {
|
||||||
|
nome: string
|
||||||
|
anno: '1' | '2' | '3' | 'M' | 'istituzioni'
|
||||||
|
cfu: number
|
||||||
|
passFailOnly?: boolean // Per materie senza voto (pass/fail)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CorsoSelezionato {
|
||||||
|
id: string
|
||||||
|
nome: string
|
||||||
|
cfu: number
|
||||||
|
voto: number | null
|
||||||
|
lode: boolean
|
||||||
|
passFailOnly?: boolean // Per materie senza voto (pass/fail)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CorsoCustom {
|
||||||
|
nome: string
|
||||||
|
cfu: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dati dei corsi aggiornati dalla tabella ufficiale
|
||||||
|
const CORSI_DISPONIBILI: Corso[] = [
|
||||||
|
// Primo Anno
|
||||||
|
{ nome: 'Analisi matematica 1', anno: '1', cfu: 15 },
|
||||||
|
{ nome: 'Aritmetica', anno: '1', cfu: 9 },
|
||||||
|
{ nome: 'Fisica I con laboratorio', anno: '1', cfu: 9 },
|
||||||
|
{ nome: 'Fondamenti di programmazione con laboratorio', anno: '1', cfu: 9 },
|
||||||
|
{ nome: 'Geometria 1', anno: '1', cfu: 15 },
|
||||||
|
{ nome: 'Laboratorio di introduzione alla matematica computazionale', anno: '1', cfu: 6, passFailOnly: true },
|
||||||
|
{ nome: 'Laboratorio di comunicazione mediante calcolatore', anno: '1', cfu: 3, passFailOnly: true },
|
||||||
|
|
||||||
|
// Secondo Anno
|
||||||
|
{ nome: 'Algebra 1', anno: '2', cfu: 6 },
|
||||||
|
{ nome: 'Algoritmi e strutture dati', anno: '2', cfu: 6 },
|
||||||
|
{ nome: 'Analisi matematica 2', anno: '2', cfu: 12 },
|
||||||
|
{ nome: 'Analisi numerica con laboratorio', anno: '2', cfu: 9 },
|
||||||
|
{ nome: 'Elementi di probabilità e statistica', anno: '2', cfu: 6 },
|
||||||
|
{ nome: 'Geometria 2', anno: '2', cfu: 12 },
|
||||||
|
{ nome: 'Inglese scientifico', anno: '2', cfu: 6, passFailOnly: true },
|
||||||
|
{ nome: 'Laboratorio didattico di matematica computazionale', anno: '2', cfu: 3, passFailOnly: true },
|
||||||
|
|
||||||
|
// Terzo Anno
|
||||||
|
{ nome: 'Algebra 2', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Analisi matematica 3', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Analisi reale', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Calcolo scientifico', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Elementi di analisi complessa', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Elementi di calcolo delle variazioni', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Elementi di geometria algebrica', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Elementi di meccanica celeste', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Elementi di teoria degli insiemi', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Elementi di teoria delle rappresentazioni', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Elementi di topologia algebrica', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Equazioni alle derivate parziali', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Fisica II', anno: '3', cfu: 9 },
|
||||||
|
{ nome: 'Fisica III', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Geometria e topologia differenziale', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Gruppi e rappresentazioni', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Laboratorio computazionale', anno: '3', cfu: 6, passFailOnly: true },
|
||||||
|
{ nome: 'Laboratorio sperimentale di matematica computazionale', anno: '3', cfu: 6, passFailOnly: true },
|
||||||
|
{ nome: 'Linguaggi di programmazione con laboratorio', anno: '3', cfu: 9 },
|
||||||
|
{ nome: 'Logica matematica', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Matematiche elementari da un punto di vista superiore: aritmetica', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Matematiche elementari da un punto di vista superiore: geometria', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Meccanica razionale', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Metodi numerici per equazioni differenziali ordinarie', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Metodi topologici in analisi globale', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Ottimizzazione non lineare', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Probabilità', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Ricerca operativa', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Sistemi dinamici', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Spazi di Sobolev', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Statistica matematica', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Storia della matematica', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Teoria algebrica dei numeri 1', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Teoria dei campi e teoria di Galois', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Teoria dei numeri elementare', anno: '3', cfu: 6 },
|
||||||
|
{ nome: 'Teoria della misura', anno: '3', cfu: 6 },
|
||||||
|
|
||||||
|
// Istituzioni (Magistrale)
|
||||||
|
{ nome: 'Istituzioni di algebra', anno: 'istituzioni', cfu: 11 },
|
||||||
|
{ nome: 'Istituzioni di analisi matematica', anno: 'istituzioni', cfu: 11 },
|
||||||
|
{ nome: 'Istituzioni di analisi numerica', anno: 'istituzioni', cfu: 11 },
|
||||||
|
{ nome: 'Istituzioni di didattica della matematica', anno: 'istituzioni', cfu: 11 },
|
||||||
|
{ nome: 'Istituzioni di fisica matematica', anno: 'istituzioni', cfu: 11 },
|
||||||
|
{ nome: 'Istituzioni di geometria', anno: 'istituzioni', cfu: 11 },
|
||||||
|
{ nome: 'Istituzioni di probabilità', anno: 'istituzioni', cfu: 11 },
|
||||||
|
|
||||||
|
// Materie a scelta (Magistrale)
|
||||||
|
{ nome: '4-varietà', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Algebra superiore A', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Algebre e gruppi di Lie', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Analisi armonica', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Analisi complessa A', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Analisi complessa B', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Analisi convessa', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Analisi dei dati', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Analisi non standard', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Analisi reale', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Analisi su spazi gaussiani', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Analisi superiore', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Analisi superiore A', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Analisi superiore B', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Aspetti matematici nella computazione quantistica', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Calcolo delle variazioni B', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Calcolo della variazioni A', anno: 'M', cfu: 6 }, // Variante nome
|
||||||
|
{ nome: 'Combinatoria algebrica', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Complementi di analisi funzionale', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Complementi di meccanica razionale', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Crittografia post-quantistica', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Curve ellittiche', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Determinazione orbitale', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Didattica della matematica e nuove tecnologie', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Dinamica del sistema solare', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Dinamica iperbolica', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Dinamica olomorfa', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Elementi di calcolo in gruppi omogenei', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Equazioni della fluidodinamica', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Equazioni differenziali stocastiche e applicazioni', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Equazioni ellittiche', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Finanza matematica', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Fisica matematica', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Forme modulari', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Geometria algebrica B', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Geometria algebrica C', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Geometria algebrica complessa', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Geometria algebrica D', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Geometria algebrica E', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Geometria algebrica F', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Geometria algebrica G', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Geometria e analisi complessa', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Geometria differenziale complessa', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Geometria iperbolica', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Geometria riemanniana', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Gruppi algebrici lineari', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Gruppi di Coxeter', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Gruppi di Galois e gruppi fondamentali', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Meccanica celeste', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Meccanica spaziale', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Meccanica superiore', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Metodi di analisi armonica in analisi non lineare', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Metodi di approssimazione', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Metodi matematici della crittografia', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Metodi matematici della meccanica quantistica', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Metodi numerici per catene di Markov', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Metodi numerici per equazioni alle derivate parziali', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Metodi numerici per il calcolo tensoriale', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Metodi numerici per il controllo ottimo', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Metodi numerici per la grafica', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Metodi numerici per problemi inversi', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: "Metodi probabilistici per l'algebra lineare numerica", anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Modelli matematici in biomedicina e fisica matematica', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Origini e sviluppo delle matematiche moderne', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Probabilità superiore', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Problemi e metodi della ricerca in didattica della matematica', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Problemi e metodi in storia della matematica', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Sistemi dinamici aleatori', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Statistica superiore', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Storia della matematica antica e della sua tradizione', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Superfici di Riemann e curve algebriche', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Tecnologie per la didattica', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Teoria algebrica dei numeri 2', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Teoria analitica dei numeri A', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Teoria dei giochi', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Teoria dei modelli', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Teoria dei nodi A', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Teoria degli insiemi', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Teoria degli insiemi A', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Teoria degli insiemi B', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Teoria delle categorie', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Teoria delle rappresentazioni A', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: "Teoria e metodi dell'ottimizzazione", anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Teoria ergodica', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Teoria geometrica della misura', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Topologia algebrica', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Topologia algebrica A', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Topologia algebrica B', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Topologia differenziale', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Topologia e geometria in bassa dimensione', anno: 'M', cfu: 6 },
|
||||||
|
{ nome: 'Ultrafiltri e metodi non-standard', anno: 'M', cfu: 6 },
|
||||||
|
]
|
||||||
|
|
||||||
|
export function MediaPesataApp() {
|
||||||
|
// Funzioni per localStorage
|
||||||
|
const loadFromStorage = () => {
|
||||||
|
try {
|
||||||
|
const savedData = localStorage.getItem('media-pesata-data')
|
||||||
|
if (savedData) {
|
||||||
|
const parsed = JSON.parse(savedData)
|
||||||
|
return {
|
||||||
|
tipoStudente: parsed.tipoStudente || 'triennale',
|
||||||
|
corsiSelezionati: parsed.corsiSelezionati || [],
|
||||||
|
sezioniAperte: parsed.sezioniAperte || {},
|
||||||
|
mostraRisultati: parsed.mostraRisultati || false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Errore nel caricamento dei dati salvati:', error)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
tipoStudente: 'triennale' as TipoStudente,
|
||||||
|
corsiSelezionati: [],
|
||||||
|
sezioniAperte: {},
|
||||||
|
mostraRisultati: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveToStorage = (data: any) => {
|
||||||
|
try {
|
||||||
|
localStorage.setItem('media-pesata-data', JSON.stringify(data))
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Errore nel salvataggio dei dati:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inizializzazione con dati salvati
|
||||||
|
// const initialData = loadFromStorage()
|
||||||
|
const [tipoStudente, setTipoStudente] = useState<TipoStudente>('triennale')
|
||||||
|
const [corsiSelezionati, setCorsiSelezionati] = useState<CorsoSelezionato[]>([])
|
||||||
|
const [showCustomForm, setShowCustomForm] = useState(false)
|
||||||
|
const [customCorso, setCustomCorso] = useState<CorsoCustom>({ nome: '', cfu: 0 })
|
||||||
|
const [sezioniAperte, setSezioniAperte] = useState<Record<string, boolean>>({})
|
||||||
|
const [mostraRisultati, setMostraRisultati] = useState(false)
|
||||||
|
|
||||||
|
// Load data from localStorage on mount
|
||||||
|
useEffect(() => {
|
||||||
|
const initialData = loadFromStorage()
|
||||||
|
setTipoStudente(initialData.tipoStudente)
|
||||||
|
setCorsiSelezionati(initialData.corsiSelezionati)
|
||||||
|
setSezioniAperte(initialData.sezioniAperte)
|
||||||
|
setMostraRisultati(initialData.mostraRisultati)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
// Salva automaticamente quando cambiano i dati importanti
|
||||||
|
useEffect(() => {
|
||||||
|
const dataToSave = {
|
||||||
|
tipoStudente,
|
||||||
|
corsiSelezionati,
|
||||||
|
sezioniAperte,
|
||||||
|
mostraRisultati,
|
||||||
|
}
|
||||||
|
saveToStorage(dataToSave)
|
||||||
|
}, [tipoStudente, corsiSelezionati, sezioniAperte, mostraRisultati])
|
||||||
|
|
||||||
|
const toggleSezione = (nomeSezione: string) => {
|
||||||
|
setSezioniAperte(prev => ({
|
||||||
|
...prev,
|
||||||
|
[nomeSezione]: !prev[nomeSezione],
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
const calcolaMedia = () => {
|
||||||
|
const corsiConVoto = corsiSelezionati.filter(corso => corso.voto !== null && !corso.passFailOnly)
|
||||||
|
if (corsiConVoto.length === 0) {
|
||||||
|
alert('Inserisci almeno un voto per calcolare la media!')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setMostraRisultati(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Funzioni per la gestione dei corsi
|
||||||
|
const aggiungiCorso = (corso: Corso) => {
|
||||||
|
// Controlla se la materia esiste già
|
||||||
|
const materiaEsiste = corsiSelezionati.some(c => c.nome.toLowerCase() === corso.nome.toLowerCase())
|
||||||
|
if (materiaEsiste) {
|
||||||
|
alert('Questa materia è già stata aggiunta')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Controllo per magistrali: massimo 3 istituzioni
|
||||||
|
if (tipoStudente === 'magistrale' && corso.anno === 'istituzioni') {
|
||||||
|
const istituzioniAttuali = corsiSelezionati.filter(c => c.nome.toLowerCase().includes('istituzioni')).length
|
||||||
|
if (istituzioniAttuali >= 3) {
|
||||||
|
alert('Puoi selezionare al massimo 3 istituzioni per la magistrale')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = Date.now().toString()
|
||||||
|
const nuovoCorso: CorsoSelezionato = {
|
||||||
|
id,
|
||||||
|
nome: corso.nome,
|
||||||
|
cfu: corso.cfu,
|
||||||
|
voto: null,
|
||||||
|
lode: false,
|
||||||
|
passFailOnly: corso.passFailOnly,
|
||||||
|
}
|
||||||
|
setCorsiSelezionati([...corsiSelezionati, nuovoCorso])
|
||||||
|
}
|
||||||
|
|
||||||
|
const aggiungiCorsoCustom = () => {
|
||||||
|
// Validazioni
|
||||||
|
if (!customCorso.nome.trim()) {
|
||||||
|
alert('Il nome della materia non può essere vuoto')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (customCorso.cfu <= 0 || customCorso.cfu > 30) {
|
||||||
|
alert('I CFU devono essere tra 1 e 30')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!Number.isInteger(customCorso.cfu)) {
|
||||||
|
alert('I CFU devono essere un numero intero')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Controlla se la materia esiste già
|
||||||
|
const materiaEsiste = corsiSelezionati.some(
|
||||||
|
corso => corso.nome.toLowerCase().trim() === customCorso.nome.toLowerCase().trim(),
|
||||||
|
)
|
||||||
|
if (materiaEsiste) {
|
||||||
|
alert('Questa materia è già stata aggiunta')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = Date.now().toString()
|
||||||
|
const nuovoCorso: CorsoSelezionato = {
|
||||||
|
id,
|
||||||
|
nome: customCorso.nome.trim(),
|
||||||
|
cfu: customCorso.cfu,
|
||||||
|
voto: null,
|
||||||
|
lode: false,
|
||||||
|
}
|
||||||
|
setCorsiSelezionati([...corsiSelezionati, nuovoCorso])
|
||||||
|
setCustomCorso({ nome: '', cfu: 0 })
|
||||||
|
setShowCustomForm(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const rimuoviCorso = (id: string) => {
|
||||||
|
setCorsiSelezionati(corsiSelezionati.filter(corso => corso.id !== id))
|
||||||
|
}
|
||||||
|
|
||||||
|
const aggiornaVoto = (id: string, voto: number | null) => {
|
||||||
|
// Validazione voto: deve essere tra 18 e 30 e intero
|
||||||
|
if (voto !== null && (voto < 18 || voto > 30 || !Number.isInteger(voto))) {
|
||||||
|
return // Ignora valori non validi
|
||||||
|
}
|
||||||
|
|
||||||
|
setCorsiSelezionati(
|
||||||
|
corsiSelezionati.map(corso =>
|
||||||
|
corso.id === id ? { ...corso, voto, lode: voto !== 30 ? false : corso.lode } : corso,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const aggiornaLode = (id: string, lode: boolean) => {
|
||||||
|
setCorsiSelezionati(corsiSelezionati.map(corso => (corso.id === id ? { ...corso, lode } : corso)))
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetTutto = () => {
|
||||||
|
if (corsiSelezionati.length > 0) {
|
||||||
|
if (confirm('Sei sicuro di voler cancellare tutte le materie selezionate?')) {
|
||||||
|
setCorsiSelezionati([])
|
||||||
|
setCustomCorso({ nome: '', cfu: 0 })
|
||||||
|
setShowCustomForm(false)
|
||||||
|
setSezioniAperte({})
|
||||||
|
setMostraRisultati(false)
|
||||||
|
// Pulisce anche il localStorage
|
||||||
|
try {
|
||||||
|
localStorage.removeItem('media-pesata-data')
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Errore nella pulizia del localStorage:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calcoli della media pesata
|
||||||
|
const calcolaMediaPesata = () => {
|
||||||
|
// Escludi le materie pass/fail dal calcolo
|
||||||
|
const corsiConVoto = corsiSelezionati.filter(corso => corso.voto !== null && !corso.passFailOnly)
|
||||||
|
if (corsiConVoto.length === 0) {
|
||||||
|
return {
|
||||||
|
mediaPesata: 0,
|
||||||
|
votoAmmissione: 0,
|
||||||
|
massimoVotoLaurea: 0,
|
||||||
|
conLode: false,
|
||||||
|
bonusLodi: 0,
|
||||||
|
errore: 'Nessun voto inserito per il calcolo della media',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calcola CFU totali con voto
|
||||||
|
const cfuTotaliConVoto = corsiConVoto.reduce((sum, corso) => sum + corso.cfu, 0)
|
||||||
|
const cfuDaEscludere = tipoStudente === 'triennale' ? 15 : 9
|
||||||
|
|
||||||
|
// Se i CFU sono insufficienti per l'esclusione, avvisa l'utente
|
||||||
|
if (cfuTotaliConVoto < cfuDaEscludere) {
|
||||||
|
return {
|
||||||
|
mediaPesata: 0,
|
||||||
|
votoAmmissione: 0,
|
||||||
|
massimoVotoLaurea: 0,
|
||||||
|
conLode: false,
|
||||||
|
bonusLodi: 0,
|
||||||
|
errore: `Hai inserito solo ${cfuTotaliConVoto} CFU con voto. Servono almeno ${cfuDaEscludere} CFU per applicare le regole di esclusione.`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ordina per voto crescente
|
||||||
|
const corsiOrdinati = [...corsiConVoto].sort((a, b) => a.voto! - b.voto!)
|
||||||
|
|
||||||
|
let cfuEsclusi = 0
|
||||||
|
const corsiValidi: CorsoSelezionato[] = []
|
||||||
|
|
||||||
|
for (const corso of corsiOrdinati) {
|
||||||
|
if (cfuEsclusi < cfuDaEscludere) {
|
||||||
|
const cfuRimanentiDaEscludere = cfuDaEscludere - cfuEsclusi
|
||||||
|
if (corso.cfu <= cfuRimanentiDaEscludere) {
|
||||||
|
// Escludi tutto il corso
|
||||||
|
cfuEsclusi += corso.cfu
|
||||||
|
} else {
|
||||||
|
// Escludi solo una parte del corso
|
||||||
|
const cfuValidi = corso.cfu - cfuRimanentiDaEscludere
|
||||||
|
corsiValidi.push({ ...corso, cfu: cfuValidi })
|
||||||
|
cfuEsclusi = cfuDaEscludere
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
corsiValidi.push(corso)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calcola media pesata
|
||||||
|
const sommaPesata = corsiValidi.reduce((sum, corso) => sum + corso.voto! * corso.cfu, 0)
|
||||||
|
const sommaCfu = corsiValidi.reduce((sum, corso) => sum + corso.cfu, 0)
|
||||||
|
|
||||||
|
const mediaPesata = sommaCfu > 0 ? sommaPesata / sommaCfu : 0
|
||||||
|
|
||||||
|
// Calcola bonus lodi
|
||||||
|
const bonusLodi = corsiConVoto.reduce((bonus, corso) => {
|
||||||
|
if (corso.lode) {
|
||||||
|
return bonus + (corso.cfu > 6 ? 0.5 : 0.25)
|
||||||
|
}
|
||||||
|
return bonus
|
||||||
|
}, 0)
|
||||||
|
|
||||||
|
// Cap del bonus lodi basato sul tipo di studente
|
||||||
|
const capBonusLodi = tipoStudente === 'triennale' ? 1.5 : 2
|
||||||
|
const bonusLodiFinal = Math.min(bonusLodi, capBonusLodi)
|
||||||
|
|
||||||
|
// Voto di ammissione alla laurea (media pesata * 11/3)
|
||||||
|
const votoAmmissione = (mediaPesata * 11) / 3
|
||||||
|
|
||||||
|
// Voto di ammissione finale = voto ammissione + bonus lodi
|
||||||
|
const votoAmmissioneFinale = votoAmmissione + bonusLodiFinal
|
||||||
|
|
||||||
|
// Massimo voto di laurea possibile = voto ammissione + 10 (cappato a 110)
|
||||||
|
const massimoVotoLaurea = Math.min(votoAmmissioneFinale + 10, 110)
|
||||||
|
const conLode = massimoVotoLaurea === 110
|
||||||
|
|
||||||
|
return {
|
||||||
|
mediaPesata: Math.round(mediaPesata * 100) / 100,
|
||||||
|
votoAmmissione: Math.round(votoAmmissioneFinale * 100) / 100,
|
||||||
|
massimoVotoLaurea: Math.round(massimoVotoLaurea * 100) / 100,
|
||||||
|
conLode,
|
||||||
|
bonusLodi: Math.round(bonusLodiFinal * 100) / 100,
|
||||||
|
errore: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filtra corsi disponibili in base al tipo di studente
|
||||||
|
const getCorsiDisponibili = () => {
|
||||||
|
if (tipoStudente === 'triennale') {
|
||||||
|
return CORSI_DISPONIBILI.filter(corso => corso.anno !== 'istituzioni')
|
||||||
|
} else {
|
||||||
|
return CORSI_DISPONIBILI.filter(
|
||||||
|
corso => corso.anno === 'istituzioni' || corso.anno === '3' || corso.anno === 'M',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Raggruppa corsi per categoria
|
||||||
|
const raggruppaCorsi = () => {
|
||||||
|
const corsi = getCorsiDisponibili()
|
||||||
|
const gruppi: Record<string, Corso[]> = {}
|
||||||
|
|
||||||
|
if (tipoStudente === 'triennale') {
|
||||||
|
gruppi['Primo Anno'] = corsi.filter(c => c.anno === '1')
|
||||||
|
gruppi['Secondo Anno'] = corsi.filter(c => c.anno === '2')
|
||||||
|
gruppi['Terzo Anno'] = corsi.filter(c => c.anno === '3')
|
||||||
|
gruppi['Materie a Scelta'] = corsi.filter(c => c.anno === 'M')
|
||||||
|
} else {
|
||||||
|
// Per magistrali: prima le istituzioni, poi tutto il resto come "Materie a Scelta"
|
||||||
|
gruppi['Istituzioni'] = corsi.filter(c => c.anno === 'istituzioni')
|
||||||
|
gruppi['Materie a Scelta'] = corsi.filter(c => c.anno === '3' || c.anno === 'M')
|
||||||
|
}
|
||||||
|
|
||||||
|
return gruppi
|
||||||
|
}
|
||||||
|
|
||||||
|
const cambiaTipoStudente = (nuovoTipo: TipoStudente) => {
|
||||||
|
setTipoStudente(nuovoTipo)
|
||||||
|
}
|
||||||
|
|
||||||
|
const gruppiCorsi = raggruppaCorsi()
|
||||||
|
const risultati = calcolaMediaPesata()
|
||||||
|
const totaleCfu = corsiSelezionati.reduce((sum, corso) => sum + corso.cfu, 0)
|
||||||
|
const maxCfu = tipoStudente === 'triennale' ? 171 : 93 // 180 − 9 e 120 − 27 per la tesi
|
||||||
|
const cfuError = totaleCfu > maxCfu
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class="media-pesata-app">
|
||||||
|
{/* Selezione tipo studente */}
|
||||||
|
<div class="card student-type-switcher wide">
|
||||||
|
<div class="grid-center">
|
||||||
|
<h2>Corso di Laurea</h2>
|
||||||
|
<div class="compound-button">
|
||||||
|
<button
|
||||||
|
class={tipoStudente === 'triennale' ? 'active' : ''}
|
||||||
|
onClick={() => cambiaTipoStudente('triennale')}
|
||||||
|
>
|
||||||
|
Triennale
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class={tipoStudente === 'magistrale' ? 'active' : ''}
|
||||||
|
onClick={() => cambiaTipoStudente('magistrale')}
|
||||||
|
>
|
||||||
|
Magistrale
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Counter CFU */}
|
||||||
|
<div class={`cfu-counter wide ${cfuError ? 'error' : ''}`}>
|
||||||
|
<h3>
|
||||||
|
CFU Totali: {totaleCfu}/{maxCfu}
|
||||||
|
{tipoStudente === 'triennale' ? ' (+9 tesi)' : ' (+27 tesi)'}
|
||||||
|
</h3>
|
||||||
|
{cfuError && <p class="error-text">⚠️ Hai superato il limite di CFU consentiti!</p>}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sezione selezione corsi */}
|
||||||
|
<div class="card">
|
||||||
|
<div class="title">
|
||||||
|
<h2>Seleziona Materie</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{Object.entries(gruppiCorsi).map(([categoria, corsi]) => (
|
||||||
|
<div key={categoria} class="course-category">
|
||||||
|
<button onClick={() => toggleSezione(categoria)}>
|
||||||
|
<div class="h-flex">
|
||||||
|
{categoria}
|
||||||
|
<div class="spacer"></div>
|
||||||
|
<span class={`toggle-icon ${sezioniAperte[categoria] ? 'expanded' : ''}`}>▶</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{sezioniAperte[categoria] && (
|
||||||
|
<div class="course-grid">
|
||||||
|
{corsi.map((corso, index) => (
|
||||||
|
<button
|
||||||
|
key={index}
|
||||||
|
class="course-button"
|
||||||
|
onClick={() => aggiungiCorso(corso)}
|
||||||
|
disabled={corsiSelezionati.some(c => c.nome === corso.nome)}
|
||||||
|
>
|
||||||
|
<span class="course-name">{corso.nome}</span>
|
||||||
|
<span class="course-cfu">{corso.cfu} CFU</span>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/* Form per materia custom */}
|
||||||
|
<div class="custom-course">
|
||||||
|
<h3>Materia Personalizzata</h3>
|
||||||
|
{!showCustomForm ? (
|
||||||
|
<button onClick={() => setShowCustomForm(true)}>+ Aggiungi Materia Personalizzata</button>
|
||||||
|
) : (
|
||||||
|
<div class="custom-form">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Nome materia"
|
||||||
|
value={customCorso.nome}
|
||||||
|
onChange={e =>
|
||||||
|
setCustomCorso({ ...customCorso, nome: (e.target as HTMLInputElement).value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="CFU"
|
||||||
|
min="1"
|
||||||
|
max="30"
|
||||||
|
step="1"
|
||||||
|
value={customCorso.cfu || ''}
|
||||||
|
onChange={e =>
|
||||||
|
setCustomCorso({
|
||||||
|
...customCorso,
|
||||||
|
cfu: parseInt((e.target as HTMLInputElement).value) || 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<button onClick={aggiungiCorsoCustom}>Aggiungi</button>
|
||||||
|
<button onClick={() => setShowCustomForm(false)}>Annulla</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sezione lista corsi selezionati */}
|
||||||
|
<div class="card">
|
||||||
|
<div class="h-flex">
|
||||||
|
<div class="title">
|
||||||
|
<h2>Materie Selezionate</h2>
|
||||||
|
</div>
|
||||||
|
<div class="spacer"></div>
|
||||||
|
{corsiSelezionati.length > 0 && <button onClick={resetTutto}>🗑️ Cancella Tutto</button>}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{corsiSelezionati.length === 0 ? (
|
||||||
|
<p>Nessuna materia selezionata</p>
|
||||||
|
) : (
|
||||||
|
<div class="courses-list">
|
||||||
|
{corsiSelezionati.map(corso => (
|
||||||
|
<div key={corso.id} class="course-item">
|
||||||
|
<span class="course-name">{corso.nome}</span>
|
||||||
|
<span class="course-cfu">{corso.cfu} CFU</span>
|
||||||
|
|
||||||
|
{!corso.passFailOnly && (
|
||||||
|
<div class="course-grade tall">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="Voto"
|
||||||
|
min="18"
|
||||||
|
max="30"
|
||||||
|
step="1"
|
||||||
|
value={corso.voto || ''}
|
||||||
|
onChange={e =>
|
||||||
|
aggiornaVoto(
|
||||||
|
corso.id,
|
||||||
|
parseInt((e.target as HTMLInputElement).value) || null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<label class={`lode-checkbox ${corso.voto !== 30 ? 'disabled' : ''}`}>
|
||||||
|
<input
|
||||||
|
class="star"
|
||||||
|
type="checkbox"
|
||||||
|
checked={corso.lode}
|
||||||
|
disabled={corso.voto !== 30}
|
||||||
|
onChange={e =>
|
||||||
|
aggiornaLode(corso.id, (e.target as HTMLInputElement).checked)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
Lode
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div class="actions tall">
|
||||||
|
<button
|
||||||
|
class="icon remove-btn"
|
||||||
|
onClick={() => rimuoviCorso(corso.id)}
|
||||||
|
title="Rimuovi materia"
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Pulsante Calcola */}
|
||||||
|
{corsiSelezionati.length > 0 && (
|
||||||
|
<div class="calculate-section wide">
|
||||||
|
<button onClick={calcolaMedia}>🧮 Calcola Media e Voto di Laurea</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Risultati */}
|
||||||
|
{risultati && mostraRisultati && (
|
||||||
|
<div class="results wide">
|
||||||
|
<h2>Risultati</h2>
|
||||||
|
{risultati.errore ? (
|
||||||
|
<div class="error-message">
|
||||||
|
<p>⚠️ {risultati.errore}</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div class="results-grid">
|
||||||
|
<div class="result-item">
|
||||||
|
<span class="label">Media Pesata:</span>
|
||||||
|
<span class="value">{risultati.mediaPesata}</span>
|
||||||
|
</div>
|
||||||
|
<div class="result-item">
|
||||||
|
<span class="label">Bonus Lodi:</span>
|
||||||
|
<span class="value">+{risultati.bonusLodi}</span>
|
||||||
|
</div>
|
||||||
|
<div class="result-item highlight">
|
||||||
|
<span class="label">Voto di Ammissione:</span>
|
||||||
|
<span class="value">{risultati.votoAmmissione}</span>
|
||||||
|
</div>
|
||||||
|
<div class="result-item highlight">
|
||||||
|
<span class="label">Massimo Voto Di Laurea Possibile:</span>
|
||||||
|
<span class="value">
|
||||||
|
{risultati.massimoVotoLaurea}
|
||||||
|
{risultati.conLode && <span class="lode-badge">(+lode)</span>}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Nota informativa */}
|
||||||
|
<div class="card wide">
|
||||||
|
<h3>📋 Come viene calcolata la media</h3>
|
||||||
|
<div class="info-content">
|
||||||
|
<h4>Regole di esclusione CFU:</h4>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<strong>Triennale:</strong> I 15 CFU con i voti più bassi vengono esclusi dalla media
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Magistrale:</strong> I 9 CFU con i voti più bassi vengono esclusi dalla media
|
||||||
|
</li>
|
||||||
|
<li>Se un corso ha più CFU di quelli da escludere, viene diviso proporzionalmente</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h4>Calcolo del voto finale:</h4>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<strong>Media pesata:</strong> Somma dei (voto × CFU) diviso per i CFU totali
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Bonus lodi:</strong> +0.5 per lodi in materie > 6 CFU, +0.25 per lodi in materie
|
||||||
|
≤ 6 CFU (max +1.5 per triennale, max +2 per magistrale)
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<strong>Voto di laurea:</strong> (Voto finale × 11) ÷ 3
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h4>Note:</h4>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
Le materie <strong>Pass/Fail</strong> non contribuiscono al calcolo della media
|
||||||
|
</li>
|
||||||
|
<li>Il voto finale è limitato a 30</li>
|
||||||
|
<li>Per i magistrali: massimo 3 istituzioni selezionabili</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Funzione per inizializzare l'app
|
||||||
|
// export function initMediaPesataApp() {
|
||||||
|
// const container = document.getElementById('media-pesata-app')
|
||||||
|
// if (container) {
|
||||||
|
// render(<MediaPesataApp />, container)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export default MediaPesataApp
|
||||||
@@ -6,6 +6,7 @@ const links = [
|
|||||||
{ href: '/notizie', text: 'Notizie' },
|
{ href: '/notizie', text: 'Notizie' },
|
||||||
{ href: '/guide', text: 'Guide' },
|
{ href: '/guide', text: 'Guide' },
|
||||||
{ href: '/domande-esami', text: 'Domande Orali' },
|
{ href: '/domande-esami', text: 'Domande Orali' },
|
||||||
|
{ href: '/media-pesata', text: 'Calcolo Media' }, // Beta testing - solo URL diretto
|
||||||
{ href: '/storia', text: 'Storia' },
|
{ href: '/storia', text: 'Storia' },
|
||||||
// { href: '/login', text: 'Login' },
|
// { href: '/login', text: 'Login' },
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
title: Calcola la tua media ed il voto di laurea con il nuovissimo calcolatore del PHC!
|
||||||
|
description: È ora disponibile uno strumento per calcolare la propria media pesata e il voto di ammissione alla laurea secondo le regole del dipartimento
|
||||||
|
publishDate: 2025-06-26
|
||||||
|
---
|
||||||
|
|
||||||
|
# Calcola la tua media ed il voto di laurea con il nuovissimo calcolatore del PHC!
|
||||||
|
|
||||||
|
È ora disponibile nella sezione "Calcolo Media" del sito uno strumento per calcolare la propria media pesata e il voto di ammissione alla laurea secondo le regole ufficiali del dipartimento.
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://phc.dm.unipi.it/media-pesata/">phc.dm.unipi.it/media-pesata/</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
Il calcolatore applica automaticamente le regole di esclusione previste dal regolamento:
|
||||||
|
|
||||||
|
- **Triennale**: vengono esclusi i 15 CFU con i voti più bassi
|
||||||
|
- **Magistrale**: vengono esclusi i 9 CFU con i voti più bassi
|
||||||
|
|
||||||
|
Il sistema calcola anche il bonus per le lodi, che vale +0.5 punti per ogni materia superiore a 6 CFU e +0.25 punti per materie da 6 CFU o meno, con un tetto massimo di +1.5 punti per la triennale e +2 punti per la magistrale.
|
||||||
|
|
||||||
|
Se dovessero esserci bug scriveteci un'email a <a href="mailto:macchinisti@lists.dm.unipi.it">macchinisti@lists.dm.unipi.it</a>!
|
||||||
@@ -25,7 +25,8 @@
|
|||||||
|
|
||||||
- fullName: Francesco Baldino
|
- fullName: Francesco Baldino
|
||||||
entranceDate: 2022
|
entranceDate: 2022
|
||||||
description: Bla bla Star Wars
|
description: |
|
||||||
|
Appassionato di Star Wars, NixOS e lunghe camminate in montagna. Pokemon preferito: Latias.
|
||||||
social:
|
social:
|
||||||
github: https://github.com/Fran314
|
github: https://github.com/Fran314
|
||||||
website: https://poisson.phc.dm.unipi.it/~baldino
|
website: https://poisson.phc.dm.unipi.it/~baldino
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
import '@/styles/pages/media-pesata.css'
|
||||||
|
import PageLayout from '../layouts/PageLayout.astro'
|
||||||
|
|
||||||
|
import { MediaPesataApp } from '@/client/MediaPesataApp'
|
||||||
|
---
|
||||||
|
|
||||||
|
<PageLayout
|
||||||
|
title="Voto Laurea"
|
||||||
|
description="Calcola la tua media pesata e il voto di laurea seguendo le regole del dipartimento"
|
||||||
|
>
|
||||||
|
<div class="media-pesata-container">
|
||||||
|
<h1>Calcolo Media e Voto di Laurea</h1>
|
||||||
|
<p>
|
||||||
|
Calcola la tua media pesata e il voto con cui ti siederai alla discussione di laurea, seguendo le regole del
|
||||||
|
dipartimento di Matematica.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<MediaPesataApp client:load />
|
||||||
|
</div>
|
||||||
|
</PageLayout>
|
||||||
+90
-1
@@ -75,7 +75,7 @@ Controls - for things like buttons, input, select
|
|||||||
input[type='text'],
|
input[type='text'],
|
||||||
input[type='password'] {
|
input[type='password'] {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 2.5rem;
|
min-height: 1.75rem;
|
||||||
|
|
||||||
/* @include neo-brutalist-card; */
|
/* @include neo-brutalist-card; */
|
||||||
border: 3px solid #222;
|
border: 3px solid #222;
|
||||||
@@ -89,6 +89,95 @@ Controls - for things like buttons, input, select
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input[type='checkbox'] {
|
||||||
|
appearance: none;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
-moz-appearance: none;
|
||||||
|
|
||||||
|
width: calc(1.5rem + 1px);
|
||||||
|
height: calc(1.5rem + 1px);
|
||||||
|
|
||||||
|
background: #fff;
|
||||||
|
border: 3px solid #222;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 3px 3px 0 0 #222;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
transition: all 64ms linear;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translate(-1px, -1px);
|
||||||
|
box-shadow: 4px 4px 0 0 #222;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translate(1px, 1px);
|
||||||
|
box-shadow: 2px 2px 0 0 #222;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:checked {
|
||||||
|
background: #1e6733;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
|
||||||
|
width: 0.8rem;
|
||||||
|
height: 0.8rem;
|
||||||
|
background: #1e6733;
|
||||||
|
|
||||||
|
clip-path: polygon(10% 55%, 35% 75%, 85% 25%, 90% 35%, 40% 95%, 5% 60%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #2b8b47;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background: #eee;
|
||||||
|
border-color: #888;
|
||||||
|
box-shadow: 3px 3px 0 0 #888;
|
||||||
|
cursor: not-allowed;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: none;
|
||||||
|
box-shadow: 3px 3px 0 0 #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:checked {
|
||||||
|
background: #aaa;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
background: #666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.star {
|
||||||
|
&:checked::after {
|
||||||
|
background: rgb(255, 197, 49);
|
||||||
|
clip-path: polygon(
|
||||||
|
50% 0%,
|
||||||
|
61% 35%,
|
||||||
|
98% 35%,
|
||||||
|
68% 57%,
|
||||||
|
79% 91%,
|
||||||
|
50% 70%,
|
||||||
|
21% 91%,
|
||||||
|
32% 57%,
|
||||||
|
2% 35%,
|
||||||
|
39% 35%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
form {
|
form {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
|
|||||||
@@ -325,6 +325,38 @@ Custom Page Styles
|
|||||||
opacity: 0 !important;
|
opacity: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.grid-center {
|
||||||
|
display: grid;
|
||||||
|
place-content: center;
|
||||||
|
place-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h-flex {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-flex {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h-flex,
|
||||||
|
.v-flex {
|
||||||
|
place-self: stretch;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .spacer {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 1024px) {
|
@media screen and (min-width: 1024px) {
|
||||||
.mobile-only {
|
.mobile-only {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user