fix: opravy a ladeni vzhledu a UX

This commit was merged in pull request #18.
This commit is contained in:
Stánek Pavel
2026-02-04 12:06:17 +01:00
parent 37cacd895a
commit d85c764c88
3 changed files with 93 additions and 44 deletions

View File

@@ -372,6 +372,10 @@ body {
&:hover { &:hover {
background: var(--luncher-bg-hover); background: var(--luncher-bg-hover);
.food-name {
color: var(--luncher-primary);
}
} }
&:last-child td { &:last-child td {
@@ -384,20 +388,34 @@ body {
border-color: var(--luncher-border-light); border-color: var(--luncher-border-light);
vertical-align: middle; vertical-align: middle;
color: var(--luncher-text); color: var(--luncher-text);
}
&:first-child { .food-name {
width: 60px; font-weight: 500;
text-align: center; margin-bottom: 4px;
font-weight: 500; }
color: var(--luncher-text-secondary);
}
&:last-child { .food-allergens {
width: 80px; white-space: nowrap;
text-align: right; }
font-weight: 600;
color: var(--luncher-primary); .food-meta {
} display: flex;
justify-content: flex-end;
align-items: center;
gap: 12px;
margin-top: 6px;
font-size: 0.75rem;
}
.food-amount {
color: var(--luncher-text-muted);
font-weight: 400;
}
.food-price {
font-weight: 600;
color: var(--luncher-primary);
} }
.allergen-link { .allergen-link {
@@ -405,7 +423,7 @@ body {
text-decoration: underline; text-decoration: underline;
text-decoration-style: dotted; text-decoration-style: dotted;
color: var(--luncher-text-muted); color: var(--luncher-text-muted);
font-size: 0.85em; font-size: 0.95em;
&:hover { &:hover {
color: var(--luncher-primary); color: var(--luncher-primary);
@@ -431,23 +449,24 @@ body {
.choice-section { .choice-section {
background: var(--luncher-bg-card); background: var(--luncher-bg-card);
border-radius: var(--luncher-radius-lg); border-radius: var(--luncher-radius);
padding: 24px; padding: 16px 20px;
box-shadow: var(--luncher-shadow); box-shadow: var(--luncher-shadow-sm);
margin-bottom: 32px; margin-bottom: 24px;
border: 1px solid var(--luncher-border-light); border: 1px solid var(--luncher-border-light);
p { p {
margin-bottom: 12px; margin-bottom: 8px;
font-weight: 500; font-weight: 500;
color: var(--luncher-text); color: var(--luncher-text);
font-size: 0.95rem;
} }
small { small {
display: block; display: block;
margin-top: 8px; margin-top: 6px;
color: var(--luncher-text-muted); color: var(--luncher-text-muted);
font-size: 0.85rem; font-size: 0.8rem;
} }
} }
@@ -578,7 +597,7 @@ input[type="text"], input[type="email"], input[type="password"] {
> td { > td {
padding: 0; padding: 0;
border-color: var(--luncher-border); border-color: var(--luncher-border);
vertical-align: top; vertical-align: middle;
&:first-child { &:first-child {
background: var(--luncher-primary-light); background: var(--luncher-primary-light);
@@ -586,6 +605,7 @@ input[type="text"], input[type="email"], input[type="password"] {
font-weight: 600; font-weight: 600;
color: var(--luncher-primary); color: var(--luncher-primary);
width: 180px; width: 180px;
white-space: nowrap;
@media (max-width: 576px) { @media (max-width: 576px) {
width: 120px; width: 120px;
@@ -615,6 +635,8 @@ input[type="text"], input[type="email"], input[type="password"] {
padding: 12px 16px; padding: 12px 16px;
border-color: var(--luncher-border-light); border-color: var(--luncher-border-light);
color: var(--luncher-text); color: var(--luncher-text);
white-space: nowrap;
vertical-align: middle;
} }
} }
@@ -628,6 +650,28 @@ input[type="text"], input[type="email"], input[type="password"] {
margin-bottom: 4px; margin-bottom: 4px;
} }
} }
.food-choices {
display: flex;
flex-direction: column;
gap: 6px;
}
.food-choice-item {
background: var(--luncher-primary-light);
padding: 8px 12px;
border-radius: var(--luncher-radius-sm);
display: flex;
align-items: center;
justify-content: space-between;
font-size: 0.9rem;
border-left: 3px solid var(--luncher-primary);
}
.food-choice-name {
color: var(--luncher-text);
font-weight: 500;
}
} }
.no-votes { .no-votes {

View File

@@ -352,24 +352,28 @@ function App() {
{menu.food.map((f: Food, index: number) => {menu.food.map((f: Food, index: number) =>
(!hideSoups || !f.isSoup) && (!hideSoups || !f.isSoup) &&
<tr key={f.name} onClick={() => doAddClickFoodChoice(location, index)}> <tr key={f.name} onClick={() => doAddClickFoodChoice(location, index)}>
<td>{f.amount}</td>
<td> <td>
{f.name} <div className="food-name">
{f.allergens && f.allergens.length > 0 && ( {f.name}
<span className="ms-1"> {f.allergens && f.allergens.length > 0 && (
({f.allergens.map((a, idx) => ( <span className="food-allergens">
<span key={a}> {' '}({f.allergens.map((a, idx) => (
<span className="allergen-link" title={ALLERGENS[a]} onClick={e => { <span key={a}>
e.stopPropagation(); <span className="allergen-link" title={ALLERGENS[a]} onClick={e => {
window.open(LINK_ALLERGENS, '_blank'); e.stopPropagation();
}}>{a}</span> window.open(LINK_ALLERGENS, '_blank');
{idx < f.allergens!.length - 1 && ', '} }}>{a}</span>
</span> {idx < f.allergens!.length - 1 && ', '}
))}) </span>
</span> ))})
)} </span>
)}
</div>
<div className="food-meta">
{f.amount && f.amount !== '-' && <span className="food-amount">{f.amount}</span>}
<span className="food-price">{f.price}</span>
</div>
</td> </td>
<td>{f.price}</td>
</tr> </tr>
)} )}
</tbody> </tbody>
@@ -379,8 +383,8 @@ function App() {
} }
return <Col md={6} lg={3} className='mt-3'> return <Col md={6} lg={3} className='mt-3'>
<div className="restaurant-card"> <div className="restaurant-card">
<div className="restaurant-header"> <div className="restaurant-header" style={{ cursor: canChangeChoice ? 'pointer' : 'default' }} onClick={() => doAddClickFoodChoice(location)}>
<h3 style={{ cursor: canChangeChoice ? 'pointer' : 'default' }} onClick={() => doAddClickFoodChoice(location)}> <h3>
{getLunchChoiceName(location)} {getLunchChoiceName(location)}
</h3> </h3>
{menu?.lastUpdate && <small>Aktualizace: {getHumanDateTime(new Date(menu.lastUpdate))}</small>} {menu?.lastUpdate && <small>Aktualizace: {getHumanDateTime(new Date(menu.lastUpdate))}</small>}
@@ -539,21 +543,21 @@ function App() {
</span>} </span>}
</td> </td>
{userChoices?.length && food ? <td> {userChoices?.length && food ? <td>
<ul> <div className="food-choices">
{userChoices?.map(foodIndex => { {userChoices?.map(foodIndex => {
const restaurantKey = key as Restaurant; const restaurantKey = key as Restaurant;
const foodName = food[restaurantKey]?.food?.[foodIndex].name; const foodName = food[restaurantKey]?.food?.[foodIndex].name;
return <li key={foodIndex}> return <div key={foodIndex} className="food-choice-item">
{foodName} <span className="food-choice-name">{foodName}</span>
{login === auth.login && canChangeChoice && {login === auth.login && canChangeChoice &&
<span title={`Odstranit ${foodName}`}> <span title={`Odstranit ${foodName}`}>
<FontAwesomeIcon onClick={() => { <FontAwesomeIcon onClick={() => {
doRemoveFoodChoice(restaurantKey, foodIndex); doRemoveFoodChoice(restaurantKey, foodIndex);
}} className='action-icon' icon={faTrashCan} /> }} className='action-icon' icon={faTrashCan} />
</span>} </span>}
</li> </div>
})} })}
</ul> </div>
</td> : null} </td> : null}
</tr> </tr>
} }

View File

@@ -37,6 +37,7 @@ export default function SettingsModal({ isOpen, onClose, onSave }: Readonly<Prop
<h4>Obecné</h4> <h4>Obecné</h4>
<Form.Group className="mb-3"> <Form.Group className="mb-3">
<Form.Check <Form.Check
id="hideSoupsCheckbox"
ref={hideSoupsRef} ref={hideSoupsRef}
type="checkbox" type="checkbox"
label="Skrýt polévky" label="Skrýt polévky"