Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
f9b5d3b
feat(ui): add Button component and update Badge variant to 'primary'
ohprettyhak Aug 13, 2025
e794811
feat(ui): update Badge and Button components with new size options an…
ohprettyhak Aug 13, 2025
269b207
feat(ui): enhance Button component with font weight options and updat…
ohprettyhak Aug 13, 2025
0473f14
feat(ui): change Button component display to inline-flex for better l…
ohprettyhak Aug 13, 2025
c2bb9f0
feat(ui): add height, font size, and font weight utilities for Button…
ohprettyhak Aug 13, 2025
a90789a
feat(ui): refactor color imports to use colors object in Badge and Bu…
ohprettyhak Aug 13, 2025
e7a4c11
feat(ui): add Input component with responsive font size and weight op…
ohprettyhak Aug 13, 2025
99b6530
feat(ui): add Input component with responsive sizing and integrate in…
ohprettyhak Aug 13, 2025
bd32083
feat(ui): enhance Input component with label positioning and placehol…
ohprettyhak Aug 13, 2025
f235493
feat(ui): update Input component for improved label positioning and p…
ohprettyhak Aug 13, 2025
8fda811
feat(ui): refine Input component styles and improve label positioning
ohprettyhak Aug 13, 2025
224151b
feat(ui): add Select component with customizable styles and responsiv…
ohprettyhak Aug 13, 2025
abb313b
feat(ui): update package.json to streamline build process and adjust …
ohprettyhak Aug 13, 2025
e4af5f3
feat(ui): add useLogin hook for user authentication
ohprettyhak Aug 13, 2025
b8f2f2d
feat(ui): implement LoginForm component for user authentication
ohprettyhak Aug 13, 2025
c4482d8
feat(ui): standardize font size for Input and Select components
ohprettyhak Aug 13, 2025
be50e10
feat(ui): add name attributes to Input fields in LoginForm component
ohprettyhak Aug 13, 2025
dac3a74
feat(ui): refactor Badge, Button, Input, and Select components to use…
ohprettyhak Aug 14, 2025
b16f9b3
feat(ui): remove unused exports from index.ts
ohprettyhak Aug 14, 2025
0a1cdc2
feat(ui): add Toast component with customizable styles and icons
ohprettyhak Aug 14, 2025
2f529ba
feat(ui): enhance LoginForm to use router for navigation and improve …
ohprettyhak Aug 14, 2025
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
1 change: 1 addition & 0 deletions apps/manager/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './mutations';
1 change: 1 addition & 0 deletions apps/manager/src/api/mutations/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './useLogin';
12 changes: 12 additions & 0 deletions apps/manager/src/api/mutations/useLogin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { fetcher, useMutation } from '@hcc/api-base';

type Request = {
email: string;
password: string;
};

export const postLogin = (request: Request) => {
return fetcher.post<void>('manager/login', { json: request });
};

export const useLogin = () => useMutation({ mutationFn: postLogin });
File renamed without changes.
57 changes: 57 additions & 0 deletions apps/manager/src/app/auth/login/login-form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
'use client';

import { Button, Input, toast } from '@hcc/ui';
import type { FormEvent } from 'react';
import { useLogin } from '~/api';

export const LoginForm = () => {
const { mutate } = useLogin();

const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
const formData = new FormData(event.currentTarget);
const email = formData.get('email') as string;
const password = formData.get('password') as string;

mutate(
{ email, password },
{
onSuccess: () => {
window.location.href = '/';
},
onError: error => {
if (error instanceof Error) {
toast.error(error.message);
} else {
toast.error('로그인에 실패했습니다. 다시 시도해주세요.');
}
},
},
);
};

return (
<form className="column w-full" onSubmit={handleSubmit}>
<Input
id="email"
name="email"
size="xl"
type="email"
placeholder="이메일"
autoComplete="email"
/>
<Input
id="password"
name="password"
className="mt-4"
size="xl"
type="password"
placeholder="비밀번호"
autoComplete="current-password"
/>
<Button className="mt-6" size="xl" color="black" variant="solid" type="submit">
로그인
</Button>
</form>
);
};
6 changes: 4 additions & 2 deletions apps/manager/src/app/auth/login/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Badge } from '@hcc/ui';
import { LoginForm } from './login-form';

const Page = () => {
return (
Expand All @@ -9,11 +10,12 @@ const Page = () => {
<br />
manager
</h1>
<Badge className="h-fit" size="small" variant="success">
<Badge className="h-fit" size="sm" variant="primary">
매니저 용
</Badge>
</div>
<div className="column w-full" />

<LoginForm />
</div>
);
};
Expand Down
7 changes: 5 additions & 2 deletions apps/manager/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import '@hcc/ui/styles.css';
import '~/styles/globals.css';

import { Toaster } from '@hcc/ui';
import { Analytics } from '@vercel/analytics/next';
import type { Metadata } from 'next';
import type { PropsWithChildren } from 'react';
import { Layout } from '~/components/layout';
import { Pretendard } from './_fonts';
import { Provider } from './provider';
import '~/styles/globals.css';
import '@hcc/ui/styles.css';

export const metadata: Metadata = {
title: '훕치치 매니저',
Expand All @@ -20,6 +22,7 @@ const RootLayout = ({ children }: PropsWithChildren) => {
<Layout>{children}</Layout>
</Provider>
<Analytics />
<Toaster />
</body>
</html>
);
Expand Down
38 changes: 0 additions & 38 deletions packages/api-base/build.js

This file was deleted.

19 changes: 3 additions & 16 deletions packages/api-base/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
"version": "0.0.1",
"private": true,
"scripts": {
"build": "rm -rf dist && node build.js && tsc",
"dev": "node build.js --watch",
"start": "next start",
"lint": "biome lint .",
"format": "biome format --write ."
Expand All @@ -15,21 +13,10 @@
"biome format --write"
]
},
"sideEffects": false,
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.cjs"
}
},
"files": [
"dist"
],
"main": "./src/index.ts",
"module": "./src/index.ts",
"types": "./src/index.ts",
"peerDependencies": {
"react": "^19.1.1",
"react-dom": "^19.1.1"
Expand Down
4 changes: 3 additions & 1 deletion packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
},
"dependencies": {
"@radix-ui/react-slot": "^1.2.3",
"clsx": "^2.1.1"
"clsx": "^2.1.1",
"sonner": "^2.0.7",
"ts-pattern": "^5.8.0"
}
}
81 changes: 36 additions & 45 deletions packages/ui/src/badge/Badge.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import { clsx } from 'clsx';
import { type ComponentProps, type CSSProperties, forwardRef } from 'react';
import { color } from '../token';
import { match } from 'ts-pattern';
import { colors, type ResponsiveFontSize } from '../token';
import { Typography } from '../typography';
import styles from './Badge.module.css';

type BadgeSize = 'small' | 'medium' | 'large';
type BadgeSize = 'sm' | 'md' | 'lg';

type BadgeVariant = 'default' | 'danger' | 'success';
type BadgeVariant = 'default' | 'danger' | 'primary';

export interface BadgeProps extends ComponentProps<'div'> {
size?: BadgeSize;
variant?: BadgeVariant;
}

export const Badge = forwardRef<HTMLDivElement, BadgeProps>(
({ className, children, size = 'medium', variant = 'default', style: _style, ...props }, ref) => {
({ className, children, size = 'md', variant = 'default', style: _style, ...props }, ref) => {
const backgroundColor = getBackgroundColor(variant);
const padding = getPadding(size);
const style = {
Expand All @@ -30,54 +31,44 @@ export const Badge = forwardRef<HTMLDivElement, BadgeProps>(

return (
<div ref={ref} className={clsx(styles.badge, className)} style={style} {...props}>
<Typography color={fontColor} size={fontSize} weight="semiBold" lineHeight="tight" asChild>
<Typography
color={fontColor}
fontSize={fontSize as ResponsiveFontSize}
weight="semibold"
lineHeight="tight"
asChild
>
<span>{children}</span>
</Typography>
</div>
);
},
);

const getPadding = (size: BadgeSize) => {
switch (size) {
case 'small':
return '6px 8px';
case 'medium':
return '8px 12px';
case 'large':
return '10px 16px';
}
};
const getPadding = (size: BadgeSize) =>
match(size)
.with('sm', () => '4px 8px')
.with('md', () => '6px 12px')
.with('lg', () => '8px 16px')
.exhaustive();

const getFontSize = (size: BadgeSize) => {
switch (size) {
case 'small':
return 12;
case 'medium':
return 14;
case 'large':
return 18;
}
};
const getFontSize = (size: BadgeSize) =>
match(size)
.with('sm', () => 12)
.with('md', () => 14)
.with('lg', () => 16)
.exhaustive();

const getFontColor = (variant: BadgeVariant) => {
switch (variant) {
case 'default':
return color.neutral600;
case 'danger':
return color.white;
case 'success':
return color.primary600;
}
};
const getFontColor = (variant: BadgeVariant) =>
match(variant)
.with('default', () => colors.neutral600)
.with('danger', () => colors.white)
.with('primary', () => colors.primary600)
.exhaustive();

const getBackgroundColor = (variant: BadgeVariant) => {
switch (variant) {
case 'default':
return color.neutral100;
case 'danger':
return color.danger600;
case 'success':
return color.primary100;
}
};
const getBackgroundColor = (variant: BadgeVariant) =>
match(variant)
.with('default', () => colors.neutral100)
.with('danger', () => colors.danger600)
.with('primary', () => colors.primary100)
.exhaustive();
40 changes: 40 additions & 0 deletions packages/ui/src/button/Button.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
.button {
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: background-color 0.15s ease-in-out;

height: var(--hcc-button-height);
color: var(--hcc-button-font-color);
font-size: var(--hcc-button-font-size);
font-weight: var(--hcc-button-font-weight);
border: var(--hcc-button-border);
border-radius: var(--hcc-button-border-radius);
background-color: var(--hcc-button-bg-color);

--hcc-button-height: inherit;
--hcc-button-font-color: inherit;
--hcc-button-font-size: inherit;
--hcc-button-font-weight: inherit;
--hcc-button-border: inherit;
--hcc-button-border-radius: inherit;
--hcc-button-bg-color: inherit;
}

.button:focus-visible {
outline: 2px solid currentColor;
outline-offset: 2px;
}

.disabled {
opacity: 0.5;
cursor: not-allowed;
pointer-events: none;
}

.button:hover {
background-color: var(--hcc-button-bg-hover-color);

--hcc-button-bg-hover-color: inherit;
}
Loading