2025-11-24 17:24:47 +09:00
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
import React, { useEffect, useState } from "react";
|
|
|
|
|
import { Label } from "@/components/ui/label";
|
|
|
|
|
import { Input } from "@/components/ui/input";
|
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
|
|
|
import { Switch } from "@/components/ui/switch";
|
2026-01-20 10:46:34 +09:00
|
|
|
import {
|
|
|
|
|
Select,
|
|
|
|
|
SelectContent,
|
|
|
|
|
SelectItem,
|
|
|
|
|
SelectTrigger,
|
|
|
|
|
SelectValue,
|
|
|
|
|
} from "@/components/ui/select";
|
|
|
|
|
import {
|
|
|
|
|
Plus,
|
|
|
|
|
X,
|
|
|
|
|
GripVertical,
|
|
|
|
|
ChevronDown,
|
|
|
|
|
ChevronRight,
|
|
|
|
|
Trash2,
|
|
|
|
|
Move,
|
|
|
|
|
} from "lucide-react";
|
2025-11-24 17:24:47 +09:00
|
|
|
import { cn } from "@/lib/utils";
|
2026-01-20 10:46:34 +09:00
|
|
|
import type { TabItem, TabInlineComponent } from "@/types/screen-management";
|
|
|
|
|
import {
|
|
|
|
|
Collapsible,
|
|
|
|
|
CollapsibleContent,
|
|
|
|
|
CollapsibleTrigger,
|
|
|
|
|
} from "@/components/ui/collapsible";
|
2025-11-24 17:24:47 +09:00
|
|
|
|
|
|
|
|
interface TabsConfigPanelProps {
|
|
|
|
|
config: any;
|
|
|
|
|
onChange: (config: any) => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function TabsConfigPanel({ config, onChange }: TabsConfigPanelProps) {
|
|
|
|
|
const [localTabs, setLocalTabs] = useState<TabItem[]>(config.tabs || []);
|
2026-01-20 10:46:34 +09:00
|
|
|
const [expandedTabs, setExpandedTabs] = useState<Set<string>>(new Set());
|
2025-11-25 13:04:58 +09:00
|
|
|
const [isUserEditing, setIsUserEditing] = useState(false);
|
2026-01-20 10:46:34 +09:00
|
|
|
|
2025-11-24 17:24:47 +09:00
|
|
|
useEffect(() => {
|
2025-11-25 13:04:58 +09:00
|
|
|
if (!isUserEditing) {
|
|
|
|
|
setLocalTabs(config.tabs || []);
|
|
|
|
|
}
|
|
|
|
|
}, [config.tabs, isUserEditing]);
|
2025-11-24 17:24:47 +09:00
|
|
|
|
2026-01-20 10:46:34 +09:00
|
|
|
// 탭 확장/축소 토글
|
|
|
|
|
const toggleTabExpand = (tabId: string) => {
|
|
|
|
|
setExpandedTabs((prev) => {
|
|
|
|
|
const newSet = new Set(prev);
|
|
|
|
|
if (newSet.has(tabId)) {
|
|
|
|
|
newSet.delete(tabId);
|
|
|
|
|
} else {
|
|
|
|
|
newSet.add(tabId);
|
|
|
|
|
}
|
|
|
|
|
return newSet;
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2025-11-24 17:24:47 +09:00
|
|
|
// 탭 추가
|
|
|
|
|
const handleAddTab = () => {
|
|
|
|
|
const newTab: TabItem = {
|
|
|
|
|
id: `tab-${Date.now()}`,
|
|
|
|
|
label: `새 탭 ${localTabs.length + 1}`,
|
|
|
|
|
order: localTabs.length,
|
|
|
|
|
disabled: false,
|
2026-01-20 10:46:34 +09:00
|
|
|
components: [],
|
2025-11-24 17:24:47 +09:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const updatedTabs = [...localTabs, newTab];
|
|
|
|
|
setLocalTabs(updatedTabs);
|
|
|
|
|
onChange({ ...config, tabs: updatedTabs });
|
2026-01-20 10:46:34 +09:00
|
|
|
|
|
|
|
|
// 새 탭 자동 확장
|
|
|
|
|
setExpandedTabs((prev) => new Set([...prev, newTab.id]));
|
2025-11-24 17:24:47 +09:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 탭 제거
|
|
|
|
|
const handleRemoveTab = (tabId: string) => {
|
|
|
|
|
const updatedTabs = localTabs.filter((tab) => tab.id !== tabId);
|
|
|
|
|
setLocalTabs(updatedTabs);
|
|
|
|
|
onChange({ ...config, tabs: updatedTabs });
|
|
|
|
|
};
|
|
|
|
|
|
2025-11-25 13:04:58 +09:00
|
|
|
// 탭 라벨 변경 (입력 중)
|
2025-11-24 17:24:47 +09:00
|
|
|
const handleLabelChange = (tabId: string, label: string) => {
|
2025-11-25 13:04:58 +09:00
|
|
|
setIsUserEditing(true);
|
2026-01-20 10:46:34 +09:00
|
|
|
const updatedTabs = localTabs.map((tab) =>
|
|
|
|
|
tab.id === tabId ? { ...tab, label } : tab
|
|
|
|
|
);
|
2025-11-24 17:24:47 +09:00
|
|
|
setLocalTabs(updatedTabs);
|
2025-11-25 13:04:58 +09:00
|
|
|
};
|
|
|
|
|
|
2026-01-20 10:46:34 +09:00
|
|
|
// 탭 라벨 변경 완료
|
2025-11-25 13:04:58 +09:00
|
|
|
const handleLabelBlur = () => {
|
|
|
|
|
setIsUserEditing(false);
|
|
|
|
|
onChange({ ...config, tabs: localTabs });
|
2025-11-24 17:24:47 +09:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 탭 비활성화 토글
|
|
|
|
|
const handleDisabledToggle = (tabId: string, disabled: boolean) => {
|
2026-01-20 10:46:34 +09:00
|
|
|
const updatedTabs = localTabs.map((tab) =>
|
|
|
|
|
tab.id === tabId ? { ...tab, disabled } : tab
|
|
|
|
|
);
|
2025-11-24 17:24:47 +09:00
|
|
|
setLocalTabs(updatedTabs);
|
|
|
|
|
onChange({ ...config, tabs: updatedTabs });
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 탭 순서 변경
|
|
|
|
|
const handleMoveTab = (tabId: string, direction: "up" | "down") => {
|
|
|
|
|
const index = localTabs.findIndex((tab) => tab.id === tabId);
|
|
|
|
|
if (
|
|
|
|
|
(direction === "up" && index === 0) ||
|
|
|
|
|
(direction === "down" && index === localTabs.length - 1)
|
|
|
|
|
) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const newTabs = [...localTabs];
|
|
|
|
|
const targetIndex = direction === "up" ? index - 1 : index + 1;
|
2026-01-20 10:46:34 +09:00
|
|
|
[newTabs[index], newTabs[targetIndex]] = [
|
|
|
|
|
newTabs[targetIndex],
|
|
|
|
|
newTabs[index],
|
|
|
|
|
];
|
2025-11-24 17:24:47 +09:00
|
|
|
|
|
|
|
|
const updatedTabs = newTabs.map((tab, idx) => ({ ...tab, order: idx }));
|
|
|
|
|
setLocalTabs(updatedTabs);
|
|
|
|
|
onChange({ ...config, tabs: updatedTabs });
|
|
|
|
|
};
|
|
|
|
|
|
2026-01-20 10:46:34 +09:00
|
|
|
// 컴포넌트 제거
|
|
|
|
|
const handleRemoveComponent = (tabId: string, componentId: string) => {
|
|
|
|
|
const updatedTabs = localTabs.map((tab) => {
|
|
|
|
|
if (tab.id === tabId) {
|
|
|
|
|
return {
|
|
|
|
|
...tab,
|
|
|
|
|
components: (tab.components || []).filter(
|
|
|
|
|
(comp) => comp.id !== componentId
|
|
|
|
|
),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
return tab;
|
|
|
|
|
});
|
|
|
|
|
setLocalTabs(updatedTabs);
|
|
|
|
|
onChange({ ...config, tabs: updatedTabs });
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 컴포넌트 위치 변경
|
|
|
|
|
const handleComponentPositionChange = (
|
|
|
|
|
tabId: string,
|
|
|
|
|
componentId: string,
|
|
|
|
|
field: "x" | "y" | "width" | "height",
|
|
|
|
|
value: number
|
|
|
|
|
) => {
|
|
|
|
|
const updatedTabs = localTabs.map((tab) => {
|
|
|
|
|
if (tab.id === tabId) {
|
|
|
|
|
return {
|
|
|
|
|
...tab,
|
|
|
|
|
components: (tab.components || []).map((comp) => {
|
|
|
|
|
if (comp.id === componentId) {
|
|
|
|
|
if (field === "x" || field === "y") {
|
|
|
|
|
return {
|
|
|
|
|
...comp,
|
|
|
|
|
position: { ...comp.position, [field]: value },
|
|
|
|
|
};
|
|
|
|
|
} else {
|
|
|
|
|
return {
|
|
|
|
|
...comp,
|
|
|
|
|
size: { ...comp.size, [field]: value },
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return comp;
|
|
|
|
|
}),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
return tab;
|
|
|
|
|
});
|
|
|
|
|
setLocalTabs(updatedTabs);
|
|
|
|
|
onChange({ ...config, tabs: updatedTabs });
|
|
|
|
|
};
|
|
|
|
|
|
2025-11-24 17:24:47 +09:00
|
|
|
return (
|
|
|
|
|
<div className="space-y-6 p-4">
|
|
|
|
|
<div>
|
|
|
|
|
<h3 className="mb-4 text-sm font-semibold">탭 설정</h3>
|
|
|
|
|
|
|
|
|
|
<div className="space-y-4">
|
|
|
|
|
{/* 탭 방향 */}
|
|
|
|
|
<div>
|
|
|
|
|
<Label className="text-xs sm:text-sm">탭 방향</Label>
|
|
|
|
|
<Select
|
|
|
|
|
value={config.orientation || "horizontal"}
|
|
|
|
|
onValueChange={(value: "horizontal" | "vertical") =>
|
|
|
|
|
onChange({ ...config, orientation: value })
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
<SelectTrigger className="h-8 text-xs sm:h-10 sm:text-sm">
|
|
|
|
|
<SelectValue />
|
|
|
|
|
</SelectTrigger>
|
|
|
|
|
<SelectContent>
|
|
|
|
|
<SelectItem value="horizontal">가로</SelectItem>
|
|
|
|
|
<SelectItem value="vertical">세로</SelectItem>
|
|
|
|
|
</SelectContent>
|
|
|
|
|
</Select>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* 탭 스타일 */}
|
|
|
|
|
<div>
|
|
|
|
|
<Label className="text-xs sm:text-sm">탭 스타일</Label>
|
|
|
|
|
<Select
|
|
|
|
|
value={config.variant || "default"}
|
|
|
|
|
onValueChange={(value: "default" | "pills" | "underline") =>
|
|
|
|
|
onChange({ ...config, variant: value })
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
<SelectTrigger className="h-8 text-xs sm:h-10 sm:text-sm">
|
|
|
|
|
<SelectValue />
|
|
|
|
|
</SelectTrigger>
|
|
|
|
|
<SelectContent>
|
|
|
|
|
<SelectItem value="default">기본</SelectItem>
|
|
|
|
|
<SelectItem value="pills">알약형</SelectItem>
|
|
|
|
|
<SelectItem value="underline">밑줄</SelectItem>
|
|
|
|
|
</SelectContent>
|
|
|
|
|
</Select>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* 선택 상태 유지 */}
|
|
|
|
|
<div className="flex items-center justify-between">
|
|
|
|
|
<div>
|
|
|
|
|
<Label className="text-xs sm:text-sm">선택 상태 유지</Label>
|
|
|
|
|
<p className="text-muted-foreground text-[10px] sm:text-xs">
|
|
|
|
|
페이지 새로고침 후에도 선택한 탭이 유지됩니다
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
<Switch
|
|
|
|
|
checked={config.persistSelection || false}
|
2026-01-20 10:46:34 +09:00
|
|
|
onCheckedChange={(checked) =>
|
|
|
|
|
onChange({ ...config, persistSelection: checked })
|
|
|
|
|
}
|
2025-11-24 17:24:47 +09:00
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* 탭 닫기 버튼 */}
|
|
|
|
|
<div className="flex items-center justify-between">
|
|
|
|
|
<div>
|
|
|
|
|
<Label className="text-xs sm:text-sm">탭 닫기 버튼</Label>
|
|
|
|
|
<p className="text-muted-foreground text-[10px] sm:text-xs">
|
|
|
|
|
각 탭에 닫기 버튼을 표시합니다
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
<Switch
|
|
|
|
|
checked={config.allowCloseable || false}
|
2026-01-20 10:46:34 +09:00
|
|
|
onCheckedChange={(checked) =>
|
|
|
|
|
onChange({ ...config, allowCloseable: checked })
|
|
|
|
|
}
|
2025-11-24 17:24:47 +09:00
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
<div className="mb-4 flex items-center justify-between">
|
|
|
|
|
<h3 className="text-sm font-semibold">탭 목록</h3>
|
|
|
|
|
<Button
|
|
|
|
|
onClick={handleAddTab}
|
|
|
|
|
size="sm"
|
|
|
|
|
variant="outline"
|
|
|
|
|
className="h-8 text-xs sm:h-9 sm:text-sm"
|
|
|
|
|
>
|
|
|
|
|
<Plus className="mr-1 h-3 w-3 sm:h-4 sm:w-4" />
|
|
|
|
|
탭 추가
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{localTabs.length === 0 ? (
|
|
|
|
|
<div className="rounded-lg border border-dashed p-8 text-center">
|
|
|
|
|
<p className="text-muted-foreground text-sm">탭이 없습니다</p>
|
|
|
|
|
<p className="text-muted-foreground mt-1 text-xs">
|
|
|
|
|
탭 추가 버튼을 클릭하여 새 탭을 생성하세요
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
) : (
|
|
|
|
|
<div className="space-y-3">
|
|
|
|
|
{localTabs.map((tab, index) => (
|
2026-01-20 10:46:34 +09:00
|
|
|
<Collapsible
|
2025-11-24 17:24:47 +09:00
|
|
|
key={tab.id}
|
2026-01-20 10:46:34 +09:00
|
|
|
open={expandedTabs.has(tab.id)}
|
|
|
|
|
onOpenChange={() => toggleTabExpand(tab.id)}
|
2025-11-24 17:24:47 +09:00
|
|
|
>
|
2026-01-20 10:46:34 +09:00
|
|
|
<div className="rounded-lg border bg-card shadow-sm">
|
|
|
|
|
{/* 탭 헤더 */}
|
|
|
|
|
<div className="flex items-center justify-between p-3">
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
<GripVertical className="h-4 w-4 cursor-grab text-muted-foreground" />
|
|
|
|
|
<CollapsibleTrigger asChild>
|
|
|
|
|
<Button variant="ghost" size="sm" className="h-6 w-6 p-0">
|
|
|
|
|
{expandedTabs.has(tab.id) ? (
|
|
|
|
|
<ChevronDown className="h-4 w-4" />
|
|
|
|
|
) : (
|
|
|
|
|
<ChevronRight className="h-4 w-4" />
|
|
|
|
|
)}
|
|
|
|
|
</Button>
|
|
|
|
|
</CollapsibleTrigger>
|
|
|
|
|
<span className="text-xs font-medium">
|
|
|
|
|
{tab.label || `탭 ${index + 1}`}
|
|
|
|
|
</span>
|
|
|
|
|
{tab.components && tab.components.length > 0 && (
|
|
|
|
|
<span className="rounded-full bg-primary/10 px-2 py-0.5 text-[10px] text-primary">
|
|
|
|
|
{tab.components.length}개 컴포넌트
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex items-center gap-1">
|
|
|
|
|
<Button
|
|
|
|
|
onClick={() => handleMoveTab(tab.id, "up")}
|
|
|
|
|
disabled={index === 0}
|
|
|
|
|
size="sm"
|
|
|
|
|
variant="ghost"
|
|
|
|
|
className="h-7 w-7 p-0"
|
|
|
|
|
>
|
|
|
|
|
↑
|
|
|
|
|
</Button>
|
|
|
|
|
<Button
|
|
|
|
|
onClick={() => handleMoveTab(tab.id, "down")}
|
|
|
|
|
disabled={index === localTabs.length - 1}
|
|
|
|
|
size="sm"
|
|
|
|
|
variant="ghost"
|
|
|
|
|
className="h-7 w-7 p-0"
|
|
|
|
|
>
|
|
|
|
|
↓
|
|
|
|
|
</Button>
|
|
|
|
|
<Button
|
|
|
|
|
onClick={() => handleRemoveTab(tab.id)}
|
|
|
|
|
size="sm"
|
|
|
|
|
variant="ghost"
|
|
|
|
|
className="h-7 w-7 p-0 text-destructive hover:text-destructive"
|
|
|
|
|
>
|
|
|
|
|
<X className="h-3 w-3" />
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
2025-11-24 17:24:47 +09:00
|
|
|
</div>
|
|
|
|
|
|
2026-01-20 10:46:34 +09:00
|
|
|
{/* 탭 컨텐츠 */}
|
|
|
|
|
<CollapsibleContent>
|
|
|
|
|
<div className="space-y-4 border-t p-3">
|
|
|
|
|
{/* 탭 라벨 */}
|
|
|
|
|
<div>
|
|
|
|
|
<Label className="text-xs">탭 라벨</Label>
|
|
|
|
|
<Input
|
|
|
|
|
value={tab.label}
|
|
|
|
|
onChange={(e) =>
|
|
|
|
|
handleLabelChange(tab.id, e.target.value)
|
|
|
|
|
}
|
|
|
|
|
onBlur={handleLabelBlur}
|
|
|
|
|
placeholder="탭 이름"
|
|
|
|
|
className="h-8 text-xs sm:h-9 sm:text-sm"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2025-11-24 17:24:47 +09:00
|
|
|
|
2026-01-20 10:46:34 +09:00
|
|
|
{/* 비활성화 */}
|
|
|
|
|
<div className="flex items-center justify-between">
|
|
|
|
|
<Label className="text-xs">비활성화</Label>
|
|
|
|
|
<Switch
|
|
|
|
|
checked={tab.disabled || false}
|
|
|
|
|
onCheckedChange={(checked) =>
|
|
|
|
|
handleDisabledToggle(tab.id, checked)
|
|
|
|
|
}
|
|
|
|
|
/>
|
2025-11-24 17:24:47 +09:00
|
|
|
</div>
|
|
|
|
|
|
2026-01-20 10:46:34 +09:00
|
|
|
{/* 컴포넌트 목록 */}
|
|
|
|
|
<div>
|
|
|
|
|
<Label className="mb-2 block text-xs">
|
|
|
|
|
배치된 컴포넌트
|
|
|
|
|
</Label>
|
|
|
|
|
{!tab.components || tab.components.length === 0 ? (
|
|
|
|
|
<div className="rounded-md border border-dashed bg-muted/30 p-4 text-center">
|
|
|
|
|
<Move className="mx-auto mb-2 h-6 w-6 text-muted-foreground" />
|
|
|
|
|
<p className="text-muted-foreground text-xs">
|
|
|
|
|
디자인 화면에서 컴포넌트를 드래그하여 추가하세요
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
) : (
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
{tab.components.map((comp: TabInlineComponent) => (
|
|
|
|
|
<div
|
|
|
|
|
key={comp.id}
|
|
|
|
|
className="flex items-center justify-between rounded-md border bg-background p-2"
|
|
|
|
|
>
|
|
|
|
|
<div className="flex-1">
|
|
|
|
|
<p className="text-xs font-medium">
|
|
|
|
|
{comp.label || comp.componentType}
|
|
|
|
|
</p>
|
|
|
|
|
<p className="text-muted-foreground text-[10px]">
|
|
|
|
|
{comp.componentType} | 위치: ({comp.position?.x || 0},{" "}
|
|
|
|
|
{comp.position?.y || 0}) | 크기: {comp.size?.width || 0}x
|
|
|
|
|
{comp.size?.height || 0}
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
<Button
|
|
|
|
|
onClick={() =>
|
|
|
|
|
handleRemoveComponent(tab.id, comp.id)
|
|
|
|
|
}
|
|
|
|
|
size="sm"
|
|
|
|
|
variant="ghost"
|
|
|
|
|
className="h-6 w-6 p-0 text-destructive hover:text-destructive"
|
|
|
|
|
>
|
|
|
|
|
<Trash2 className="h-3 w-3" />
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</CollapsibleContent>
|
2025-11-24 17:24:47 +09:00
|
|
|
</div>
|
2026-01-20 10:46:34 +09:00
|
|
|
</Collapsible>
|
2025-11-24 17:24:47 +09:00
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-01-20 10:46:34 +09:00
|
|
|
{/* 사용 안내 */}
|
|
|
|
|
<div className="rounded-lg border border-blue-200 bg-blue-50 p-3">
|
|
|
|
|
<h4 className="mb-1 text-xs font-semibold text-blue-900">
|
|
|
|
|
컴포넌트 추가 방법
|
|
|
|
|
</h4>
|
|
|
|
|
<ol className="list-inside list-decimal space-y-1 text-[10px] text-blue-800">
|
|
|
|
|
<li>디자인 화면에서 탭을 선택합니다</li>
|
|
|
|
|
<li>좌측 패널에서 원하는 컴포넌트를 드래그합니다</li>
|
|
|
|
|
<li>선택한 탭 영역에 드롭하여 배치합니다</li>
|
|
|
|
|
<li>컴포넌트를 드래그하여 위치를 조정합니다</li>
|
|
|
|
|
</ol>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-11-24 17:24:47 +09:00
|
|
|
);
|
|
|
|
|
}
|