diff options
Diffstat (limited to 'components')
| -rw-r--r-- | components/data-table/data-table-view-options.tsx | 8 | ||||
| -rw-r--r-- | components/documents/view-document-dialog.tsx | 235 | ||||
| -rw-r--r-- | components/login/login-form.tsx | 11 |
3 files changed, 244 insertions, 10 deletions
diff --git a/components/data-table/data-table-view-options.tsx b/components/data-table/data-table-view-options.tsx index 6120fff9..c55617ec 100644 --- a/components/data-table/data-table-view-options.tsx +++ b/components/data-table/data-table-view-options.tsx @@ -1,7 +1,7 @@ "use client" import * as React from "react" -import { type Table } from "@tanstack/react-table" +import { RowData, type Table } from "@tanstack/react-table" import { Check, ChevronsUpDown, @@ -41,12 +41,12 @@ interface DataTableViewOptionsProps<TData> { table: Table<TData> } -declare module "@tanstack/react-table" { - interface ColumnMeta<TData, TValue> { +declare module "@tanstack/table-core" { + interface ColumnMeta<TData extends RowData, TValue> { excelHeader?: string group?: string type?: string - // or whatever other fields you actually use + // ...anything else you want } } /** diff --git a/components/documents/view-document-dialog.tsx b/components/documents/view-document-dialog.tsx index 752252ee..7603fdc0 100644 --- a/components/documents/view-document-dialog.tsx +++ b/components/documents/view-document-dialog.tsx @@ -1,3 +1,80 @@ +<<<<<<< HEAD +"use client"; + +import * as React from "react"; +import { WebViewerInstance } from "@pdftron/webviewer"; +import { + Dialog, + DialogTrigger, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, + DialogFooter, +} from "@/components/ui/dialog"; +import { Building2, FileIcon, Loader2 } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import fs from "fs"; + +// 인터페이스 +interface Attachment { + id: number; + fileName: string; + filePath: string; + fileType?: string; +} + +interface Version { + id: number; + stage: string; + revision: string; + uploaderType: string; + uploaderName: string | null; + comment: string | null; + status: string | null; + planDate: string | null; + actualDate: string | null; + approvedDate: string | null; + DocumentSubmitDate: Date; + attachments: Attachment[]; + selected: boolean; +} + +type ViewDocumentDialogProps = { + versions: Version[]; +}; + +export function ViewDocumentDialog({ versions }: ViewDocumentDialogProps) { + const [open, setOpen] = React.useState(false); + + return ( + <> + <Button + size="sm" + className="border-blue-200" + variant="outline" + onClick={() => setOpen((prev) => !prev)} + > + 문서 보기 + </Button> + {open && ( + <DocumentViewer open={open} setOpen={setOpen} versions={versions} /> + )} + </> + ); +} + +const DocumentViewer: React.FC<{ + open: boolean; + setOpen: React.Dispatch<React.SetStateAction<boolean>>; + versions: Version[]; +}> = ({ open, setOpen, versions }) => { + const [instance, setInstance] = React.useState<null | WebViewerInstance>( + null + ); + const [viwerLoading, setViewerLoading] = React.useState<boolean>(true); + const [fileSetLoading, setFileSetLoading] = React.useState<boolean>(true); +======= "use client" import * as React from "react" @@ -58,22 +135,34 @@ function DocumentViewer({open, setOpen, versions}){ const [instance, setInstance] = React.useState<null | WebViewerInstance>(null) const [viwerLoading, setViewerLoading] = React.useState<boolean>(true) const [fileSetLoading, setFileSetLoading] = React.useState<boolean>(true) +>>>>>>> cac978d5c77e9b30165e4fbe6930eeac9862204d const viewer = React.useRef<HTMLDivElement>(null); const initialized = React.useRef(false); const isCancelled = React.useRef(false); // 초기화 중단용 flag const cleanupHtmlStyle = () => { const htmlElement = document.documentElement; +<<<<<<< HEAD + + // 기존 style 속성 가져오기 + const originalStyle = htmlElement.getAttribute("style") || ""; + +======= // 기존 style 속성 가져오기 const originalStyle = htmlElement.getAttribute("style") || ""; +>>>>>>> cac978d5c77e9b30165e4fbe6930eeac9862204d // "color-scheme: light" 또는 "color-scheme: dark" 찾기 const colorSchemeStyle = originalStyle .split(";") .map((s) => s.trim()) .find((s) => s.startsWith("color-scheme:")); +<<<<<<< HEAD + +======= +>>>>>>> cac978d5c77e9b30165e4fbe6930eeac9862204d // 새로운 스타일 적용 (color-scheme만 유지) if (colorSchemeStyle) { htmlElement.setAttribute("style", colorSchemeStyle + ";"); @@ -81,13 +170,46 @@ function DocumentViewer({open, setOpen, versions}){ htmlElement.removeAttribute("style"); // color-scheme도 없으면 style 속성 자체 삭제 } +<<<<<<< HEAD + console.log("html style 삭제"); +======= console.log("html style 삭제") +>>>>>>> cac978d5c77e9b30165e4fbe6930eeac9862204d }; React.useEffect(() => { if (open && !initialized.current) { initialized.current = true; isCancelled.current = false; // 다시 열릴 때는 false로 리셋 +<<<<<<< HEAD + + requestAnimationFrame(() => { + if (viewer.current) { + import("@pdftron/webviewer").then(({ default: WebViewer }) => { + console.log(isCancelled.current); + if (isCancelled.current) { + console.log("📛 WebViewer 초기화 취소됨 (Dialog 닫힘)"); + + return; + } + + WebViewer( + { + path: "/pdftronWeb", + licenseKey: process.env.NEXT_PUBLIC_PDFTRON_WEBVIEW_KEY, + fullAPI: true, + css: "/globals.css", + }, + viewer.current as HTMLDivElement + ).then(async (instance: WebViewerInstance) => { + setInstance(instance); + instance.UI.enableFeatures([instance.UI.Feature.MultiTab]); + instance.UI.disableElements([ + "addTabButton", + "multiTabsEmptyPage", + ]); + setViewerLoading(false); +======= requestAnimationFrame(() => { if (viewer.current) { @@ -115,11 +237,21 @@ function DocumentViewer({open, setOpen, versions}){ instance.UI.disableElements(["addTabButton", "multiTabsEmptyPage"]); setViewerLoading(false); +>>>>>>> cac978d5c77e9b30165e4fbe6930eeac9862204d }); }); } }); } +<<<<<<< HEAD + + return () => { + // cleanup 시에는 중단 flag 세움 + if (instance) { + instance.UI.dispose(); + } + setTimeout(() => cleanupHtmlStyle(), 500); +======= return async () => { // cleanup 시에는 중단 flag 세움 @@ -127,10 +259,78 @@ function DocumentViewer({open, setOpen, versions}){ await instance.UI.dispose() } await setTimeout(() => cleanupHtmlStyle(), 500) +>>>>>>> cac978d5c77e9b30165e4fbe6930eeac9862204d }; }, [open]); React.useEffect(() => { +<<<<<<< HEAD + const loadDocument = async () => { + if (instance && versions.length > 0) { + const { UI } = instance; + + const optionsArray: any[] = []; + + versions.forEach((c) => { + const { attachments } = c; + attachments.forEach((c2) => { + const { fileName, filePath, fileType } = c2; + + const fileTypeCur = fileType ?? ""; + + const options = { + filename: fileName, + ...(fileTypeCur.includes("xlsx") && { + officeOptions: { + formatOptions: { + applyPageBreaksToSheet: true, + }, + }, + }), + }; + + optionsArray.push({ + filePath, + options, + }); + }); + }); + + const tabIds = []; + + for (const option of optionsArray) { + const { filePath, options } = option; + const response = await fetch(filePath); + const blob = await response.blob(); + + const tab = await UI.TabManager.addTab(blob, options); + tabIds.push(tab); // 탭 ID 저장 + } + + if (tabIds.length > 0) { + await UI.TabManager.setActiveTab(tabIds[0]); + } + + setFileSetLoading(false); + } + }; + loadDocument(); + }, [instance, versions]); + + return ( + <Dialog + open={open} + onOpenChange={async (val) => { + console.log({ val, fileSetLoading }); + if (!val && fileSetLoading) { + return; + } + + if (instance) { + try { + await instance.UI.dispose(); + setInstance(null); // 상태도 초기화 +======= const loadDocument = async () => { if(instance && versions.length > 0){ @@ -195,10 +395,42 @@ function DocumentViewer({open, setOpen, versions}){ await instance.UI.dispose(); setInstance(null); // 상태도 초기화 +>>>>>>> cac978d5c77e9b30165e4fbe6930eeac9862204d } catch (e) { console.warn("dispose error", e); } } +<<<<<<< HEAD + + // cleanupHtmlStyle() + setViewerLoading(false); + setOpen((prev) => !prev); + await setTimeout(() => cleanupHtmlStyle(), 1000); + }} + > + <DialogContent className="w-[70vw] h-[90vh]" style={{ maxWidth: "none" }}> + <DialogHeader className="h-[38px]"> + <DialogTitle>문서 미리보기</DialogTitle> + <DialogDescription>첨부파일 미리보기</DialogDescription> + </DialogHeader> + <div + ref={viewer} + style={{ height: "calc(90vh - 20px - 38px - 1rem - 48px)" }} + > + {viwerLoading && ( + <div className="flex flex-col items-center justify-center py-12"> + <Loader2 className="h-8 w-8 text-blue-500 animate-spin mb-4" /> + <p className="text-sm text-muted-foreground"> + 문서 뷰어 로딩 중... + </p> + </div> + )} + </div> + </DialogContent> + </Dialog> + ); +}; +======= // cleanupHtmlStyle() setViewerLoading(false); @@ -223,4 +455,5 @@ function DocumentViewer({open, setOpen, versions}){ </DialogContent> </Dialog> ); -}
\ No newline at end of file +} +>>>>>>> cac978d5c77e9b30165e4fbe6930eeac9862204d diff --git a/components/login/login-form.tsx b/components/login/login-form.tsx index 2a51f2e2..41d232f8 100644 --- a/components/login/login-form.tsx +++ b/components/login/login-form.tsx @@ -27,11 +27,12 @@ export function LoginForm({ ...props }: React.ComponentProps<"div">) { - const params = useParams(); - const pathname = usePathname(); + const params = useParams() || {}; + const pathname = usePathname() || ''; const router = useRouter(); const searchParams = useSearchParams(); - const token = searchParams.get('token'); + const token = searchParams?.get('token') || null; + const lng = params.lng as string; const { t, i18n } = useTranslation(lng, 'login'); @@ -104,9 +105,9 @@ export function LoginForm({ console.log(session) if (userDomain === 'evcp') { - router.push(`/${lng}/evcp`); + router.push(`/${lng}/evcp/report`); } else if (userDomain === 'partners') { - router.push(`/${lng}/partners`); + router.push(`/${lng}/partners/dashboard`); } else { // 기본 리다이렉션 경로 router.push(`/${lng}/dashboard`); |
