mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-03-21 18:04:39 -05:00
Various improvements
This commit is contained in:
parent
54d4bbe308
commit
d652e76154
|
|
@ -14,6 +14,7 @@ Competitive Splatoon Hub with over 20k registered users.
|
|||
- Plus Server for top player "looking for group purposes" voting and suggestion tools.
|
||||
- User pages
|
||||
- User search
|
||||
- Form teams (featuring uploading profile and banner pictures)
|
||||
- Object Damage Calculator (how much does each weapon deal vs. different objects)
|
||||
- Build Analyzer (exact stats of your builds)
|
||||
- Auth via Discord
|
||||
|
|
@ -26,6 +27,8 @@ Competitive Splatoon Hub with over 20k registered users.
|
|||
- Remix
|
||||
- Sqlite3
|
||||
- CSS (plain)
|
||||
- E2E tests via Playwright
|
||||
- Unit tests via uvu
|
||||
|
||||
## Screenshots
|
||||
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ export function Button(props: ButtonProps) {
|
|||
|
||||
type LinkButtonProps = Pick<
|
||||
ButtonProps,
|
||||
"variant" | "children" | "className" | "size" | "testId"
|
||||
"variant" | "children" | "className" | "size" | "testId" | "icon"
|
||||
> &
|
||||
Pick<LinkProps, "to" | "prefetch" | "state"> & { "data-cy"?: string } & {
|
||||
isExternal?: boolean;
|
||||
|
|
@ -77,6 +77,7 @@ export function LinkButton({
|
|||
isExternal,
|
||||
state,
|
||||
testId,
|
||||
icon,
|
||||
}: LinkButtonProps) {
|
||||
if (isExternal) {
|
||||
return (
|
||||
|
|
@ -92,6 +93,10 @@ export function LinkButton({
|
|||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{icon &&
|
||||
React.cloneElement(icon, {
|
||||
className: clsx("button-icon", { lonely: !children }),
|
||||
})}
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
|
|
@ -110,6 +115,10 @@ export function LinkButton({
|
|||
prefetch={prefetch}
|
||||
state={state}
|
||||
>
|
||||
{icon &&
|
||||
React.cloneElement(icon, {
|
||||
className: clsx("button-icon", { lonely: !children }),
|
||||
})}
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
export function UserIcon({ className }: { className?: string }) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,13 +1,9 @@
|
|||
import { Link } from "@remix-run/react";
|
||||
import { useTranslation } from "~/hooks/useTranslation";
|
||||
import { useUser } from "~/modules/auth";
|
||||
import { LOG_OUT_URL, userPage } from "~/utils/urls";
|
||||
import { userPage } from "~/utils/urls";
|
||||
import { Avatar } from "../Avatar";
|
||||
import { Button } from "../Button";
|
||||
import { LogInIcon } from "../icons/LogIn";
|
||||
import { LogOutIcon } from "../icons/LogOut";
|
||||
import { UserIcon } from "../icons/User";
|
||||
import { Popover } from "../Popover";
|
||||
import { LogInButtonContainer } from "./LogInButtonContainer";
|
||||
|
||||
export function UserItem() {
|
||||
|
|
@ -16,42 +12,16 @@ export function UserItem() {
|
|||
|
||||
if (user) {
|
||||
return (
|
||||
<Popover
|
||||
buttonChildren={
|
||||
<Avatar
|
||||
user={user}
|
||||
alt={t("header.loggedInAs", {
|
||||
userName: `${user.discordName}`,
|
||||
})}
|
||||
className="layout__avatar"
|
||||
size="sm"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<div className="layout__user-popover">
|
||||
<Link to={userPage(user)}>
|
||||
<Button
|
||||
className="w-full"
|
||||
size="tiny"
|
||||
variant="outlined"
|
||||
icon={<UserIcon />}
|
||||
>
|
||||
{t("pages.myPage")}
|
||||
</Button>
|
||||
</Link>
|
||||
<form method="post" action={LOG_OUT_URL}>
|
||||
<Button
|
||||
size="tiny"
|
||||
variant="outlined"
|
||||
icon={<LogOutIcon />}
|
||||
type="submit"
|
||||
className="w-full"
|
||||
>
|
||||
{t("header.logout")}
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
</Popover>
|
||||
<Link to={userPage(user)} prefetch="intent">
|
||||
<Avatar
|
||||
user={user}
|
||||
alt={t("header.loggedInAs", {
|
||||
userName: `${user.discordName}`,
|
||||
})}
|
||||
className="layout__avatar"
|
||||
size="sm"
|
||||
/>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -160,7 +160,11 @@ export default function EditTeamPage() {
|
|||
<NameInput />
|
||||
<TwitterInput />
|
||||
<BioTextarea />
|
||||
<SubmitButton className="mt-4" _action="EDIT">
|
||||
<SubmitButton
|
||||
className="mt-4"
|
||||
_action="EDIT"
|
||||
testId="edit-team-submit-button"
|
||||
>
|
||||
{t("common:actions.submit")}
|
||||
</SubmitButton>
|
||||
<FormErrors namespace="team" />
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@ import { Avatar } from "~/components/Avatar";
|
|||
import { Button, LinkButton } from "~/components/Button";
|
||||
import { Flag } from "~/components/Flag";
|
||||
import { FormWithConfirm } from "~/components/FormWithConfirm";
|
||||
import { EditIcon } from "~/components/icons/Edit";
|
||||
import { TwitterIcon } from "~/components/icons/Twitter";
|
||||
import { UsersIcon } from "~/components/icons/Users";
|
||||
import { WeaponImage } from "~/components/Image";
|
||||
import { Main } from "~/components/Main";
|
||||
import { Placement } from "~/components/Placement";
|
||||
|
|
@ -228,6 +230,7 @@ function ActionButtons() {
|
|||
to={manageTeamRosterPage(team.customUrl)}
|
||||
variant="outlined"
|
||||
prefetch="intent"
|
||||
icon={<UsersIcon />}
|
||||
testId="manage-roster-button"
|
||||
>
|
||||
{t("team:actionButtons.manageRoster")}
|
||||
|
|
@ -239,6 +242,7 @@ function ActionButtons() {
|
|||
to={editTeamPage(team.customUrl)}
|
||||
variant="outlined"
|
||||
prefetch="intent"
|
||||
icon={<EditIcon />}
|
||||
testId="edit-team-button"
|
||||
>
|
||||
{t("team:actionButtons.editTeam")}
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@
|
|||
|
||||
.team__action-buttons {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
justify-content: center;
|
||||
gap: var(--s-2);
|
||||
}
|
||||
|
||||
|
|
@ -331,4 +331,8 @@
|
|||
.team__banner__placeholder {
|
||||
height: 12rem;
|
||||
}
|
||||
|
||||
.team__action-buttons {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ export default function UserPageLayout() {
|
|||
<SubNav>
|
||||
<SubNavLink to={userPage(data)}>{t("header.profile")}</SubNavLink>
|
||||
{isOwnPage && (
|
||||
<SubNavLink to={userEditProfilePage(data)}>
|
||||
<SubNavLink to={userEditProfilePage(data)} prefetch="intent">
|
||||
{t("actions.edit")}
|
||||
</SubNavLink>
|
||||
)}
|
||||
|
|
@ -116,7 +116,7 @@ export default function UserPageLayout() {
|
|||
</SubNavLink>
|
||||
)}
|
||||
{(isOwnPage || data.buildsCount > 0) && (
|
||||
<SubNavLink to={userBuildsPage(data)}>
|
||||
<SubNavLink to={userBuildsPage(data)} prefetch="intent">
|
||||
{t("pages.builds")} ({data.buildsCount})
|
||||
</SubNavLink>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ a {
|
|||
}
|
||||
|
||||
/* :not([name="text"]) workaround not to select textarea on map planner */
|
||||
textarea:not(.plain):not([name="text"]) {
|
||||
textarea:not(.plain, [name="text"]) {
|
||||
width: 18rem;
|
||||
max-width: 100%;
|
||||
height: 8rem;
|
||||
|
|
@ -159,7 +159,7 @@ progress {
|
|||
accent-color: var(--theme);
|
||||
}
|
||||
|
||||
textarea:not(.plain):not([name="text"]):focus-within {
|
||||
textarea:not(.plain, [name="text"]):focus-within {
|
||||
border-color: transparent;
|
||||
|
||||
/* TODO: rectangle on Safari */
|
||||
|
|
|
|||
|
|
@ -114,8 +114,4 @@
|
|||
.front__nav-item.round {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.front__log-out-container {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ test.describe("Team page", () => {
|
|||
await page.getByTestId("bio-textarea").clear();
|
||||
await page.getByTestId("bio-textarea").type("shorter bio");
|
||||
|
||||
await submit(page);
|
||||
await page.getByTestId("edit-team-submit-button").click();
|
||||
|
||||
await expect(page).toHaveURL(/better-alliance-rogue/);
|
||||
await page.getByText("shorter bio").isVisible();
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@
|
|||
"upload.imageToUpload": "Image to upload",
|
||||
"upload.title": "Uploading {{type}}. Recommended size is {{width}}×{{height}}.",
|
||||
"upload.type.team-pfp": "team profile picture",
|
||||
"upload.type.team-banner": "team banner",
|
||||
"upload.type.team-banner": "team picture banner",
|
||||
"upload.commonExplanation": "Before the image is publicly displayed a moderator will validate it. Images uploaded by patrons are shown without validation.",
|
||||
"upload.afterExplanation_one": "You have {{count}} image pending. The image will show up automatically after validation.",
|
||||
"upload.afterExplanation_other": "You have {{count}} images pending. The images will show up automatically after validation."
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
"forms.fields.bio": "Bio",
|
||||
"forms.fields.uploadImages": "Upload images",
|
||||
"forms.fields.uploadImages.pfp": "Profile picture",
|
||||
"forms.fields.uploadImages.banner": "Banner",
|
||||
"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",
|
||||
"forms.errors.duplicateName": "There is already a team with this name",
|
||||
"roster.teamFull": "Team is full",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user