From d66d308169e559457878c02e3b0443da22693241 Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Tue, 1 Jul 2025 02:53:18 +0000
Subject: (최겸) 정보시스템 인포메이션 기능 개발
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 components/information/information-button.tsx | 259 ++++++++++++++++++++++++++
 1 file changed, 259 insertions(+)
 create mode 100644 components/information/information-button.tsx

(limited to 'components/information/information-button.tsx')

diff --git a/components/information/information-button.tsx b/components/information/information-button.tsx
new file mode 100644
index 00000000..da0de548
--- /dev/null
+++ b/components/information/information-button.tsx
@@ -0,0 +1,259 @@
+"use client"
+
+import React, { useState, useEffect } from "react"
+import { Info, Download, Edit } from "lucide-react"
+import { Button } from "@/components/ui/button"
+import {
+  Dialog,
+  DialogContent,
+  DialogDescription,
+  DialogHeader,
+  DialogTitle,
+  DialogTrigger,
+} from "@/components/ui/dialog"
+import { getCachedPageInformation, getCachedEditPermission } from "@/lib/information/service"
+import { UpdateInformationDialog } from "@/lib/information/table/update-information-dialog"
+import type { PageInformation } from "@/db/schema/information"
+import { useSession } from "next-auth/react"
+
+interface InformationButtonProps {
+  pageCode: string
+  className?: string
+  variant?: "default" | "outline" | "ghost" | "secondary"
+  size?: "default" | "sm" | "lg" | "icon"
+}
+
+export function InformationButton({ 
+  pageCode, 
+  className,
+  variant = "ghost",
+  size = "icon"
+}: InformationButtonProps) {
+  const { data: session } = useSession()
+  const [information, setInformation] = useState<PageInformation | null>(null)
+  const [isLoading, setIsLoading] = useState(false)
+  const [isOpen, setIsOpen] = useState(false)
+  const [hasEditPermission, setHasEditPermission] = useState(false)
+  const [isEditDialogOpen, setIsEditDialogOpen] = useState(false)
+
+  useEffect(() => {
+    if (isOpen && !information) {
+      loadInformation()
+    }
+  }, [isOpen, information])
+
+  // 편집 권한 확인
+  useEffect(() => {
+    const checkEditPermission = async () => {
+      if (session?.user?.id) {
+        try {
+          const permission = await getCachedEditPermission(pageCode, session.user.id)
+          setHasEditPermission(permission)
+        } catch (error) {
+          console.error("Failed to check edit permission:", error)
+          setHasEditPermission(false)
+        }
+      }
+    }
+    
+    checkEditPermission()
+  }, [pageCode, session?.user?.id])
+
+  const loadInformation = async () => {
+    setIsLoading(true)
+    try {
+      const data = await getCachedPageInformation(pageCode)
+      setInformation(data)
+    } catch (error) {
+      console.error("Failed to load information:", error)
+    } finally {
+      setIsLoading(false)
+    }
+  }
+
+  const handleDownload = () => {
+    if (information?.attachmentFilePath && information?.attachmentFileName) {
+      const link = document.createElement('a')
+      link.href = information.attachmentFilePath
+      link.download = information.attachmentFileName
+      document.body.appendChild(link)
+      link.click()
+      document.body.removeChild(link)
+    }
+  }
+
+  const handleEditClick = () => {
+    setIsEditDialogOpen(true)
+  }
+
+  const handleEditClose = () => {
+    setIsEditDialogOpen(false)
+    refreshInformation()
+  }
+
+  const refreshInformation = () => {
+    // 편집 후 정보 다시 로드
+    setInformation(null)
+    if (isOpen) {
+      loadInformation()
+    }
+    // 캐시 무효화를 위해 다시 확인
+    setTimeout(() => {
+      loadInformation()
+    }, 500)
+  }
+
+  // 인포메이션이 없으면 버튼을 숨김
+  const [hasInformation, setHasInformation] = useState<boolean | null>(null)
+
+  useEffect(() => {
+    const checkInformation = async () => {
+      try {
+        const data = await getCachedPageInformation(pageCode)
+        setHasInformation(!!data)
+      } catch {
+        setHasInformation(false)
+      }
+    }
+    checkInformation()
+  }, [pageCode])
+
+  // 인포메이션이 없으면 버튼을 숨김
+  if (hasInformation === false) {
+    return null
+  }
+
+  return (
+    <>
+      <Dialog open={isOpen} onOpenChange={setIsOpen}>
+        <DialogTrigger asChild>
+          <Button 
+            variant={variant} 
+            size={size} 
+            className={className}
+            title="페이지 정보"
+          >
+            <Info className="h-4 w-4" />
+            {size !== "icon" && <span className="ml-1">정보</span>}
+          </Button>
+        </DialogTrigger>
+        <DialogContent className="max-w-4xl max-h-[80vh] overflow-y-auto">
+          <DialogHeader>
+            <div className="flex items-center justify-between">
+              <div className="flex items-center gap-2">
+                {/* <Info className="h-5 w-5" /> */}
+                <div>
+                  <DialogTitle>{information?.title || "페이지 정보"}</DialogTitle>
+                  <DialogDescription>{information?.pageName}</DialogDescription>
+                </div>
+              </div>
+              {hasEditPermission && (
+                <Button
+                  variant="outline"
+                  size="sm"
+                  onClick={handleEditClick}
+                  className="flex items-center gap-2 mr-2"
+                >
+                  <Edit className="h-4 w-4" />
+                  편집
+                </Button>
+              )}
+            </div>
+          </DialogHeader>
+          
+          <div className="mt-4 space-y-6">
+            {isLoading ? (
+              <div className="flex items-center justify-center py-8">
+                <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900"></div>
+              </div>
+            ) : information ? (
+              <>
+                {/* 공지사항 */}
+                {(information.noticeTitle || information.noticeContent) && (
+                  <div className="space-y-4">
+                    <div className="flex items-center gap-2">
+                      <h4 className="font-semibold text-xl">공지사항</h4>
+                    </div>
+                    <div className="bg-blue-50 border-2 border-blue-200 rounded-xl p-6">
+                      {information.noticeTitle && (
+                        <div className="text-base font-semibold  mb-4">
+                          제목: {information.noticeTitle}
+                        </div>
+                      )}
+                      {information.noticeContent && (
+                        <div className="bg-white border-2 border-blue-200 rounded-lg p-4 max-h-48 overflow-y-auto">
+                          <div className="text-base  whitespace-pre-wrap leading-relaxed">
+                            {information.noticeContent}
+                          </div>
+                        </div>
+                      )}
+                    </div>
+                  </div>
+                )}
+
+                {/* 페이지 정보 */}
+                <div className="space-y-3">
+                  <h4 className="font-medium text-lg">도움말</h4>
+                  <div className="bg-gray-50 border rounded-lg p-4">
+                    <div className="text-sm text-gray-600 whitespace-pre-wrap max-h-40 overflow-y-auto">
+                      {information.description || "페이지 설명이 없습니다."}
+                    </div>
+                  </div>
+                </div>
+
+                {/* 첨부파일 */}
+                <div className="space-y-3">
+                  <h4 className="font-medium text-lg">첨부파일</h4>
+                  {information.attachmentFileName ? (
+                    <div className="bg-gray-50 border rounded-lg p-4">
+                      <div className="flex items-center justify-between p-3 bg-white rounded border">
+                        <div className="flex-1">
+                          <div className="text-sm font-medium">
+                            {information.attachmentFileName}
+                          </div>
+                          {information.attachmentFileSize && (
+                            <div className="text-xs text-gray-500 mt-1">
+                              {information.attachmentFileSize}
+                            </div>
+                          )}
+                        </div>
+                        <Button
+                          size="sm"
+                          variant="outline"
+                          onClick={handleDownload}
+                          className="flex items-center gap-1"
+                        >
+                          <Download className="h-3 w-3" />
+                          다운로드
+                        </Button>
+                      </div>
+                    </div>
+                  ) : (
+                    <div className="text-center py-6 text-gray-500 bg-gray-50 rounded-lg">
+                      <Download className="h-6 w-6 mx-auto mb-2 text-gray-400" />
+                      <p className="text-sm">첨부된 파일이 없습니다.</p>
+                    </div>
+                  )}
+                </div>
+              </>
+            ) : (
+              <div className="text-center py-8 text-gray-500">
+                이 페이지에 대한 정보가 없습니다.
+              </div>
+            )}
+          </div>
+        </DialogContent>
+      </Dialog>
+
+      {/* 편집 다이얼로그 */}
+      {information && (
+        <UpdateInformationDialog
+          open={isEditDialogOpen}
+          onOpenChange={setIsEditDialogOpen}
+          information={information}
+          onClose={handleEditClose}
+        />
+      )}
+    </>
+  )
+} 
\ No newline at end of file
-- 
cgit v1.2.3

