chore(WEB): package-lock.json git 트래킹 제거

- .gitignore에 이미 등록되어 있으나 과거 커밋으로 트래킹 중이던 파일 제거

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
유병철
2026-02-11 20:13:31 +09:00
parent ec0d97867f
commit 113d82c254
7 changed files with 1930 additions and 10848 deletions

View File

@@ -1,6 +1,5 @@
import { NextRequest, NextResponse } from 'next/server';
import puppeteer from 'puppeteer-core';
import chromium from '@sparticuz/chromium';
import puppeteer from 'puppeteer';
/**
* PDF 생성 API
@@ -36,20 +35,17 @@ export async function POST(request: NextRequest) {
);
}
// 로컬 개발 vs Vercel 환경 분기
const isVercel = process.env.VERCEL === '1';
// Puppeteer 브라우저 실행 (Docker Alpine에서는 시스템 Chromium 사용)
const browser = await puppeteer.launch({
args: isVercel ? chromium.args : [
headless: true,
executablePath: process.env.PUPPETEER_EXECUTABLE_PATH || undefined,
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
'--disable-gpu',
'--disable-software-rasterizer',
],
executablePath: isVercel
? await chromium.executablePath()
: process.env.PUPPETEER_EXECUTABLE_PATH || '/usr/bin/google-chrome-stable',
headless: true,
});
const page = await browser.newPage();

View File

@@ -2,7 +2,7 @@
import { useRouter, usePathname, useParams } from 'next/navigation';
import { ChevronDown, LayoutDashboard, LayoutGrid, Target, Activity, Columns3 } from 'lucide-react';
import { useState, useRef, useEffect } from 'react';
import { useState, useRef, useEffect, useCallback } from 'react';
const dashboards = [
{ path: '/dashboard', label: '보고서형', icon: LayoutDashboard },
@@ -18,11 +18,21 @@ export function DashboardSwitcher() {
const params = useParams();
const locale = (params.locale as string) || 'ko';
const [open, setOpen] = useState(false);
const [alignRight, setAlignRight] = useState(true);
const ref = useRef<HTMLDivElement>(null);
// 현재 활성 대시보드 찾기
const current = dashboards.find((d) => pathname.endsWith(d.path)) ?? dashboards[0];
// 드롭다운 열릴 때 버튼 위치 기반으로 정렬 방향 결정
const updateAlignment = useCallback(() => {
if (!ref.current) return;
const rect = ref.current.getBoundingClientRect();
const viewportWidth = window.innerWidth;
// 버튼 중심이 화면 오른쪽 절반이면 right 정렬, 아니면 left 정렬
setAlignRight(rect.left + rect.width / 2 > viewportWidth / 2);
}, []);
// 외부 클릭 시 닫기
useEffect(() => {
function handleClick(e: MouseEvent) {
@@ -32,10 +42,15 @@ export function DashboardSwitcher() {
return () => document.removeEventListener('mousedown', handleClick);
}, [open]);
const handleToggle = () => {
if (!open) updateAlignment();
setOpen(!open);
};
return (
<div ref={ref} className="relative">
<button
onClick={() => setOpen(!open)}
onClick={handleToggle}
className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg border bg-background text-sm font-medium hover:bg-muted transition-colors"
>
<current.icon className="w-4 h-4 text-primary" />
@@ -44,7 +59,7 @@ export function DashboardSwitcher() {
</button>
{open && (
<div className="absolute right-0 top-full mt-1 w-44 rounded-lg border bg-background shadow-lg z-50 py-1">
<div className={`absolute top-full mt-1 w-44 rounded-lg border bg-background shadow-lg z-50 py-1 ${alignRight ? 'right-0' : 'left-0'}`}>
{dashboards.map((d) => {
const Icon = d.icon;
const isActive = d.path === current.path;