dev #1
3 changed files with 93 additions and 55 deletions
|
|
@ -43,12 +43,14 @@ model Base {
|
|||
}
|
||||
|
||||
model TrophyReset {
|
||||
id String @id @default(cuid())
|
||||
trophiesAtStart Int
|
||||
trophiesLost Int
|
||||
createdAt DateTime @default(now())
|
||||
base Base @relation(fields: [baseId], references: [id])
|
||||
baseId String
|
||||
id String @id @default(cuid())
|
||||
date DateTime
|
||||
trophiesAtStart Int
|
||||
trophiesLost Int
|
||||
numberOfDefenses Int
|
||||
createdAt DateTime @default(now())
|
||||
base Base @relation(fields: [baseId], references: [id])
|
||||
baseId String
|
||||
}
|
||||
|
||||
model Defense {
|
||||
|
|
|
|||
|
|
@ -271,6 +271,11 @@ app.get('/bases', requireAuth, async (req, res) => {
|
|||
const bases = await prisma.base.findMany({
|
||||
where: { userId: req.user.id },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
include: {
|
||||
trophyResets: {
|
||||
orderBy: { date: 'desc' },
|
||||
},
|
||||
},
|
||||
});
|
||||
res.json({
|
||||
bases: bases.map((base) => ({
|
||||
|
|
@ -281,6 +286,14 @@ app.get('/bases', requireAuth, async (req, res) => {
|
|||
imageUrl: buildImageUrl(base),
|
||||
isPrivate: base.isPrivate,
|
||||
createdAt: base.createdAt,
|
||||
trophyResets: base.trophyResets.map((reset) => ({
|
||||
id: reset.id,
|
||||
date: reset.date,
|
||||
trophiesAtStart: reset.trophiesAtStart,
|
||||
trophiesLost: reset.trophiesLost,
|
||||
numberOfDefenses: reset.numberOfDefenses,
|
||||
createdAt: reset.createdAt,
|
||||
})),
|
||||
})),
|
||||
});
|
||||
});
|
||||
|
|
@ -483,17 +496,25 @@ app.delete('/bases/:baseId', requireAuth, async (req, res) => {
|
|||
app.post('/bases/:baseId/trophy-resets', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const { baseId } = req.params;
|
||||
const { trophiesAtStart, trophiesLost } = req.body || {};
|
||||
const { date, trophiesAtStart, trophiesLost, numberOfDefenses } = req.body || {};
|
||||
|
||||
const parsedDate = new Date(date);
|
||||
const parsedTrophiesAtStart = Number(trophiesAtStart);
|
||||
const parsedTrophiesLost = Number(trophiesLost);
|
||||
const parsedNumberOfDefenses = Number(numberOfDefenses);
|
||||
|
||||
if (isNaN(parsedDate.getTime())) {
|
||||
return res.status(400).json({ error: 'Invalid date' });
|
||||
}
|
||||
if (!Number.isFinite(parsedTrophiesAtStart) || parsedTrophiesAtStart < 0) {
|
||||
return res.status(400).json({ error: 'Trophies at start must be a positive number' });
|
||||
}
|
||||
if (!Number.isFinite(parsedTrophiesLost)) {
|
||||
return res.status(400).json({ error: 'Trophies lost must be a number' });
|
||||
}
|
||||
if (!Number.isFinite(parsedNumberOfDefenses) || parsedNumberOfDefenses < 0) {
|
||||
return res.status(400).json({ error: 'Number of defenses must be a positive number' });
|
||||
}
|
||||
|
||||
const base = await prisma.base.findFirst({ where: { id: baseId, userId: req.user.id } });
|
||||
if (!base) {
|
||||
|
|
@ -503,8 +524,10 @@ app.post('/bases/:baseId/trophy-resets', requireAuth, async (req, res) => {
|
|||
await prisma.trophyReset.create({
|
||||
data: {
|
||||
baseId: base.id,
|
||||
date: parsedDate,
|
||||
trophiesAtStart: parsedTrophiesAtStart,
|
||||
trophiesLost: parsedTrophiesLost,
|
||||
numberOfDefenses: parsedNumberOfDefenses,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -874,8 +897,10 @@ app.get('/profiles/:username', requireAuth, async (req, res) => {
|
|||
}));
|
||||
const trophyResets = base.trophyResets.map((reset) => ({
|
||||
id: reset.id,
|
||||
date: reset.date,
|
||||
trophiesAtStart: reset.trophiesAtStart,
|
||||
trophiesLost: reset.trophiesLost,
|
||||
numberOfDefenses: reset.numberOfDefenses,
|
||||
createdAt: reset.createdAt,
|
||||
}));
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -65,6 +65,15 @@ type ArmyCategory = {
|
|||
createdAt: string;
|
||||
};
|
||||
|
||||
type TrophyReset = {
|
||||
id: string;
|
||||
date: string;
|
||||
trophiesAtStart: number;
|
||||
trophiesLost: number;
|
||||
numberOfDefenses: number;
|
||||
createdAt: string;
|
||||
};
|
||||
|
||||
type BaseItem = {
|
||||
id: string;
|
||||
title: string;
|
||||
|
|
@ -73,6 +82,7 @@ type BaseItem = {
|
|||
imageUrl: string;
|
||||
isPrivate: boolean;
|
||||
createdAt: string;
|
||||
trophyResets: TrophyReset[];
|
||||
};
|
||||
|
||||
type DefenseItem = {
|
||||
|
|
@ -105,13 +115,6 @@ type ProfileDefense = {
|
|||
armyCategoryName: string;
|
||||
};
|
||||
|
||||
type TrophyReset = {
|
||||
id: string;
|
||||
trophiesAtStart: number;
|
||||
trophiesLost: number;
|
||||
createdAt: string;
|
||||
};
|
||||
|
||||
type ProfileBase = {
|
||||
id: string;
|
||||
title: string;
|
||||
|
|
@ -122,7 +125,6 @@ type ProfileBase = {
|
|||
createdAt: string;
|
||||
summary: Summary;
|
||||
defenses: ProfileDefense[];
|
||||
trophyResets: TrophyReset[];
|
||||
};
|
||||
|
||||
type ProfileCategorySummary = Summary & {
|
||||
|
|
@ -628,7 +630,7 @@ export default function Page() {
|
|||
|
||||
async function handleTrophyResetSubmit(event: FormEvent<HTMLFormElement>) {
|
||||
event.preventDefault();
|
||||
if (!profileSelectedBase) {
|
||||
if (!selectedBaseId) {
|
||||
return;
|
||||
}
|
||||
const form = event.currentTarget;
|
||||
|
|
@ -636,9 +638,9 @@ export default function Page() {
|
|||
const payload = Object.fromEntries(formData.entries());
|
||||
try {
|
||||
setErrors((prev) => ({ ...prev, trophyReset: '' }));
|
||||
await request('POST', API.addTrophyReset(profileSelectedBase.id), payload);
|
||||
await request('POST', API.addTrophyReset(selectedBaseId), payload);
|
||||
form.reset();
|
||||
await refreshOwnProfileDetail();
|
||||
await refreshData();
|
||||
} catch (error: any) {
|
||||
setErrors((prev) => ({ ...prev, trophyReset: error.message }));
|
||||
}
|
||||
|
|
@ -1542,6 +1544,52 @@ function summarizeProfileDefenses(defenses: ProfileDefense[]): Summary {
|
|||
)}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="card">
|
||||
<h3>Legend League Day Reset</h3>
|
||||
<form className="form compact" onSubmit={handleTrophyResetSubmit}>
|
||||
<label>
|
||||
Date
|
||||
<input type="date" name="date" required />
|
||||
</label>
|
||||
<label>
|
||||
Trophies at Start
|
||||
<input type="number" name="trophiesAtStart" min={0} step={1} required className="styled-number" />
|
||||
</label>
|
||||
<label>
|
||||
Trophies Lost
|
||||
<input type="number" name="trophiesLost" step={1} required className="styled-number" />
|
||||
</label>
|
||||
<label>
|
||||
Number of Defenses
|
||||
<input type="number" name="numberOfDefenses" min={0} step={1} required className="styled-number" />
|
||||
</label>
|
||||
<button type="submit" className="primary">
|
||||
Add Reset
|
||||
</button>
|
||||
<p className="form-error">{errors.trophyReset}</p>
|
||||
</form>
|
||||
<div className="subsection">
|
||||
<h4>Recent Resets</h4>
|
||||
<ul className="list compact">
|
||||
{baseDetailMeta?.trophyResets?.length ? (
|
||||
baseDetailMeta.trophyResets.map((reset) => (
|
||||
<li key={reset.id} className="list-item">
|
||||
<div className="defense-header">
|
||||
<span>{new Date(reset.date).toLocaleDateString()}</span>
|
||||
<div className="defense-meta">
|
||||
<span>{reset.trophiesAtStart} trophies at start</span>
|
||||
<span>{formatTrophies(reset.trophiesLost)} lost</span>
|
||||
<span>{reset.numberOfDefenses} defenses</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
))
|
||||
) : (
|
||||
<li>No resets logged yet.</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card">
|
||||
<h3>Defenses</h3>
|
||||
<ul id="base-detail-defenses" className="list">
|
||||
|
|
@ -1656,43 +1704,6 @@ function summarizeProfileDefenses(defenses: ProfileDefense[]): Summary {
|
|||
)}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="card">
|
||||
<h3>Legend League Day Reset</h3>
|
||||
<form className="form compact" onSubmit={handleTrophyResetSubmit}>
|
||||
<label>
|
||||
Trophies at Start
|
||||
<input type="number" name="trophiesAtStart" min={0} step={1} required className="styled-number" />
|
||||
</label>
|
||||
<label>
|
||||
Trophies Lost
|
||||
<input type="number" name="trophiesLost" step={1} required className="styled-number" />
|
||||
</label>
|
||||
<button type="submit" className="primary">
|
||||
Add Reset
|
||||
</button>
|
||||
<p className="form-error">{errors.trophyReset}</p>
|
||||
</form>
|
||||
<div className="subsection">
|
||||
<h4>Recent Resets</h4>
|
||||
<ul className="list compact">
|
||||
{profileSelectedBase.trophyResets.length ? (
|
||||
profileSelectedBase.trophyResets.map((reset) => (
|
||||
<li key={reset.id} className="list-item">
|
||||
<div className="defense-header">
|
||||
<span>{new Date(reset.createdAt).toLocaleDateString()}</span>
|
||||
<div className="defense-meta">
|
||||
<span>{reset.trophiesAtStart} trophies at start</span>
|
||||
<span>{formatTrophies(reset.trophiesLost)} lost</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
))
|
||||
) : (
|
||||
<li>No resets logged yet.</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card">
|
||||
<h3>Defenses</h3>
|
||||
<ul className="list">
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue