dev #1

Merged
Hymmel merged 10 commits from dev into main 2025-10-15 14:43:35 +02:00
Showing only changes of commit f01d77fde7 - Show all commits

View file

@ -752,8 +752,10 @@ export default function Page() {
function formatTrophies(value: number) { function formatTrophies(value: number) {
const sign = value > 0 ? '' : '+'; const sign = value > 0 ? '' : '+';
let gained = false;
return `${sign}${value} trophies`; if(sign === '+') gained = true;
let suffix = gained ? 'gained' : 'lost';
return `${sign}${value} trophies ${suffix}`;
} }
function summarizeProfileDefenses(defenses: ProfileDefense[]): Summary { function summarizeProfileDefenses(defenses: ProfileDefense[]): Summary {
@ -1399,50 +1401,50 @@ function summarizeProfileDefenses(defenses: ProfileDefense[]): Summary {
<details> <details>
<summary><h3>Manage Attacks</h3></summary> <summary><h3>Manage Attacks</h3></summary>
<ul className="list compact"> <ul className="list compact">
{defenses.length ? ( {defenses.length ? (
defenses.slice(0, 10).map((defense) => { defenses.slice(0, 10).map((defense) => {
const categoryName = const categoryName =
categoryNameMap.get(defense.armyCategoryId) || defense.categoryName || '(No category)'; categoryNameMap.get(defense.armyCategoryId) || defense.categoryName || '(No category)';
return ( return (
<li key={defense.id} className="list-item"> <li key={defense.id} className="list-item">
<div className="defense-header"> <div className="defense-header">
<div> <div>
<strong>{defense.baseTitle}</strong>{' '} <strong>{defense.baseTitle}</strong>{' '}
<span className="badge">{categoryName}</span> <span className="badge">{categoryName}</span>
</div>
<div className="defense-meta">
<button
type="button"
className="ghost small"
onClick={() => startEditingDefense(defense.id)}
>
Edit
</button>
<button
type="button"
className="ghost small"
onClick={() => handleDeleteDefense(defense.id)}
>
Delete
</button>
</div>
</div> </div>
<div className="defense-meta"> <div className="defense-meta">
<span>{defense.stars}</span> <button
<span>{defense.percent}%</span> type="button"
<span>{formatTrophies(defense.trophies)}</span> className="ghost small"
<span>{new Date(defense.createdAt).toLocaleString()}</span> onClick={() => startEditingDefense(defense.id)}
>
Edit
</button>
<button
type="button"
className="ghost small"
onClick={() => handleDeleteDefense(defense.id)}
>
Delete
</button>
</div> </div>
</li> </div>
); <div className="defense-meta">
}) <span>{defense.stars}</span>
) : ( <span>{defense.percent}%</span>
<li>No attacks logged yet.</li> <span>{formatTrophies(defense.trophies)}</span>
)} <span>{new Date(defense.createdAt).toLocaleString()}</span>
</ul> </div>
{defenses.length > 10 ? ( </li>
<p className="muted">Showing the latest 10 entries.</p> );
) : null} })
) : (
<li>No attacks logged yet.</li>
)}
</ul>
{defenses.length > 10 ? (
<p className="muted">Showing the latest 10 entries.</p>
) : null}
</details> </details>
</div> </div>
{defenseBeingEdited && ( {defenseBeingEdited && (
@ -1555,7 +1557,7 @@ function summarizeProfileDefenses(defenses: ProfileDefense[]): Summary {
<input type="number" name="trophiesAtStart" min={0} step={1} required className="styled-number" /> <input type="number" name="trophiesAtStart" min={0} step={1} required className="styled-number" />
</label> </label>
<label> <label>
Trophies Lost Trophies Gained/Lost
<input type="number" name="trophiesLost" step={1} required className="styled-number" /> <input type="number" name="trophiesLost" step={1} required className="styled-number" />
</label> </label>
<label> <label>
@ -1600,7 +1602,7 @@ function summarizeProfileDefenses(defenses: ProfileDefense[]): Summary {
</div> </div>
<div className="defense-meta"> <div className="defense-meta">
<span>{reset.trophiesAtStart} trophies</span> <span>{reset.trophiesAtStart} trophies</span>
<span>{formatTrophies(reset.trophiesLost)} lost</span> <span>{formatTrophies(reset.trophiesLost)}</span>
<span>{reset.numberOfDefenses} defenses</span> <span>{reset.numberOfDefenses} defenses</span>
</div> </div>
</li> </li>
@ -1638,7 +1640,7 @@ function summarizeProfileDefenses(defenses: ProfileDefense[]): Summary {
<input type="number" name="trophiesAtStart" min={0} step={1} required className="styled-number" defaultValue={trophyResetBeingEdited.trophiesAtStart} /> <input type="number" name="trophiesAtStart" min={0} step={1} required className="styled-number" defaultValue={trophyResetBeingEdited.trophiesAtStart} />
</label> </label>
<label> <label>
Trophies Lost Trophies Gained/Lost
<input type="number" name="trophiesLost" step={1} required className="styled-number" defaultValue={trophyResetBeingEdited.trophiesLost} /> <input type="number" name="trophiesLost" step={1} required className="styled-number" defaultValue={trophyResetBeingEdited.trophiesLost} />
</label> </label>
<label> <label>
@ -1714,28 +1716,28 @@ function summarizeProfileDefenses(defenses: ProfileDefense[]): Summary {
<details open> <details open>
<summary><h3>Army Categories vs This Base</h3></summary> <summary><h3>Army Categories vs This Base</h3></summary>
<ul id="base-detail-categories" className="list"> <ul id="base-detail-categories" className="list">
{baseDetail && baseDetail.categories.length ? ( {baseDetail && baseDetail.categories.length ? (
baseDetail.categories.map((category) => ( baseDetail.categories.map((category) => (
<li <li
key={category.categoryId} key={category.categoryId}
className="list-item clickable" className="list-item clickable"
onClick={() => openCategoryDetail(category.categoryId)} onClick={() => openCategoryDetail(category.categoryId)}
> >
<div className="defense-header"> <div className="defense-header">
<strong>{category.name}</strong> <strong>{category.name}</strong>
<span className="badge">{category.count} attacks</span> <span className="badge">{category.count} attacks</span>
</div> </div>
<div className="defense-meta"> <div className="defense-meta">
<span>{category.averageStars} avg</span> <span>{category.averageStars} avg</span>
<span>{category.averagePercent}% avg</span> <span>{category.averagePercent}% avg</span>
<span>{formatTrophies(category.averageTrophies)} avg</span> <span>{formatTrophies(category.averageTrophies)} avg</span>
</div> </div>
</li> </li>
)) ))
) : ( ) : (
<li>No army categories have attacked this base yet.</li> <li>No army categories have attacked this base yet.</li>
)} )}
</ul> </ul>
</details> </details>
</div> </div>
<div className="card"> <div className="card">
@ -1765,8 +1767,8 @@ function summarizeProfileDefenses(defenses: ProfileDefense[]): Summary {
</div> </div>
</div> </div>
<div className="defense-meta"> <div className="defense-meta">
<span>{reset.trophiesAtStart} trophies at start</span> <span>{reset.trophiesAtStart} trophies</span>
<span>{formatTrophies(reset.trophiesLost)} lost</span> <span>{formatTrophies(reset.trophiesLost)}</span>
<span>{reset.numberOfDefenses} defenses</span> <span>{reset.numberOfDefenses} defenses</span>
</div> </div>
</li> </li>
@ -1781,30 +1783,30 @@ function summarizeProfileDefenses(defenses: ProfileDefense[]): Summary {
<details open> <details open>
<summary><h3>Defenses</h3></summary> <summary><h3>Defenses</h3></summary>
<ul id="base-detail-defenses" className="list"> <ul id="base-detail-defenses" className="list">
{defenses.filter((defense) => defense.baseId === selectedBaseId).length ? ( {defenses.filter((defense) => defense.baseId === selectedBaseId).length ? (
defenses defenses
.filter((defense) => defense.baseId === selectedBaseId) .filter((defense) => defense.baseId === selectedBaseId)
.map((defense) => { .map((defense) => {
const date = new Date(defense.createdAt); const date = new Date(defense.createdAt);
const categoryName = categoryNameMap.get(defense.armyCategoryId) || '(No category)'; const categoryName = categoryNameMap.get(defense.armyCategoryId) || '(No category)';
return ( return (
<li key={defense.id} className="list-item"> <li key={defense.id} className="list-item">
<div className="defense-header"> <div className="defense-header">
<strong>{categoryName}</strong> <strong>{categoryName}</strong>
<div> <div>
<strong>{defense.stars}</strong> {defense.percent}% {formatTrophies(defense.trophies)} <strong>{defense.stars}</strong> {defense.percent}% {formatTrophies(defense.trophies)}
</div>
</div> </div>
<div className="defense-meta"> </div>
<span>{date.toLocaleString()}</span> <div className="defense-meta">
</div> <span>{date.toLocaleString()}</span>
</li> </div>
); </li>
}) );
) : ( })
<li>No defenses recorded for this base yet.</li> ) : (
)} <li>No defenses recorded for this base yet.</li>
</ul> )}
</ul>
</details> </details>
</div> </div>
</section> </section>