refactor(WEB): 회계/결재/건설 등 공통화 3차 및 검색/상태 유틸 추가
- search.ts: 범용 검색 유틸리티 추출 (텍스트/날짜/상태 필터링) - status-config.ts: 상태 설정 공통 유틸 추가 - 회계 모듈 types 간소화 및 컬럼 설정 공통 패턴 적용 - 회계 page.tsx 통일 (bad-debt/bills/deposits/sales 등 9개) - 결재함(승인/기안/참조) 공통 패턴 적용 - 건설 모듈 견적/인수인계/이슈/기성 등 코드 정리 - IntegratedListTemplateV2 개선 - LanguageSelect/ThemeSelect 정리 - 체크리스트 문서 업데이트 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -37,7 +37,7 @@ export function LanguageSelect({ native = true }: LanguageSelectProps) {
|
||||
// 네이티브 select
|
||||
if (native) {
|
||||
return (
|
||||
<div className="relative w-[140px]">
|
||||
<div className="relative min-w-[140px] w-auto">
|
||||
<div className="absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none z-10">
|
||||
<Globe className="w-4 h-4" />
|
||||
</div>
|
||||
@@ -64,7 +64,7 @@ export function LanguageSelect({ native = true }: LanguageSelectProps) {
|
||||
// Radix UI 모달 select
|
||||
return (
|
||||
<Select value={locale} onValueChange={handleLanguageChange}>
|
||||
<SelectTrigger className="w-[140px] rounded-xl border-border/50 bg-background/50 backdrop-blur">
|
||||
<SelectTrigger className="min-w-[140px] w-auto rounded-xl border-border/50 bg-background/50 backdrop-blur">
|
||||
<div className="flex items-center gap-2">
|
||||
<Globe className="w-4 h-4" />
|
||||
<SelectValue>
|
||||
|
||||
@@ -29,7 +29,7 @@ export function ThemeSelect({ native = true }: ThemeSelectProps) {
|
||||
// 네이티브 select
|
||||
if (native) {
|
||||
return (
|
||||
<div className="relative w-[140px]">
|
||||
<div className="relative min-w-[140px] w-auto">
|
||||
<div className="absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none z-10">
|
||||
<CurrentIcon className={`w-4 h-4 ${currentTheme?.color}`} />
|
||||
</div>
|
||||
@@ -56,7 +56,7 @@ export function ThemeSelect({ native = true }: ThemeSelectProps) {
|
||||
// Radix UI 모달 select
|
||||
return (
|
||||
<Select value={theme} onValueChange={(value) => setTheme(value as "light" | "dark" | "senior")}>
|
||||
<SelectTrigger className="w-[140px] rounded-xl border-border/50 bg-background/50 backdrop-blur">
|
||||
<SelectTrigger className="min-w-[140px] w-auto rounded-xl border-border/50 bg-background/50 backdrop-blur">
|
||||
<div className="flex items-center gap-2">
|
||||
<CurrentIcon className={`w-4 h-4 ${currentTheme?.color}`} />
|
||||
<SelectValue>
|
||||
|
||||
@@ -43,6 +43,7 @@ import {
|
||||
SORT_OPTIONS,
|
||||
} from './types';
|
||||
import { formatNumber } from '@/lib/utils/amount';
|
||||
import { applyFilters, enumFilter } from '@/lib/utils/search';
|
||||
import { deleteBadDebt, toggleBadDebt } from './actions';
|
||||
|
||||
// ===== 테이블 컬럼 정의 =====
|
||||
@@ -235,19 +236,10 @@ export function BadDebtCollection({ initialData, initialSummary }: BadDebtCollec
|
||||
// 커스텀 필터 함수
|
||||
customFilterFn: (items) => {
|
||||
if (!items || items.length === 0) return items;
|
||||
let result = [...items];
|
||||
|
||||
// 거래처 필터
|
||||
if (vendorFilter !== 'all') {
|
||||
result = result.filter((item) => item.vendorId === vendorFilter);
|
||||
}
|
||||
|
||||
// 상태 필터
|
||||
if (statusFilter !== 'all') {
|
||||
result = result.filter((item) => item.status === statusFilter);
|
||||
}
|
||||
|
||||
return result;
|
||||
return applyFilters([...items], [
|
||||
enumFilter('vendorId', vendorFilter),
|
||||
enumFilter('status', statusFilter),
|
||||
]);
|
||||
},
|
||||
|
||||
// 커스텀 정렬 함수
|
||||
@@ -275,7 +267,7 @@ export function BadDebtCollection({ initialData, initialSummary }: BadDebtCollec
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
{/* 거래처 필터 */}
|
||||
<Select value={vendorFilter} onValueChange={setVendorFilter}>
|
||||
<SelectTrigger className="w-[150px]">
|
||||
<SelectTrigger className="min-w-[150px] w-auto">
|
||||
<SelectValue placeholder="전체" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -290,7 +282,7 @@ export function BadDebtCollection({ initialData, initialSummary }: BadDebtCollec
|
||||
|
||||
{/* 상태 필터 */}
|
||||
<Select value={statusFilter} onValueChange={setStatusFilter}>
|
||||
<SelectTrigger className="w-[120px]">
|
||||
<SelectTrigger className="min-w-[120px] w-auto">
|
||||
<SelectValue placeholder="상태" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -307,7 +299,7 @@ export function BadDebtCollection({ initialData, initialSummary }: BadDebtCollec
|
||||
value={sortOption}
|
||||
onValueChange={(value) => setSortOption(value as SortOption)}
|
||||
>
|
||||
<SelectTrigger className="w-[120px]">
|
||||
<SelectTrigger className="min-w-[120px] w-auto">
|
||||
<SelectValue placeholder="정렬" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -88,38 +88,15 @@ export interface BadDebtRecord {
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
// ===== 상태 라벨 =====
|
||||
export const COLLECTION_STATUS_LABELS: Record<CollectionStatus, string> = {
|
||||
collecting: '추심중',
|
||||
legalAction: '법적조치',
|
||||
recovered: '회수완료',
|
||||
badDebt: '대손처리',
|
||||
};
|
||||
// ===== 상태 설정 (status-config 기반) =====
|
||||
import { BAD_DEBT_COLLECTION_STATUS_CONFIG } from '@/lib/utils/status-config';
|
||||
|
||||
// ===== 상태 필터 옵션 =====
|
||||
export const STATUS_FILTER_OPTIONS = [
|
||||
{ value: 'all', label: '전체' },
|
||||
{ value: 'collecting', label: '추심중' },
|
||||
{ value: 'legalAction', label: '법적조치' },
|
||||
{ value: 'recovered', label: '회수완료' },
|
||||
{ value: 'badDebt', label: '대손처리' },
|
||||
] as const;
|
||||
|
||||
// ===== 상태 셀렉트 옵션 (상세 페이지용, 전체 제외) =====
|
||||
export const STATUS_SELECT_OPTIONS = [
|
||||
{ value: 'collecting', label: '추심중' },
|
||||
{ value: 'legalAction', label: '법적조치' },
|
||||
{ value: 'recovered', label: '회수완료' },
|
||||
{ value: 'badDebt', label: '대손처리' },
|
||||
] as const;
|
||||
|
||||
// ===== 상태 Badge 스타일 =====
|
||||
export const STATUS_BADGE_STYLES: Record<CollectionStatus, string> = {
|
||||
collecting: 'border-orange-300 text-orange-600 bg-orange-50',
|
||||
legalAction: 'border-red-300 text-red-600 bg-red-50',
|
||||
recovered: 'border-green-300 text-green-600 bg-green-50',
|
||||
badDebt: 'border-gray-300 text-gray-600 bg-gray-50',
|
||||
};
|
||||
export const COLLECTION_STATUS_LABELS = BAD_DEBT_COLLECTION_STATUS_CONFIG.STATUS_LABELS;
|
||||
export const STATUS_FILTER_OPTIONS = BAD_DEBT_COLLECTION_STATUS_CONFIG.STATUS_OPTIONS;
|
||||
export const STATUS_SELECT_OPTIONS = BAD_DEBT_COLLECTION_STATUS_CONFIG.STATUS_OPTIONS.filter(
|
||||
(o): o is { value: CollectionStatus; label: string } => o.value !== 'all'
|
||||
);
|
||||
export const STATUS_BADGE_STYLES = BAD_DEBT_COLLECTION_STATUS_CONFIG.STATUS_STYLES;
|
||||
|
||||
// ===== 정렬 옵션 =====
|
||||
export const SORT_OPTIONS = [
|
||||
|
||||
@@ -373,7 +373,7 @@ export function BankTransactionInquiry() {
|
||||
value={accountCategoryFilter}
|
||||
onValueChange={(v) => setAccountCategoryFilter(v as AccountCategoryFilter)}
|
||||
>
|
||||
<SelectTrigger className="w-[120px]">
|
||||
<SelectTrigger className="min-w-[120px] w-auto">
|
||||
<SelectValue placeholder="구분" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -390,7 +390,7 @@ export function BankTransactionInquiry() {
|
||||
value={financialInstitutionFilter}
|
||||
onValueChange={setFinancialInstitutionFilter}
|
||||
>
|
||||
<SelectTrigger className="w-[140px]">
|
||||
<SelectTrigger className="min-w-[140px] w-auto">
|
||||
<SelectValue placeholder="금융기관" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -405,7 +405,7 @@ export function BillManagementClient({
|
||||
</div>
|
||||
</RadioGroup>
|
||||
<Select value={statusFilter} onValueChange={setStatusFilter}>
|
||||
<SelectTrigger className="w-[100px]">
|
||||
<SelectTrigger className="min-w-[100px] w-auto">
|
||||
<SelectValue placeholder="상태" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -427,7 +427,7 @@ export function BillManagementClient({
|
||||
tableHeaderActions: (
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<Select value={vendorFilter} onValueChange={setVendorFilter}>
|
||||
<SelectTrigger className="w-[120px]">
|
||||
<SelectTrigger className="min-w-[120px] w-auto">
|
||||
<SelectValue placeholder="거래처명" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -440,7 +440,7 @@ export function BillManagementClient({
|
||||
</Select>
|
||||
|
||||
<Select value={billTypeFilter} onValueChange={(value) => { setBillTypeFilter(value); loadData(1); }}>
|
||||
<SelectTrigger className="w-[100px]">
|
||||
<SelectTrigger className="min-w-[100px] w-auto">
|
||||
<SelectValue placeholder="구분" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -453,7 +453,7 @@ export function BillManagementClient({
|
||||
</Select>
|
||||
|
||||
<Select value={statusFilter} onValueChange={(value) => { setStatusFilter(value); loadData(1); }}>
|
||||
<SelectTrigger className="w-[110px]">
|
||||
<SelectTrigger className="min-w-[110px] w-auto">
|
||||
<SelectValue placeholder="보관중" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -305,7 +305,7 @@ export function BillManagement({ initialVendorId, initialBillType }: BillManagem
|
||||
<div className="flex items-center gap-4">
|
||||
{/* 상태 필터 */}
|
||||
<Select value={statusFilter} onValueChange={setStatusFilter}>
|
||||
<SelectTrigger className="w-[110px]">
|
||||
<SelectTrigger className="min-w-[110px] w-auto">
|
||||
<SelectValue placeholder="보관중" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -350,7 +350,7 @@ export function BillManagement({ initialVendorId, initialBillType }: BillManagem
|
||||
|
||||
{/* 거래처명 필터 */}
|
||||
<Select value={vendorFilter} onValueChange={setVendorFilter}>
|
||||
<SelectTrigger className="w-[120px]">
|
||||
<SelectTrigger className="min-w-[120px] w-auto">
|
||||
<SelectValue placeholder="거래처명" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -54,6 +54,7 @@ import { ManualInputModal } from './ManualInputModal';
|
||||
import { JournalEntryModal } from './JournalEntryModal';
|
||||
import { isNextRedirectError } from '@/lib/utils/redirect-error';
|
||||
import { formatNumber } from '@/lib/utils/amount';
|
||||
import { filterByEnum } from '@/lib/utils/search';
|
||||
|
||||
// ===== 테이블 컬럼 정의 (체크박스/No. 제외 15개) =====
|
||||
const tableColumns = [
|
||||
@@ -177,11 +178,7 @@ export function CardTransactionInquiry() {
|
||||
|
||||
// ===== 필터링된 데이터 =====
|
||||
const filteredData = useMemo(() => {
|
||||
let result = [...data];
|
||||
if (cardFilter !== 'all') {
|
||||
result = result.filter(item => item.cardName === cardFilter);
|
||||
}
|
||||
return result;
|
||||
return filterByEnum(data, 'cardName', cardFilter);
|
||||
}, [data, cardFilter]);
|
||||
|
||||
// ===== 인라인 편집 핸들러 =====
|
||||
@@ -382,7 +379,7 @@ export function CardTransactionInquiry() {
|
||||
)}
|
||||
</Button>
|
||||
<Select value={cardFilter} onValueChange={setCardFilter}>
|
||||
<SelectTrigger className="w-[120px] h-8 text-sm">
|
||||
<SelectTrigger className="min-w-[120px] w-auto h-8 text-sm">
|
||||
<SelectValue placeholder="카드" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -392,7 +389,7 @@ export function CardTransactionInquiry() {
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Select value={sortOption} onValueChange={(v) => setSortOption(v as SortOption)}>
|
||||
<SelectTrigger className="w-[110px] h-8 text-sm">
|
||||
<SelectTrigger className="min-w-[110px] w-auto h-8 text-sm">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -489,7 +486,7 @@ export function CardTransactionInquiry() {
|
||||
value={getEditValue(item.id, 'deductionType', item.deductionType)}
|
||||
onValueChange={(v) => handleInlineEdit(item.id, 'deductionType', v)}
|
||||
>
|
||||
<SelectTrigger className="h-7 text-xs w-[88px]">
|
||||
<SelectTrigger className="h-7 text-xs min-w-[88px] w-auto">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -547,7 +544,7 @@ export function CardTransactionInquiry() {
|
||||
value={getEditValue(item.id, 'accountSubject', item.accountSubject) || 'none'}
|
||||
onValueChange={(v) => handleInlineEdit(item.id, 'accountSubject', v === 'none' ? '' : v)}
|
||||
>
|
||||
<SelectTrigger className="h-7 text-xs w-[90px]">
|
||||
<SelectTrigger className="h-7 text-xs min-w-[90px] w-auto">
|
||||
<SelectValue placeholder="선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -37,17 +37,11 @@ export interface DailyAccountItem {
|
||||
currency: 'KRW' | 'USD';
|
||||
}
|
||||
|
||||
/** 매칭 상태 라벨 */
|
||||
export const MATCH_STATUS_LABELS: Record<MatchStatus, string> = {
|
||||
matched: '매칭',
|
||||
unmatched: '비매칭',
|
||||
};
|
||||
/** 매칭 상태 설정 (status-config 기반) */
|
||||
import { MATCH_STATUS_CONFIG } from '@/lib/utils/status-config';
|
||||
|
||||
/** 매칭 상태 색상 */
|
||||
export const MATCH_STATUS_COLORS: Record<MatchStatus, string> = {
|
||||
matched: 'bg-green-100 text-green-700',
|
||||
unmatched: 'bg-orange-100 text-orange-700',
|
||||
};
|
||||
export const MATCH_STATUS_LABELS = MATCH_STATUS_CONFIG.STATUS_LABELS;
|
||||
export const MATCH_STATUS_COLORS = MATCH_STATUS_CONFIG.STATUS_STYLES;
|
||||
|
||||
/**
|
||||
* 일일 일보 요약 데이터
|
||||
|
||||
@@ -71,6 +71,7 @@ import {
|
||||
} from './types';
|
||||
import { deleteDeposit, updateDepositTypes, getDeposits } from './actions';
|
||||
import { formatNumber } from '@/lib/utils/amount';
|
||||
import { applyFilters, textFilter, enumFilter } from '@/lib/utils/search';
|
||||
import { toast } from 'sonner';
|
||||
import { useDateRange } from '@/hooks';
|
||||
import {
|
||||
@@ -254,31 +255,14 @@ export function DepositManagement({ initialData, initialPagination }: DepositMan
|
||||
);
|
||||
},
|
||||
|
||||
// 커스텀 필터 함수 (인라인 필터 사용)
|
||||
customFilterFn: (items, filterValues) => {
|
||||
// 커스텀 필터 함수
|
||||
customFilterFn: (items) => {
|
||||
if (!items || items.length === 0) return items;
|
||||
return items.filter((item) => {
|
||||
// 검색어 필터
|
||||
if (searchQuery) {
|
||||
const search = searchQuery.toLowerCase();
|
||||
const matchesSearch =
|
||||
item.depositorName.toLowerCase().includes(search) ||
|
||||
item.accountName.toLowerCase().includes(search) ||
|
||||
(item.note?.toLowerCase().includes(search) || false) ||
|
||||
(item.vendorName?.toLowerCase().includes(search) || false);
|
||||
if (!matchesSearch) return false;
|
||||
}
|
||||
|
||||
// 거래처 필터
|
||||
if (vendorFilter !== 'all' && item.vendorName !== vendorFilter) {
|
||||
return false;
|
||||
}
|
||||
// 입금유형 필터
|
||||
if (depositTypeFilter !== 'all' && item.depositType !== depositTypeFilter) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return applyFilters(items, [
|
||||
textFilter(searchQuery, ['depositorName', 'accountName', 'note', 'vendorName']),
|
||||
enumFilter('vendorName', vendorFilter),
|
||||
enumFilter('depositType', depositTypeFilter),
|
||||
]);
|
||||
},
|
||||
|
||||
// 커스텀 정렬 함수
|
||||
@@ -326,7 +310,7 @@ export function DepositManagement({ initialData, initialPagination }: DepositMan
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm font-medium text-gray-700 whitespace-nowrap">계정과목명</span>
|
||||
<Select value={selectedAccountSubject} onValueChange={setSelectedAccountSubject}>
|
||||
<SelectTrigger className="w-[120px]">
|
||||
<SelectTrigger className="min-w-[120px] w-auto">
|
||||
<SelectValue placeholder="선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -373,7 +357,7 @@ export function DepositManagement({ initialData, initialPagination }: DepositMan
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
{/* 거래처 필터 */}
|
||||
<Select value={vendorFilter} onValueChange={setVendorFilter}>
|
||||
<SelectTrigger className="w-[140px]">
|
||||
<SelectTrigger className="min-w-[140px] w-auto">
|
||||
<SelectValue placeholder="거래처" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -387,7 +371,7 @@ export function DepositManagement({ initialData, initialPagination }: DepositMan
|
||||
|
||||
{/* 입금유형 필터 */}
|
||||
<Select value={depositTypeFilter} onValueChange={setDepositTypeFilter}>
|
||||
<SelectTrigger className="w-[130px]">
|
||||
<SelectTrigger className="min-w-[130px] w-auto">
|
||||
<SelectValue placeholder="입금유형" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -401,7 +385,7 @@ export function DepositManagement({ initialData, initialPagination }: DepositMan
|
||||
|
||||
{/* 정렬 */}
|
||||
<Select value={sortOption} onValueChange={(value) => setSortOption(value as SortOption)}>
|
||||
<SelectTrigger className="w-[120px]">
|
||||
<SelectTrigger className="min-w-[120px] w-auto">
|
||||
<SelectValue placeholder="정렬" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -69,25 +69,11 @@ export type DepositStatus =
|
||||
| 'error' // 오류
|
||||
| 'confirmed'; // 확정완료
|
||||
|
||||
export const DEPOSIT_STATUS_LABELS: Record<DepositStatus, string> = {
|
||||
inputWaiting: '입력대기',
|
||||
requesting: '신청중',
|
||||
rejected: '반려',
|
||||
pending: '보류',
|
||||
incomplete: '미완',
|
||||
error: '오류',
|
||||
confirmed: '확정완료',
|
||||
};
|
||||
// 입금 상태 설정 (status-config 기반)
|
||||
import { DEPOSIT_STATUS_CONFIG } from '@/lib/utils/status-config';
|
||||
|
||||
export const DEPOSIT_STATUS_COLORS: Record<DepositStatus, string> = {
|
||||
inputWaiting: 'bg-yellow-100 text-yellow-800',
|
||||
requesting: 'bg-blue-100 text-blue-800',
|
||||
rejected: 'bg-red-100 text-red-800',
|
||||
pending: 'bg-gray-100 text-gray-800',
|
||||
incomplete: 'bg-orange-100 text-orange-800',
|
||||
error: 'bg-red-100 text-red-800',
|
||||
confirmed: 'bg-green-100 text-green-800',
|
||||
};
|
||||
export const DEPOSIT_STATUS_LABELS = DEPOSIT_STATUS_CONFIG.STATUS_LABELS;
|
||||
export const DEPOSIT_STATUS_COLORS = DEPOSIT_STATUS_CONFIG.STATUS_STYLES;
|
||||
|
||||
// ===== 상태 탭 옵션 =====
|
||||
export const STATUS_TAB_OPTIONS: { value: DepositStatus | 'all'; label: string }[] = [
|
||||
|
||||
@@ -92,6 +92,7 @@ import {
|
||||
} from './types';
|
||||
import { extractUniqueOptions } from '../shared';
|
||||
import { formatNumber } from '@/lib/utils/amount';
|
||||
import { applyFilters, textFilter, enumFilter } from '@/lib/utils/search';
|
||||
|
||||
// ===== 테이블 행 타입 (데이터 + 그룹 헤더 + 소계) =====
|
||||
type RowType = 'data' | 'monthHeader' | 'monthSubtotal' | 'totalExpense' | 'expectedBalance' | 'finalBalance';
|
||||
@@ -305,16 +306,10 @@ export function ExpectedExpenseManagement({
|
||||
|
||||
// ===== 필터링된 원본 데이터 =====
|
||||
const filteredRawData = useMemo(() => {
|
||||
let result = data.filter(item =>
|
||||
item.vendorName.includes(searchQuery) ||
|
||||
item.accountSubject.includes(searchQuery) ||
|
||||
item.note.includes(searchQuery)
|
||||
);
|
||||
|
||||
// 거래처 필터
|
||||
if (vendorFilter !== 'all') {
|
||||
result = result.filter(item => item.vendorName === vendorFilter);
|
||||
}
|
||||
const result = applyFilters(data, [
|
||||
textFilter(searchQuery, ['vendorName', 'accountSubject', 'note']),
|
||||
enumFilter('vendorName', vendorFilter),
|
||||
]);
|
||||
|
||||
// 정렬 적용
|
||||
switch (sortOption) {
|
||||
@@ -928,7 +923,7 @@ export function ExpectedExpenseManagement({
|
||||
<div className="flex items-center gap-2">
|
||||
{/* 거래처 필터 */}
|
||||
<Select value={vendorFilter} onValueChange={setVendorFilter}>
|
||||
<SelectTrigger className="w-[140px] h-8 text-sm">
|
||||
<SelectTrigger className="min-w-[140px] w-auto h-8 text-sm">
|
||||
<SelectValue placeholder="전체" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -942,7 +937,7 @@ export function ExpectedExpenseManagement({
|
||||
|
||||
{/* 정렬 필터 (최신순/등록순) */}
|
||||
<Select value={sortOption} onValueChange={(value) => setSortOption(value as SortOption)}>
|
||||
<SelectTrigger className="w-[100px] h-8 text-sm">
|
||||
<SelectTrigger className="min-w-[100px] w-auto h-8 text-sm">
|
||||
<SelectValue placeholder="최신순" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -1208,7 +1203,7 @@ export function ExpectedExpenseManagement({
|
||||
value={formData.paymentStatus}
|
||||
onValueChange={(value) => setFormData(prev => ({ ...prev, paymentStatus: value as PaymentStatus }))}
|
||||
>
|
||||
<SelectTrigger className="w-[200px]">
|
||||
<SelectTrigger className="min-w-[200px] w-auto">
|
||||
<SelectValue placeholder="결제상태 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -76,22 +76,11 @@ export const TRANSACTION_TYPE_FILTER_OPTIONS = [
|
||||
{ value: 'other', label: '기타' },
|
||||
];
|
||||
|
||||
// 지급상태 레이블
|
||||
export const PAYMENT_STATUS_LABELS: Record<PaymentStatus, string> = {
|
||||
pending: '미지급',
|
||||
partial: '부분지급',
|
||||
paid: '지급완료',
|
||||
overdue: '연체',
|
||||
};
|
||||
// 지급상태 설정 (status-config 기반)
|
||||
import { PAYMENT_STATUS_CONFIG } from '@/lib/utils/status-config';
|
||||
|
||||
// 지급상태 필터 옵션
|
||||
export const PAYMENT_STATUS_FILTER_OPTIONS = [
|
||||
{ value: 'all', label: '전체' },
|
||||
{ value: 'pending', label: '미지급' },
|
||||
{ value: 'partial', label: '부분지급' },
|
||||
{ value: 'paid', label: '지급완료' },
|
||||
{ value: 'overdue', label: '연체' },
|
||||
];
|
||||
export const PAYMENT_STATUS_LABELS = PAYMENT_STATUS_CONFIG.STATUS_LABELS;
|
||||
export const PAYMENT_STATUS_FILTER_OPTIONS = PAYMENT_STATUS_CONFIG.STATUS_OPTIONS;
|
||||
|
||||
// 정렬 옵션
|
||||
export const SORT_OPTIONS: { value: SortOption; label: string }[] = [
|
||||
|
||||
@@ -259,7 +259,7 @@ export function AccountSubjectSettingModal({
|
||||
className="max-w-[250px] h-9 text-sm"
|
||||
/>
|
||||
<Select value={categoryFilter} onValueChange={setCategoryFilter}>
|
||||
<SelectTrigger className="w-[100px] h-9 text-sm">
|
||||
<SelectTrigger className="min-w-[100px] w-auto h-9 text-sm">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -47,6 +47,7 @@ import {
|
||||
} from './actions';
|
||||
import { isNextRedirectError } from '@/lib/utils/redirect-error';
|
||||
import { formatNumber as formatAmount } from '@/lib/utils/amount';
|
||||
import { applyFilters, enumFilter } from '@/lib/utils/search';
|
||||
import { useDateRange } from '@/hooks';
|
||||
|
||||
// ===== 테이블 컬럼 정의 (체크박스/No. 제외) =====
|
||||
@@ -201,7 +202,7 @@ export function GiftCertificateManagement() {
|
||||
value={statusFilter}
|
||||
onValueChange={setStatusFilter}
|
||||
>
|
||||
<SelectTrigger className="w-[120px]">
|
||||
<SelectTrigger className="min-w-[120px] w-auto">
|
||||
<SelectValue placeholder="상태" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -218,7 +219,7 @@ export function GiftCertificateManagement() {
|
||||
value={entertainmentFilter}
|
||||
onValueChange={setEntertainmentFilter}
|
||||
>
|
||||
<SelectTrigger className="w-[130px]">
|
||||
<SelectTrigger className="min-w-[130px] w-auto">
|
||||
<SelectValue placeholder="접대비" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -234,14 +235,10 @@ export function GiftCertificateManagement() {
|
||||
|
||||
// 클라이언트 사이드 커스텀 필터 (상태 + 접대비)
|
||||
customFilterFn: (items) => {
|
||||
let filtered = items;
|
||||
if (statusFilter !== 'all') {
|
||||
filtered = filtered.filter((item) => item.status === statusFilter);
|
||||
}
|
||||
if (entertainmentFilter !== 'all') {
|
||||
filtered = filtered.filter((item) => item.entertainmentExpense === entertainmentFilter);
|
||||
}
|
||||
return filtered;
|
||||
return applyFilters(items, [
|
||||
enumFilter('status', statusFilter),
|
||||
enumFilter('entertainmentExpense', entertainmentFilter),
|
||||
]);
|
||||
},
|
||||
|
||||
// 통계 카드 4개 (기획서: 전체 상품권, 보유 상품권, 사용 상품권, 접대비 해당)
|
||||
|
||||
@@ -368,7 +368,7 @@ export function PurchaseManagement() {
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm font-medium text-gray-700 whitespace-nowrap">계정과목명</span>
|
||||
<Select value={selectedAccountSubject} onValueChange={setSelectedAccountSubject}>
|
||||
<SelectTrigger className="w-[120px]">
|
||||
<SelectTrigger className="min-w-[120px] w-auto">
|
||||
<SelectValue placeholder="선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
} from './types';
|
||||
import { getReceivablesList, getReceivablesSummary, updateOverdueStatus, updateMemos, exportReceivablesExcel } from './actions';
|
||||
import { toast } from 'sonner';
|
||||
import { filterByText } from '@/lib/utils/search';
|
||||
import { isNextRedirectError } from '@/lib/utils/redirect-error';
|
||||
import { usePermission } from '@/hooks/usePermission';
|
||||
|
||||
@@ -138,10 +139,7 @@ export function ReceivablesStatus({ highlightVendorId, initialData, initialSumma
|
||||
|
||||
// ===== 필터링된 데이터 =====
|
||||
const filteredData = useMemo(() => {
|
||||
if (!searchQuery) return data;
|
||||
return data.filter(item =>
|
||||
item.vendorName.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
);
|
||||
return filterByText(data, searchQuery, ['vendorName']);
|
||||
}, [data, searchQuery]);
|
||||
|
||||
// ===== 정렬된 데이터 =====
|
||||
@@ -359,7 +357,7 @@ export function ReceivablesStatus({ highlightVendorId, initialData, initialSumma
|
||||
value={String(selectedYear)}
|
||||
onValueChange={(value) => setSelectedYear(Number(value))}
|
||||
>
|
||||
<SelectTrigger className="w-[120px]">
|
||||
<SelectTrigger className="min-w-[120px] w-auto">
|
||||
<SelectValue placeholder="연도 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -379,7 +377,7 @@ export function ReceivablesStatus({ highlightVendorId, initialData, initialSumma
|
||||
value={sortOption}
|
||||
onValueChange={(value) => setSortOption(value as SortOption)}
|
||||
>
|
||||
<SelectTrigger className="w-[140px]">
|
||||
<SelectTrigger className="min-w-[140px] w-auto">
|
||||
<SelectValue placeholder="정렬 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -72,6 +72,7 @@ import {
|
||||
type SortDirection,
|
||||
} from '../shared';
|
||||
import { formatNumber } from '@/lib/utils/amount';
|
||||
import { applyFilters, enumFilter } from '@/lib/utils/search';
|
||||
|
||||
// ===== 테이블 컬럼 정의 =====
|
||||
const tableColumns = [
|
||||
@@ -321,28 +322,22 @@ export function SalesManagement({ initialData, initialPagination }: SalesManagem
|
||||
// 검색은 searchFilter에서 처리하므로 여기서는 필터만 처리
|
||||
customFilterFn: (items, fv) => {
|
||||
if (!items || items.length === 0) return items;
|
||||
return items.filter((item) => {
|
||||
const vendorVal = fv.vendor as string;
|
||||
const salesTypeVal = fv.salesType as string;
|
||||
const issuanceVal = fv.issuance as string;
|
||||
const issuanceVal = fv.issuance as string;
|
||||
|
||||
// 거래처 필터
|
||||
if (vendorVal && vendorVal !== 'all' && item.vendorName !== vendorVal) {
|
||||
return false;
|
||||
}
|
||||
// 매출유형 필터
|
||||
if (salesTypeVal && salesTypeVal !== 'all' && item.salesType !== salesTypeVal) {
|
||||
return false;
|
||||
}
|
||||
// 발행여부 필터
|
||||
if (issuanceVal === 'taxInvoicePending' && item.taxInvoiceIssued) {
|
||||
return false;
|
||||
}
|
||||
if (issuanceVal === 'transactionStatementPending' && item.transactionStatementIssued) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
let result = applyFilters(items, [
|
||||
enumFilter('vendorName', fv.vendor as string),
|
||||
enumFilter('salesType', fv.salesType as string),
|
||||
]);
|
||||
|
||||
// 발행여부 필터 (특수 로직 - enumFilter로 대체 불가)
|
||||
if (issuanceVal === 'taxInvoicePending') {
|
||||
result = result.filter(item => !item.taxInvoiceIssued);
|
||||
}
|
||||
if (issuanceVal === 'transactionStatementPending') {
|
||||
result = result.filter(item => !item.transactionStatementIssued);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
// 커스텀 정렬 함수
|
||||
@@ -368,7 +363,7 @@ export function SalesManagement({ initialData, initialPagination }: SalesManagem
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm font-medium text-gray-700 whitespace-nowrap">계정과목명</span>
|
||||
<Select value={selectedAccountSubject} onValueChange={setSelectedAccountSubject}>
|
||||
<SelectTrigger className="w-[120px]">
|
||||
<SelectTrigger className="min-w-[120px] w-auto">
|
||||
<SelectValue placeholder="선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -244,7 +244,7 @@ export function TaxInvoiceIssuancePage({
|
||||
value={filters.dateType}
|
||||
onValueChange={(v) => updateFilter('dateType', v)}
|
||||
>
|
||||
<SelectTrigger className="w-full lg:w-[120px] h-9">
|
||||
<SelectTrigger className="w-full lg:min-w-[120px] lg:w-auto h-9">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -298,7 +298,7 @@ export function TaxInvoiceIssuancePage({
|
||||
value={filters.status}
|
||||
onValueChange={(v) => updateFilter('status', v)}
|
||||
>
|
||||
<SelectTrigger className="w-full sm:w-[130px] h-9">
|
||||
<SelectTrigger className="w-full sm:min-w-[130px] sm:w-auto h-9">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -314,7 +314,7 @@ export function TaxInvoiceIssuancePage({
|
||||
value={filters.sortBy}
|
||||
onValueChange={(v) => updateFilter('sortBy', v)}
|
||||
>
|
||||
<SelectTrigger className="w-full sm:w-[130px] h-9">
|
||||
<SelectTrigger className="w-full sm:min-w-[130px] sm:w-auto h-9">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -330,7 +330,7 @@ export function TaxInvoiceIssuancePage({
|
||||
value={filters.sortOrder}
|
||||
onValueChange={(v) => updateFilter('sortOrder', v)}
|
||||
>
|
||||
<SelectTrigger className="w-full sm:w-[130px] h-9">
|
||||
<SelectTrigger className="w-full sm:min-w-[130px] sm:w-auto h-9">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -284,7 +284,7 @@ export function TaxInvoiceManagement() {
|
||||
{/* Row1: 일자타입 + 날짜범위 + 분기 버튼 + 조회 */}
|
||||
<div className="flex flex-col lg:flex-row lg:items-center gap-2">
|
||||
<Select value={dateType} onValueChange={setDateType}>
|
||||
<SelectTrigger className="w-full lg:w-[120px] h-9 text-sm">
|
||||
<SelectTrigger className="w-full lg:min-w-[120px] lg:w-auto h-9 text-sm">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
import { useState, useMemo, useCallback } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { formatNumber } from '@/lib/utils/amount';
|
||||
import { applyFilters, textFilter, enumFilter } from '@/lib/utils/search';
|
||||
import {
|
||||
Building2,
|
||||
Pencil,
|
||||
@@ -116,31 +117,13 @@ export function VendorManagementClient({ initialData, initialTotal }: VendorMana
|
||||
|
||||
// ===== 필터링된 데이터 =====
|
||||
const filteredData = useMemo(() => {
|
||||
let result = data.filter(item =>
|
||||
item.vendorName.includes(searchQuery) ||
|
||||
item.vendorCode.includes(searchQuery) ||
|
||||
item.businessNumber.includes(searchQuery)
|
||||
);
|
||||
|
||||
// 구분 필터
|
||||
if (categoryFilter !== 'all') {
|
||||
result = result.filter(item => item.category === categoryFilter);
|
||||
}
|
||||
|
||||
// 신용등급 필터
|
||||
if (creditRatingFilter !== 'all') {
|
||||
result = result.filter(item => item.creditRating === creditRatingFilter);
|
||||
}
|
||||
|
||||
// 거래등급 필터
|
||||
if (transactionGradeFilter !== 'all') {
|
||||
result = result.filter(item => item.transactionGrade === transactionGradeFilter);
|
||||
}
|
||||
|
||||
// 악성채권 필터
|
||||
if (badDebtFilter !== 'all') {
|
||||
result = result.filter(item => item.badDebtStatus === badDebtFilter);
|
||||
}
|
||||
const result = applyFilters(data, [
|
||||
textFilter(searchQuery, ['vendorName', 'vendorCode', 'businessNumber']),
|
||||
enumFilter('category', categoryFilter),
|
||||
enumFilter('creditRating', creditRatingFilter),
|
||||
enumFilter('transactionGrade', transactionGradeFilter),
|
||||
enumFilter('badDebtStatus', badDebtFilter),
|
||||
]);
|
||||
|
||||
// 정렬
|
||||
switch (sortOption) {
|
||||
@@ -450,7 +433,7 @@ export function VendorManagementClient({ initialData, initialTotal }: VendorMana
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
{/* 구분 필터 */}
|
||||
<Select value={categoryFilter} onValueChange={setCategoryFilter}>
|
||||
<SelectTrigger className="w-[120px]">
|
||||
<SelectTrigger className="min-w-[120px] w-auto">
|
||||
<SelectValue placeholder="구분" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -464,7 +447,7 @@ export function VendorManagementClient({ initialData, initialTotal }: VendorMana
|
||||
|
||||
{/* 신용등급 필터 */}
|
||||
<Select value={creditRatingFilter} onValueChange={setCreditRatingFilter}>
|
||||
<SelectTrigger className="w-[110px]">
|
||||
<SelectTrigger className="min-w-[110px] w-auto">
|
||||
<SelectValue placeholder="신용등급" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -478,7 +461,7 @@ export function VendorManagementClient({ initialData, initialTotal }: VendorMana
|
||||
|
||||
{/* 거래등급 필터 */}
|
||||
<Select value={transactionGradeFilter} onValueChange={setTransactionGradeFilter}>
|
||||
<SelectTrigger className="w-[120px]">
|
||||
<SelectTrigger className="min-w-[120px] w-auto">
|
||||
<SelectValue placeholder="거래등급" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -492,7 +475,7 @@ export function VendorManagementClient({ initialData, initialTotal }: VendorMana
|
||||
|
||||
{/* 악성채권 필터 */}
|
||||
<Select value={badDebtFilter} onValueChange={setBadDebtFilter}>
|
||||
<SelectTrigger className="w-[110px]">
|
||||
<SelectTrigger className="min-w-[110px] w-auto">
|
||||
<SelectValue placeholder="악성채권" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -506,7 +489,7 @@ export function VendorManagementClient({ initialData, initialTotal }: VendorMana
|
||||
|
||||
{/* 정렬 */}
|
||||
<Select value={sortOption} onValueChange={(value) => setSortOption(value as SortOption)}>
|
||||
<SelectTrigger className="w-[150px]">
|
||||
<SelectTrigger className="min-w-[150px] w-auto">
|
||||
<SelectValue placeholder="정렬" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -70,6 +70,7 @@ import {
|
||||
} from './types';
|
||||
import { deleteWithdrawal, updateWithdrawalTypes, getWithdrawals } from './actions';
|
||||
import { formatNumber } from '@/lib/utils/amount';
|
||||
import { applyFilters, textFilter, enumFilter } from '@/lib/utils/search';
|
||||
import { toast } from 'sonner';
|
||||
import { useDateRange } from '@/hooks';
|
||||
import {
|
||||
@@ -291,32 +292,13 @@ export function WithdrawalManagement({ initialData, initialPagination }: Withdra
|
||||
// 검색창 숨김 (dateRangeSelector extraActions로 렌더링)
|
||||
hideSearch: true,
|
||||
|
||||
// 커스텀 필터 함수 (검색 + 필터)
|
||||
// 커스텀 필터 함수
|
||||
customFilterFn: (items) => {
|
||||
return items.filter((item) => {
|
||||
// 검색어 필터
|
||||
if (searchQuery) {
|
||||
const search = searchQuery.toLowerCase();
|
||||
const matchesSearch =
|
||||
item.recipientName.toLowerCase().includes(search) ||
|
||||
item.accountName.toLowerCase().includes(search) ||
|
||||
item.note.toLowerCase().includes(search) ||
|
||||
item.vendorName.toLowerCase().includes(search);
|
||||
if (!matchesSearch) return false;
|
||||
}
|
||||
|
||||
// 거래처 필터
|
||||
if (vendorFilter !== 'all' && item.vendorName !== vendorFilter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 출금유형 필터
|
||||
if (withdrawalTypeFilter !== 'all' && item.withdrawalType !== withdrawalTypeFilter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
return applyFilters(items, [
|
||||
textFilter(searchQuery, ['recipientName', 'accountName', 'note', 'vendorName']),
|
||||
enumFilter('vendorName', vendorFilter),
|
||||
enumFilter('withdrawalType', withdrawalTypeFilter),
|
||||
]);
|
||||
},
|
||||
|
||||
// 커스텀 정렬 함수
|
||||
@@ -344,7 +326,7 @@ export function WithdrawalManagement({ initialData, initialPagination }: Withdra
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm font-medium text-gray-700 whitespace-nowrap">계정과목명</span>
|
||||
<Select value={selectedAccountSubject} onValueChange={setSelectedAccountSubject}>
|
||||
<SelectTrigger className="w-[120px]">
|
||||
<SelectTrigger className="min-w-[120px] w-auto">
|
||||
<SelectValue placeholder="선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -384,7 +366,7 @@ export function WithdrawalManagement({ initialData, initialPagination }: Withdra
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
{/* 거래처 필터 */}
|
||||
<Select value={vendorFilter} onValueChange={setVendorFilter}>
|
||||
<SelectTrigger className="w-[140px]">
|
||||
<SelectTrigger className="min-w-[140px] w-auto">
|
||||
<SelectValue placeholder="거래처" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -398,7 +380,7 @@ export function WithdrawalManagement({ initialData, initialPagination }: Withdra
|
||||
|
||||
{/* 출금유형 필터 */}
|
||||
<Select value={withdrawalTypeFilter} onValueChange={setWithdrawalTypeFilter}>
|
||||
<SelectTrigger className="w-[130px]">
|
||||
<SelectTrigger className="min-w-[130px] w-auto">
|
||||
<SelectValue placeholder="출금유형" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -412,7 +394,7 @@ export function WithdrawalManagement({ initialData, initialPagination }: Withdra
|
||||
|
||||
{/* 정렬 */}
|
||||
<Select value={sortOption} onValueChange={(value) => setSortOption(value as SortOption)}>
|
||||
<SelectTrigger className="w-[120px]">
|
||||
<SelectTrigger className="min-w-[120px] w-auto">
|
||||
<SelectValue placeholder="정렬" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -609,7 +609,7 @@ export function ApprovalBox() {
|
||||
value={filterOption}
|
||||
onValueChange={(value) => setFilterOption(value as FilterOption)}
|
||||
>
|
||||
<SelectTrigger className="w-[140px]">
|
||||
<SelectTrigger className="min-w-[140px] w-auto">
|
||||
<SelectValue placeholder="필터 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -625,7 +625,7 @@ export function ApprovalBox() {
|
||||
value={sortOption}
|
||||
onValueChange={(value) => setSortOption(value as SortOption)}
|
||||
>
|
||||
<SelectTrigger className="w-[140px]">
|
||||
<SelectTrigger className="min-w-[140px] w-auto">
|
||||
<SelectValue placeholder="정렬 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -588,7 +588,7 @@ export function DraftBox() {
|
||||
value={filterOption}
|
||||
onValueChange={(value) => setFilterOption(value as FilterOption)}
|
||||
>
|
||||
<SelectTrigger className="w-[120px]">
|
||||
<SelectTrigger className="min-w-[120px] w-auto">
|
||||
<SelectValue placeholder="필터 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -604,7 +604,7 @@ export function DraftBox() {
|
||||
value={sortOption}
|
||||
onValueChange={(value) => setSortOption(value as SortOption)}
|
||||
>
|
||||
<SelectTrigger className="w-[120px]">
|
||||
<SelectTrigger className="min-w-[120px] w-auto">
|
||||
<SelectValue placeholder="정렬 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -396,7 +396,7 @@ export function ReferenceBox() {
|
||||
<div className="flex items-center gap-2">
|
||||
{/* 필터 셀렉트박스 */}
|
||||
<Select value={filterOption} onValueChange={(value) => setFilterOption(value as FilterOption)}>
|
||||
<SelectTrigger className="w-[140px]">
|
||||
<SelectTrigger className="min-w-[140px] w-auto">
|
||||
<SelectValue placeholder="필터 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -410,7 +410,7 @@ export function ReferenceBox() {
|
||||
|
||||
{/* 정렬 셀렉트박스 */}
|
||||
<Select value={sortOption} onValueChange={(value) => setSortOption(value as SortOption)}>
|
||||
<SelectTrigger className="w-[140px]">
|
||||
<SelectTrigger className="min-w-[140px] w-auto">
|
||||
<SelectValue placeholder="정렬 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -145,7 +145,7 @@ export function BoardForm({ mode, board, onSubmit }: BoardFormProps) {
|
||||
value={formData.target}
|
||||
onValueChange={(value) => handleTargetChange(value as BoardTarget)}
|
||||
>
|
||||
<SelectTrigger id="target" className="w-[120px]">
|
||||
<SelectTrigger id="target" className="min-w-[120px] w-auto">
|
||||
<SelectValue placeholder="대상 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -563,7 +563,7 @@ const TableSection = ({ config }: { config: TableConfig }) => {
|
||||
value={filters[filter.key]}
|
||||
onValueChange={(value) => handleFilterChange(filter.key, value)}
|
||||
>
|
||||
<SelectTrigger className="h-8 w-auto min-w-[80px] text-xs">
|
||||
<SelectTrigger className="h-8 w-auto min-w-[80px] w-auto text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -244,7 +244,7 @@ export function CalendarSection({
|
||||
value={deptFilter}
|
||||
onValueChange={(value) => setDeptFilter(value as CalendarDeptFilterType)}
|
||||
>
|
||||
<SelectTrigger className="w-[80px] h-8">
|
||||
<SelectTrigger className="min-w-[80px] w-auto h-8">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -261,7 +261,7 @@ export function CalendarSection({
|
||||
value={taskFilter}
|
||||
onValueChange={(value) => setTaskFilter(value as CalendarTaskFilterType)}
|
||||
>
|
||||
<SelectTrigger className="w-[80px] h-8">
|
||||
<SelectTrigger className="min-w-[80px] w-auto h-8">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -82,7 +82,7 @@ export function EnhancedDailyReportSection({ data, onClick }: EnhancedDailyRepor
|
||||
{/* 카드 1: 현금성 자산 */}
|
||||
<div
|
||||
style={{ backgroundColor: '#ecfdf5', borderColor: '#a7f3d0' }}
|
||||
className="rounded-xl p-4 cursor-pointer transition-all hover:shadow-lg border h-[110px] flex flex-col"
|
||||
className="rounded-xl p-4 cursor-pointer transition-all hover:shadow-lg border min-h-[110px] flex flex-col"
|
||||
onClick={onClick}
|
||||
>
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
@@ -114,7 +114,7 @@ export function EnhancedDailyReportSection({ data, onClick }: EnhancedDailyRepor
|
||||
{/* 카드 2: 외국환 */}
|
||||
<div
|
||||
style={{ backgroundColor: '#eff6ff', borderColor: '#bfdbfe' }}
|
||||
className="rounded-xl p-4 cursor-pointer transition-all hover:shadow-lg border h-[110px] flex flex-col"
|
||||
className="rounded-xl p-4 cursor-pointer transition-all hover:shadow-lg border min-h-[110px] flex flex-col"
|
||||
onClick={onClick}
|
||||
>
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
@@ -148,7 +148,7 @@ export function EnhancedDailyReportSection({ data, onClick }: EnhancedDailyRepor
|
||||
{/* 카드 3: 입금 */}
|
||||
<div
|
||||
style={{ backgroundColor: '#f0fdf4', borderColor: '#bbf7d0' }}
|
||||
className="rounded-xl p-4 cursor-pointer transition-all hover:shadow-lg border h-[110px] flex flex-col"
|
||||
className="rounded-xl p-4 cursor-pointer transition-all hover:shadow-lg border min-h-[110px] flex flex-col"
|
||||
onClick={onClick}
|
||||
>
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
@@ -180,7 +180,7 @@ export function EnhancedDailyReportSection({ data, onClick }: EnhancedDailyRepor
|
||||
{/* 카드 4: 출금 */}
|
||||
<div
|
||||
style={{ backgroundColor: '#fff1f2', borderColor: '#fecdd3' }}
|
||||
className="rounded-xl p-4 cursor-pointer transition-all hover:shadow-lg border h-[110px] flex flex-col"
|
||||
className="rounded-xl p-4 cursor-pointer transition-all hover:shadow-lg border min-h-[110px] flex flex-col"
|
||||
onClick={onClick}
|
||||
>
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
@@ -329,7 +329,7 @@ export function EnhancedStatusBoardSection({ items, itemSettings }: EnhancedStat
|
||||
<div
|
||||
key={item.id}
|
||||
style={{ backgroundColor: bgColor, borderColor: borderColor }}
|
||||
className="relative p-4 rounded-xl border cursor-pointer transition-all hover:scale-[1.02] hover:shadow-md h-[130px] flex flex-col"
|
||||
className="relative p-4 rounded-xl border cursor-pointer transition-all hover:scale-[1.02] hover:shadow-md min-h-[130px] flex flex-col"
|
||||
onClick={() => handleItemClick(item.path)}
|
||||
>
|
||||
{/* 아이콘 + 라벨 */}
|
||||
@@ -401,7 +401,7 @@ export function EnhancedMonthlyExpenseSection({ data, onCardClick }: EnhancedMon
|
||||
{/* 카드 1: 매입 */}
|
||||
<div
|
||||
style={{ backgroundColor: '#f5f3ff', borderColor: '#ddd6fe' }}
|
||||
className="rounded-xl p-4 cursor-pointer transition-all hover:scale-[1.02] hover:shadow-lg border h-[130px] flex flex-col"
|
||||
className="rounded-xl p-4 cursor-pointer transition-all hover:scale-[1.02] hover:shadow-lg border min-h-[130px] flex flex-col"
|
||||
onClick={() => onCardClick?.(data.cards[0]?.id || 'me1')}
|
||||
>
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
@@ -426,7 +426,7 @@ export function EnhancedMonthlyExpenseSection({ data, onCardClick }: EnhancedMon
|
||||
{/* 카드 2: 카드 */}
|
||||
<div
|
||||
style={{ backgroundColor: '#eff6ff', borderColor: '#bfdbfe' }}
|
||||
className="rounded-xl p-4 cursor-pointer transition-all hover:scale-[1.02] hover:shadow-lg border h-[130px] flex flex-col"
|
||||
className="rounded-xl p-4 cursor-pointer transition-all hover:scale-[1.02] hover:shadow-lg border min-h-[130px] flex flex-col"
|
||||
onClick={() => onCardClick?.(data.cards[1]?.id || 'me2')}
|
||||
>
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
@@ -451,7 +451,7 @@ export function EnhancedMonthlyExpenseSection({ data, onCardClick }: EnhancedMon
|
||||
{/* 카드 3: 발행어음 */}
|
||||
<div
|
||||
style={{ backgroundColor: '#fffbeb', borderColor: '#fde68a' }}
|
||||
className="rounded-xl p-4 cursor-pointer transition-all hover:scale-[1.02] hover:shadow-lg border h-[130px] flex flex-col"
|
||||
className="rounded-xl p-4 cursor-pointer transition-all hover:scale-[1.02] hover:shadow-lg border min-h-[130px] flex flex-col"
|
||||
onClick={() => onCardClick?.(data.cards[2]?.id || 'me3')}
|
||||
>
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
@@ -476,7 +476,7 @@ export function EnhancedMonthlyExpenseSection({ data, onCardClick }: EnhancedMon
|
||||
{/* 카드 4: 총 예상 지출 합계 (강조 - 인라인 스타일) */}
|
||||
<div
|
||||
style={{ backgroundColor: '#f43f5e', borderColor: '#f43f5e' }}
|
||||
className="rounded-xl p-4 cursor-pointer transition-all hover:scale-[1.02] hover:shadow-lg border h-[130px] flex flex-col"
|
||||
className="rounded-xl p-4 cursor-pointer transition-all hover:scale-[1.02] hover:shadow-lg border min-h-[130px] flex flex-col"
|
||||
onClick={() => onCardClick?.(data.cards[3]?.id || 'me4')}
|
||||
>
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
|
||||
@@ -161,7 +161,7 @@ export function ElectronicApprovalModal({
|
||||
value={person.department || undefined}
|
||||
onValueChange={(val) => onChange(person.id, 'department', val)}
|
||||
>
|
||||
<SelectTrigger className="w-[140px]">
|
||||
<SelectTrigger className="min-w-[140px] w-auto">
|
||||
<SelectValue placeholder="부서명" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -177,7 +177,7 @@ export function ElectronicApprovalModal({
|
||||
value={person.position || undefined}
|
||||
onValueChange={(val) => onChange(person.id, 'position', val)}
|
||||
>
|
||||
<SelectTrigger className="w-[100px]">
|
||||
<SelectTrigger className="min-w-[100px] w-auto">
|
||||
<SelectValue placeholder="직책명" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -193,7 +193,7 @@ export function ElectronicApprovalModal({
|
||||
value={person.name || undefined}
|
||||
onValueChange={(val) => onChange(person.id, 'name', val)}
|
||||
>
|
||||
<SelectTrigger className="w-[100px]">
|
||||
<SelectTrigger className="min-w-[100px] w-auto">
|
||||
<SelectValue placeholder="이름" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -346,7 +346,7 @@ export function EstimateDetailTableSection({
|
||||
onValueChange={(val) => onItemChange(item.id, 'material', val)}
|
||||
disabled={isViewMode}
|
||||
>
|
||||
<SelectTrigger className={`w-full min-w-[70px] ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}>
|
||||
<SelectTrigger className={`w-full min-w-[70px] w-auto ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}>
|
||||
<SelectValue placeholder="선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -463,7 +463,7 @@ export function EstimateDetailTableSection({
|
||||
onValueChange={(val) => onItemChange(item.id, 'coating', Number(val))}
|
||||
disabled={isViewMode}
|
||||
>
|
||||
<SelectTrigger className={`w-full min-w-[70px] ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}>
|
||||
<SelectTrigger className={`w-full min-w-[70px] w-auto ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}>
|
||||
<SelectValue placeholder="선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -482,7 +482,7 @@ export function EstimateDetailTableSection({
|
||||
onValueChange={(val) => onItemChange(item.id, 'mounting', Number(val))}
|
||||
disabled={isViewMode}
|
||||
>
|
||||
<SelectTrigger className={`w-full min-w-[70px] ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}>
|
||||
<SelectTrigger className={`w-full min-w-[70px] w-auto ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}>
|
||||
<SelectValue placeholder="선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -501,7 +501,7 @@ export function EstimateDetailTableSection({
|
||||
onValueChange={(val) => onItemChange(item.id, 'controller', Number(val))}
|
||||
disabled={isViewMode}
|
||||
>
|
||||
<SelectTrigger className={`w-full min-w-[70px] ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}>
|
||||
<SelectTrigger className={`w-full min-w-[70px] w-auto ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}>
|
||||
<SelectValue placeholder="선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -520,7 +520,7 @@ export function EstimateDetailTableSection({
|
||||
onValueChange={(val) => onItemChange(item.id, 'widthConstruction', Number(val))}
|
||||
disabled={isViewMode}
|
||||
>
|
||||
<SelectTrigger className={`w-full min-w-[90px] ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}>
|
||||
<SelectTrigger className={`w-full min-w-[90px] w-auto ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}>
|
||||
<SelectValue placeholder="선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -539,7 +539,7 @@ export function EstimateDetailTableSection({
|
||||
onValueChange={(val) => onItemChange(item.id, 'heightConstruction', Number(val))}
|
||||
disabled={isViewMode}
|
||||
>
|
||||
<SelectTrigger className={`w-full min-w-[90px] ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}>
|
||||
<SelectTrigger className={`w-full min-w-[90px] w-auto ${isViewMode ? 'bg-gray-50' : 'bg-white'}`}>
|
||||
<SelectValue placeholder="선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -410,7 +410,7 @@ export default function HandoverReportDetailForm({
|
||||
value={manager.name}
|
||||
onValueChange={(value) => handleManagerChange(manager.id, 'name', value)}
|
||||
>
|
||||
<SelectTrigger className="w-[150px]">
|
||||
<SelectTrigger className="min-w-[150px] w-auto">
|
||||
<SelectValue placeholder="이름" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -432,7 +432,7 @@ export default function IssueDetailForm({ issue, mode = 'view' }: IssueDetailFor
|
||||
onValueChange={(value) => handleSelectChange('status')(value as IssueStatus)}
|
||||
disabled={isReadOnly}
|
||||
>
|
||||
<SelectTrigger id="status" className="w-full md:w-[200px]">
|
||||
<SelectTrigger id="status" className="w-full md:min-w-[200px] md:w-auto">
|
||||
<SelectValue placeholder="상태 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -145,7 +145,7 @@ export default function ProjectKanbanBoard({
|
||||
{/* 필터 영역 */}
|
||||
<div className="flex items-center justify-end gap-3">
|
||||
<Select value={selectedPartner} onValueChange={setSelectedPartner}>
|
||||
<SelectTrigger className="w-[140px]">
|
||||
<SelectTrigger className="min-w-[140px] w-auto">
|
||||
<SelectValue placeholder="전체" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -159,7 +159,7 @@ export default function ProjectKanbanBoard({
|
||||
</Select>
|
||||
|
||||
<Select value={selectedSite} onValueChange={setSelectedSite}>
|
||||
<SelectTrigger className="w-[140px]">
|
||||
<SelectTrigger className="min-w-[140px] w-auto">
|
||||
<SelectValue placeholder="전체" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -435,7 +435,7 @@ export default function ProjectListClient({ initialData = [], initialStats }: Pr
|
||||
|
||||
{/* 상태 필터 */}
|
||||
<Select value={statusFilter} onValueChange={(v) => { setStatusFilter(v); setCurrentPage(1); }}>
|
||||
<SelectTrigger className="w-[100px]">
|
||||
<SelectTrigger className="min-w-[100px] w-auto">
|
||||
<SelectValue placeholder="전체" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -449,7 +449,7 @@ export default function ProjectListClient({ initialData = [], initialStats }: Pr
|
||||
|
||||
{/* 정렬 */}
|
||||
<Select value={sortBy} onValueChange={(v) => setSortBy(v as typeof sortBy)}>
|
||||
<SelectTrigger className="w-[100px]">
|
||||
<SelectTrigger className="min-w-[100px] w-auto">
|
||||
<SelectValue placeholder="최신순" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -120,7 +120,7 @@ export function ProgressBillingItemTable({
|
||||
value={item.product}
|
||||
onValueChange={(value) => onItemChange(item.id, 'product', value)}
|
||||
>
|
||||
<SelectTrigger className="min-w-[80px]">
|
||||
<SelectTrigger className="min-w-[80px] w-auto">
|
||||
<SelectValue placeholder="제품 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -165,7 +165,7 @@ function WeekRow({
|
||||
// 셀 최소 높이 계산 (이벤트 행 수에 따라) - 더 넉넉하게 확보
|
||||
const segmentRowIndices = eventSegments.map(s => globalRowAssignments.get(s.event.id) || 0);
|
||||
const maxRowIndex = Math.max(0, ...segmentRowIndices);
|
||||
const rowHeight = Math.max(120, 48 + Math.min(maxRowIndex + 1, visibleRows) * 28 + 24);
|
||||
const rowHeight = Math.max(120, 48 + Math.min(maxRowIndex + 1, visibleRows) * 40 + 24);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -63,7 +63,7 @@ export function ScheduleBar({
|
||||
onClick(event);
|
||||
}}
|
||||
className={cn(
|
||||
'absolute h-5 px-2 text-xs font-medium truncate',
|
||||
'absolute h-6 px-2 text-xs font-medium truncate',
|
||||
'transition-all hover:opacity-80 hover:shadow-sm',
|
||||
'flex items-center cursor-pointer',
|
||||
colorClass,
|
||||
@@ -76,7 +76,7 @@ export function ScheduleBar({
|
||||
style={{
|
||||
width: `calc(${widthPercent}% - 4px)`,
|
||||
left: `calc(${leftPercent}% + 2px)`,
|
||||
top: `${rowIndex * 24 + 40}px`, // 날짜 영역(40px) 아래부터 시작 (간격 8px 추가)
|
||||
top: `${rowIndex * 36 + 40}px`, // 날짜 영역(40px) 아래부터 시작
|
||||
}}
|
||||
>
|
||||
{isStart && <span className="truncate">{event.title}</span>}
|
||||
|
||||
@@ -107,7 +107,7 @@ export function AttendanceInfoDialog({
|
||||
value={formData.employeeId}
|
||||
onValueChange={(value) => handleChange('employeeId', value)}
|
||||
>
|
||||
<SelectTrigger className="w-[200px]">
|
||||
<SelectTrigger className="min-w-[200px] w-auto">
|
||||
<SelectValue placeholder="선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -139,7 +139,7 @@ export function AttendanceInfoDialog({
|
||||
value={formData.checkInHour}
|
||||
onValueChange={(value) => handleChange('checkInHour', value)}
|
||||
>
|
||||
<SelectTrigger className="w-[90px]">
|
||||
<SelectTrigger className="min-w-[90px] w-auto">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -154,7 +154,7 @@ export function AttendanceInfoDialog({
|
||||
value={formData.checkInMinute}
|
||||
onValueChange={(value) => handleChange('checkInMinute', value)}
|
||||
>
|
||||
<SelectTrigger className="w-[90px]">
|
||||
<SelectTrigger className="min-w-[90px] w-auto">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -176,7 +176,7 @@ export function AttendanceInfoDialog({
|
||||
value={formData.checkOutHour}
|
||||
onValueChange={(value) => handleChange('checkOutHour', value)}
|
||||
>
|
||||
<SelectTrigger className="w-[90px]">
|
||||
<SelectTrigger className="min-w-[90px] w-auto">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -191,7 +191,7 @@ export function AttendanceInfoDialog({
|
||||
value={formData.checkOutMinute}
|
||||
onValueChange={(value) => handleChange('checkOutMinute', value)}
|
||||
>
|
||||
<SelectTrigger className="w-[90px]">
|
||||
<SelectTrigger className="min-w-[90px] w-auto">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -213,7 +213,7 @@ export function AttendanceInfoDialog({
|
||||
value={formData.nightOvertimeHours}
|
||||
onValueChange={(value) => handleChange('nightOvertimeHours', value)}
|
||||
>
|
||||
<SelectTrigger className="w-[90px]">
|
||||
<SelectTrigger className="min-w-[90px] w-auto">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -228,7 +228,7 @@ export function AttendanceInfoDialog({
|
||||
value={formData.nightOvertimeMinutes}
|
||||
onValueChange={(value) => handleChange('nightOvertimeMinutes', value)}
|
||||
>
|
||||
<SelectTrigger className="w-[90px]">
|
||||
<SelectTrigger className="min-w-[90px] w-auto">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -250,7 +250,7 @@ export function AttendanceInfoDialog({
|
||||
value={formData.weekendOvertimeHours}
|
||||
onValueChange={(value) => handleChange('weekendOvertimeHours', value)}
|
||||
>
|
||||
<SelectTrigger className="w-[90px]">
|
||||
<SelectTrigger className="min-w-[90px] w-auto">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -265,7 +265,7 @@ export function AttendanceInfoDialog({
|
||||
value={formData.weekendOvertimeMinutes}
|
||||
onValueChange={(value) => handleChange('weekendOvertimeMinutes', value)}
|
||||
>
|
||||
<SelectTrigger className="w-[90px]">
|
||||
<SelectTrigger className="min-w-[90px] w-auto">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -73,7 +73,7 @@ export function ReasonInfoDialog({
|
||||
value={formData.employeeId}
|
||||
onValueChange={(value) => handleChange('employeeId', value)}
|
||||
>
|
||||
<SelectTrigger className="w-[200px]">
|
||||
<SelectTrigger className="min-w-[200px] w-auto">
|
||||
<SelectValue placeholder="선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -104,7 +104,7 @@ export function ReasonInfoDialog({
|
||||
value={formData.reasonType}
|
||||
onValueChange={(value) => handleChange('reasonType', value)}
|
||||
>
|
||||
<SelectTrigger className="w-[200px]">
|
||||
<SelectTrigger className="min-w-[200px] w-auto">
|
||||
<SelectValue placeholder="선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -164,7 +164,7 @@ export function CardManagement() {
|
||||
tableHeaderActions: (
|
||||
<div className="flex items-center gap-2">
|
||||
<Select value={cardCompanyFilter} onValueChange={setCardCompanyFilter}>
|
||||
<SelectTrigger className="w-[130px] h-9">
|
||||
<SelectTrigger className="min-w-[130px] w-auto h-9">
|
||||
<SelectValue placeholder="카드사" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -175,7 +175,7 @@ export function CardManagement() {
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Select value={statusFilter} onValueChange={setStatusFilter}>
|
||||
<SelectTrigger className="w-[100px] h-9">
|
||||
<SelectTrigger className="min-w-[100px] w-auto h-9">
|
||||
<SelectValue placeholder="상태" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -171,7 +171,7 @@ export function SalaryDetailDialog({
|
||||
value={editedStatus}
|
||||
onValueChange={(value) => handleStatusChange(value as PaymentStatus)}
|
||||
>
|
||||
<SelectTrigger className="w-[140px]">
|
||||
<SelectTrigger className="min-w-[140px] w-auto">
|
||||
<SelectValue>
|
||||
<Badge className={PAYMENT_STATUS_COLORS[editedStatus]}>
|
||||
{PAYMENT_STATUS_LABELS[editedStatus]}
|
||||
|
||||
@@ -148,7 +148,7 @@ export function InventoryAdjustmentDialog({ open, onOpenChange, onSave }: Props)
|
||||
총 <strong>{filteredItems.length}</strong>건
|
||||
</span>
|
||||
<Select value={typeFilter} onValueChange={setTypeFilter}>
|
||||
<SelectTrigger className="w-[120px] h-8 text-sm">
|
||||
<SelectTrigger className="min-w-[120px] w-auto h-8 text-sm">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -67,7 +67,7 @@ export function YearQuarterFilter({
|
||||
return (
|
||||
<div className={cn('flex items-center gap-2 flex-wrap min-w-0', className)}>
|
||||
<Select value={String(year)} onValueChange={(v) => onYearChange(Number(v))}>
|
||||
<SelectTrigger className="w-[100px] h-9 text-sm">
|
||||
<SelectTrigger className="min-w-[100px] w-auto h-9 text-sm">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -356,7 +356,7 @@ export function PriceDistributionDetail({ id, mode: propMode }: Props) {
|
||||
<CardTitle className="text-base">단가표 목록</CardTitle>
|
||||
<div className="flex items-center gap-3">
|
||||
<Select value={gradeFilter} onValueChange={setGradeFilter}>
|
||||
<SelectTrigger className="h-8 w-[120px] text-sm">
|
||||
<SelectTrigger className="h-8 min-w-[120px] w-auto text-sm">
|
||||
<SelectValue placeholder="등급" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -243,7 +243,7 @@ export function RuleModal({ open, onOpenChange, onAdd, editRule, processId, proc
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Select value={processFilter} onValueChange={setProcessFilter}>
|
||||
<SelectTrigger className="w-[100px] h-8 text-xs">
|
||||
<SelectTrigger className="min-w-[100px] w-auto h-8 text-xs">
|
||||
<SelectValue placeholder="공정" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -260,7 +260,7 @@ export function RuleModal({ open, onOpenChange, onAdd, editRule, processId, proc
|
||||
onValueChange={setCategoryFilter}
|
||||
disabled={categoryFilterOptions.length <= 1}
|
||||
>
|
||||
<SelectTrigger className="w-[100px] h-8 text-xs">
|
||||
<SelectTrigger className="min-w-[100px] w-auto h-8 text-xs">
|
||||
<SelectValue placeholder="구분" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -182,7 +182,7 @@ export function InspectionList() {
|
||||
() => (
|
||||
<div className="flex items-center gap-2">
|
||||
<Select value={calendarStatusFilter} onValueChange={setCalendarStatusFilter}>
|
||||
<SelectTrigger className="w-[100px] h-8 text-xs">
|
||||
<SelectTrigger className="min-w-[100px] w-auto h-8 text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -193,7 +193,7 @@ export function InspectionList() {
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Select value={calendarInspectorFilter} onValueChange={setCalendarInspectorFilter}>
|
||||
<SelectTrigger className="w-[100px] h-8 text-xs">
|
||||
<SelectTrigger className="min-w-[100px] w-auto h-8 text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -311,7 +311,7 @@ export function QuoteManagementClient({
|
||||
<div className="flex items-center gap-2">
|
||||
{/* 제품분류 필터 */}
|
||||
<Select value={productCategoryFilter} onValueChange={setProductCategoryFilter}>
|
||||
<SelectTrigger className="w-[100px]">
|
||||
<SelectTrigger className="min-w-[100px] w-auto">
|
||||
<SelectValue placeholder="제품분류" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -324,7 +324,7 @@ export function QuoteManagementClient({
|
||||
|
||||
{/* 상태 필터 */}
|
||||
<Select value={statusFilter} onValueChange={setStatusFilter}>
|
||||
<SelectTrigger className="w-[100px]">
|
||||
<SelectTrigger className="min-w-[100px] w-auto">
|
||||
<SelectValue placeholder="상태" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -412,7 +412,7 @@ export default function ComprehensiveAnalysis({ initialData }: ComprehensiveAnal
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<SectionTitle title="오늘의 이슈" badge="warning" />
|
||||
<Select value={issueFilter} onValueChange={setIssueFilter}>
|
||||
<SelectTrigger className="w-[140px]">
|
||||
<SelectTrigger className="min-w-[140px] w-auto">
|
||||
<SelectValue placeholder="필터 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -252,7 +252,7 @@ export function AccountManagement() {
|
||||
tableHeaderActions: (
|
||||
<div className="flex items-center gap-2">
|
||||
<Select value={categoryFilter} onValueChange={setCategoryFilter}>
|
||||
<SelectTrigger className="w-[130px] h-9">
|
||||
<SelectTrigger className="min-w-[130px] w-auto h-9">
|
||||
<SelectValue placeholder="구분" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -262,7 +262,7 @@ export function AccountManagement() {
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Select value={institutionFilter} onValueChange={setInstitutionFilter}>
|
||||
<SelectTrigger className="w-[150px] h-9">
|
||||
<SelectTrigger className="min-w-[150px] w-auto h-9">
|
||||
<SelectValue placeholder="금융기관" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -219,7 +219,7 @@ export function AttendanceSettingsManagement() {
|
||||
onValueChange={handleRadiusChange}
|
||||
disabled={!settings.gpsEnabled}
|
||||
>
|
||||
<SelectTrigger className="w-[120px]">
|
||||
<SelectTrigger className="min-w-[120px] w-auto">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -77,7 +77,7 @@ function NotificationItemRow({ label, item, onChange, disabled }: NotificationIt
|
||||
}
|
||||
disabled={isDisabled}
|
||||
>
|
||||
<SelectTrigger className="w-[140px] h-8">
|
||||
<SelectTrigger className="min-w-[140px] w-auto h-8">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -189,7 +189,7 @@ export function PaymentHistoryManagement({
|
||||
// <div className="flex items-center gap-2 flex-wrap">
|
||||
// {/* 정렬 */}
|
||||
// <Select value={sortOption} onValueChange={(value) => setSortOption(value as SortOption)}>
|
||||
// <SelectTrigger className="w-[120px]">
|
||||
// <SelectTrigger className="min-w-[120px] w-auto">
|
||||
// <SelectValue placeholder="정렬" />
|
||||
// </SelectTrigger>
|
||||
// <SelectContent>
|
||||
|
||||
@@ -498,7 +498,7 @@ export function IntegratedListTemplateV2<T = any>({
|
||||
value={(filterValues[field.key] as string) || 'all'}
|
||||
onValueChange={(value) => onFilterChange(field.key, value)}
|
||||
>
|
||||
<SelectTrigger className="w-[120px]">
|
||||
<SelectTrigger className="min-w-[120px] w-auto">
|
||||
<SelectValue placeholder={field.allOptionLabel || field.label} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
Reference in New Issue
Block a user