Tasti per modificare lo stato di una soluzione e dettagli estetici

pull/1/head
Antonio De Lucreziis 2 years ago
parent 9dd5a259ae
commit ea3b7aeecf

@ -17,7 +17,7 @@ export const Problem = ({ id, content, solutionsCount }: Props) => {
<div class="problem-content">
<Markdown source={content} />
</div>
{solutionsCount && solutionsCount > 0 && (
{solutionsCount !== undefined && solutionsCount > 0 && (
<div class="problem-footer">
{solutionsCount} soluzion{solutionsCount === 1 ? 'e' : 'i'}
</div>

@ -1,16 +1,25 @@
import { JSX } from 'preact/jsx-runtime'
import { ProblemId, SolutionStatus, UserId } from '../../shared/model'
import { Markdown } from './Markdown'
import { Select } from './Select'
const STATUS_SELECT_OPTIONS: Record<SolutionStatus, JSX.Element> = {
['pending']: <div class="pending">Soluzione in attesa di correzione</div>,
['correct']: <div class="correct">Soluzione corretta</div>,
['wrong']: <div class="wrong">Soluzione sbagliata</div>,
}
type Props = {
sentBy?: UserId
forProblem: ProblemId
content: string
status?: SolutionStatus
adminControls: boolean
}
export const Solution = ({ sentBy, forProblem, content }: Props) => {
export const Solution = ({ sentBy, forProblem, content, status, adminControls }: Props) => {
return (
<div class="solution">
<div class={['solution', status].join(' ')}>
<div class="solution-header">
<div>
Soluzione
@ -31,6 +40,32 @@ export const Solution = ({ sentBy, forProblem, content }: Props) => {
<div class="solution-content">
<Markdown source={content} />
</div>
{status && (
<div class="solution-footer">
{/* <div class="row">
<div class="label">Stato</div>
<Select value={status} options={STATUS_SELECT_OPTIONS} />
</div> */}
<div class="row">
<div class="status-label">{STATUS_SELECT_OPTIONS[status]}</div>
{adminControls && (
<>
<button disabled={status === 'pending'} class="icon">
<span class="material-symbols-outlined">hourglass_empty</span>
</button>
<button disabled={status === 'correct'} class="icon">
<span class="material-symbols-outlined correct">
check_circle
</span>
</button>
<button disabled={status === 'wrong'} class="icon">
<span class="material-symbols-outlined wrong">cancel</span>
</button>
</>
)}
</div>
</div>
)}
</div>
)
}

@ -1,5 +1,9 @@
import { useContext, useEffect, useRef, useState } from 'preact/hooks'
import { Problem as ProblemModel, Solution as SolutionModel } from '../../shared/model'
import {
isAdministrator,
Problem as ProblemModel,
Solution as SolutionModel,
} from '../../shared/model'
import { server } from '../api'
import { Header } from '../components/Header'
import { MarkdownEditor } from '../components/MarkdownEditor'
@ -44,7 +48,10 @@ export const ProblemPage = ({ id }: RouteProps) => {
<summary>Soluzioni</summary>
<div class="solution-list">
{solutions.map(s => (
<Solution {...s} />
<Solution
{...s}
adminControls={user !== null && isAdministrator(user.role)}
/>
))}
</div>
</details>

@ -1,19 +1,33 @@
import { route } from 'preact-router'
import { useState } from 'preact/hooks'
import { isAdministrator, Solution as SolutionModel, User } from '../../shared/model'
import { server } from '../api'
import { Header } from '../components/Header'
import { Select } from '../components/Select'
import { Solution } from '../components/Solution'
import { useCurrentUser } from '../hooks'
import { useCurrentUser, useReadResource } from '../hooks'
export const ProfilePage = ({}) => {
const [solutions, setSolutions] = useState([])
const SolutionList = ({ user }: { user: User }) => {
const [solutions, refresh] = useReadResource<SolutionModel[]>(
`/api/solutions?user=${user.id}`,
[]
)
return (
<div class="solution-list">
{solutions.map(({ forProblem, content, status }) => (
<Solution
{...{ forProblem, content, status }}
adminControls={isAdministrator(user.role)}
/>
))}
</div>
)
}
export const ProfilePage = ({}) => {
const [user, logout] = useCurrentUser(user => {
if (user) {
server.get(`/api/solutions?user=${user.id}`).then(solutions => {
setSolutions(solutions)
})
} else {
if (!user) {
route('/login', true)
}
})
@ -28,11 +42,7 @@ export const ProfilePage = ({}) => {
<main class="page-profile">
<Header {...{ user }} />
<div class="subtitle">Le tue soluzioni</div>
<div class="solution-list">
{solutions.map(({ forProblem, content }) => (
<Solution {...{ forProblem, content }} />
))}
</div>
<SolutionList {...{ user }} />
<div class="subtitle">Altro</div>
<button onClick={handleLogout}>Logout</button>
</main>

@ -35,7 +35,7 @@ textarea {
padding: 1rem;
box-shadow: -2px 4px 8px 1px #00000020, -1px 1px 1px 0px #00000010;
box-shadow: -2px 4px 8px 1px #00000010, 0 0 4px 0px #00000010;
border-radius: 0.25rem;
background: #ffffff;
}
@ -52,13 +52,13 @@ input[type='text'] {
padding: 0.5rem;
box-shadow: -2px 4px 4px 0 #00000020;
box-shadow: -2px 4px 4px 0 #00000010;
border-radius: 0.25rem;
background: #ffffff;
font-family: 'Lato';
font-weight: 400;
font-size: 18px;
font-size: 16px;
color: #555;
}
@ -74,7 +74,7 @@ input[type='text'] {
color: #555;
border: 1px solid #c8c8c8;
padding: 0 0.5rem;
padding: 0;
margin: 0;
box-shadow: -2px 2px 16px 0 #00000010;
@ -94,7 +94,9 @@ input[type='text'] {
appearance: none;
padding: 0.5rem 0;
transform: translate(0, -0.0625rem);
padding: 0.35rem 0 0.35rem 0.35rem;
margin: 0;
border: none;
outline: none;
@ -105,6 +107,16 @@ input[type='text'] {
font-size: 16px;
color: #555;
}
.material-symbols-outlined {
display: grid;
place-content: center;
width: 1.75rem;
height: 1.75rem;
transform: translate(0, 0.0625rem);
}
}
button {
@ -130,6 +142,34 @@ button {
box-shadow: -2px 2px 20px 0 #00000010;
background: linear-gradient(180deg, #fff, #ededed 20%, #e8e8e8 90%, #c0c0c0);
}
&.compact {
padding: 0.35rem;
font-size: 15px;
}
&:disabled {
cursor: default;
opacity: 0.6;
filter: grayscale(1);
&:hover {
border: 1px solid #c8c8c8;
box-shadow: -2px 2px 16px 0 #00000010;
background: linear-gradient(180deg, #f0f0f0, #e8e8e8 20%, #e0e0e0 90%, #cdcdcd);
}
}
&.icon {
padding: 0.25rem;
display: grid;
place-content: center;
.material-symbols-outlined {
font-size: 20px;
}
}
}
.link,
@ -234,6 +274,14 @@ main {
.subtitle {
font-size: 22px;
}
.fill-main {
display: flex;
gap: 0.5rem;
width: 100%;
max-width: 80ch;
}
}
main.page-home {
@ -270,7 +318,14 @@ main.page-problem {
// Components
//
details {
width: 100%;
max-width: 80ch;
}
.solution-list {
width: 100%;
display: flex;
flex-direction: column;
gap: 1rem;
@ -305,7 +360,7 @@ header {
align-items: center;
.nav-item {
font-size: 24px;
font-size: 20px;
font-weight: 300;
a.active {
@ -325,12 +380,12 @@ header {
max-width: 80ch;
box-shadow: -2px 4px 8px 1px #00000020, -1px 1px 1px 0px #00000010;
box-shadow: -2px 4px 8px 1px #00000010, 0 0 4px 0px #00000010;
border-radius: 0.5rem;
background: #ffffff;
display: grid;
grid-template-rows: auto 1fr auto;
display: flex;
flex-direction: column;
gap: 0.5rem;
.problem-header {
@ -356,16 +411,26 @@ header {
.solution {
padding: 1rem;
width: 100%;
max-width: 80ch;
box-shadow: -2px 4px 8px 1px #00000020, -1px 1px 1px 0px #00000010;
box-shadow: -2px 4px 8px 1px #00000010, 0 0 4px 0px #00000010;
border-radius: 0.5rem;
background: #ffffff;
display: grid;
grid-template-rows: auto 1fr;
display: flex;
flex-direction: column;
gap: 0.5rem;
&.correct {
// background: hsl(120, 100%, 90%);
box-shadow: 0 0 12px 2px #00990030, 0 0 1px 1px #00330030;
}
&.wrong {
// background: hsl(0, 100%, 90%);
box-shadow: 0 0 12px 2px #99000030, 0 0 1px 1px #33000030;
}
.solution-header {
display: grid;
grid-template-columns: auto;
@ -377,6 +442,40 @@ header {
.solution-content {
@extend .text-body;
}
.solution-footer {
display: flex;
justify-content: end;
font-size: 16px;
.label {
font-weight: 400;
}
.row {
display: flex;
gap: 0.5rem;
align-items: center;
}
.pending {
color: #1653a3;
font-style: italic;
}
.correct {
color: green;
// font-weight: 400;
}
.wrong {
color: darkred;
// font-weight: 400;
}
.status-label {
text-align: right;
}
}
}
.form {
@ -450,7 +549,7 @@ header {
padding: 1rem;
box-shadow: -2px 4px 8px 1px #00000020, -1px 1px 1px 0px #00000010;
box-shadow: -2px 4px 8px 1px #00000010, 0 0 4px 0px #00000010;
border-radius: 0.25rem;
background: #ffffff;
}

@ -1,25 +0,0 @@
import { ProblemId, SolutionId, UserId } from './model'
// export function refToUserId(ref: string): UserId {
// if (!ref.startsWith('user/')) {
// throw new Error(`"${ref}" is not a reference to a User`)
// }
// return ref.slice('user/'.length)
// }
// export function refToProblemId(ref: string): ProblemId {
// if (!ref.startsWith('problem/')) {
// throw new Error(`"${ref}" is not a reference to a Problem`)
// }
// return ref.slice('problem/'.length)
// }
// export function refToSolutionId(ref: string): SolutionId {
// if (!ref.startsWith('solution/')) {
// throw new Error(`"${ref}" is not a reference to a Solution`)
// }
// return ref.slice('solution/'.length)
// }

@ -48,7 +48,7 @@ export type Problem = {
// Solutions
//
export type SolutionStatus = 'pending' | 'rejected' | 'accepted'
export type SolutionStatus = 'pending' | 'correct' | 'wrong'
export type SolutionId = string

Loading…
Cancel
Save