Skip to content
Open
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
81 changes: 42 additions & 39 deletions package-lock.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@
background: transparent;
height: 100%;
position: relative;
overflow-x: hidden;
max-width: 100%;
}

.mainContent {
background: transparent;
padding: 0;
margin-right: 0;
transition: margin-right 0.2s;
overflow-x: hidden;
max-width: 100%;
}

.sider {
Expand Down
5 changes: 3 additions & 2 deletions packages/web/app/components/board-page/angle-selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import useSWR from 'swr';
import { ANGLES } from '@/app/lib/board-data';
import { BoardName, Climb } from '@/app/lib/types';
import { ClimbStatsForAngle } from '@/app/lib/data/queries';
import { themeTokens } from '@/app/theme/theme-config';

const { Text, Title } = Typography;

Expand Down Expand Up @@ -131,8 +132,8 @@ export default function AngleSelector({ boardName, currentAngle, currentClimb }:
hoverable
onClick={() => handleAngleChange(angle)}
style={{
backgroundColor: angle === currentAngle ? '#e6f7ff' : undefined,
borderColor: angle === currentAngle ? '#1890ff' : undefined,
backgroundColor: angle === currentAngle ? themeTokens.semantic.selected : undefined,
borderColor: angle === currentAngle ? themeTokens.colors.primary : undefined,
minHeight: currentClimb && !isLoading ? (hasStats ? '160px' : '60px') : '60px',
}}
>
Expand Down
19 changes: 8 additions & 11 deletions packages/web/app/components/board-page/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
import React from 'react';
import { Flex, Button, Dropdown, MenuProps } from 'antd';
import { Header } from 'antd/es/layout/layout';
import Title from 'antd/es/typography/Title';
import Link from 'next/link';
import { useSession, signIn, signOut } from 'next-auth/react';
import SearchButton from '../search-drawer/search-button';
import SearchClimbNameInput from '../search-drawer/search-climb-name-input';
Expand All @@ -16,7 +14,9 @@ import { useBoardProvider } from '../board-provider/board-provider-context';
import { useQueueContext } from '../graphql-queue';
import { UserOutlined, LogoutOutlined, LoginOutlined, PlusOutlined, MoreOutlined } from '@ant-design/icons';
import AngleSelector from './angle-selector';
import Logo from '../brand/logo';
import styles from './header.module.css';
import Link from 'next/link';

type BoardSeshHeaderProps = {
boardDetails: BoardDetails;
Expand Down Expand Up @@ -60,23 +60,20 @@ export default function BoardSeshHeader({ boardDetails, angle }: BoardSeshHeader
];
return (
<Header
className={styles.header}
className={`${styles.header} header-shadow`}
style={{
background: '#fff',
height: '8dvh',
minHeight: 48,
display: 'flex',
padding: '0 4px',
padding: '0 12px',
}}
>
<UISearchParamsProvider>
<Flex justify="space-between" align="center" style={{ width: '100%' }} gap={7}>
<Flex justify="space-between" align="center" style={{ width: '100%' }} gap={8}>
{/* Logo - Fixed to left */}
<Flex>
<Title level={4} style={{ margin: 0, lineHeight: '1.2', whiteSpace: 'nowrap' }}>
<Link href="/" style={{ textDecoration: 'none', color: 'inherit' }}>
BS
</Link>
</Title>
<Flex align="center">
<Logo size="sm" showText={false} />
</Flex>

{/* Center Section - Mobile only */}
Expand Down
30 changes: 17 additions & 13 deletions packages/web/app/components/board-page/share-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { usePartyContext } from '../party-manager/party-context';
import { useBackendUrl } from '../connection-manager/connection-settings-context';
import { useQueueContext } from '../graphql-queue';
import { BackendSetupPanel } from './backend-setup-panel';
import { themeTokens } from '@/app/theme/theme-config';

const { Text } = Typography;

Expand Down Expand Up @@ -109,16 +110,16 @@ export const ShareBoardButton = () => {
<div
style={{
padding: '12px',
background: '#e6f7ff',
border: '1px solid #1890ff',
borderRadius: '6px',
background: themeTokens.semantic.selected,
border: `1px solid ${themeTokens.colors.primary}`,
borderRadius: themeTokens.borderRadius.md,
marginBottom: '16px',
}}
>
<Flex align="center" gap="small">
<span style={{ fontSize: '18px' }}>🎮</span>
<div>
<Text strong style={{ color: '#1890ff' }}>
<Text strong style={{ color: themeTokens.colors.primary }}>
Board Controller Connected
</Text>
<br />
Expand Down Expand Up @@ -146,7 +147,7 @@ export const ShareBoardButton = () => {
{/* Connecting */}
{isConnecting && (
<Flex vertical align="center" gap="middle" style={{ padding: '24px' }}>
<LoadingOutlined style={{ fontSize: '32px', color: '#1890ff' }} />
<LoadingOutlined style={{ fontSize: '32px', color: themeTokens.colors.primary }} />
<Text>Connecting to backend...</Text>
<Text type="secondary" style={{ fontSize: '12px' }}>
{backendUrl}
Expand All @@ -162,14 +163,14 @@ export const ShareBoardButton = () => {
gap="small"
style={{
padding: '12px',
background: '#f6ffed',
border: '1px solid #b7eb8f',
borderRadius: '6px',
background: themeTokens.colors.successBg,
border: `1px solid ${themeTokens.colors.success}`,
borderRadius: themeTokens.borderRadius.md,
}}
>
<CheckCircleOutlined style={{ color: '#52c41a', fontSize: '18px' }} />
<CheckCircleOutlined style={{ color: themeTokens.colors.success, fontSize: '18px' }} />
<div>
<Text strong style={{ color: '#52c41a' }}>
<Text strong style={{ color: themeTokens.colors.success }}>
Connected to Backend
</Text>
<br />
Expand Down Expand Up @@ -198,9 +199,12 @@ export const ShareBoardButton = () => {
justify="space-between"
align="center"
style={{
background: user.id === currentUserId ? '#e6f7ff' : '#f5f5f5',
background:
user.id === currentUserId
? themeTokens.semantic.selected
: themeTokens.neutral[100],
padding: '8px 12px',
borderRadius: '8px',
borderRadius: themeTokens.borderRadius.md,
width: '100%',
}}
>
Expand All @@ -211,7 +215,7 @@ export const ShareBoardButton = () => {
</Text>
</Flex>
{user.isLeader && (
<CrownFilled style={{ color: '#FFD700', fontSize: '16px' }} />
<CrownFilled style={{ color: themeTokens.colors.warning, fontSize: '16px' }} />
)}
</Flex>
))}
Expand Down
84 changes: 84 additions & 0 deletions packages/web/app/components/brand/logo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
'use client';

import React from 'react';
import Link from 'next/link';
import { themeTokens } from '@/app/theme/theme-config';

type LogoProps = {
size?: 'sm' | 'md' | 'lg';
showText?: boolean;
linkToHome?: boolean;
};

const sizes = {
sm: { icon: 24, fontSize: 14, gap: 6 },
md: { icon: 28, fontSize: 16, gap: 8 },
lg: { icon: 36, fontSize: 20, gap: 10 },
};

export const Logo = ({ size = 'md', showText = true, linkToHome = true }: LogoProps) => {
const { icon, fontSize, gap } = sizes[size];

const logoContent = (
<div
style={{
display: 'flex',
alignItems: 'center',
gap,
textDecoration: 'none',
color: 'inherit',
}}
>
<svg
width={icon}
height={icon}
viewBox="0 0 48 48"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-label="BoardSesh logo"
>
{/* Board background with rounded corners */}
<rect x="2" y="2" width="44" height="44" rx="8" fill={themeTokens.colors.primary} />

{/* Climbing holds pattern - arranged like a real board */}
{/* Top row */}
<circle cx="14" cy="12" r="4" fill={themeTokens.semantic.selected} />
<circle cx="34" cy="14" r="3.5" fill={themeTokens.semantic.selected} />

{/* Middle section */}
<circle cx="24" cy="22" r="5" fill={themeTokens.semantic.selected} />
<circle cx="10" cy="26" r="3" fill={themeTokens.semantic.selected} />
<circle cx="38" cy="28" r="3.5" fill={themeTokens.semantic.selected} />

{/* Bottom row */}
<circle cx="18" cy="38" r="4" fill={themeTokens.semantic.selected} />
<circle cx="32" cy="36" r="3" fill={themeTokens.semantic.selected} />
</svg>
{showText && (
<span
style={{
fontSize,
fontWeight: themeTokens.typography.fontWeight.bold,
color: themeTokens.neutral[800],
letterSpacing: '-0.02em',
lineHeight: 1,
}}
>
BoardSesh
</span>
)}
</div>
);

if (linkToHome) {
return (
<Link href="/" style={{ textDecoration: 'none', color: 'inherit', display: 'flex' }}>
{logoContent}
</Link>
);
}

return logoContent;
};

export default Logo;
23 changes: 16 additions & 7 deletions packages/web/app/components/climb-card/climb-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { CopyrightOutlined } from '@ant-design/icons';
import ClimbCardCover from './climb-card-cover';
import { Climb, BoardDetails } from '@/app/lib/types';
import ClimbCardActions from './climb-card-actions';
import { themeTokens } from '@/app/theme/theme-config';

type ClimbCardProps = {
climb?: Climb;
Expand All @@ -23,17 +24,21 @@ const ClimbCard = ({ climb, boardDetails, onCoverClick, selected, actions }: Cli
const cardTitle = climb ? (
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
{/* LEFT: Name, Angle, Benchmark */}
<div>
<div style={{ fontWeight: themeTokens.typography.fontWeight.semibold }}>
{climb.name} @ {climb.angle}°
{climb.benchmark_difficulty !== null && <CopyrightOutlined style={{ marginLeft: 4 }} />}
{climb.benchmark_difficulty !== null && (
<CopyrightOutlined style={{ marginLeft: 4, color: themeTokens.colors.primary }} />
)}
</div>

{/* RIGHT: Difficulty, Quality */}
<div>
<div style={{ color: themeTokens.neutral[600] }}>
{climb.difficulty && climb.quality_average && climb.quality_average !== '0' ? (
`${climb.difficulty} ★${climb.quality_average}`
) : (
<span style={{ fontWeight: 400, fontStyle: 'italic' }}>project</span>
<span style={{ fontWeight: 400, fontStyle: 'italic', color: themeTokens.neutral[400] }}>
project
</span>
)}
</div>
</div>
Expand All @@ -45,11 +50,15 @@ const ClimbCard = ({ climb, boardDetails, onCoverClick, selected, actions }: Cli
<Card
title={cardTitle}
size="small"
style={{ backgroundColor: selected ? '#eeffff' : '#FFF' }}
style={{
backgroundColor: selected ? themeTokens.semantic.selected : themeTokens.semantic.surface,
borderColor: selected ? themeTokens.colors.primary : undefined,
}}
actions={actions || ClimbCardActions({ climb, boardDetails })}
>
{/* TODO: Make a link to the list with the setter_name filter */}
{climb ? `By ${climb.setter_username} - ${climb.ascensionist_count} ascents` : null}
<div style={{ color: themeTokens.neutral[500], fontSize: themeTokens.typography.fontSize.sm }}>
{climb ? `By ${climb.setter_username} - ${climb.ascensionist_count} ascents` : null}
</div>
{cover}
</Card>
);
Expand Down
Loading