diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/bidding/list/biddings-page-header.tsx | 7 | ||||
| -rw-r--r-- | lib/information/repository.ts | 40 | ||||
| -rw-r--r-- | lib/information/service.ts | 36 | ||||
| -rw-r--r-- | lib/information/table/update-information-dialog.tsx | 27 |
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>
|
