밑꺽쇠 수정했음!

This commit is contained in:
leeheejin 2026-01-21 16:40:37 +09:00
parent e6bb366ec7
commit 62a82b3bcf
4 changed files with 100 additions and 2 deletions

View File

@ -16,6 +16,7 @@ import {
PivotAreaType,
AggregationType,
FieldDataType,
DateGroupInterval,
} from "./types";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
@ -202,6 +203,28 @@ const AreaDropZone: React.FC<AreaDropZoneProps> = ({
</Select>
)}
{/* 행/열 영역에서 날짜 타입일 때 그룹화 옵션 */}
{(area === "row" || area === "column") && field.dataType === "date" && (
<Select
value={field.groupInterval || "__none__"}
onValueChange={(v) => onUpdateField(idx, {
groupInterval: v === "__none__" ? undefined : v as DateGroupInterval
})}
>
<SelectTrigger className="h-6 w-16 text-xs">
<SelectValue placeholder="그룹" />
</SelectTrigger>
<SelectContent>
<SelectItem value="__none__"></SelectItem>
<SelectItem value="year"></SelectItem>
<SelectItem value="quarter"></SelectItem>
<SelectItem value="month"></SelectItem>
<SelectItem value="week"></SelectItem>
<SelectItem value="day"></SelectItem>
</SelectContent>
</Select>
)}
<Button
variant="ghost"
size="icon"
@ -295,7 +318,8 @@ export const PivotGridConfigPanel: React.FC<PivotGridConfigPanelProps> = ({
const mappedColumns: ColumnInfo[] = columnList.map((c: any) => ({
column_name: c.columnName || c.column_name,
data_type: c.dataType || c.data_type || "text",
column_comment: c.columnLabel || c.column_label || c.columnName || c.column_name,
// 라벨 우선순위: displayName > comment > columnLabel > columnName
column_comment: c.displayName || c.comment || c.columnLabel || c.column_label || c.columnName || c.column_name,
}));
setColumns(mappedColumns);
} catch (error) {

View File

@ -41,6 +41,8 @@ import {
FilterX,
LayoutGrid,
Trash2,
Calendar,
Check,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import {
@ -132,6 +134,16 @@ const SortableFieldChip: React.FC<FieldChipProps> = ({
// 필터 적용 여부 확인
const hasFilter = field.filterValues && field.filterValues.length > 0;
const filterCount = field.filterValues?.length || 0;
// 그룹화 상태 확인
const hasGrouping = field.groupInterval && field.dataType === "date";
const groupLabels: Record<string, string> = {
year: "연도",
quarter: "분기",
month: "월",
week: "주",
day: "일",
};
return (
<div
@ -169,6 +181,12 @@ const SortableFieldChip: React.FC<FieldChipProps> = ({
<span className={cn("font-medium", hasFilter && "text-primary")}>
{field.caption}
</span>
{/* 그룹화 적용 표시 */}
{hasGrouping && (
<span className="bg-blue-100 text-blue-700 dark:bg-blue-900 dark:text-blue-300 text-[10px] px-1 rounded">
{groupLabels[field.groupInterval!]}
</span>
)}
{/* 필터 적용 개수 배지 */}
{hasFilter && (
<span className="bg-primary text-primary-foreground text-[10px] px-1 rounded">
@ -224,6 +242,59 @@ const SortableFieldChip: React.FC<FieldChipProps> = ({
<DropdownMenuSeparator />
</>
)}
{/* 날짜 그룹화 옵션 (행/열 영역의 날짜 타입 필드만) */}
{(field.area === "row" || field.area === "column") &&
field.dataType === "date" && (
<>
<div className="px-2 py-1.5 text-xs font-semibold text-muted-foreground flex items-center gap-1">
<Calendar className="h-3 w-3" />
</div>
<DropdownMenuItem
onClick={() => onSettingsChange?.({ ...field, groupInterval: undefined })}
className="pl-6"
>
{!field.groupInterval && <Check className="h-3 w-3 mr-2" />}
<span className={!field.groupInterval ? "font-medium" : ""}> </span>
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => onSettingsChange?.({ ...field, groupInterval: "year" })}
className="pl-6"
>
{field.groupInterval === "year" && <Check className="h-3 w-3 mr-2" />}
<span className={field.groupInterval === "year" ? "font-medium" : ""}></span>
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => onSettingsChange?.({ ...field, groupInterval: "quarter" })}
className="pl-6"
>
{field.groupInterval === "quarter" && <Check className="h-3 w-3 mr-2" />}
<span className={field.groupInterval === "quarter" ? "font-medium" : ""}></span>
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => onSettingsChange?.({ ...field, groupInterval: "month" })}
className="pl-6"
>
{field.groupInterval === "month" && <Check className="h-3 w-3 mr-2" />}
<span className={field.groupInterval === "month" ? "font-medium" : ""}></span>
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => onSettingsChange?.({ ...field, groupInterval: "week" })}
className="pl-6"
>
{field.groupInterval === "week" && <Check className="h-3 w-3 mr-2" />}
<span className={field.groupInterval === "week" ? "font-medium" : ""}></span>
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => onSettingsChange?.({ ...field, groupInterval: "day" })}
className="pl-6"
>
{field.groupInterval === "day" && <Check className="h-3 w-3 mr-2" />}
<span className={field.groupInterval === "day" ? "font-medium" : ""}></span>
</DropdownMenuItem>
<DropdownMenuSeparator />
</>
)}
<DropdownMenuItem
onClick={() =>
onSettingsChange?.({

View File

@ -304,6 +304,7 @@ export interface PivotHeaderNode {
level: number; // 깊이
children?: PivotHeaderNode[]; // 자식 노드
isExpanded: boolean; // 확장 상태
hasChildren: boolean; // 자식 존재 가능 여부 (다음 레벨 필드 있음)
path: string[]; // 경로 (드릴다운용)
subtotal?: PivotCellValue[]; // 소계
span?: number; // colspan/rowspan

View File

@ -129,6 +129,7 @@ function buildHeaderTree(
caption: key,
level: 0,
isExpanded: expandedPaths.has(pathKey),
hasChildren: remainingFields.length > 0, // 다음 레벨 필드가 있으면 자식 있음
path: path,
span: 1,
};
@ -195,6 +196,7 @@ function buildChildNodes(
caption: key,
level: level,
isExpanded: expandedPaths.has(pathKey),
hasChildren: remainingFields.length > 0, // 다음 레벨 필드가 있으면 자식 있음
path: path,
span: 1,
};
@ -238,7 +240,7 @@ function flattenRows(nodes: PivotHeaderNode[]): PivotFlatRow[] {
level: node.level,
caption: node.caption,
isExpanded: node.isExpanded,
hasChildren: !!(node.children && node.children.length > 0),
hasChildren: node.hasChildren, // 노드에서 직접 가져옴 (다음 레벨 필드 존재 여부 기준)
});
if (node.isExpanded && node.children) {