diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-09-26 09:57:24 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-09-26 09:57:24 +0000 |
| commit | 8b23b471638a155fd1bfa3a8c853b26d9315b272 (patch) | |
| tree | 47353e9dd342011cb2f1dcd24b09661707a8421b /app | |
| parent | d62368d2b68d73da895977e60a18f9b1286b0545 (diff) | |
(대표님) 권한관리, 문서업로드, rfq첨부, SWP문서룰 등
(최겸) 입찰
Diffstat (limited to 'app')
| -rw-r--r-- | app/[lng]/evcp/(evcp)/permissions/page.tsx | 72 | ||||
| -rw-r--r-- | app/[lng]/evcp/(evcp)/permissions/settings/page.tsx | 54 | ||||
| -rw-r--r-- | app/[lng]/partners/(partners)/document-upload/page.tsx | 2 | ||||
| -rw-r--r-- | app/api/rfq-attachments/upload/route.ts | 20 |
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}`; } |
