UI/UX 개선: 사이드바 레이아웃 안정화 및 메뉴 hover 효과 개선
- 사이드바 고정 너비 설정으로 메뉴 클릭 시 너비 변화 방지 - 메뉴 항목 hover 효과 일관성 개선 (고정 높이, 부드러운 색상 전환) - 디버깅 로그 제거로 성능 최적화 - 관리자 페이지 카드 디자인 개선 (그라데이션 배경, 아이콘 색상 조정)
This commit is contained in:
parent
1a60177fe4
commit
e3cd6dc3a0
|
|
@ -14,7 +14,7 @@ export default function CommonCodeManagementPage() {
|
|||
<div className="min-h-screen bg-gray-50">
|
||||
<div className="container mx-auto p-6 space-y-6">
|
||||
{/* 페이지 제목 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center justify-between bg-white rounded-lg shadow-sm border p-6">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-900">공통코드 관리</h1>
|
||||
<p className="mt-2 text-gray-600">시스템에서 사용하는 공통코드를 관리합니다</p>
|
||||
|
|
@ -26,8 +26,8 @@ export default function CommonCodeManagementPage() {
|
|||
<div className="flex flex-col gap-6 lg:flex-row lg:gap-8">
|
||||
{/* 카테고리 패널 - PC에서 좌측 고정 너비, 모바일에서 전체 너비 */}
|
||||
<div className="w-full lg:w-80 lg:flex-shrink-0">
|
||||
<Card className="h-full">
|
||||
<CardHeader>
|
||||
<Card className="h-full shadow-sm">
|
||||
<CardHeader className="bg-gray-50/50">
|
||||
<CardTitle className="flex items-center gap-2">📂 코드 카테고리</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="p-0">
|
||||
|
|
@ -38,8 +38,8 @@ export default function CommonCodeManagementPage() {
|
|||
|
||||
{/* 코드 상세 패널 - PC에서 나머지 공간, 모바일에서 전체 너비 */}
|
||||
<div className="min-w-0 flex-1">
|
||||
<Card className="h-fit">
|
||||
<CardHeader>
|
||||
<Card className="h-fit shadow-sm">
|
||||
<CardHeader className="bg-gray-50/50">
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
📋 코드 상세 정보
|
||||
{selectedCategoryCode && (
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ export default function CompanyPage() {
|
|||
<div className="min-h-screen bg-gray-50">
|
||||
<div className="container mx-auto p-6 space-y-6">
|
||||
{/* 페이지 제목 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center justify-between bg-white rounded-lg shadow-sm border p-6">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-900">회사 관리</h1>
|
||||
<p className="mt-2 text-gray-600">시스템에서 사용하는 회사 정보를 관리합니다</p>
|
||||
|
|
|
|||
|
|
@ -79,13 +79,13 @@ export default function DataFlowPage() {
|
|||
<div className="min-h-screen bg-gray-50">
|
||||
<div className="container mx-auto p-6 space-y-6">
|
||||
{/* 페이지 제목 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center justify-between bg-white rounded-lg shadow-sm border p-6">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-900">데이터 흐름 관리</h1>
|
||||
<p className="mt-2 text-gray-600">테이블 간 데이터 관계를 시각적으로 설계하고 관리합니다</p>
|
||||
</div>
|
||||
{currentStep !== "list" && (
|
||||
<Button variant="outline" onClick={goToPreviousStep} className="flex items-center">
|
||||
<Button variant="outline" onClick={goToPreviousStep} className="flex items-center shadow-sm">
|
||||
<ArrowLeft className="mr-2 h-4 w-4" />
|
||||
이전
|
||||
</Button>
|
||||
|
|
@ -97,7 +97,7 @@ export default function DataFlowPage() {
|
|||
{/* 관계도 목록 단계 */}
|
||||
{currentStep === "list" && (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center justify-between bg-white rounded-lg shadow-sm border p-4">
|
||||
<h2 className="text-xl font-semibold text-gray-800">{stepConfig.list.title}</h2>
|
||||
</div>
|
||||
<DataFlowList onDesignDiagram={handleDesignDiagram} />
|
||||
|
|
@ -107,7 +107,7 @@ export default function DataFlowPage() {
|
|||
{/* 관계도 설계 단계 */}
|
||||
{currentStep === "design" && (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center justify-between bg-white rounded-lg shadow-sm border p-4">
|
||||
<h2 className="text-xl font-semibold text-gray-800">{stepConfig.design.title}</h2>
|
||||
</div>
|
||||
<DataFlowDesigner
|
||||
|
|
|
|||
|
|
@ -223,7 +223,7 @@ export default function ExternalConnectionsPage() {
|
|||
<div className="min-h-screen bg-gray-50">
|
||||
<div className="container mx-auto p-6 space-y-6">
|
||||
{/* 페이지 제목 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center justify-between bg-white rounded-lg shadow-sm border p-6">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-900">외부 커넥션 관리</h1>
|
||||
<p className="mt-2 text-gray-600">외부 데이터베이스 연결 정보를 관리합니다</p>
|
||||
|
|
@ -231,7 +231,7 @@ export default function ExternalConnectionsPage() {
|
|||
</div>
|
||||
|
||||
{/* 검색 및 필터 */}
|
||||
<Card className="mb-6">
|
||||
<Card className="mb-6 shadow-sm">
|
||||
<CardContent className="pt-6">
|
||||
<div className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
|
||||
<div className="flex flex-col gap-4 md:flex-row md:items-center">
|
||||
|
|
@ -289,7 +289,7 @@ export default function ExternalConnectionsPage() {
|
|||
<div className="text-gray-500">로딩 중...</div>
|
||||
</div>
|
||||
) : connections.length === 0 ? (
|
||||
<Card>
|
||||
<Card className="shadow-sm">
|
||||
<CardContent className="pt-6">
|
||||
<div className="py-8 text-center text-gray-500">
|
||||
<Database className="mx-auto mb-4 h-12 w-12 text-gray-400" />
|
||||
|
|
@ -302,7 +302,7 @@ export default function ExternalConnectionsPage() {
|
|||
</CardContent>
|
||||
</Card>
|
||||
) : (
|
||||
<Card>
|
||||
<Card className="shadow-sm">
|
||||
<CardContent className="p-0">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
|
|
|
|||
|
|
@ -223,18 +223,18 @@ export default function LayoutManagementPage() {
|
|||
<div className="min-h-screen bg-gray-50">
|
||||
<div className="container mx-auto p-6 space-y-6">
|
||||
{/* 페이지 제목 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center justify-between bg-white rounded-lg shadow-sm border p-6">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-900">레이아웃 관리</h1>
|
||||
<p className="mt-2 text-gray-600">화면 레이아웃을 생성하고 관리합니다</p>
|
||||
</div>
|
||||
<Button className="flex items-center gap-2" onClick={() => setCreateModalOpen(true)}>
|
||||
<Plus className="h-4 w-4" />새 레이아웃
|
||||
</Button>
|
||||
</div>
|
||||
<Button className="flex items-center gap-2 shadow-sm" onClick={() => setCreateModalOpen(true)}>
|
||||
<Plus className="h-4 w-4" />새 레이아웃
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* 검색 및 필터 */}
|
||||
<Card className="mb-6">
|
||||
<Card className="mb-6 shadow-sm">
|
||||
<CardContent className="pt-6">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex-1">
|
||||
|
|
@ -284,7 +284,7 @@ export default function LayoutManagementPage() {
|
|||
{layouts.map((layout) => {
|
||||
const CategoryIcon = CATEGORY_ICONS[layout.category as keyof typeof CATEGORY_ICONS];
|
||||
return (
|
||||
<Card key={layout.layoutCode} className="transition-shadow hover:shadow-lg">
|
||||
<Card key={layout.layoutCode} className="shadow-sm transition-shadow hover:shadow-lg">
|
||||
<CardHeader className="pb-3">
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ export default function MenuPage() {
|
|||
<div className="min-h-screen bg-gray-50">
|
||||
<div className="container mx-auto p-6 space-y-6">
|
||||
{/* 페이지 제목 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center justify-between bg-white rounded-lg shadow-sm border p-6">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-900">메뉴 관리</h1>
|
||||
<p className="mt-2 text-gray-600">시스템 메뉴를 관리하고 화면을 할당합니다</p>
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ export default function ScreenManagementPage() {
|
|||
<div className="min-h-screen bg-gray-50">
|
||||
<div className="container mx-auto p-6 space-y-6">
|
||||
{/* 페이지 제목 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center justify-between bg-white rounded-lg shadow-sm border p-6">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-900">화면 관리</h1>
|
||||
<p className="mt-2 text-gray-600">화면을 설계하고 템플릿을 관리합니다</p>
|
||||
|
|
@ -81,12 +81,12 @@ export default function ScreenManagementPage() {
|
|||
{/* 화면 목록 단계 */}
|
||||
{currentStep === "list" && (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center justify-between bg-white rounded-lg shadow-sm border p-4">
|
||||
<h2 className="text-xl font-semibold text-gray-800">{stepConfig.list.title}</h2>
|
||||
<Button className="bg-blue-600 hover:bg-blue-700" onClick={() => goToNextStep("design")}>
|
||||
화면 설계하기 <ArrowRight className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<Button className="bg-blue-600 hover:bg-blue-700 shadow-sm" onClick={() => goToNextStep("design")}>
|
||||
화면 설계하기 <ArrowRight className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<ScreenList
|
||||
onScreenSelect={setSelectedScreen}
|
||||
selectedScreen={selectedScreen}
|
||||
|
|
@ -101,9 +101,9 @@ export default function ScreenManagementPage() {
|
|||
{/* 화면 설계 단계 */}
|
||||
{currentStep === "design" && (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center justify-between bg-white rounded-lg shadow-sm border p-4">
|
||||
<h2 className="text-xl font-semibold text-gray-800">{stepConfig.design.title}</h2>
|
||||
<Button variant="outline" onClick={() => goToStep("list")}>
|
||||
<Button variant="outline" className="shadow-sm" onClick={() => goToStep("list")}>
|
||||
<ArrowLeft className="mr-2 h-4 w-4" />목록으로 돌아가기
|
||||
</Button>
|
||||
</div>
|
||||
|
|
@ -114,18 +114,18 @@ export default function ScreenManagementPage() {
|
|||
{/* 템플릿 관리 단계 */}
|
||||
{currentStep === "template" && (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center justify-between bg-white rounded-lg shadow-sm border p-4">
|
||||
<h2 className="text-xl font-semibold text-gray-800">{stepConfig.template.title}</h2>
|
||||
<div className="flex gap-2">
|
||||
<Button variant="outline" onClick={goToPreviousStep}>
|
||||
<ArrowLeft className="mr-2 h-4 w-4" />
|
||||
이전 단계
|
||||
</Button>
|
||||
<Button className="bg-blue-600 hover:bg-blue-700" onClick={() => goToStep("list")}>
|
||||
목록으로 돌아가기
|
||||
</Button>
|
||||
<div className="flex gap-2">
|
||||
<Button variant="outline" className="shadow-sm" onClick={goToPreviousStep}>
|
||||
<ArrowLeft className="mr-2 h-4 w-4" />
|
||||
이전 단계
|
||||
</Button>
|
||||
<Button className="bg-blue-600 hover:bg-blue-700 shadow-sm" onClick={() => goToStep("list")}>
|
||||
목록으로 돌아가기
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<TemplateManager selectedScreen={selectedScreen} onBackToList={() => goToStep("list")} />
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -130,44 +130,44 @@ export default function WebTypesManagePage() {
|
|||
<div className="min-h-screen bg-gray-50">
|
||||
<div className="container mx-auto p-6 space-y-6">
|
||||
{/* 페이지 제목 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center justify-between bg-white rounded-lg shadow-sm border p-6">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-900">웹타입 관리</h1>
|
||||
<p className="mt-2 text-gray-600">화면관리에서 사용할 웹타입들을 관리합니다</p>
|
||||
</div>
|
||||
<Link href="/admin/standards/new">
|
||||
<Button>
|
||||
<Button className="shadow-sm">
|
||||
<Plus className="mr-2 h-4 w-4" />새 웹타입 추가
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* 필터 및 검색 */}
|
||||
<Card className="mb-6">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2 text-lg">
|
||||
<Filter className="h-5 w-5" />
|
||||
필터 및 검색
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-4">
|
||||
{/* 검색 */}
|
||||
<div className="relative">
|
||||
<Search className="text-muted-foreground absolute top-3 left-3 h-4 w-4" />
|
||||
<Input
|
||||
placeholder="웹타입명, 설명 검색..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="pl-10"
|
||||
/>
|
||||
</div>
|
||||
{/* 필터 및 검색 */}
|
||||
<Card className="shadow-sm">
|
||||
<CardHeader className="bg-gray-50/50">
|
||||
<CardTitle className="flex items-center gap-2 text-lg">
|
||||
<Filter className="h-5 w-5 text-gray-600" />
|
||||
필터 및 검색
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
{/* 검색 */}
|
||||
<div className="relative">
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-gray-400" />
|
||||
<Input
|
||||
placeholder="웹타입명, 설명 검색..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="pl-10"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 카테고리 필터 */}
|
||||
<Select value={categoryFilter} onValueChange={setCategoryFilter}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="카테고리 선택" />
|
||||
</SelectTrigger>
|
||||
{/* 카테고리 필터 */}
|
||||
<Select value={categoryFilter} onValueChange={setCategoryFilter}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="카테고리 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">전체 카테고리</SelectItem>
|
||||
{categories.map((category) => (
|
||||
|
|
@ -178,96 +178,96 @@ export default function WebTypesManagePage() {
|
|||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
{/* 활성화 상태 필터 */}
|
||||
<Select value={activeFilter} onValueChange={setActiveFilter}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="상태 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">전체</SelectItem>
|
||||
<SelectItem value="Y">활성화</SelectItem>
|
||||
<SelectItem value="N">비활성화</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{/* 활성화 상태 필터 */}
|
||||
<Select value={activeFilter} onValueChange={setActiveFilter}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="상태 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">전체</SelectItem>
|
||||
<SelectItem value="Y">활성화</SelectItem>
|
||||
<SelectItem value="N">비활성화</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
{/* 초기화 버튼 */}
|
||||
<Button variant="outline" onClick={resetFilters}>
|
||||
<RotateCcw className="mr-2 h-4 w-4" />
|
||||
초기화
|
||||
</Button>
|
||||
{/* 초기화 버튼 */}
|
||||
<Button variant="outline" onClick={resetFilters}>
|
||||
<RotateCcw className="mr-2 h-4 w-4" />
|
||||
초기화
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 결과 통계 */}
|
||||
<div className="mb-4">
|
||||
<p className="text-muted-foreground text-sm">총 {filteredAndSortedWebTypes.length}개의 웹타입이 있습니다.</p>
|
||||
</div>
|
||||
{/* 결과 통계 */}
|
||||
<div className="bg-white rounded-lg border px-4 py-3">
|
||||
<p className="text-gray-700 text-sm font-medium">총 {filteredAndSortedWebTypes.length}개의 웹타입이 있습니다.</p>
|
||||
</div>
|
||||
|
||||
{/* 웹타입 목록 테이블 */}
|
||||
<Card>
|
||||
<CardContent className="p-0">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="hover:bg-muted/50 cursor-pointer" onClick={() => handleSort("sort_order")}>
|
||||
<div className="flex items-center gap-2">
|
||||
순서
|
||||
{sortField === "sort_order" &&
|
||||
(sortDirection === "asc" ? <SortAsc className="h-4 w-4" /> : <SortDesc className="h-4 w-4" />)}
|
||||
</div>
|
||||
</TableHead>
|
||||
<TableHead className="hover:bg-muted/50 cursor-pointer" onClick={() => handleSort("web_type")}>
|
||||
<div className="flex items-center gap-2">
|
||||
웹타입 코드
|
||||
{sortField === "web_type" &&
|
||||
(sortDirection === "asc" ? <SortAsc className="h-4 w-4" /> : <SortDesc className="h-4 w-4" />)}
|
||||
</div>
|
||||
</TableHead>
|
||||
<TableHead className="hover:bg-muted/50 cursor-pointer" onClick={() => handleSort("type_name")}>
|
||||
<div className="flex items-center gap-2">
|
||||
웹타입명
|
||||
{sortField === "type_name" &&
|
||||
(sortDirection === "asc" ? <SortAsc className="h-4 w-4" /> : <SortDesc className="h-4 w-4" />)}
|
||||
</div>
|
||||
</TableHead>
|
||||
<TableHead className="hover:bg-muted/50 cursor-pointer" onClick={() => handleSort("category")}>
|
||||
<div className="flex items-center gap-2">
|
||||
카테고리
|
||||
{sortField === "category" &&
|
||||
(sortDirection === "asc" ? <SortAsc className="h-4 w-4" /> : <SortDesc className="h-4 w-4" />)}
|
||||
</div>
|
||||
</TableHead>
|
||||
<TableHead>설명</TableHead>
|
||||
<TableHead className="hover:bg-muted/50 cursor-pointer" onClick={() => handleSort("component_name")}>
|
||||
<div className="flex items-center gap-2">
|
||||
연결된 컴포넌트
|
||||
{sortField === "component_name" &&
|
||||
(sortDirection === "asc" ? <SortAsc className="h-4 w-4" /> : <SortDesc className="h-4 w-4" />)}
|
||||
</div>
|
||||
</TableHead>
|
||||
<TableHead className="hover:bg-muted/50 cursor-pointer" onClick={() => handleSort("config_panel")}>
|
||||
<div className="flex items-center gap-2">
|
||||
설정 패널
|
||||
{sortField === "config_panel" &&
|
||||
(sortDirection === "asc" ? <SortAsc className="h-4 w-4" /> : <SortDesc className="h-4 w-4" />)}
|
||||
</div>
|
||||
</TableHead>
|
||||
<TableHead className="hover:bg-muted/50 cursor-pointer" onClick={() => handleSort("is_active")}>
|
||||
<div className="flex items-center gap-2">
|
||||
상태
|
||||
{sortField === "is_active" &&
|
||||
(sortDirection === "asc" ? <SortAsc className="h-4 w-4" /> : <SortDesc className="h-4 w-4" />)}
|
||||
</div>
|
||||
</TableHead>
|
||||
<TableHead className="hover:bg-muted/50 cursor-pointer" onClick={() => handleSort("updated_date")}>
|
||||
<div className="flex items-center gap-2">
|
||||
최종 수정일
|
||||
{sortField === "updated_date" &&
|
||||
(sortDirection === "asc" ? <SortAsc className="h-4 w-4" /> : <SortDesc className="h-4 w-4" />)}
|
||||
</div>
|
||||
</TableHead>
|
||||
<TableHead className="text-center">작업</TableHead>
|
||||
{/* 웹타입 목록 테이블 */}
|
||||
<Card className="shadow-sm">
|
||||
<CardContent className="p-0">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="hover:bg-muted/50 cursor-pointer" onClick={() => handleSort("sort_order")}>
|
||||
<div className="flex items-center gap-2">
|
||||
순서
|
||||
{sortField === "sort_order" &&
|
||||
(sortDirection === "asc" ? <SortAsc className="h-4 w-4" /> : <SortDesc className="h-4 w-4" />)}
|
||||
</div>
|
||||
</TableHead>
|
||||
<TableHead className="hover:bg-muted/50 cursor-pointer" onClick={() => handleSort("web_type")}>
|
||||
<div className="flex items-center gap-2">
|
||||
웹타입 코드
|
||||
{sortField === "web_type" &&
|
||||
(sortDirection === "asc" ? <SortAsc className="h-4 w-4" /> : <SortDesc className="h-4 w-4" />)}
|
||||
</div>
|
||||
</TableHead>
|
||||
<TableHead className="hover:bg-muted/50 cursor-pointer" onClick={() => handleSort("type_name")}>
|
||||
<div className="flex items-center gap-2">
|
||||
웹타입명
|
||||
{sortField === "type_name" &&
|
||||
(sortDirection === "asc" ? <SortAsc className="h-4 w-4" /> : <SortDesc className="h-4 w-4" />)}
|
||||
</div>
|
||||
</TableHead>
|
||||
<TableHead className="hover:bg-muted/50 cursor-pointer" onClick={() => handleSort("category")}>
|
||||
<div className="flex items-center gap-2">
|
||||
카테고리
|
||||
{sortField === "category" &&
|
||||
(sortDirection === "asc" ? <SortAsc className="h-4 w-4" /> : <SortDesc className="h-4 w-4" />)}
|
||||
</div>
|
||||
</TableHead>
|
||||
<TableHead>설명</TableHead>
|
||||
<TableHead className="hover:bg-muted/50 cursor-pointer" onClick={() => handleSort("component_name")}>
|
||||
<div className="flex items-center gap-2">
|
||||
연결된 컴포넌트
|
||||
{sortField === "component_name" &&
|
||||
(sortDirection === "asc" ? <SortAsc className="h-4 w-4" /> : <SortDesc className="h-4 w-4" />)}
|
||||
</div>
|
||||
</TableHead>
|
||||
<TableHead className="hover:bg-muted/50 cursor-pointer" onClick={() => handleSort("config_panel")}>
|
||||
<div className="flex items-center gap-2">
|
||||
설정 패널
|
||||
{sortField === "config_panel" &&
|
||||
(sortDirection === "asc" ? <SortAsc className="h-4 w-4" /> : <SortDesc className="h-4 w-4" />)}
|
||||
</div>
|
||||
</TableHead>
|
||||
<TableHead className="hover:bg-muted/50 cursor-pointer" onClick={() => handleSort("is_active")}>
|
||||
<div className="flex items-center gap-2">
|
||||
상태
|
||||
{sortField === "is_active" &&
|
||||
(sortDirection === "asc" ? <SortAsc className="h-4 w-4" /> : <SortDesc className="h-4 w-4" />)}
|
||||
</div>
|
||||
</TableHead>
|
||||
<TableHead className="hover:bg-muted/50 cursor-pointer" onClick={() => handleSort("updated_date")}>
|
||||
<div className="flex items-center gap-2">
|
||||
최종 수정일
|
||||
{sortField === "updated_date" &&
|
||||
(sortDirection === "asc" ? <SortAsc className="h-4 w-4" /> : <SortDesc className="h-4 w-4" />)}
|
||||
</div>
|
||||
</TableHead>
|
||||
<TableHead className="text-center">작업</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
|
|
@ -310,24 +310,24 @@ export default function WebTypesManagePage() {
|
|||
<TableCell className="text-muted-foreground text-sm">
|
||||
{webType.updated_date ? new Date(webType.updated_date).toLocaleDateString("ko-KR") : "-"}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div className="flex items-center gap-2">
|
||||
<Link href={`/admin/standards/${webType.web_type}`}>
|
||||
<Button variant="ghost" size="sm">
|
||||
<Eye className="h-4 w-4" />
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href={`/admin/standards/${webType.web_type}/edit`}>
|
||||
<Button variant="ghost" size="sm">
|
||||
<Edit className="h-4 w-4" />
|
||||
</Button>
|
||||
</Link>
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button variant="ghost" size="sm">
|
||||
<Trash2 className="h-4 w-4 text-red-500" />
|
||||
</Button>
|
||||
</AlertDialogTrigger>
|
||||
<TableCell>
|
||||
<div className="flex items-center gap-2">
|
||||
<Link href={`/admin/standards/${webType.web_type}`}>
|
||||
<Button variant="ghost" size="sm">
|
||||
<Eye className="h-4 w-4" />
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href={`/admin/standards/${webType.web_type}/edit`}>
|
||||
<Button variant="ghost" size="sm">
|
||||
<Edit className="h-4 w-4" />
|
||||
</Button>
|
||||
</Link>
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button variant="ghost" size="sm">
|
||||
<Trash2 className="h-4 w-4 text-red-500" />
|
||||
</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>웹타입 삭제</AlertDialogTitle>
|
||||
|
|
|
|||
|
|
@ -543,7 +543,7 @@ export default function TableManagementPage() {
|
|||
return (
|
||||
<div className="container mx-auto p-6 space-y-6">
|
||||
{/* 페이지 제목 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center justify-between bg-white rounded-lg shadow-sm border p-6">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-900">
|
||||
{getTextFromUI(TABLE_MANAGEMENT_KEYS.PAGE_TITLE, "테이블 타입 관리")}
|
||||
|
|
@ -593,10 +593,10 @@ export default function TableManagementPage() {
|
|||
|
||||
<div className="grid grid-cols-1 gap-6 lg:grid-cols-5">
|
||||
{/* 테이블 목록 */}
|
||||
<Card className="lg:col-span-1">
|
||||
<CardHeader>
|
||||
<Card className="lg:col-span-1 shadow-sm">
|
||||
<CardHeader className="bg-gray-50/50">
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Database className="h-5 w-5" />
|
||||
<Database className="h-5 w-5 text-gray-600" />
|
||||
{getTextFromUI(TABLE_MANAGEMENT_KEYS.TABLE_NAME, "테이블 목록")}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
|
|
@ -663,10 +663,10 @@ export default function TableManagementPage() {
|
|||
</Card>
|
||||
|
||||
{/* 컬럼 타입 관리 */}
|
||||
<Card className="lg:col-span-4">
|
||||
<CardHeader>
|
||||
<Card className="lg:col-span-4 shadow-sm">
|
||||
<CardHeader className="bg-gray-50/50">
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Settings className="h-5 w-5" />
|
||||
<Settings className="h-5 w-5 text-gray-600" />
|
||||
{selectedTable ? <>테이블 설정 - {selectedTable}</> : "테이블 타입 관리"}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
|
|
|
|||
|
|
@ -148,25 +148,25 @@ export default function TemplatesManagePage() {
|
|||
<div className="min-h-screen bg-gray-50">
|
||||
<div className="container mx-auto p-6 space-y-6">
|
||||
{/* 페이지 제목 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center justify-between bg-white rounded-lg shadow-sm border p-6">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-900">템플릿 관리</h1>
|
||||
<p className="mt-2 text-gray-600">화면 디자이너에서 사용할 템플릿을 관리합니다</p>
|
||||
</div>
|
||||
<div className="flex space-x-2">
|
||||
<Button asChild>
|
||||
<Link href="/admin/templates/new">
|
||||
<Plus className="mr-2 h-4 w-4" />새 템플릿
|
||||
</Link>
|
||||
</Button>
|
||||
<div className="flex space-x-2">
|
||||
<Button asChild className="shadow-sm">
|
||||
<Link href="/admin/templates/new">
|
||||
<Plus className="mr-2 h-4 w-4" />새 템플릿
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 필터 및 검색 */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<Card className="shadow-sm">
|
||||
<CardHeader className="bg-gray-50/50">
|
||||
<CardTitle className="flex items-center">
|
||||
<Filter className="mr-2 h-5 w-5" />
|
||||
<Filter className="mr-2 h-5 w-5 text-gray-600" />
|
||||
필터 및 검색
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
|
|
@ -231,8 +231,8 @@ export default function TemplatesManagePage() {
|
|||
</Card>
|
||||
|
||||
{/* 템플릿 목록 테이블 */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<Card className="shadow-sm">
|
||||
<CardHeader className="bg-gray-50/50">
|
||||
<CardTitle>템플릿 목록 ({filteredAndSortedTemplates.length}개)</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ export default function UserMngPage() {
|
|||
<div className="min-h-screen bg-gray-50">
|
||||
<div className="container mx-auto p-6 space-y-6">
|
||||
{/* 페이지 제목 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center justify-between bg-white rounded-lg shadow-sm border p-6">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-900">사용자 관리</h1>
|
||||
<p className="mt-2 text-gray-600">시스템 사용자 계정 및 권한을 관리합니다</p>
|
||||
|
|
|
|||
|
|
@ -821,8 +821,11 @@ export const MenuManagement: React.FC = () => {
|
|||
{/* 좌측 사이드바 - 메뉴 타입 선택 (20%) */}
|
||||
<div className="w-[20%] border-r bg-gray-50">
|
||||
<div className="p-6">
|
||||
<h2 className="mb-4 text-lg font-semibold">{getUITextSync("menu.type.title")}</h2>
|
||||
<div className="space-y-3">
|
||||
<Card className="shadow-sm">
|
||||
<CardHeader className="bg-gray-50/50 pb-3">
|
||||
<CardTitle className="text-lg">{getUITextSync("menu.type.title")}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3 pt-4">
|
||||
<Card
|
||||
className={`cursor-pointer transition-all ${
|
||||
selectedMenuType === "admin" ? "border-blue-500 bg-blue-50" : "hover:border-gray-300"
|
||||
|
|
@ -864,21 +867,23 @@ export const MenuManagement: React.FC = () => {
|
|||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 우측 메인 영역 - 메뉴 목록 (80%) */}
|
||||
<div className="w-[80%] overflow-hidden">
|
||||
<div className="flex h-full flex-col p-6">
|
||||
<div className="mb-6 flex-shrink-0">
|
||||
<h2 className="mb-2 text-xl font-semibold">
|
||||
{getMenuTypeString()} {getUITextSync("menu.list.title")}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
{/* 검색 및 필터 영역 */}
|
||||
<div className="mb-4 flex-shrink-0">
|
||||
<Card className="flex-1 shadow-sm">
|
||||
<CardHeader className="bg-gray-50/50">
|
||||
<CardTitle className="text-xl">
|
||||
{getMenuTypeString()} {getUITextSync("menu.list.title")}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="flex-1 overflow-hidden">
|
||||
{/* 검색 및 필터 영역 */}
|
||||
<div className="mb-4 flex-shrink-0">
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-4">
|
||||
<div>
|
||||
<Label htmlFor="company">{getUITextSync("filter.company")}</Label>
|
||||
|
|
@ -997,52 +1002,54 @@ export const MenuManagement: React.FC = () => {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<div className="mb-4 flex items-center justify-between">
|
||||
<div className="text-sm text-gray-600">
|
||||
{getUITextSync("menu.list.total", { count: getCurrentMenus().length })}
|
||||
</div>
|
||||
<div className="flex space-x-2">
|
||||
<Button variant="outline" onClick={() => handleAddTopLevelMenu()} className="min-w-[100px]">
|
||||
{getUITextSync("button.add.top.level")}
|
||||
</Button>
|
||||
{selectedMenus.size > 0 && (
|
||||
<Button
|
||||
variant="destructive"
|
||||
onClick={handleDeleteSelectedMenus}
|
||||
disabled={deleting}
|
||||
className="min-w-[120px]"
|
||||
>
|
||||
{deleting ? (
|
||||
<>
|
||||
<LoadingSpinner size="sm" className="mr-2" />
|
||||
{getUITextSync("button.delete.processing")}
|
||||
</>
|
||||
) : (
|
||||
getUITextSync("button.delete.selected.count", {
|
||||
count: selectedMenus.size,
|
||||
})
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<div className="mb-4 flex items-center justify-between">
|
||||
<div className="text-sm text-gray-600">
|
||||
{getUITextSync("menu.list.total", { count: getCurrentMenus().length })}
|
||||
</div>
|
||||
<div className="flex space-x-2">
|
||||
<Button variant="outline" onClick={() => handleAddTopLevelMenu()} className="min-w-[100px]">
|
||||
{getUITextSync("button.add.top.level")}
|
||||
</Button>
|
||||
{selectedMenus.size > 0 && (
|
||||
<Button
|
||||
variant="destructive"
|
||||
onClick={handleDeleteSelectedMenus}
|
||||
disabled={deleting}
|
||||
className="min-w-[120px]"
|
||||
>
|
||||
{deleting ? (
|
||||
<>
|
||||
<LoadingSpinner size="sm" className="mr-2" />
|
||||
{getUITextSync("button.delete.processing")}
|
||||
</>
|
||||
) : (
|
||||
getUITextSync("button.delete.selected.count", {
|
||||
count: selectedMenus.size,
|
||||
})
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<MenuTable
|
||||
menus={getCurrentMenus()}
|
||||
title=""
|
||||
onAddMenu={handleAddMenu}
|
||||
onEditMenu={handleEditMenu}
|
||||
onToggleStatus={handleToggleStatus}
|
||||
selectedMenus={selectedMenus}
|
||||
onMenuSelectionChange={handleMenuSelectionChange}
|
||||
onSelectAllMenus={handleSelectAllMenus}
|
||||
expandedMenus={expandedMenus}
|
||||
onToggleExpand={handleToggleExpand}
|
||||
uiTexts={uiTexts}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<MenuTable
|
||||
menus={getCurrentMenus()}
|
||||
title=""
|
||||
onAddMenu={handleAddMenu}
|
||||
onEditMenu={handleEditMenu}
|
||||
onToggleStatus={handleToggleStatus}
|
||||
selectedMenus={selectedMenus}
|
||||
onMenuSelectionChange={handleMenuSelectionChange}
|
||||
onSelectAllMenus={handleSelectAllMenus}
|
||||
expandedMenus={expandedMenus}
|
||||
onToggleExpand={handleToggleExpand}
|
||||
uiTexts={uiTexts}
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1050,8 +1057,15 @@ export const MenuManagement: React.FC = () => {
|
|||
</TabsContent>
|
||||
|
||||
{/* 화면 할당 탭 */}
|
||||
<TabsContent value="screen-assignment" className="flex-1 overflow-hidden">
|
||||
<ScreenAssignmentTab menus={[...adminMenus, ...userMenus]} />
|
||||
<TabsContent value="screen-assignment" className="flex-1 overflow-hidden p-6">
|
||||
<Card className="h-full shadow-sm">
|
||||
<CardHeader className="bg-gray-50/50">
|
||||
<CardTitle>화면 할당</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="h-full overflow-hidden">
|
||||
<ScreenAssignmentTab menus={[...adminMenus, ...userMenus]} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import { useState, Suspense } from "react";
|
||||
import { useState, Suspense, useEffect } from "react";
|
||||
import { useRouter, usePathname, useSearchParams } from "next/navigation";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
|
|
@ -197,8 +197,27 @@ function AppLayoutInner({ children }: AppLayoutProps) {
|
|||
const searchParams = useSearchParams();
|
||||
const { user, logout, refreshUserData } = useAuth();
|
||||
const { userMenus, adminMenus, loading, refreshMenus } = useMenu();
|
||||
const [sidebarOpen, setSidebarOpen] = useState(false);
|
||||
const [sidebarOpen, setSidebarOpen] = useState(true);
|
||||
const [expandedMenus, setExpandedMenus] = useState<Set<string>>(new Set());
|
||||
const [isMobile, setIsMobile] = useState(false);
|
||||
|
||||
// 화면 크기 감지 및 사이드바 초기 상태 설정
|
||||
useEffect(() => {
|
||||
const checkIsMobile = () => {
|
||||
const mobile = window.innerWidth < 1024; // lg 브레이크포인트
|
||||
setIsMobile(mobile);
|
||||
// 모바일에서만 사이드바를 닫음
|
||||
if (mobile) {
|
||||
setSidebarOpen(false);
|
||||
} else {
|
||||
setSidebarOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
checkIsMobile();
|
||||
window.addEventListener('resize', checkIsMobile);
|
||||
return () => window.removeEventListener('resize', checkIsMobile);
|
||||
}, []);
|
||||
|
||||
// 프로필 관련 로직
|
||||
const {
|
||||
|
|
@ -253,15 +272,10 @@ function AppLayoutInner({ children }: AppLayoutProps) {
|
|||
? `/screens/${firstScreen.screenId}?mode=admin`
|
||||
: `/screens/${firstScreen.screenId}`;
|
||||
|
||||
console.log("🎯 메뉴에서 화면으로 이동:", {
|
||||
menuName: menu.name,
|
||||
screenId: firstScreen.screenId,
|
||||
isAdminMode,
|
||||
targetPath: screenPath,
|
||||
});
|
||||
|
||||
router.push(screenPath);
|
||||
setSidebarOpen(false);
|
||||
if (isMobile) {
|
||||
setSidebarOpen(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
@ -271,10 +285,11 @@ function AppLayoutInner({ children }: AppLayoutProps) {
|
|||
// 할당된 화면이 없고 URL이 있으면 기존 URL로 이동
|
||||
if (menu.url && menu.url !== "#") {
|
||||
router.push(menu.url);
|
||||
setSidebarOpen(false);
|
||||
if (isMobile) {
|
||||
setSidebarOpen(false);
|
||||
}
|
||||
} else {
|
||||
// URL도 없고 할당된 화면도 없으면 경고 메시지
|
||||
console.warn("메뉴에 URL이나 할당된 화면이 없습니다:", menu);
|
||||
toast.warning("이 메뉴에는 연결된 페이지나 화면이 없습니다.");
|
||||
}
|
||||
}
|
||||
|
|
@ -295,7 +310,7 @@ function AppLayoutInner({ children }: AppLayoutProps) {
|
|||
await logout();
|
||||
router.push("/login");
|
||||
} catch (error) {
|
||||
console.error("로그아웃 실패:", error);
|
||||
// 로그아웃 실패 시 처리
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -306,7 +321,7 @@ function AppLayoutInner({ children }: AppLayoutProps) {
|
|||
return (
|
||||
<div key={menu.id}>
|
||||
<div
|
||||
className={`group flex cursor-pointer items-center justify-between rounded-lg px-3 py-2 text-sm font-medium transition-colors hover:cursor-pointer ${
|
||||
className={`group flex cursor-pointer items-center justify-between rounded-lg px-3 py-2 text-sm font-medium transition-colors duration-200 ease-in-out h-10 ${
|
||||
pathname === menu.url
|
||||
? "bg-gradient-to-br from-slate-100 to-blue-100/40 text-slate-900 border-l-4 border-blue-500"
|
||||
: isExpanded
|
||||
|
|
@ -315,9 +330,9 @@ function AppLayoutInner({ children }: AppLayoutProps) {
|
|||
} ${level > 0 ? "ml-6" : ""}`}
|
||||
onClick={() => handleMenuClick(menu)}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<div className="flex items-center min-w-0 flex-1">
|
||||
{menu.icon}
|
||||
<span className="ml-3">{menu.name}</span>
|
||||
<span className="ml-3 truncate" title={menu.name}>{menu.name}</span>
|
||||
</div>
|
||||
{menu.hasChildren && (
|
||||
<div className="ml-auto">
|
||||
|
|
@ -339,8 +354,10 @@ function AppLayoutInner({ children }: AppLayoutProps) {
|
|||
}`}
|
||||
onClick={() => handleMenuClick(child)}
|
||||
>
|
||||
{child.icon}
|
||||
<span className="ml-3">{child.name}</span>
|
||||
<div className="flex items-center min-w-0 flex-1">
|
||||
{child.icon}
|
||||
<span className="ml-3 truncate" title={child.name}>{child.name}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -369,22 +386,29 @@ function AppLayoutInner({ children }: AppLayoutProps) {
|
|||
{/* MainHeader 컴포넌트 사용 */}
|
||||
<MainHeader
|
||||
user={user}
|
||||
onSidebarToggle={() => setSidebarOpen(!sidebarOpen)}
|
||||
onSidebarToggle={() => {
|
||||
// 모바일에서만 토글 동작
|
||||
if (isMobile) {
|
||||
setSidebarOpen(!sidebarOpen);
|
||||
}
|
||||
}}
|
||||
onProfileClick={openProfileModal}
|
||||
onLogout={handleLogout}
|
||||
/>
|
||||
|
||||
<div className="flex flex-1">
|
||||
{/* 모바일 사이드바 오버레이 */}
|
||||
{sidebarOpen && (
|
||||
{sidebarOpen && isMobile && (
|
||||
<div className="fixed inset-0 z-30 bg-black/50 lg:hidden" onClick={() => setSidebarOpen(false)} />
|
||||
)}
|
||||
|
||||
{/* 왼쪽 사이드바 */}
|
||||
<aside
|
||||
className={`${
|
||||
sidebarOpen ? "translate-x-0" : "-translate-x-full"
|
||||
} fixed top-14 left-0 z-40 flex h-full w-64 flex-col border-r border-slate-200 bg-white transition-transform duration-300 lg:relative lg:top-0 lg:z-auto lg:h-full lg:translate-x-0 lg:transform-none`}
|
||||
isMobile
|
||||
? (sidebarOpen ? "translate-x-0" : "-translate-x-full") + " fixed top-14 left-0 z-40"
|
||||
: "translate-x-0 relative top-0 z-auto"
|
||||
} flex h-full w-72 min-w-72 max-w-72 flex-col border-r border-slate-200 bg-white transition-transform duration-300`}
|
||||
>
|
||||
{/* 사이드바 상단 - Admin/User 모드 전환 버튼 (관리자만) */}
|
||||
{(user as ExtendedUserInfo)?.userType === "admin" && (
|
||||
|
|
@ -428,7 +452,7 @@ function AppLayoutInner({ children }: AppLayoutProps) {
|
|||
</aside>
|
||||
|
||||
{/* 가운데 컨텐츠 영역 */}
|
||||
<main className="flex-1 bg-white">{children}</main>
|
||||
<main className="flex-1 min-w-0 bg-white overflow-hidden">{children}</main>
|
||||
</div>
|
||||
|
||||
{/* 프로필 수정 모달 */}
|
||||
|
|
|
|||
Loading…
Reference in New Issue