[agent-pipeline] pipe-20260306194817-rw8w round-1

This commit is contained in:
DDD1542 2026-03-07 05:02:42 +09:00
parent 8b9f9a3a67
commit e07cf00171
5 changed files with 46 additions and 36 deletions

View File

@ -14,8 +14,14 @@ export default function LoginPage() {
useLogin(); useLogin();
return ( return (
<div className="flex min-h-screen items-center justify-center bg-gradient-to-br from-slate-50 to-slate-100 p-4"> <div className="relative flex min-h-screen items-center justify-center bg-gradient-to-br from-background to-muted p-4">
<div className="w-full max-w-md space-y-8"> {/* 배경 장식 원 */}
<div className="absolute inset-0 overflow-hidden">
<div className="absolute -top-40 -right-40 h-80 w-80 rounded-full bg-primary/5" />
<div className="absolute -bottom-40 -left-40 h-80 w-80 rounded-full bg-primary/5" />
</div>
<div className="relative w-full max-w-md space-y-8">
<LoginHeader /> <LoginHeader />
<LoginForm <LoginForm

View File

@ -5,7 +5,7 @@ import { UI_CONFIG } from "@/constants/auth";
*/ */
export function LoginFooter() { export function LoginFooter() {
return ( return (
<div className="text-center text-sm text-slate-500"> <div className="text-center text-sm text-muted-foreground">
<p>{UI_CONFIG.COPYRIGHT}</p> <p>{UI_CONFIG.COPYRIGHT}</p>
<p className="mt-1">{UI_CONFIG.POWERED_BY}</p> <p className="mt-1">{UI_CONFIG.POWERED_BY}</p>
</div> </div>

View File

@ -29,7 +29,7 @@ export function LoginForm({
onTogglePassword, onTogglePassword,
}: LoginFormProps) { }: LoginFormProps) {
return ( return (
<Card className="border-0 shadow-xl"> <Card className="border-0 shadow-xl bg-card/80 backdrop-blur-sm">
<CardHeader className="space-y-1"> <CardHeader className="space-y-1">
<CardTitle className="text-center text-2xl"></CardTitle> <CardTitle className="text-center text-2xl"></CardTitle>
<CardDescription className="text-center"> </CardDescription> <CardDescription className="text-center"> </CardDescription>
@ -74,7 +74,7 @@ export function LoginForm({
<button <button
type="button" type="button"
onClick={onTogglePassword} onClick={onTogglePassword}
className="absolute top-1/2 right-3 -translate-y-1/2 transform text-slate-400 transition-colors hover:text-slate-600" className="absolute top-1/2 right-3 -translate-y-1/2 transform text-muted-foreground transition-colors hover:text-foreground"
disabled={isLoading} disabled={isLoading}
> >
{showPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />} {showPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
@ -85,7 +85,7 @@ export function LoginForm({
{/* 로그인 버튼 */} {/* 로그인 버튼 */}
<Button <Button
type="submit" type="submit"
className="h-11 w-full bg-slate-900 font-medium text-white hover:bg-slate-800" className="h-11 w-full font-medium"
disabled={isLoading} disabled={isLoading}
> >
{isLoading ? ( {isLoading ? (

View File

@ -407,12 +407,12 @@ function AppLayoutInner({ children }: AppLayoutProps) {
return ( return (
<div key={menu.id}> <div key={menu.id}>
<div <div
className={`group flex h-10 cursor-pointer items-center justify-between rounded-lg px-3 py-2 text-sm font-medium transition-colors duration-200 ease-in-out ${ className={`group flex h-10 cursor-pointer items-center justify-between rounded-lg px-3 py-2 text-sm font-medium transition-colors duration-150 ease-in-out ${
pathname === menu.url pathname === menu.url
? "border-primary border-l-4 bg-gradient-to-br from-slate-100 to-blue-100/40 text-slate-900" ? "bg-primary/10 text-primary font-semibold"
: isExpanded : isExpanded
? "bg-slate-100 text-slate-900" ? "bg-accent text-foreground"
: "text-slate-600 hover:bg-slate-50 hover:text-slate-900" : "text-muted-foreground hover:bg-accent hover:text-foreground"
} ${level > 0 ? "ml-6" : ""}`} } ${level > 0 ? "ml-6" : ""}`}
onClick={() => handleMenuClick(menu)} onClick={() => handleMenuClick(menu)}
> >
@ -431,14 +431,14 @@ function AppLayoutInner({ children }: AppLayoutProps) {
{/* 서브메뉴 */} {/* 서브메뉴 */}
{menu.hasChildren && isExpanded && ( {menu.hasChildren && isExpanded && (
<div className="mt-1 space-y-1 pl-6"> <div className="mt-0.5 space-y-0.5 pl-9">
{menu.children?.map((child: any) => ( {menu.children?.map((child: any) => (
<div <div
key={child.id} key={child.id}
className={`flex cursor-pointer items-center rounded-lg px-3 py-2 text-sm transition-colors hover:cursor-pointer ${ className={`flex cursor-pointer items-center rounded-lg px-3 py-2 text-sm transition-colors duration-150 hover:cursor-pointer ${
pathname === child.url pathname === child.url
? "border-primary border-l-4 bg-gradient-to-br from-slate-100 to-blue-100/40 text-slate-900" ? "bg-primary/10 text-primary font-semibold"
: "text-slate-600 hover:bg-slate-50 hover:text-slate-900" : "text-muted-foreground hover:bg-accent hover:text-foreground"
}`} }`}
onClick={() => handleMenuClick(child)} onClick={() => handleMenuClick(child)}
> >
@ -459,7 +459,7 @@ function AppLayoutInner({ children }: AppLayoutProps) {
// 프리뷰 모드: 사이드바/헤더 없이 화면만 표시 (인증 대기 없이 즉시 렌더링) // 프리뷰 모드: 사이드바/헤더 없이 화면만 표시 (인증 대기 없이 즉시 렌더링)
if (isPreviewMode) { if (isPreviewMode) {
return ( return (
<div className="h-screen w-full overflow-auto bg-white p-4"> <div className="h-screen w-full overflow-auto bg-background p-4">
{children} {children}
</div> </div>
); );
@ -481,10 +481,10 @@ function AppLayoutInner({ children }: AppLayoutProps) {
const uiMenus = convertMenuToUI(currentMenus, user as ExtendedUserInfo); const uiMenus = convertMenuToUI(currentMenus, user as ExtendedUserInfo);
return ( return (
<div className="flex h-screen flex-col bg-white"> <div className="flex h-screen flex-col bg-background">
{/* 모바일 헤더 - 모바일에서만 표시 */} {/* 모바일 헤더 - 모바일에서만 표시 */}
{isMobile && ( {isMobile && (
<header className="fixed top-0 left-0 right-0 z-50 flex h-14 items-center justify-between border-b border-slate-200 bg-white px-4"> <header className="fixed top-0 left-0 right-0 z-50 flex h-14 items-center justify-between border-b border-border bg-background/95 backdrop-blur-sm px-4">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
{/* 햄버거 메뉴 버튼 */} {/* 햄버거 메뉴 버튼 */}
<SideMenu onSidebarToggle={() => setSidebarOpen(!sidebarOpen)} /> <SideMenu onSidebarToggle={() => setSidebarOpen(!sidebarOpen)} />
@ -493,7 +493,7 @@ function AppLayoutInner({ children }: AppLayoutProps) {
{/* 사용자 드롭다운 */} {/* 사용자 드롭다운 */}
<DropdownMenu modal={false}> <DropdownMenu modal={false}>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<button className="flex items-center gap-2 rounded-lg px-2 py-1 transition-colors hover:bg-slate-100"> <button className="flex items-center gap-2 rounded-lg px-2 py-1 transition-colors hover:bg-accent">
<div className="relative flex h-8 w-8 shrink-0 overflow-hidden rounded-full"> <div className="relative flex h-8 w-8 shrink-0 overflow-hidden rounded-full">
{user.photo && user.photo.trim() !== "" && user.photo !== "null" ? ( {user.photo && user.photo.trim() !== "" && user.photo !== "null" ? (
<img <img
@ -502,7 +502,7 @@ function AppLayoutInner({ children }: AppLayoutProps) {
className="aspect-square h-full w-full object-cover" className="aspect-square h-full w-full object-cover"
/> />
) : ( ) : (
<div className="flex h-full w-full items-center justify-center rounded-full bg-slate-200 text-sm font-semibold text-slate-700"> <div className="flex h-full w-full items-center justify-center rounded-full bg-muted text-sm font-semibold text-foreground">
{user.userName?.substring(0, 1)?.toUpperCase() || "U"} {user.userName?.substring(0, 1)?.toUpperCase() || "U"}
</div> </div>
)} )}
@ -543,7 +543,7 @@ function AppLayoutInner({ children }: AppLayoutProps) {
<div className={`flex flex-1 ${isMobile ? "pt-14" : ""}`}> <div className={`flex flex-1 ${isMobile ? "pt-14" : ""}`}>
{/* 모바일 사이드바 오버레이 */} {/* 모바일 사이드바 오버레이 */}
{sidebarOpen && isMobile && ( {sidebarOpen && isMobile && (
<div className="fixed inset-0 z-30 bg-black/50 lg:hidden" onClick={() => setSidebarOpen(false)} /> <div className="fixed inset-0 z-30 bg-black/40 backdrop-blur-sm lg:hidden" onClick={() => setSidebarOpen(false)} />
)} )}
{/* 왼쪽 사이드바 */} {/* 왼쪽 사이드바 */}
@ -552,11 +552,11 @@ function AppLayoutInner({ children }: AppLayoutProps) {
isMobile isMobile
? (sidebarOpen ? "translate-x-0" : "-translate-x-full") + " fixed top-14 left-0 z-40 h-[calc(100vh-56px)]" ? (sidebarOpen ? "translate-x-0" : "-translate-x-full") + " fixed top-14 left-0 z-40 h-[calc(100vh-56px)]"
: "relative z-auto h-screen translate-x-0" : "relative z-auto h-screen translate-x-0"
} flex w-[200px] flex-col border-r border-slate-200 bg-white transition-transform duration-300`} } flex w-[220px] lg:w-[240px] flex-col border-r border-border bg-background transition-transform duration-300`}
> >
{/* 사이드바 최상단 - 로고 (데스크톱에서만 표시) */} {/* 사이드바 최상단 - 로고 (데스크톱에서만 표시) */}
{!isMobile && ( {!isMobile && (
<div className="flex h-14 items-center justify-between border-b border-slate-200 px-4"> <div className="flex h-14 items-center justify-between border-b border-border px-4">
<Logo /> <Logo />
</div> </div>
)} )}
@ -580,14 +580,14 @@ function AppLayoutInner({ children }: AppLayoutProps) {
{((user as ExtendedUserInfo)?.userType === "SUPER_ADMIN" || {((user as ExtendedUserInfo)?.userType === "SUPER_ADMIN" ||
(user as ExtendedUserInfo)?.userType === "COMPANY_ADMIN" || (user as ExtendedUserInfo)?.userType === "COMPANY_ADMIN" ||
(user as ExtendedUserInfo)?.userType === "admin") && ( (user as ExtendedUserInfo)?.userType === "admin") && (
<div className="space-y-2 border-b border-slate-200 p-3"> <div className="space-y-2 border-b border-border p-3">
{/* 관리자/사용자 메뉴 전환 */} {/* 관리자/사용자 메뉴 전환 */}
<Button <Button
onClick={handleModeSwitch} onClick={handleModeSwitch}
className={`flex w-full items-center justify-center gap-2 rounded-lg px-3 py-2 text-sm font-medium transition-colors duration-200 hover:cursor-pointer ${ className={`flex w-full items-center justify-center gap-2 rounded-lg px-3 py-2 text-sm font-medium transition-colors duration-150 hover:cursor-pointer ${
isAdminMode isAdminMode
? "border border-orange-200 bg-orange-50 text-orange-700 hover:bg-orange-100" ? "border border-warning/20 bg-warning/10 text-warning hover:bg-warning/20"
: "border-primary/20 bg-accent hover:bg-primary/20 border text-blue-700" : "border border-primary/20 bg-primary/10 text-primary hover:bg-primary/20"
}`} }`}
> >
{isAdminMode ? ( {isAdminMode ? (
@ -607,7 +607,7 @@ function AppLayoutInner({ children }: AppLayoutProps) {
{(user as ExtendedUserInfo)?.userType === "SUPER_ADMIN" && ( {(user as ExtendedUserInfo)?.userType === "SUPER_ADMIN" && (
<Button <Button
onClick={() => { console.log("🔴 회사 선택 버튼 클릭!"); setShowCompanySwitcher(true); }} onClick={() => { console.log("🔴 회사 선택 버튼 클릭!"); setShowCompanySwitcher(true); }}
className="flex w-full items-center justify-center gap-2 rounded-lg border border-purple-200 bg-purple-50 px-3 py-2 text-sm font-medium text-purple-700 transition-colors duration-200 hover:cursor-pointer hover:bg-purple-100" className="flex w-full items-center justify-center gap-2 rounded-lg border border-primary/20 bg-primary/10 px-3 py-2 text-sm font-medium text-primary transition-colors duration-150 hover:cursor-pointer hover:bg-primary/20"
> >
<Building2 className="h-4 w-4" /> <Building2 className="h-4 w-4" />
@ -618,11 +618,11 @@ function AppLayoutInner({ children }: AppLayoutProps) {
{/* 메뉴 영역 */} {/* 메뉴 영역 */}
<div className="flex-1 overflow-y-auto py-4"> <div className="flex-1 overflow-y-auto py-4">
<nav className="space-y-1 px-3"> <nav className="space-y-0.5 px-3">
{loading ? ( {loading ? (
<div className="animate-pulse space-y-2"> <div className="animate-pulse space-y-2">
{[...Array(5)].map((_, i) => ( {[...Array(5)].map((_, i) => (
<div key={i} className="h-8 rounded bg-slate-200"></div> <div key={i} className="h-8 rounded bg-muted"></div>
))} ))}
</div> </div>
) : ( ) : (
@ -632,10 +632,10 @@ function AppLayoutInner({ children }: AppLayoutProps) {
</div> </div>
{/* 사이드바 하단 - 사용자 프로필 */} {/* 사이드바 하단 - 사용자 프로필 */}
<div className="border-t border-slate-200 p-3"> <div className="border-t border-border bg-muted/30 p-3">
<DropdownMenu modal={false}> <DropdownMenu modal={false}>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<button className="flex w-full items-center gap-3 rounded-lg px-2 py-2 text-left transition-colors hover:bg-slate-100"> <button className="flex w-full items-center gap-3 rounded-lg px-2 py-2 text-left transition-colors hover:bg-accent">
{/* 프로필 아바타 */} {/* 프로필 아바타 */}
<div className="relative flex h-9 w-9 shrink-0 overflow-hidden rounded-full"> <div className="relative flex h-9 w-9 shrink-0 overflow-hidden rounded-full">
{user.photo && user.photo.trim() !== "" && user.photo !== "null" ? ( {user.photo && user.photo.trim() !== "" && user.photo !== "null" ? (
@ -645,17 +645,17 @@ function AppLayoutInner({ children }: AppLayoutProps) {
className="aspect-square h-full w-full object-cover" className="aspect-square h-full w-full object-cover"
/> />
) : ( ) : (
<div className="flex h-full w-full items-center justify-center rounded-full bg-slate-200 text-sm font-semibold text-slate-700"> <div className="flex h-full w-full items-center justify-center rounded-full bg-muted text-sm font-semibold text-foreground">
{user.userName?.substring(0, 1)?.toUpperCase() || "U"} {user.userName?.substring(0, 1)?.toUpperCase() || "U"}
</div> </div>
)} )}
</div> </div>
{/* 사용자 정보 */} {/* 사용자 정보 */}
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
<p className="truncate text-sm font-medium text-slate-900"> <p className="truncate text-sm font-medium text-foreground">
{user.userName || "사용자"} {user.userName || "사용자"}
</p> </p>
<p className="truncate text-xs text-slate-500"> <p className="truncate text-xs text-muted-foreground">
{user.deptName || user.email || user.userId} {user.deptName || user.email || user.userId}
</p> </p>
</div> </div>
@ -673,7 +673,7 @@ function AppLayoutInner({ children }: AppLayoutProps) {
className="aspect-square h-full w-full object-cover" className="aspect-square h-full w-full object-cover"
/> />
) : ( ) : (
<div className="flex h-full w-full items-center justify-center rounded-full bg-slate-200 text-base font-semibold text-slate-700"> <div className="flex h-full w-full items-center justify-center rounded-full bg-muted text-base font-semibold text-foreground">
{user.userName?.substring(0, 1)?.toUpperCase() || "U"} {user.userName?.substring(0, 1)?.toUpperCase() || "U"}
</div> </div>
)} )}
@ -713,7 +713,7 @@ function AppLayoutInner({ children }: AppLayoutProps) {
</aside> </aside>
{/* 가운데 컨텐츠 영역 - 스크롤 가능 */} {/* 가운데 컨텐츠 영역 - 스크롤 가능 */}
<main className={`min-w-0 flex-1 overflow-auto bg-white ${isMobile ? "h-[calc(100vh-56px)]" : "h-screen"}`}> <main className={`min-w-0 flex-1 overflow-auto bg-background ${isMobile ? "h-[calc(100vh-56px)]" : "h-screen"}`}>
{children} {children}
</main> </main>
</div> </div>

4
run-current-e2e.sh Normal file
View File

@ -0,0 +1,4 @@
#!/bin/bash
cd /Users/gbpark/ERP-node
./node_modules/.bin/playwright test ".agent-pipeline/browser-tests/e2e-test.spec.ts" --config=".agent-pipeline/browser-tests/playwright.config.ts" --reporter=line 2>&1 | tee /tmp/playwright-result.txt
echo "EXIT_CODE: $?" >> /tmp/playwright-result.txt