summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/[lng]/evcp/(evcp)/permissions/page.tsx72
-rw-r--r--app/[lng]/evcp/(evcp)/permissions/settings/page.tsx54
-rw-r--r--app/[lng]/partners/(partners)/document-upload/page.tsx2
-rw-r--r--app/api/rfq-attachments/upload/route.ts20
4 files changed, 141 insertions, 7 deletions
diff --git a/app/[lng]/evcp/(evcp)/permissions/page.tsx b/app/[lng]/evcp/(evcp)/permissions/page.tsx
new file mode 100644
index 00000000..2d7b94e2
--- /dev/null
+++ b/app/[lng]/evcp/(evcp)/permissions/page.tsx
@@ -0,0 +1,72 @@
+// app/evcp/(evcp)/permissions/page.tsx
+
+"use client";
+
+import { useState } from "react";
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
+import { Badge } from "@/components/ui/badge";
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { Shield, Users, Key, Menu, Search, Plus } from "lucide-react";
+import { RolePermissionManager } from "@/components/permissions/role-permission-manager";
+import { PermissionAssignmentManager } from "@/components/permissions/permission-assignment-manager";
+import { UserPermissionManager } from "@/components/permissions/user-permission-manager";
+import { MenuPermissionManager } from "@/components/permissions/menu-permission-manager";
+
+export default function PermissionManagementPage() {
+ const [searchTerm, setSearchTerm] = useState("");
+ const [selectedTab, setSelectedTab] = useState("by-role");
+
+ return (
+ <div className="container mx-auto p-6">
+ <div className="mb-6">
+ <h1 className="text-3xl font-bold mb-2">권한 관리</h1>
+ <p className="text-muted-foreground">
+ 시스템 권한을 역할, 사용자, 메뉴별로 관리합니다.
+ </p>
+ </div>
+
+ <Tabs value={selectedTab} onValueChange={setSelectedTab}>
+ <TabsList className="grid w-full grid-cols-4">
+ <TabsTrigger value="by-role">
+ <Users className="mr-2 h-4 w-4" />
+ 역할별 관리
+ </TabsTrigger>
+ <TabsTrigger value="by-user">
+ <Shield className="mr-2 h-4 w-4" />
+ 사용자별 관리
+ </TabsTrigger>
+ <TabsTrigger value="by-permission">
+ <Key className="mr-2 h-4 w-4" />
+ 권한별 관리
+ </TabsTrigger>
+ <TabsTrigger value="by-menu">
+ <Menu className="mr-2 h-4 w-4" />
+ 메뉴별 관리
+ </TabsTrigger>
+ </TabsList>
+
+ {/* 역할별 권한 관리 */}
+ <TabsContent value="by-role">
+ <RolePermissionManager />
+ </TabsContent>
+
+ {/* 사용자별 권한 관리 */}
+ <TabsContent value="by-user">
+ <UserPermissionManager />
+ </TabsContent>
+
+ {/* 권한별 사용자/역할 관리 */}
+ <TabsContent value="by-permission">
+ <PermissionAssignmentManager />
+ </TabsContent>
+
+ {/* 메뉴별 권한 설정 */}
+ <TabsContent value="by-menu">
+ <MenuPermissionManager />
+ </TabsContent>
+ </Tabs>
+ </div>
+ );
+} \ No newline at end of file
diff --git a/app/[lng]/evcp/(evcp)/permissions/settings/page.tsx b/app/[lng]/evcp/(evcp)/permissions/settings/page.tsx
new file mode 100644
index 00000000..e258124f
--- /dev/null
+++ b/app/[lng]/evcp/(evcp)/permissions/settings/page.tsx
@@ -0,0 +1,54 @@
+// app/(evcp)/admin/permissions/settings/page.tsx
+
+"use client";
+
+import { useState } from "react";
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
+import { Button } from "@/components/ui/button";
+import { Shield, Key, Settings, RefreshCw } from "lucide-react";
+import { PermissionCrudManager } from "@/components/permissions/permission-crud-manager";
+import { MenuBasedPermissionGenerator } from "@/components/permissions/menu-permission-generator";
+import { PermissionGroupManager } from "@/components/permissions/permission-group-manager";
+
+export default function PermissionSettingsPage() {
+ return (
+ <div className="container mx-auto p-6">
+ <div className="mb-6">
+ <h1 className="text-3xl font-bold mb-2">권한 설정</h1>
+ <p className="text-muted-foreground">
+ 시스템 권한을 생성, 수정, 삭제하고 메뉴 기반으로 권한을 자동 생성합니다.
+ </p>
+ </div>
+
+ <Tabs defaultValue="permissions" className="space-y-4">
+ <TabsList>
+ <TabsTrigger value="permissions">
+ <Key className="mr-2 h-4 w-4" />
+ 권한 관리
+ </TabsTrigger>
+ <TabsTrigger value="generate">
+ <RefreshCw className="mr-2 h-4 w-4" />
+ 메뉴 기반 생성
+ </TabsTrigger>
+ <TabsTrigger value="groups">
+ <Shield className="mr-2 h-4 w-4" />
+ 권한 그룹
+ </TabsTrigger>
+ </TabsList>
+
+ <TabsContent value="permissions">
+ <PermissionCrudManager />
+ </TabsContent>
+
+ <TabsContent value="generate">
+ <MenuBasedPermissionGenerator />
+ </TabsContent>
+
+ <TabsContent value="groups">
+ <PermissionGroupManager />
+ </TabsContent>
+ </Tabs>
+ </div>
+ );
+} \ No newline at end of file
diff --git a/app/[lng]/partners/(partners)/document-upload/page.tsx b/app/[lng]/partners/(partners)/document-upload/page.tsx
index 9df82fd4..003b4984 100644
--- a/app/[lng]/partners/(partners)/document-upload/page.tsx
+++ b/app/[lng]/partners/(partners)/document-upload/page.tsx
@@ -56,7 +56,7 @@ export default async function StageSubmissionsPage({
{/* Header */}
<div className="flex items-center justify-between">
<div>
- <h1 className="text-3xl font-bold tracking-tight">My Stage Submissions</h1>
+ <h2 className="text-2xl font-bold tracking-tight">My Stage Submissions</h2>
<p className="text-muted-foreground mt-1">
Manage document submissions for your approved stages
</p>
diff --git a/app/api/rfq-attachments/upload/route.ts b/app/api/rfq-attachments/upload/route.ts
index 3343c905..0f9a1902 100644
--- a/app/api/rfq-attachments/upload/route.ts
+++ b/app/api/rfq-attachments/upload/route.ts
@@ -2,7 +2,7 @@ import { NextRequest, NextResponse } from "next/server";
import { getServerSession } from 'next-auth';
import { authOptions } from '@/app/api/auth/[...nextauth]/route';
import db from "@/db/db";
-import { eq, and } from "drizzle-orm";
+import { eq, and, sql } from "drizzle-orm";
import { rfqLastAttachments, rfqLastAttachmentRevisions } from "@/db/schema";
import { saveFile } from "@/lib/file-stroage";
@@ -10,19 +10,27 @@ import { saveFile } from "@/lib/file-stroage";
async function generateSerialNo(rfqId: number, attachmentType: string, index: number = 0): Promise<string> {
const prefix = attachmentType === "설계" ? "DES" : "PUR";
- const existingAttachments = await db
- .select({ id: rfqLastAttachments.id })
+ // 데이터베이스에서 최대 시리얼 번호 찾기
+ const maxSerialResult = await db
+ .select({ serialNo: rfqLastAttachments.serialNo })
.from(rfqLastAttachments)
.where(
and(
eq(rfqLastAttachments.rfqId, rfqId),
eq(rfqLastAttachments.attachmentType, attachmentType as "설계" | "구매")
)
- );
+ )
+ .orderBy(sql`CAST(SUBSTRING(${rfqLastAttachments.serialNo} FROM '[^-]+$') AS INTEGER) DESC`)
+ .limit(1);
- const nextNumber = existingAttachments.length + 1 + index;
- const paddedNumber = String(nextNumber).padStart(4, "0");
+ let nextNumber = 1;
+ if (maxSerialResult.length > 0) {
+ const lastSerialNo = maxSerialResult[0].serialNo;
+ const lastNumber = parseInt(lastSerialNo.split('-').pop() || '0');
+ nextNumber = lastNumber + 1;
+ }
+ const paddedNumber = String(nextNumber).padStart(4, "0");
return `${prefix}-${rfqId}-${paddedNumber}`;
}