mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-09 04:36:02 -05:00
Add main team quick access
This commit is contained in:
@@ -265,14 +265,15 @@
|
||||
}
|
||||
|
||||
.youPanelUserRow {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: var(--s-1) var(--s-2);
|
||||
}
|
||||
|
||||
.youPanelUser {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
align-items: center;
|
||||
gap: var(--s-3);
|
||||
padding: var(--s-2);
|
||||
@@ -311,6 +312,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
.youPanelTeamAvatar {
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.panelSectionLink {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
SENDOU_INK_BASE_URL,
|
||||
SETTINGS_PAGE,
|
||||
SUPPORT_PAGE,
|
||||
teamPage,
|
||||
userPage,
|
||||
} from "~/utils/urls";
|
||||
import { Avatar } from "./Avatar";
|
||||
@@ -580,6 +581,26 @@ function YouPanel({
|
||||
<Avatar user={user} size="sm" />
|
||||
<span className={styles.youPanelUsername}>{user.username}</span>
|
||||
</Link>
|
||||
{user.team ? (
|
||||
<Link
|
||||
to={teamPage(user.team.customUrl)}
|
||||
className={styles.youPanelSettingsButton}
|
||||
onClick={onClose}
|
||||
aria-label={t("common:header.myTeam")}
|
||||
>
|
||||
{user.team.avatarUrl ? (
|
||||
<img
|
||||
src={user.team.avatarUrl}
|
||||
alt=""
|
||||
className={styles.youPanelTeamAvatar}
|
||||
width={22}
|
||||
height={22}
|
||||
/>
|
||||
) : (
|
||||
<Image path={navIconUrl("t")} alt="" width={22} height={22} />
|
||||
)}
|
||||
</Link>
|
||||
) : null}
|
||||
<Link
|
||||
to={SETTINGS_PAGE}
|
||||
className={styles.youPanelSettingsButton}
|
||||
|
||||
@@ -395,6 +395,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
.sideNavFooterTeamAvatar {
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.sideNavFooterNotification {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -38,8 +38,10 @@ import type { Breadcrumb, SendouRouteHandle } from "~/utils/remix.server";
|
||||
import {
|
||||
EVENTS_PAGE,
|
||||
FRIENDS_PAGE,
|
||||
navIconUrl,
|
||||
PLANNER_URL,
|
||||
SETTINGS_PAGE,
|
||||
teamPage,
|
||||
userPage,
|
||||
} from "~/utils/urls";
|
||||
import { Avatar, generateIdenticon } from "../Avatar";
|
||||
@@ -663,6 +665,26 @@ function SideNavUserPanel() {
|
||||
<span className={styles.sideNavFooterUsername}>{user.username}</span>
|
||||
</Link>
|
||||
<div className={styles.sideNavFooterActions}>
|
||||
{user.team ? (
|
||||
<Link
|
||||
to={teamPage(user.team.customUrl)}
|
||||
className={styles.sideNavFooterButton}
|
||||
aria-label={t("header.myTeam")}
|
||||
title={t("header.myTeam")}
|
||||
>
|
||||
{user.team.avatarUrl ? (
|
||||
<img
|
||||
src={user.team.avatarUrl}
|
||||
alt=""
|
||||
className={styles.sideNavFooterTeamAvatar}
|
||||
width={22}
|
||||
height={22}
|
||||
/>
|
||||
) : (
|
||||
<Image path={navIconUrl("t")} alt="" width={22} height={22} />
|
||||
)}
|
||||
</Link>
|
||||
) : null}
|
||||
{notifications ? (
|
||||
<div
|
||||
className={styles.sideNavFooterNotification}
|
||||
|
||||
@@ -58,7 +58,7 @@ export function validateInviteCode({
|
||||
inviteCode: string;
|
||||
realInviteCode: string;
|
||||
team: TeamRepository.findByCustomUrl;
|
||||
user?: { id: number; team?: { name: string } };
|
||||
user?: { id: number };
|
||||
reachedTeamCountLimit: boolean;
|
||||
}) {
|
||||
if (inviteCode.length !== SHORT_NANOID_LENGTH) {
|
||||
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
concatUserSubmittedImagePrefix,
|
||||
customAvatarUrl,
|
||||
jsonArrayFrom,
|
||||
jsonObjectFrom,
|
||||
tournamentLogoOrNull,
|
||||
userByIdentifierQuery,
|
||||
userProfileWeapons,
|
||||
@@ -438,6 +439,24 @@ export async function findLeanById(id: number) {
|
||||
.orderBy("UserFriendCode.createdAt", "desc")
|
||||
.limit(1)
|
||||
.as("friendCode"),
|
||||
jsonObjectFrom(
|
||||
eb
|
||||
.selectFrom("TeamMember")
|
||||
.innerJoin("Team", "Team.id", "TeamMember.teamId")
|
||||
.leftJoin(
|
||||
"UserSubmittedImage",
|
||||
"UserSubmittedImage.id",
|
||||
"Team.avatarImgId",
|
||||
)
|
||||
.select(({ ref }) => [
|
||||
"Team.name",
|
||||
"Team.customUrl",
|
||||
concatUserSubmittedImagePrefix(ref("UserSubmittedImage.url")).as(
|
||||
"avatarUrl",
|
||||
),
|
||||
])
|
||||
.where("TeamMember.userId", "=", id),
|
||||
).as("team"),
|
||||
])
|
||||
.executeTakeFirst();
|
||||
|
||||
|
||||
@@ -170,6 +170,7 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
|
||||
plusTier: user.plusTier,
|
||||
roles: user.roles,
|
||||
createdAt: user.createdAt,
|
||||
team: user.team,
|
||||
}
|
||||
: undefined,
|
||||
customTheme: isSupporter(user) ? user?.customTheme : undefined,
|
||||
|
||||
4
changelog/2026-09-04-my-team-nav-shortcut.md
Normal file
4
changelog/2026-09-04-my-team-nav-shortcut.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
type: feature
|
||||
---
|
||||
Quick access to your team's page: your main team's avatar is now shown next to the notification bell in the side navigation (and in the "You" panel on mobile)
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NZAP_TEST_ID } from "~/db/seed/constants";
|
||||
import { SENDOUQ_PAGE } from "~/utils/urls";
|
||||
import { SENDOUQ_PAGE, teamPage } from "~/utils/urls";
|
||||
import {
|
||||
expect,
|
||||
impersonate,
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
test,
|
||||
} from "./helpers/playwright";
|
||||
import { MobileNav } from "./pages/layout/mobile-nav";
|
||||
import { SideNav } from "./pages/layout/side-nav";
|
||||
import { TopNavMenus } from "./pages/layout/top-nav-menus";
|
||||
|
||||
test.describe("Navigation", () => {
|
||||
@@ -62,6 +63,28 @@ test.describe("Navigation", () => {
|
||||
await expect(mobileNav.menuLink("SendouQ")).not.toBeVisible();
|
||||
});
|
||||
|
||||
test("my team shortcut navigates to the team page on desktop and mobile", async ({
|
||||
page,
|
||||
factories,
|
||||
}) => {
|
||||
const team = await factories.TeamFactory.create({
|
||||
memberUserIds: [NZAP_TEST_ID],
|
||||
});
|
||||
|
||||
await impersonate(page, NZAP_TEST_ID);
|
||||
await navigate({ page, url: "/" });
|
||||
|
||||
await new SideNav(page).locators.footerTeamLink.click();
|
||||
await expect(page).toHaveURL(teamPage(team.customUrl));
|
||||
|
||||
await navigate({ page, url: "/" });
|
||||
await page.setViewportSize(MOBILE_VIEWPORT);
|
||||
const mobileNav = new MobileNav(page);
|
||||
await mobileNav.openPanel("you");
|
||||
await mobileNav.locators.youPanelTeamLink.click();
|
||||
await expect(page).toHaveURL(teamPage(team.customUrl));
|
||||
});
|
||||
|
||||
test("tablet navigation", async ({ page }) => {
|
||||
await page.setViewportSize(TABLET_VIEWPORT);
|
||||
await impersonate(page, NZAP_TEST_ID);
|
||||
|
||||
@@ -33,6 +33,9 @@ export class MobileNav {
|
||||
youPanelSettingsLink: this.openPanelDialog.getByRole("link", {
|
||||
name: "Settings",
|
||||
}),
|
||||
youPanelTeamLink: this.openPanelDialog.getByRole("link", {
|
||||
name: "My team",
|
||||
}),
|
||||
friendItems: this.openPanelDialog.locator("button[class*='listButton']"),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ export class SideNav {
|
||||
name: "Log in via Discord",
|
||||
}),
|
||||
footerUsername: this.root.locator("[class*='sideNavFooterUsername']"),
|
||||
footerTeamLink: this.root.getByRole("link", { name: "My team" }),
|
||||
collapseButton: page.getByTestId("sidenav-collapse-button"),
|
||||
modalTrigger: page.getByTestId("sidenav-modal-trigger"),
|
||||
siteLogoLink: this.root.locator("a[class*='siteLogo']"),
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"header.adder.calendarEvent": "",
|
||||
"header.adder.build": "",
|
||||
"header.adder.team": "",
|
||||
"header.myTeam": "",
|
||||
"header.adder.scrimPost": "",
|
||||
"header.adder.association": "",
|
||||
"header.adder.lfgPost": "",
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"header.adder.calendarEvent": "",
|
||||
"header.adder.build": "",
|
||||
"header.adder.team": "",
|
||||
"header.myTeam": "",
|
||||
"header.adder.scrimPost": "",
|
||||
"header.adder.association": "",
|
||||
"header.adder.lfgPost": "",
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"header.adder.calendarEvent": "Calendar event",
|
||||
"header.adder.build": "Build",
|
||||
"header.adder.team": "Team",
|
||||
"header.myTeam": "My team",
|
||||
"header.adder.scrimPost": "Scrim post",
|
||||
"header.adder.association": "Association",
|
||||
"header.adder.lfgPost": "LFG post",
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"header.adder.calendarEvent": "Evento de calendario",
|
||||
"header.adder.build": "Build",
|
||||
"header.adder.team": "Equipo",
|
||||
"header.myTeam": "",
|
||||
"header.adder.scrimPost": "Publicación de scrim",
|
||||
"header.adder.association": "Asociación",
|
||||
"header.adder.lfgPost": "Publicación de LFG",
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"header.adder.calendarEvent": "Evento de calendario",
|
||||
"header.adder.build": "Build",
|
||||
"header.adder.team": "Equipo",
|
||||
"header.myTeam": "",
|
||||
"header.adder.scrimPost": "Publicación de scrim",
|
||||
"header.adder.association": "Asociación",
|
||||
"header.adder.lfgPost": "Publicación de LFG",
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"header.adder.calendarEvent": "",
|
||||
"header.adder.build": "",
|
||||
"header.adder.team": "",
|
||||
"header.myTeam": "",
|
||||
"header.adder.scrimPost": "",
|
||||
"header.adder.association": "",
|
||||
"header.adder.lfgPost": "",
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"header.adder.calendarEvent": "Calendrier d'évenement",
|
||||
"header.adder.build": "set",
|
||||
"header.adder.team": "Equipe",
|
||||
"header.myTeam": "",
|
||||
"header.adder.scrimPost": "Poste scrim",
|
||||
"header.adder.association": "Association",
|
||||
"header.adder.lfgPost": "Post LFG",
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"header.adder.calendarEvent": "אירוע בלוח שנה",
|
||||
"header.adder.build": "ערכה",
|
||||
"header.adder.team": "צוות",
|
||||
"header.myTeam": "",
|
||||
"header.adder.scrimPost": "",
|
||||
"header.adder.association": "",
|
||||
"header.adder.lfgPost": "",
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"header.adder.calendarEvent": "Evento calendario",
|
||||
"header.adder.build": "Build",
|
||||
"header.adder.team": "Team",
|
||||
"header.myTeam": "",
|
||||
"header.adder.scrimPost": "",
|
||||
"header.adder.association": "",
|
||||
"header.adder.lfgPost": "Post LFG",
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"header.adder.calendarEvent": "イベント",
|
||||
"header.adder.build": "ギア構成",
|
||||
"header.adder.team": "チーム",
|
||||
"header.myTeam": "",
|
||||
"header.adder.scrimPost": "対抗戦募集",
|
||||
"header.adder.association": "コミュニティ",
|
||||
"header.adder.lfgPost": "メンバー探し",
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"header.adder.calendarEvent": "",
|
||||
"header.adder.build": "",
|
||||
"header.adder.team": "",
|
||||
"header.myTeam": "",
|
||||
"header.adder.scrimPost": "",
|
||||
"header.adder.association": "",
|
||||
"header.adder.lfgPost": "",
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"header.adder.calendarEvent": "",
|
||||
"header.adder.build": "",
|
||||
"header.adder.team": "",
|
||||
"header.myTeam": "",
|
||||
"header.adder.scrimPost": "",
|
||||
"header.adder.association": "",
|
||||
"header.adder.lfgPost": "",
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"header.adder.calendarEvent": "",
|
||||
"header.adder.build": "",
|
||||
"header.adder.team": "",
|
||||
"header.myTeam": "",
|
||||
"header.adder.scrimPost": "",
|
||||
"header.adder.association": "",
|
||||
"header.adder.lfgPost": "",
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"header.adder.calendarEvent": "",
|
||||
"header.adder.build": "",
|
||||
"header.adder.team": "",
|
||||
"header.myTeam": "",
|
||||
"header.adder.scrimPost": "",
|
||||
"header.adder.association": "",
|
||||
"header.adder.lfgPost": "",
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"header.adder.calendarEvent": "Календарное событие",
|
||||
"header.adder.build": "Сборка",
|
||||
"header.adder.team": "Команда",
|
||||
"header.myTeam": "",
|
||||
"header.adder.scrimPost": "Скрим пост",
|
||||
"header.adder.association": "Ассоциация",
|
||||
"header.adder.lfgPost": "LFG пост",
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"header.adder.calendarEvent": "赛事日程",
|
||||
"header.adder.build": "配装",
|
||||
"header.adder.team": "队伍",
|
||||
"header.myTeam": "",
|
||||
"header.adder.scrimPost": "对抗战招募帖",
|
||||
"header.adder.association": "群组",
|
||||
"header.adder.lfgPost": "组队招募帖",
|
||||
|
||||
Reference in New Issue
Block a user