style changes
This commit is contained in:
+185
-195
@@ -186,7 +186,7 @@ const CORSI_DISPONIBILI: Corso[] = [
|
||||
{ nome: 'Ultrafiltri e metodi non-standard', anno: 'M', cfu: 6 },
|
||||
]
|
||||
|
||||
function MediaPesataApp() {
|
||||
export function MediaPesataApp() {
|
||||
// Funzioni per localStorage
|
||||
const loadFromStorage = () => {
|
||||
try {
|
||||
@@ -220,13 +220,22 @@ function MediaPesataApp() {
|
||||
}
|
||||
|
||||
// Inizializzazione con dati salvati
|
||||
const initialData = loadFromStorage()
|
||||
const [tipoStudente, setTipoStudente] = useState<TipoStudente>(initialData.tipoStudente)
|
||||
const [corsiSelezionati, setCorsiSelezionati] = useState<CorsoSelezionato[]>(initialData.corsiSelezionati)
|
||||
// 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>>(initialData.sezioniAperte)
|
||||
const [mostraRisultati, setMostraRisultati] = useState(initialData.mostraRisultati)
|
||||
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(() => {
|
||||
@@ -482,15 +491,6 @@ function MediaPesataApp() {
|
||||
}
|
||||
|
||||
const cambiaTipoStudente = (nuovoTipo: TipoStudente) => {
|
||||
if (corsiSelezionati.length > 0 && nuovoTipo !== tipoStudente) {
|
||||
if (
|
||||
!confirm(
|
||||
'Cambiando il tipo di studente, alcune materie potrebbero non essere più disponibili. Continuare?',
|
||||
)
|
||||
) {
|
||||
return
|
||||
}
|
||||
}
|
||||
setTipoStudente(nuovoTipo)
|
||||
}
|
||||
|
||||
@@ -501,217 +501,207 @@ function MediaPesataApp() {
|
||||
const cfuError = totaleCfu > maxCfu
|
||||
|
||||
return (
|
||||
<div className="media-pesata-app">
|
||||
<div class="media-pesata-app">
|
||||
{/* Selezione tipo studente */}
|
||||
<div className="student-type-selector">
|
||||
<h2>Corso di Laurea</h2>
|
||||
<div className="radio-group">
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
value="triennale"
|
||||
checked={tipoStudente === 'triennale'}
|
||||
onChange={e => cambiaTipoStudente((e.target as HTMLInputElement).value as TipoStudente)}
|
||||
/>
|
||||
Triennale
|
||||
</label>
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
value="magistrale"
|
||||
checked={tipoStudente === 'magistrale'}
|
||||
onChange={e => cambiaTipoStudente((e.target as HTMLInputElement).value as TipoStudente)}
|
||||
/>
|
||||
Magistrale
|
||||
</label>
|
||||
<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 className={`cfu-counter ${cfuError ? 'error' : ''}`}>
|
||||
<div class={`cfu-counter wide ${cfuError ? 'error' : ''}`}>
|
||||
<h3>
|
||||
CFU Totali: {totaleCfu}/{maxCfu}
|
||||
{tipoStudente === 'triennale' ? ' (+9 tesi)' : ' (+27 tesi)'}
|
||||
</h3>
|
||||
{cfuError && <p className="error-text">⚠️ Hai superato il limite di CFU consentiti!</p>}
|
||||
{cfuError && <p class="error-text">⚠️ Hai superato il limite di CFU consentiti!</p>}
|
||||
</div>
|
||||
|
||||
<div className="main-content">
|
||||
{/* Sezione selezione corsi */}
|
||||
<div className="course-selection">
|
||||
{/* Sezione selezione corsi */}
|
||||
<div class="card">
|
||||
<div class="title">
|
||||
<h2>Seleziona Materie</h2>
|
||||
</div>
|
||||
|
||||
{Object.entries(gruppiCorsi).map(([categoria, corsi]) => (
|
||||
<div key={categoria} className="course-category">
|
||||
<button className="category-header" onClick={() => toggleSezione(categoria)}>
|
||||
<h3>{categoria}</h3>
|
||||
<span className={`toggle-icon ${sezioniAperte[categoria] ? 'expanded' : ''}`}>▶</span>
|
||||
</button>
|
||||
{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 className="course-grid">
|
||||
{corsi.map((corso, index) => (
|
||||
<button
|
||||
key={index}
|
||||
className="course-button"
|
||||
onClick={() => aggiungiCorso(corso)}
|
||||
disabled={corsiSelezionati.some(c => c.nome === corso.nome)}
|
||||
>
|
||||
<span className="course-name">{corso.nome}</span>
|
||||
<span className="course-cfu">{corso.cfu} CFU</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* Form per materia custom */}
|
||||
<div className="custom-course">
|
||||
<h3>Materia Personalizzata</h3>
|
||||
{!showCustomForm ? (
|
||||
<button onClick={() => setShowCustomForm(true)} className="add-custom-btn">
|
||||
+ Aggiungi Materia Personalizzata
|
||||
</button>
|
||||
) : (
|
||||
<div className="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} className="confirm-btn">
|
||||
Aggiungi
|
||||
</button>
|
||||
<button onClick={() => setShowCustomForm(false)} className="cancel-btn">
|
||||
Annulla
|
||||
</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>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* Sezione lista corsi selezionati */}
|
||||
<div className="selected-courses">
|
||||
<div className="section-header">
|
||||
<h2>Materie Selezionate</h2>
|
||||
{corsiSelezionati.length > 0 && (
|
||||
<button onClick={resetTutto} className="reset-btn">
|
||||
🗑️ Cancella Tutto
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{corsiSelezionati.length === 0 ? (
|
||||
<p className="no-courses">Nessuna materia selezionata</p>
|
||||
{/* Form per materia custom */}
|
||||
<div class="custom-course">
|
||||
<h3>Materia Personalizzata</h3>
|
||||
{!showCustomForm ? (
|
||||
<button onClick={() => setShowCustomForm(true)}>+ Aggiungi Materia Personalizzata</button>
|
||||
) : (
|
||||
<div className="courses-list">
|
||||
{corsiSelezionati.map(corso => (
|
||||
<div key={corso.id} className="course-item">
|
||||
<div className="course-info">
|
||||
<span className="course-name">{corso.nome}</span>
|
||||
<span className="course-cfu">{corso.cfu} CFU</span>
|
||||
{corso.passFailOnly && <span className="pass-fail-badge">Pass/Fail</span>}
|
||||
</div>
|
||||
|
||||
{!corso.passFailOnly && (
|
||||
<div className="course-grade">
|
||||
<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 className={`lode-checkbox ${corso.voto !== 30 ? 'disabled' : ''}`}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={corso.lode}
|
||||
disabled={corso.voto !== 30}
|
||||
onChange={e =>
|
||||
aggiornaLode(corso.id, (e.target as HTMLInputElement).checked)
|
||||
}
|
||||
/>
|
||||
Lode
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={() => rimuoviCorso(corso.id)}
|
||||
className="remove-btn"
|
||||
title="Rimuovi materia"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
<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 className="calculate-section">
|
||||
<button onClick={calcolaMedia} className="calculate-btn">
|
||||
🧮 Calcola Media e Voto di Laurea
|
||||
</button>
|
||||
<div class="calculate-section wide">
|
||||
<button onClick={calcolaMedia}>🧮 Calcola Media e Voto di Laurea</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Risultati */}
|
||||
{risultati && mostraRisultati && (
|
||||
<div className="results">
|
||||
<div class="results wide">
|
||||
<h2>Risultati</h2>
|
||||
{risultati.errore ? (
|
||||
<div className="error-message">
|
||||
<div class="error-message">
|
||||
<p>⚠️ {risultati.errore}</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="results-grid">
|
||||
<div className="result-item">
|
||||
<span className="label">Media Pesata:</span>
|
||||
<span className="value">{risultati.mediaPesata}</span>
|
||||
<div class="results-grid">
|
||||
<div class="result-item">
|
||||
<span class="label">Media Pesata:</span>
|
||||
<span class="value">{risultati.mediaPesata}</span>
|
||||
</div>
|
||||
<div className="result-item">
|
||||
<span className="label">Bonus Lodi:</span>
|
||||
<span className="value">+{risultati.bonusLodi}</span>
|
||||
<div class="result-item">
|
||||
<span class="label">Bonus Lodi:</span>
|
||||
<span class="value">+{risultati.bonusLodi}</span>
|
||||
</div>
|
||||
<div className="result-item highlight">
|
||||
<span className="label">Voto di Ammissione:</span>
|
||||
<span className="value">{risultati.votoAmmissione}</span>
|
||||
<div class="result-item highlight">
|
||||
<span class="label">Voto di Ammissione:</span>
|
||||
<span class="value">{risultati.votoAmmissione}</span>
|
||||
</div>
|
||||
<div className="result-item highlight">
|
||||
<span className="label">Massimo Voto Di Laurea Possibile:</span>
|
||||
<span className="value">
|
||||
<div class="result-item highlight">
|
||||
<span class="label">Massimo Voto Di Laurea Possibile:</span>
|
||||
<span class="value">
|
||||
{risultati.massimoVotoLaurea}
|
||||
{risultati.conLode && <span className="lode-badge">(+lode)</span>}
|
||||
{risultati.conLode && <span class="lode-badge">(+lode)</span>}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -720,9 +710,9 @@ function MediaPesataApp() {
|
||||
)}
|
||||
|
||||
{/* Nota informativa */}
|
||||
<div className="info-section">
|
||||
<div class="card wide">
|
||||
<h3>📋 Come viene calcolata la media</h3>
|
||||
<div className="info-content">
|
||||
<div class="info-content">
|
||||
<h4>Regole di esclusione CFU:</h4>
|
||||
<ul>
|
||||
<li>
|
||||
@@ -763,11 +753,11 @@ function MediaPesataApp() {
|
||||
}
|
||||
|
||||
// Funzione per inizializzare l'app
|
||||
export function initMediaPesataApp() {
|
||||
const container = document.getElementById('media-pesata-app')
|
||||
if (container) {
|
||||
render(<MediaPesataApp />, container)
|
||||
}
|
||||
}
|
||||
// export function initMediaPesataApp() {
|
||||
// const container = document.getElementById('media-pesata-app')
|
||||
// if (container) {
|
||||
// render(<MediaPesataApp />, container)
|
||||
// }
|
||||
// }
|
||||
|
||||
export default MediaPesataApp
|
||||
// export default MediaPesataApp
|
||||
|
||||
Reference in New Issue
Block a user