Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions convex/_generated/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import type * as _model_matchResults_queries_getMatchResults from "../_model/mat
import type * as _model_matchResults_queries_getMatchResultsByTournament from "../_model/matchResults/queries/getMatchResultsByTournament.js";
import type * as _model_matchResults_queries_getMatchResultsByTournamentPairing from "../_model/matchResults/queries/getMatchResultsByTournamentPairing.js";
import type * as _model_matchResults_queries_getMatchResultsByTournamentRound from "../_model/matchResults/queries/getMatchResultsByTournamentRound.js";
import type * as _model_matchResults_queries_getMatchResultsByUser from "../_model/matchResults/queries/getMatchResultsByUser.js";
import type * as _model_tournamentCompetitors__helpers_deepenTournamentCompetitor from "../_model/tournamentCompetitors/_helpers/deepenTournamentCompetitor.js";
import type * as _model_tournamentCompetitors__helpers_sortTournamentCompetitorsByName from "../_model/tournamentCompetitors/_helpers/sortTournamentCompetitorsByName.js";
import type * as _model_tournamentCompetitors_fields from "../_model/tournamentCompetitors/fields.js";
Expand Down Expand Up @@ -258,6 +259,7 @@ declare const fullApi: ApiFromModules<{
"_model/matchResults/queries/getMatchResultsByTournament": typeof _model_matchResults_queries_getMatchResultsByTournament;
"_model/matchResults/queries/getMatchResultsByTournamentPairing": typeof _model_matchResults_queries_getMatchResultsByTournamentPairing;
"_model/matchResults/queries/getMatchResultsByTournamentRound": typeof _model_matchResults_queries_getMatchResultsByTournamentRound;
"_model/matchResults/queries/getMatchResultsByUser": typeof _model_matchResults_queries_getMatchResultsByUser;
"_model/tournamentCompetitors/_helpers/deepenTournamentCompetitor": typeof _model_tournamentCompetitors__helpers_deepenTournamentCompetitor;
"_model/tournamentCompetitors/_helpers/sortTournamentCompetitorsByName": typeof _model_tournamentCompetitors__helpers_sortTournamentCompetitorsByName;
"_model/tournamentCompetitors/fields": typeof _model_tournamentCompetitors_fields;
Expand Down
4 changes: 4 additions & 0 deletions convex/_model/matchResults/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,7 @@ export {
getMatchResultsByTournamentRound,
getMatchResultsByTournamentRoundArgs,
} from './queries/getMatchResultsByTournamentRound';
export {
getMatchResultsByUser,
getMatchResultsByUserArgs,
} from './queries/getMatchResultsByUser';
29 changes: 29 additions & 0 deletions convex/_model/matchResults/queries/getMatchResultsByUser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { paginationOptsValidator, PaginationResult } from 'convex/server';
import { Infer, v } from 'convex/values';

import { QueryCtx } from '../../../_generated/server';
import { deepenMatchResult, DeepMatchResult } from '../_helpers/deepenMatchResult';

export const getMatchResultsByUserArgs = v.object({
userId: v.id('users'),
paginationOpts: paginationOptsValidator,
});

export const getMatchResultsByUser = async (
ctx: QueryCtx,
args: Infer<typeof getMatchResultsByUserArgs>,
): Promise<PaginationResult<DeepMatchResult>> => {
const results = await ctx.db.query('matchResults')
.filter((q) => q.or(
q.eq(q.field('player0UserId'), args.userId),
q.eq(q.field('player1UserId'), args.userId),
))
.order('desc')
.paginate(args.paginationOpts);
return {
...results,
page: await Promise.all(results.page.map(
async (item) => await deepenMatchResult(ctx, item),
)),
};
};
5 changes: 5 additions & 0 deletions convex/matchResults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ export const getMatchResultsByTournament = query({
handler: model.getMatchResultsByTournament,
});

export const getMatchResultsByUser = query({
args: model.getMatchResultsByUserArgs,
handler: model.getMatchResultsByUser,
});

export const getMatchResultsByTournamentPairing = query({
args: model.getMatchResultsByTournamentPairingArgs,
handler: model.getMatchResultsByTournamentPairing,
Expand Down
29 changes: 26 additions & 3 deletions src/components/AccountMenu/AccountMenu.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,41 @@
}

.Content {
@include flex.column($gap: 0.25rem);

@include variants.card($elevated: true);
@include animate.duration-quick;

min-width: 12rem;
margin: 0.25rem 0;
padding: 0.25rem;

@include animate.style-pop; // Must list last because it contains nested declarations
}

.UserDisplayName {
@include text.ui($muted: true);
@include text.single-line;

height: 2.5rem;
padding: 0.75rem;
}

.Items {
@include flex.column($gap: 0);

padding: 0.25rem;
}

.Item {
@include variants.ghost;
@include corners.normal;
@include flex.row($gap: 0.5rem);
@include text.ui;

height: 2.5rem;
padding: 0 0.5rem;

padding: 0.5rem 1rem;
svg {
width: 1rem;
height: 1rem;
}
}
39 changes: 26 additions & 13 deletions src/components/AccountMenu/AccountMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,35 @@
import { useNavigate } from 'react-router-dom';
import { generatePath, useNavigate } from 'react-router-dom';
import * as Popover from '@radix-ui/react-popover';
import { Cog, LogOut } from 'lucide-react';
import {
Cog,
LogOut,
User,
} from 'lucide-react';

import { useAuth } from '~/components/AuthProvider';
import { Avatar } from '~/components/generic/Avatar';
import { Button } from '~/components/generic/Button';
import { Separator } from '~/components/generic/Separator';
import { useSignOut } from '~/services/auth/useSignOut';
import { PATHS } from '~/settings';
import { getUserDisplayNameString } from '~/utils/common/getUserDisplayNameString';

import styles from './AccountMenu.module.scss';

export const AccountMenu = (): JSX.Element => {
const navigate = useNavigate();
const user = useAuth();
const { signOut } = useSignOut();

const displayName = user ? getUserDisplayNameString(user) : 'Unknown User';

const navigate = useNavigate();
const items = [
{
icon: <User />,
label: 'Profile',
onClick: (): void => {
navigate(generatePath(PATHS.userProfile, { id: user!._id }));
},
},
{
icon: <Cog />,
label: 'Settings',
Expand All @@ -38,17 +49,19 @@ export const AccountMenu = (): JSX.Element => {
<Avatar url={user?.avatarUrl} />
</Popover.Trigger>
<Popover.Content className={styles.Content} align="end">
<span className={styles.UserDisplayName}>
<div className={styles.UserDisplayName}>
{displayName}
</span>
</div>
<Separator />
{items.map((item, i) => (
<Popover.Close key={i} asChild>
<Button variant="ghost" onClick={item.onClick}>
{item.icon}{item.label}
</Button>
</Popover.Close>
))}
<div className={styles.Items} >
{items.map((item, i) => (
<Popover.Close key={i} asChild>
<div className={styles.Item} onClick={item.onClick}>
{item.icon}{item.label}
</div>
</Popover.Close>
))}
</div>
</Popover.Content>
</Popover.Root>
);
Expand Down
12 changes: 12 additions & 0 deletions src/components/IdentityBadge/IdentityBadge.module.scss
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
@use "/src/style/flex";
@use "/src/style/corners";
@use "/src/style/text";
@use "/src/style/variants";

.IdentityBadge {
--avatar-size: 2.5rem;
--avatar-spacing: 0.75rem;

margin: -0.5rem;
padding: 0.5rem;

// background-color: blue;

@include flex.row($gap: var(--avatar-spacing));
@include corners.normal;

&[data-type="user"] {
@include variants.ghost;
}

&[data-flipped="true"] {
justify-content: flex-end;
Expand Down
12 changes: 11 additions & 1 deletion src/components/IdentityBadge/IdentityBadge.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { cloneElement } from 'react';
import { generatePath, useNavigate } from 'react-router-dom';
import clsx from 'clsx';

import { TournamentCompetitor, User } from '~/api';
import { PATHS } from '~/settings';
import { ElementSize } from '~/types/componentLib';
import { useIdentityElements } from './IdentityBadge.hooks';
import { IdentityBadgePlaceholder } from './IdentityBadge.types';
Expand Down Expand Up @@ -34,6 +36,8 @@ export const IdentityBadge = ({
size = 'normal',
user,
}: IdentityBadgeProps): JSX.Element | null => {
const navigate = useNavigate();

const [displayAvatar, displayName] = useIdentityElements({
user,
competitor,
Expand All @@ -48,8 +52,14 @@ export const IdentityBadge = ({
}),
// TODO: Add claim button
];
const type = user ? 'user' : 'competitor';
return (
<div className={clsx(styles.IdentityBadge, sizeClasses[size], className)} data-flipped={flipped}>
<div
className={clsx(styles.IdentityBadge, sizeClasses[size], className)}
data-flipped={flipped}
data-type={type}
onClick={() => user ? navigate(generatePath(PATHS.userProfile, { id: user._id })) : null}
>
{flipped ? elements.reverse() : elements}
{/* TODO: Add factions */}
</div>
Expand Down
27 changes: 27 additions & 0 deletions src/components/generic/Card/CardHeader.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
@use "/src/style/flex";
@use "/src/style/variables";
@use "/src/style/text";
@use "/src/style/borders";

.CardHeader {
@include flex.row;
@include borders.normal($side: bottom);

box-sizing: unset;
min-height: 2.5rem;
padding:
calc(1rem - var(--border-width))
calc(1rem - var(--border-width))
1rem
var(--container-padding-x);

h2 {
@include text.single-line;
}

&_Actions {
@include flex.row;

margin-left: auto;
}
}
29 changes: 29 additions & 0 deletions src/components/generic/Card/CardHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { ReactNode } from 'react';
import clsx from 'clsx';

import styles from './CardHeader.module.scss';

export interface CardHeaderProps {
className?: string;
title: ReactNode | string;
children?: ReactNode;
}

export const CardHeader = ({
className,
title,
children,
}: CardHeaderProps): JSX.Element => (
<div className={clsx(styles.CardHeader, className)}>
{typeof title === 'string' ? (
<h2>
{title}
</h2>
) : title}
{children && (
<div className={styles.CardHeader_Actions}>
{children}
</div>
)}
</div>
);
10 changes: 8 additions & 2 deletions src/components/generic/Card/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
export type { CardProps } from './Card';
export { Card } from './Card';
export {
Card,
type CardProps,
} from './Card';
export {
CardHeader,
type CardHeaderProps,
} from './CardHeader';
Loading