mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-04-24 23:19:39 -05:00
Add options to delete team images (#2328)
* Add options to delete team images * Update test * Add image deleting to test
This commit is contained in:
parent
5cc0be347f
commit
5c78b60b2b
|
|
@ -259,6 +259,37 @@ export function del(teamId: number) {
|
|||
});
|
||||
}
|
||||
|
||||
export function removeTeamImage(
|
||||
teamId: number,
|
||||
imageType: "avatar" | "banner",
|
||||
) {
|
||||
const imageIdField = imageType === "avatar" ? "avatarImgId" : "bannerImgId";
|
||||
|
||||
return db.transaction().execute(async (trx) => {
|
||||
const team = await trx
|
||||
.selectFrom("Team")
|
||||
.select(imageIdField)
|
||||
.where("id", "=", teamId)
|
||||
.executeTakeFirst();
|
||||
|
||||
const imageId = team?.[imageIdField];
|
||||
if (imageId) {
|
||||
await trx
|
||||
.deleteFrom("UnvalidatedUserSubmittedImage")
|
||||
.where("id", "=", imageId)
|
||||
.execute();
|
||||
}
|
||||
|
||||
await trx
|
||||
.updateTable("AllTeam")
|
||||
.set({
|
||||
[imageIdField]: null,
|
||||
})
|
||||
.where("id", "=", teamId)
|
||||
.execute();
|
||||
});
|
||||
}
|
||||
|
||||
export function resetInviteCode(teamId: number) {
|
||||
return db
|
||||
.updateTable("AllTeam")
|
||||
|
|
|
|||
|
|
@ -28,17 +28,26 @@ export const action: ActionFunction = async ({ request, params }) => {
|
|||
schema: editTeamSchema,
|
||||
});
|
||||
|
||||
if (data._action.includes("DELETE")) {
|
||||
errorToastIfFalsy(
|
||||
isTeamOwner({ team, user }),
|
||||
"You are not the team owner",
|
||||
);
|
||||
}
|
||||
|
||||
switch (data._action) {
|
||||
case "DELETE": {
|
||||
errorToastIfFalsy(
|
||||
isTeamOwner({ team, user }),
|
||||
"You are not the team owner",
|
||||
);
|
||||
|
||||
case "DELETE_TEAM": {
|
||||
await TeamRepository.del(team.id);
|
||||
|
||||
throw redirect(TEAM_SEARCH_PAGE);
|
||||
}
|
||||
case "DELETE_AVATAR": {
|
||||
await TeamRepository.removeTeamImage(team.id, "avatar");
|
||||
throw redirect(teamPage(team.customUrl));
|
||||
}
|
||||
case "DELETE_BANNER": {
|
||||
await TeamRepository.removeTeamImage(team.id, "banner");
|
||||
throw redirect(teamPage(team.customUrl));
|
||||
}
|
||||
case "EDIT": {
|
||||
const newCustomUrl = mySlugify(data.name);
|
||||
const existingTeam = await TeamRepository.findByCustomUrl(newCustomUrl);
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ export default function EditTeamPage() {
|
|||
{isTeamOwner({ team, user }) ? (
|
||||
<FormWithConfirm
|
||||
dialogHeading={t("team:deleteTeam.header", { teamName: team.name })}
|
||||
fields={[["_action", "DELETE"]]}
|
||||
fields={[["_action", "DELETE_TEAM"]]}
|
||||
>
|
||||
<Button
|
||||
className="ml-auto"
|
||||
|
|
@ -80,6 +80,7 @@ export default function EditTeamPage() {
|
|||
) : null}
|
||||
<Form method="post" className="stack md items-start">
|
||||
<ImageUploadLinks />
|
||||
<ImageRemoveButtons />
|
||||
{canAddCustomizedColors(team) ? (
|
||||
<CustomizedColorsInput initialColors={css} />
|
||||
) : null}
|
||||
|
|
@ -132,6 +133,57 @@ function ImageUploadLinks() {
|
|||
);
|
||||
}
|
||||
|
||||
function ImageRemoveButtons() {
|
||||
const { t } = useTranslation(["common", "team"]);
|
||||
const { team } = useLoaderData<typeof loader>();
|
||||
|
||||
return team.avatarSrc || team.bannerSrc ? (
|
||||
<div>
|
||||
<Label>{t("team:forms.fields.removeImages")}</Label>
|
||||
<ol className="team__image-links-list">
|
||||
{team.avatarSrc ? (
|
||||
<li>
|
||||
<FormWithConfirm
|
||||
dialogHeading={t("team:deleteTeam.profilePicture.header", {
|
||||
teamName: team.name,
|
||||
})}
|
||||
fields={[["_action", "DELETE_AVATAR"]]}
|
||||
submitButtonText={t("common:actions.remove")}
|
||||
>
|
||||
<Button
|
||||
className="ml-auto"
|
||||
variant="minimal-destructive"
|
||||
data-testid="delete-team-button"
|
||||
>
|
||||
{t("team:actionButtons.deleteTeam.profilePicture")}
|
||||
</Button>
|
||||
</FormWithConfirm>
|
||||
</li>
|
||||
) : null}
|
||||
{team.bannerSrc ? (
|
||||
<li>
|
||||
<FormWithConfirm
|
||||
dialogHeading={t("team:deleteTeam.banner.header", {
|
||||
teamName: team.name,
|
||||
})}
|
||||
fields={[["_action", "DELETE_BANNER"]]}
|
||||
submitButtonText={t("common:actions.remove")}
|
||||
>
|
||||
<Button
|
||||
className="ml-auto"
|
||||
variant="minimal-destructive"
|
||||
data-testid="delete-team-button"
|
||||
>
|
||||
{t("team:actionButtons.deleteTeam.banner")}
|
||||
</Button>
|
||||
</FormWithConfirm>
|
||||
</li>
|
||||
) : null}
|
||||
</ol>
|
||||
</div>
|
||||
) : null;
|
||||
}
|
||||
|
||||
function NameInput() {
|
||||
const { t } = useTranslation(["common", "team"]);
|
||||
const { team } = useLoaderData<typeof loader>();
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ describe("Secondary teams", () => {
|
|||
|
||||
await editTeamAction(
|
||||
{
|
||||
_action: "DELETE",
|
||||
_action: "DELETE_TEAM",
|
||||
},
|
||||
{
|
||||
user: "regular",
|
||||
|
|
@ -185,4 +185,95 @@ describe("Secondary teams", () => {
|
|||
const { secondaryTeams } = await loadTeams();
|
||||
expect(secondaryTeams).toHaveLength(2);
|
||||
});
|
||||
|
||||
const createTeamWithImage = async (imageType: "avatar" | "banner") => {
|
||||
await createTeamAction({ name: "Team 1" }, { user: "regular" });
|
||||
|
||||
const imageId = await db
|
||||
.insertInto("UnvalidatedUserSubmittedImage")
|
||||
.values({
|
||||
url: `https://example.com/test-${imageType}.jpg`,
|
||||
submitterUserId: REGULAR_USER_TEST_ID,
|
||||
})
|
||||
.returning("id")
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
const imageField = imageType === "avatar" ? "avatarImgId" : "bannerImgId";
|
||||
await db
|
||||
.updateTable("AllTeam")
|
||||
.set({ [imageField]: imageId.id })
|
||||
.where("customUrl", "=", "team-1")
|
||||
.execute();
|
||||
|
||||
return imageId.id;
|
||||
};
|
||||
|
||||
it("deletes team avatar", async () => {
|
||||
const imageId = await createTeamWithImage("avatar");
|
||||
|
||||
await editTeamAction(
|
||||
{ _action: "DELETE_AVATAR" },
|
||||
{ user: "regular", params: { customUrl: "team-1" } },
|
||||
);
|
||||
|
||||
const team = await db
|
||||
.selectFrom("Team")
|
||||
.select("avatarImgId")
|
||||
.where("customUrl", "=", "team-1")
|
||||
.executeTakeFirst();
|
||||
|
||||
expect(team?.avatarImgId).toBeNull();
|
||||
|
||||
const image = await db
|
||||
.selectFrom("UnvalidatedUserSubmittedImage")
|
||||
.select("id")
|
||||
.where("id", "=", imageId)
|
||||
.executeTakeFirst();
|
||||
|
||||
expect(image).toBeUndefined();
|
||||
});
|
||||
|
||||
it("deletes team banner", async () => {
|
||||
const imageId = await createTeamWithImage("banner");
|
||||
|
||||
await editTeamAction(
|
||||
{ _action: "DELETE_BANNER" },
|
||||
{ user: "regular", params: { customUrl: "team-1" } },
|
||||
);
|
||||
|
||||
const team = await db
|
||||
.selectFrom("Team")
|
||||
.select("bannerImgId")
|
||||
.where("customUrl", "=", "team-1")
|
||||
.executeTakeFirst();
|
||||
|
||||
expect(team?.bannerImgId).toBeNull();
|
||||
|
||||
const image = await db
|
||||
.selectFrom("UnvalidatedUserSubmittedImage")
|
||||
.select("id")
|
||||
.where("id", "=", imageId)
|
||||
.executeTakeFirst();
|
||||
|
||||
expect(image).toBeUndefined();
|
||||
});
|
||||
|
||||
it("only team owner can delete images", async () => {
|
||||
await createTeamWithImage("avatar");
|
||||
|
||||
await db
|
||||
.insertInto("User")
|
||||
.values({
|
||||
discordName: "otheruser",
|
||||
discordId: "999",
|
||||
})
|
||||
.execute();
|
||||
|
||||
const response = await editTeamAction(
|
||||
{ _action: "DELETE_AVATAR" },
|
||||
{ user: "regular", params: { customUrl: "team-1" } },
|
||||
);
|
||||
|
||||
expect(response.status).toBe(302);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -26,10 +26,16 @@ export const teamProfilePageActionSchema = z.union([
|
|||
}),
|
||||
]);
|
||||
|
||||
const deleteActionsSchema = z.object({
|
||||
_action: z.union([
|
||||
_action("DELETE_TEAM"),
|
||||
_action("DELETE_AVATAR"),
|
||||
_action("DELETE_BANNER"),
|
||||
]),
|
||||
});
|
||||
|
||||
export const editTeamSchema = z.union([
|
||||
z.object({
|
||||
_action: _action("DELETE"),
|
||||
}),
|
||||
deleteActionsSchema,
|
||||
z.object({
|
||||
_action: _action("EDIT"),
|
||||
name: z.string().min(TEAM.NAME_MIN_LENGTH).max(TEAM.NAME_MAX_LENGTH),
|
||||
|
|
|
|||
|
|
@ -9,12 +9,16 @@
|
|||
"actionButtons.editTeam": "Edit Team",
|
||||
"actionButtons.manageRoster": "Manage Roster",
|
||||
"actionButtons.deleteTeam": "Delete Team",
|
||||
"actionButtons.deleteTeam.profilePicture": "Remove Profile Picture",
|
||||
"actionButtons.deleteTeam.banner": "Remove Banner",
|
||||
"actionButtons.kick": "Kick",
|
||||
"kick.header": "Kick {{user}} from {{teamName}}?",
|
||||
"actionButtons.transferOwnership": "Transfer Ownership",
|
||||
"transferOwnership.header": "Transfer ownership of {{teamName}} to {{user}}?",
|
||||
"actionButtons.transferOwnership.confirm": "Transfer",
|
||||
"deleteTeam.header": "Are you sure you want to delete {{teamName}}?",
|
||||
"deleteTeam.profilePicture.header": "Are you sure you want to remove the teams profile picture?",
|
||||
"deleteTeam.banner.header": "Are you sure you want to remove the teams banner picture?",
|
||||
"roles.CAPTAIN": "Captain",
|
||||
"roles.CO_CAPTAIN": "Co-Captain",
|
||||
"roles.FRONTLINE": "Frontline",
|
||||
|
|
@ -30,6 +34,7 @@
|
|||
"forms.fields.teamBsky": "Team Bluesky",
|
||||
"forms.fields.bio": "Bio",
|
||||
"forms.fields.uploadImages": "Upload images",
|
||||
"forms.fields.removeImages": "Remove images",
|
||||
"forms.fields.uploadImages.pfp": "Profile Picture",
|
||||
"forms.fields.uploadImages.banner": "Team Picture Banner",
|
||||
"forms.info.name": "Note that if you change your team's name then someone else can claim the name and URL for their team",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user