summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/bidding/list/biddings-page-header.tsx7
-rw-r--r--lib/information/repository.ts40
-rw-r--r--lib/information/service.ts36
-rw-r--r--lib/information/table/update-information-dialog.tsx27
4 files changed, 78 insertions, 32 deletions
diff --git a/lib/bidding/list/biddings-page-header.tsx b/lib/bidding/list/biddings-page-header.tsx
index 64e588c3..0be2172b 100644
--- a/lib/bidding/list/biddings-page-header.tsx
+++ b/lib/bidding/list/biddings-page-header.tsx
@@ -3,7 +3,7 @@
import { Button } from "@/components/ui/button"
import { Plus, FileText, TrendingUp } from "lucide-react"
import { useRouter } from "next/navigation"
-
+import { InformationButton } from "@/components/information/information-button"
export function BiddingsPageHeader() {
const router = useRouter()
@@ -11,7 +11,10 @@ export function BiddingsPageHeader() {
<div className="flex items-center justify-between">
{/* 좌측: 제목과 설명 */}
<div className="space-y-1">
- <h2 className="text-3xl font-bold tracking-tight">입찰 목록 관리</h2>
+ <div className="flex items-center gap-2">
+ <h2 className="text-3xl font-bold tracking-tight">입찰 목록 관리</h2>
+ <InformationButton pagePath="evcp/bid" />
+ </div>
<p className="text-muted-foreground">
입찰 공고를 생성하고 진행 상황을 관리할 수 있습니다.
</p>
diff --git a/lib/information/repository.ts b/lib/information/repository.ts
index c7c000b1..c3b82d4a 100644
--- a/lib/information/repository.ts
+++ b/lib/information/repository.ts
@@ -1,13 +1,14 @@
import { asc, desc, eq, and } from "drizzle-orm"
import db from "@/db/db"
-import {
- pageInformation,
+import {
+ pageInformation,
informationAttachments,
- type PageInformation,
+ type PageInformation,
type NewPageInformation,
type InformationAttachment,
type NewInformationAttachment
} from "@/db/schema/information"
+import { users } from "@/db/schema/users"
@@ -22,10 +23,22 @@ export async function updateInformation(id: number, data: Partial<NewPageInforma
return result[0] || null
}
-// 인포메이션과 첨부파일 함께 조회
+// 인포메이션과 첨부파일 함께 조회 (사용자 정보 포함)
export async function getInformationWithAttachments(id: number) {
const information = await db
- .select()
+ .select({
+ id: pageInformation.id,
+ pagePath: pageInformation.pagePath,
+ pageName: pageInformation.pageName,
+ informationContent: pageInformation.informationContent,
+ isActive: pageInformation.isActive,
+ createdBy: pageInformation.createdBy,
+ createdAt: pageInformation.createdAt,
+ updatedBy: pageInformation.updatedBy,
+ updatedAt: pageInformation.updatedAt,
+ updatedByName: users.name,
+ updatedByEmail: users.email,
+ })
.from(pageInformation)
.where(eq(pageInformation.id, id))
.limit(1)
@@ -44,11 +57,24 @@ export async function getInformationWithAttachments(id: number) {
}
}
-// 페이지 경로로 인포메이션과 첨부파일 함께 조회
+// 페이지 경로로 인포메이션과 첨부파일 함께 조회 (사용자 정보 포함)
export async function getInformationByPagePathWithAttachments(pagePath: string) {
const information = await db
- .select()
+ .select({
+ id: pageInformation.id,
+ pagePath: pageInformation.pagePath,
+ pageName: pageInformation.pageName,
+ informationContent: pageInformation.informationContent,
+ isActive: pageInformation.isActive,
+ createdBy: pageInformation.createdBy,
+ createdAt: pageInformation.createdAt,
+ updatedBy: pageInformation.updatedBy,
+ updatedAt: pageInformation.updatedAt,
+ updatedByName: users.name,
+ updatedByEmail: users.email,
+ })
.from(pageInformation)
+ .leftJoin(users, eq(pageInformation.updatedBy, users.id))
.where(and(
eq(pageInformation.pagePath, pagePath),
eq(pageInformation.isActive, true)
diff --git a/lib/information/service.ts b/lib/information/service.ts
index 2d3ad079..02efe616 100644
--- a/lib/information/service.ts
+++ b/lib/information/service.ts
@@ -3,7 +3,7 @@
import { getErrorMessage } from "@/lib/handle-error"
import { desc, or, eq } from "drizzle-orm"
import db from "@/db/db"
-import { pageInformation, menuAssignments } from "@/db/schema"
+import { pageInformation, menuAssignments, users } from "@/db/schema"
import { saveDRMFile } from "@/lib/file-stroage"
import { decryptWithServerAction } from "@/components/drm/drmUtils"
@@ -25,10 +25,23 @@ import type { PageInformation, InformationAttachment } from "@/db/schema/informa
// 간단한 인포메이션 목록 조회 (페이지네이션 없이 전체 조회)
export async function getInformationLists() {
try {
- // 전체 데이터 조회 (클라이언트에서 검색 처리)
+ // 전체 데이터 조회 (클라이언트에서 검색 처리, 생성자/수정자 정보 포함)
const data = await db
- .select()
+ .select({
+ id: pageInformation.id,
+ pagePath: pageInformation.pagePath,
+ pageName: pageInformation.pageName,
+ informationContent: pageInformation.informationContent,
+ isActive: pageInformation.isActive,
+ createdBy: pageInformation.createdBy,
+ createdAt: pageInformation.createdAt,
+ updatedBy: pageInformation.updatedBy,
+ updatedAt: pageInformation.updatedAt,
+ updatedByName: users.name,
+ updatedByEmail: users.email,
+ })
.from(pageInformation)
+ .leftJoin(users, eq(pageInformation.updatedBy, users.id))
.orderBy(desc(pageInformation.createdAt))
return { data }
@@ -68,28 +81,29 @@ export async function getPageInformationDirect(pagePath: string) {
}
// 인포메이션 수정 (내용과 첨부파일만)
-export async function updateInformationData(input: UpdateInformationSchema) {
+export async function updateInformationData(input: UpdateInformationSchema, userId?: string) {
try {
const { id, ...updateData } = input
-
+
// 수정 가능한 필드만 허용
const allowedFields = {
informationContent: updateData.informationContent,
isActive: updateData.isActive,
+ updatedBy: userId ? parseInt(userId) : null,
updatedAt: new Date()
}
-
+
const result = await updateInformation(id, allowedFields)
-
+
if (!result) {
return {
success: false,
message: "인포메이션을 찾을 수 없거나 수정에 실패했습니다."
}
}
-
+
// 캐시 무효화 제거됨
-
+
return {
success: true,
message: "인포메이션이 성공적으로 수정되었습니다."
@@ -174,8 +188,8 @@ export async function syncInformationFromMenuAssignments() {
for (const menu of menuItems) {
try {
// 맨 앞의 / 제거하여 pagePath 정규화
- const normalizedPagePath = menu.menuPath.startsWith('/')
- ? menu.menuPath.slice(1)
+ const normalizedPagePath = menu.menuPath.startsWith('/')
+ ? menu.menuPath.slice(1)
: menu.menuPath;
await db.insert(pageInformation)
diff --git a/lib/information/table/update-information-dialog.tsx b/lib/information/table/update-information-dialog.tsx
index 370eb763..147dc2ab 100644
--- a/lib/information/table/update-information-dialog.tsx
+++ b/lib/information/table/update-information-dialog.tsx
@@ -46,13 +46,14 @@ import {
import type { PageInformation, InformationAttachment } from "@/db/schema/information"
// downloadFile은 동적으로 import
import prettyBytes from "pretty-bytes"
+import { useSession } from "next-auth/react"
const MAX_FILE_SIZE = 50 * 1024 * 1024 // 50MB
// 폼 스키마
const updateInformationSchema = z.object({
id: z.number(),
- informationContent: z.string().min(1, "인포메이션 내용을 입력해주세요"),
+ informationContent: z.string().min(1, "안내사항 내용을 입력해주세요"),
isActive: z.boolean(),
newFiles: z.array(z.any()).optional(),
})
@@ -79,6 +80,8 @@ export function UpdateInformationDialog({
const [isLoading, setIsLoading] = React.useState(false)
const [isUploadingFiles, setIsUploadingFiles] = React.useState(false)
const [existingAttachments, setExistingAttachments] = React.useState<InformationAttachment[]>([])
+ const session = useSession()
+ const userId = session.data?.user?.id
const form = useForm<UpdateInformationSchema>({
resolver: zodResolver(updateInformationSchema),
@@ -95,7 +98,7 @@ export function UpdateInformationDialog({
name: "newFiles",
})
- // 인포메이션 데이터가 변경되면 폼 업데이트
+ // 안내사항 데이터가 변경되면 폼 업데이트
React.useEffect(() => {
if (information && open) {
form.reset({
@@ -160,12 +163,12 @@ export function UpdateInformationDialog({
const onSubmit = async (values: UpdateInformationSchema) => {
setIsLoading(true)
try {
- // 1. 인포메이션 정보 업데이트
+ // 1. 안내사항 정보 업데이트
const updateResult = await updateInformationData({
id: values.id,
informationContent: values.informationContent,
isActive: values.isActive,
- })
+ }, userId)
if (!updateResult.success) {
toast.error(updateResult.message)
@@ -189,13 +192,13 @@ export function UpdateInformationDialog({
setIsUploadingFiles(false)
}
- toast.success("인포메이션이 성공적으로 수정되었습니다.")
+ toast.success("안내사항이 성공적으로 수정되었습니다.")
if (onSuccess) onSuccess()
onOpenChange(false)
router.refresh()
} catch (error) {
- console.error("인포메이션 수정 오류:", error)
- toast.error("인포메이션 수정에 실패했습니다.")
+ console.error("안내사항 수정 오류:", error)
+ toast.error("안내사항 수정에 실패했습니다.")
} finally {
setIsLoading(false)
setIsUploadingFiles(false)
@@ -212,9 +215,9 @@ export function UpdateInformationDialog({
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-4xl max-h-[90vh] overflow-y-auto">
<DialogHeader>
- <DialogTitle>{t('information.edit.title', '인포메이션 수정')}</DialogTitle>
+ <DialogTitle>{t('information.edit.title', '안내사항 수정')}</DialogTitle>
<DialogDescription>
- {t('information.edit.description', '페이지 인포메이션 정보를 수정합니다.')}
+ {t('information.edit.description', '페이지 안내사항 정보를 수정합니다.')}
</DialogDescription>
</DialogHeader>
@@ -237,10 +240,10 @@ export function UpdateInformationDialog({
name="informationContent"
render={({ field }) => (
<FormItem>
- <FormLabel>{t('information.content.label', '인포메이션 내용')}</FormLabel>
+ <FormLabel>{t('information.content.label', '안내사항')}</FormLabel>
<FormControl>
<Textarea
- placeholder={t('information.content.placeholder', '인포메이션 내용을 입력하세요')}
+ placeholder={t('information.content.placeholder', '안내사항을 입력하세요')}
rows={6}
{...field}
/>
@@ -355,7 +358,7 @@ export function UpdateInformationDialog({
<div className="space-y-0.5">
<FormLabel className="text-base">{t('information.status.label', '활성 상태')}</FormLabel>
<div className="text-sm text-muted-foreground">
- {t('information.status.description', '활성화하면 해당 페이지에서 인포메이션 버튼이 표시됩니다.')}
+ {t('information.status.description', '활성화하면 해당 페이지에서 안내사항 버튼이 표시됩니다.')}
</div>
</div>
<FormControl>