Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ coverage
# TypeScript
tsconfig.tsbuildinfo

# MCP
.playwright-mcp/

# Logs
*.log
npm-debug.log*
Expand Down
52 changes: 27 additions & 25 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 36 additions & 0 deletions src/components/ui/StatusBadge.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
@use '../../styles/abstracts/functions' as fn;

.badge {
display: inline-flex;
align-items: center;
justify-content: center;
align-self: flex-start;
min-width: fn.r(58);
height: fn.r(24);
padding: fn.r(2) fn.r(8);
border-radius: fn.r(4);
font-size: 0.75rem;
font-weight: 600;
line-height: 1rem;
white-space: nowrap;

&--done {
background-color: var(--status-done-bg);
color: var(--status-done-text);
}

&--progress {
background-color: var(--status-progress-bg);
color: var(--status-progress-text);
}

&--disabled {
background-color: var(--status-disabled-bg);
color: var(--status-disabled-text);
}

&--error {
background-color: var(--status-danger-bg);
color: var(--status-danger-text);
}
}
40 changes: 40 additions & 0 deletions src/components/ui/StatusBadge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import styles from './StatusBadge.module.scss';

type DisputeStatus =
| 'draft'
| 'waiting_opponent'
| 'opponent_joined'
| 'both_submitted'
| 'judging'
| 'judged'
| 'closed'
| 'expired'
| 'deleted';

type StatusVariant = 'done' | 'progress' | 'disabled' | 'error';

interface StatusBadgeProps {
status: DisputeStatus;
}

const STATUS_MAP: Record<DisputeStatus, { variant: StatusVariant; label: string }> = {
draft: { variant: 'progress', label: '진행중' },
waiting_opponent: { variant: 'progress', label: '진행중' },
opponent_joined: { variant: 'progress', label: '진행중' },
both_submitted: { variant: 'progress', label: '진행중' },
judging: { variant: 'progress', label: '진행중' },
judged: { variant: 'done', label: '진행완료' },
closed: { variant: 'done', label: '진행완료' },
expired: { variant: 'disabled', label: '진행불가' },
deleted: { variant: 'error', label: '진행오류' },
};

export default function StatusBadge({ status }: StatusBadgeProps) {
const { variant, label } = STATUS_MAP[status];

return (
<span className={`${styles.badge} ${styles[`badge--${variant}`]}`}>
{label}
</span>
);
}
31 changes: 31 additions & 0 deletions src/components/ui/Tab.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
@use '../../styles/abstracts/functions' as fn;

.tabList {
display: inline-flex;
gap: fn.r(8);
}

.tabItem {
height: fn.r(44);
Comment thread
evenif99 marked this conversation as resolved.
padding: 0 fn.r(16);
border-radius: fn.r(8);
font-family: var(--font-family-base);
font-size: 1rem;
line-height: 1.5rem;
cursor: pointer;
transition: all 0.15s ease;
border: 1.5px solid var(--border-strong);

&--inactive {
background-color: var(--bg-page);
color: var(--text-secondary);
font-weight: 400;
}

&--active {
background-color: var(--bg-page);
color: var(--text-primary);
font-weight: 700;
border-color: var(--text-primary);
}
}
33 changes: 33 additions & 0 deletions src/components/ui/Tab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import styles from './Tab.module.scss';

export interface TabItem {
label: string;
value: string;
}

interface TabProps {
items: TabItem[];
activeValue: string;
onChange: (value: string) => void;
className?: string;
}

export default function Tab({ items, activeValue, onChange, className }: TabProps) {
return (
<div className={[styles.tabList, className].filter(Boolean).join(' ')}>
{items.map((item) => {
const isActive = item.value === activeValue;
return (
<button
key={item.value}
className={`${styles.tabItem} ${isActive ? styles['tabItem--active'] : styles['tabItem--inactive']}`}
onClick={() => onChange(item.value)}
type="button"
>
{item.label}
Comment thread
evenif99 marked this conversation as resolved.
</button>
);
})}
</div>
);
}