feat: ESLint 정리 및 전체 코드 품질 개선
- eslint.config.mjs 규칙 강화 및 정리 - 전역 unused import/변수 제거 (312개 파일) - next.config.ts, middleware, proxy route 개선 - CopyableCell molecule 추가 - 회계/결재/HR/생산/건설/품질/영업 등 전 도메인 lint 정리 - IntegratedListTemplateV2, DataTable, MobileCard 등 공통 컴포넌트 개선 - execute-server-action 에러 핸들링 보강
This commit is contained in:
61
src/components/molecules/CopyableCell.tsx
Normal file
61
src/components/molecules/CopyableCell.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
'use client';
|
||||
|
||||
import { ReactNode, useCallback, useState } from 'react';
|
||||
import { Check, Copy } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
interface CopyableCellProps {
|
||||
/** 복사할 텍스트 값 */
|
||||
value: string;
|
||||
/** 셀에 표시할 내용 (기본: value) */
|
||||
children?: ReactNode;
|
||||
/** 추가 클래스 */
|
||||
className?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 테이블 셀 내 텍스트에 hover 시 복사 버튼을 표시하는 래퍼
|
||||
*
|
||||
* 사용법:
|
||||
* ```tsx
|
||||
* <TableCell>
|
||||
* <CopyableCell value={item.itemCode}>{item.itemCode}</CopyableCell>
|
||||
* </TableCell>
|
||||
* ```
|
||||
*/
|
||||
export function CopyableCell({ value, children, className }: CopyableCellProps) {
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
const handleCopy = useCallback((e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
navigator.clipboard.writeText(value).then(() => {
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 1500);
|
||||
});
|
||||
}, [value]);
|
||||
|
||||
return (
|
||||
<span className={cn('group/copy relative block max-w-full overflow-hidden', className)}>
|
||||
<span className="block truncate">{children ?? value}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleCopy}
|
||||
className={cn(
|
||||
'absolute right-0 top-1/2 -translate-y-1/2',
|
||||
'opacity-0 group-hover/copy:opacity-100 transition-opacity',
|
||||
'pl-4 pr-0.5 py-0.5',
|
||||
'bg-gradient-to-l from-background via-background to-transparent',
|
||||
copied && 'opacity-100',
|
||||
)}
|
||||
title="복사"
|
||||
>
|
||||
{copied ? (
|
||||
<Check className="h-3.5 w-3.5 text-green-500" />
|
||||
) : (
|
||||
<Copy className="h-3.5 w-3.5 text-muted-foreground hover:text-foreground" />
|
||||
)}
|
||||
</button>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@@ -90,7 +90,7 @@ export function DateRangeSelector({
|
||||
extraActions,
|
||||
hidePresets = false,
|
||||
hideDateInputs = false,
|
||||
dateInputWidth = 'w-[140px]',
|
||||
dateInputWidth: _dateInputWidth = 'w-[140px]',
|
||||
presetsPosition = 'inline',
|
||||
variant = 'combined',
|
||||
}: DateRangeSelectorProps) {
|
||||
|
||||
@@ -11,7 +11,6 @@ import { AlertCircle } from "lucide-react";
|
||||
import { PhoneInput } from "../ui/phone-input";
|
||||
import { BusinessNumberInput } from "../ui/business-number-input";
|
||||
import { PersonalNumberInput } from "../ui/personal-number-input";
|
||||
import { NumberInput } from "../ui/number-input";
|
||||
import { CurrencyInput } from "../ui/currency-input";
|
||||
import { QuantityInput } from "../ui/quantity-input";
|
||||
import { DatePicker } from "../ui/date-picker";
|
||||
@@ -104,9 +103,9 @@ export function FormField({
|
||||
// 새 입력 타입 전용 옵션
|
||||
showValidation,
|
||||
maskBack,
|
||||
allowDecimal,
|
||||
decimalPlaces,
|
||||
useComma,
|
||||
allowDecimal: _allowDecimal,
|
||||
decimalPlaces: _decimalPlaces,
|
||||
useComma: _useComma,
|
||||
suffix,
|
||||
showButtons,
|
||||
maxLength,
|
||||
|
||||
@@ -96,7 +96,7 @@ function countActiveFilters(
|
||||
/**
|
||||
* 필터 필드 요약 텍스트 생성
|
||||
*/
|
||||
function getFieldSummary(
|
||||
function _getFieldSummary(
|
||||
field: FilterFieldConfig,
|
||||
value: string | string[] | undefined
|
||||
): string {
|
||||
|
||||
@@ -64,7 +64,7 @@ export function StandardDialog({
|
||||
children,
|
||||
footer,
|
||||
size = "md",
|
||||
showClose = true,
|
||||
showClose: _showClose = true,
|
||||
className,
|
||||
}: StandardDialogProps) {
|
||||
return (
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { BadgeSm } from "@/components/atoms/BadgeSm";
|
||||
import { LucideIcon } from "lucide-react";
|
||||
import { BADGE_STYLE_PRESETS, type StatusStylePreset } from "@/lib/utils/status-config";
|
||||
|
||||
/**
|
||||
* 상태 뱃지 컴포넌트
|
||||
@@ -61,7 +60,7 @@ export function StatusBadge({
|
||||
icon: Icon,
|
||||
className = "",
|
||||
size = "md",
|
||||
showDot
|
||||
showDot: _showDot
|
||||
}: StatusBadgeProps) {
|
||||
// variant에 따른 기본 스타일
|
||||
const variantStyles: Record<string, string> = {
|
||||
|
||||
@@ -15,4 +15,6 @@ export type { Quarter } from "./YearQuarterFilter";
|
||||
export { GenericCRUDDialog } from "./GenericCRUDDialog";
|
||||
export type { GenericCRUDDialogProps, CRUDFieldDefinition } from "./GenericCRUDDialog";
|
||||
|
||||
export { ReorderButtons } from "./ReorderButtons";
|
||||
export { ReorderButtons } from "./ReorderButtons";
|
||||
|
||||
export { CopyableCell } from "./CopyableCell";
|
||||
Reference in New Issue
Block a user