summaryrefslogtreecommitdiff
path: root/lib/rfqs/table
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rfqs/table')
-rw-r--r--lib/rfqs/table/ItemsDialog.tsx112
-rw-r--r--lib/rfqs/table/add-rfq-dialog.tsx6
2 files changed, 65 insertions, 53 deletions
diff --git a/lib/rfqs/table/ItemsDialog.tsx b/lib/rfqs/table/ItemsDialog.tsx
index f1dbf90e..3d822499 100644
--- a/lib/rfqs/table/ItemsDialog.tsx
+++ b/lib/rfqs/table/ItemsDialog.tsx
@@ -96,16 +96,16 @@ export function RfqsItemsDialog({
rfqType
}: RfqsItemsDialogProps) {
const rfqId = rfq?.rfqId ?? 0;
-
+
// 편집 가능 여부 확인 - DRAFT 상태일 때만 편집 가능
const isEditable = rfq?.status === "DRAFT";
-
+
// 초기 아이템 ID 목록을 추적하기 위한 상태 추가
const [initialItemIds, setInitialItemIds] = React.useState<(number | undefined)[]>([]);
-
+
// 삭제된 아이템 ID를 저장하는 상태 추가
const [deletedItemIds, setDeletedItemIds] = React.useState<number[]>([]);
-
+
// 1) form
const form = useForm<ItemsFormSchema>({
resolver: zodResolver(itemsFormSchema),
@@ -125,24 +125,24 @@ export function RfqsItemsDialog({
// 다이얼로그가 열릴 때마다 폼 초기화 및 초기 아이템 ID 저장
React.useEffect(() => {
if (open) {
- const initialItems = defaultItems.length > 0
+ const initialItems = defaultItems.length > 0
? defaultItems.map((it) => ({
- id: it.id,
- quantity: it.quantity ?? 1,
- uom: it.uom ?? "each",
- itemCode: it.itemCode ?? "",
- description: it.description ?? "",
- }))
+ id: it.id,
+ quantity: it.quantity ?? 1,
+ uom: it.uom ?? "each",
+ itemCode: it.itemCode ?? "",
+ description: it.description ?? "",
+ }))
: [{ itemCode: "", description: "", quantity: 1, uom: "each" }];
-
+
form.reset({
rfqId,
items: initialItems,
});
-
+
// 초기 아이템 ID 목록 저장
setInitialItemIds(defaultItems.map(item => item.id));
-
+
// 삭제된 아이템 목록 초기화
setDeletedItemIds([]);
setHasUnsavedChanges(false);
@@ -158,7 +158,7 @@ export function RfqsItemsDialog({
// 폼 변경 감지 - 편집 가능한 경우에만 변경 감지
React.useEffect(() => {
if (!isEditable) return;
-
+
const subscription = form.watch(() => {
setHasUnsavedChanges(true);
});
@@ -177,16 +177,16 @@ export function RfqsItemsDialog({
// 4) Add item row with auto-focus
function handleAddItem() {
if (!isEditable) return;
-
+
// 명시적으로 숫자 타입으로 지정
- append({
- itemCode: "",
- description: "",
- quantity: 1,
- uom: "each"
+ append({
+ itemCode: "",
+ description: "",
+ quantity: 1,
+ uom: "each"
});
setHasUnsavedChanges(true);
-
+
// 다음 렌더링 사이클에서 새로 추가된 항목에 포커스
setTimeout(() => {
const newIndex = fields.length;
@@ -200,17 +200,17 @@ export function RfqsItemsDialog({
// 항목 직접 삭제 - 기존 ID가 있을 경우 삭제 목록에 추가
const handleRemoveItem = (index: number) => {
if (!isEditable) return;
-
+
const itemToRemove = form.getValues().items[index];
-
+
// 기존 ID가 있는 아이템이라면 삭제 목록에 추가
if (itemToRemove.id !== undefined) {
setDeletedItemIds(prev => [...prev, itemToRemove.id as number]);
}
-
+
remove(index);
setHasUnsavedChanges(true);
-
+
// 포커스 처리: 다음 항목이 있으면 다음 항목으로, 없으면 마지막 항목으로
setTimeout(() => {
const nextIndex = Math.min(index, fields.length - 1);
@@ -232,7 +232,7 @@ export function RfqsItemsDialog({
// 필드 포커스 유틸리티 함수
const focusField = (selector: string) => {
if (!isEditable) return;
-
+
setTimeout(() => {
const element = document.querySelector(selector) as HTMLInputElement | null;
if (element) {
@@ -244,28 +244,28 @@ export function RfqsItemsDialog({
// 5) Submit - 업데이트된 제출 로직 (생성/수정 + 삭제 처리)
async function onSubmit(data: ItemsFormSchema) {
if (!isEditable) return;
-
+
try {
setIsSubmitting(true);
-
+
// 각 아이템이 유효한지 확인
const anyInvalidItems = data.items.some(item => !item.itemCode || item.quantity < 1);
-
+
if (anyInvalidItems) {
toast.error("유효하지 않은 아이템이 있습니다. 모든 필드를 확인해주세요.");
setIsSubmitting(false);
return;
}
-
+
// 1. 삭제 처리 - 삭제된 아이템 ID가 있으면 삭제 요청
- const deletePromises = deletedItemIds.map(id =>
+ const deletePromises = deletedItemIds.map(id =>
deleteRfqItem({
id: id,
rfqId: rfqId,
rfqType: rfqType ?? RfqType.PURCHASE
})
);
-
+
// 2. 생성/수정 처리 - 폼에 남아있는 아이템들
const upsertPromises = data.items.map((item) =>
createRfqItem({
@@ -273,13 +273,13 @@ export function RfqsItemsDialog({
itemCode: item.itemCode,
description: item.description,
// 명시적으로 숫자로 변환
- quantity: Number(item.quantity),
+ quantity: Number(item.quantity),
uom: item.uom,
rfqType: rfqType ?? RfqType.PURCHASE,
id: item.id // 기존 ID가 있으면 업데이트, 없으면 생성
})
);
-
+
// 모든 요청 병렬 처리
await Promise.all([...deletePromises, ...upsertPromises]);
@@ -296,7 +296,7 @@ export function RfqsItemsDialog({
// 단축키 처리 - 편집 가능한 경우에만 단축키 활성화
React.useEffect(() => {
if (!isEditable) return;
-
+
const handleKeyDown = (e: KeyboardEvent) => {
// Alt+N: 새 항목 추가
if (e.altKey && e.key === 'n') {
@@ -336,8 +336,8 @@ export function RfqsItemsDialog({
</Badge>
)}
{rfq?.status && (
- <Badge
- variant={rfq.status === "DRAFT" ? "outline" : "secondary"}
+ <Badge
+ variant={rfq.status === "DRAFT" ? "outline" : "secondary"}
className="ml-1"
>
{rfq.status}
@@ -345,8 +345,8 @@ export function RfqsItemsDialog({
)}
</DialogTitle>
<DialogDescription>
- {isEditable
- ? (rfq?.description || '아이템을 각 행에 하나씩 추가할 수 있습니다.')
+ {isEditable
+ ? (rfq?.description || '아이템을 각 행에 하나씩 추가할 수 있습니다.')
: '드래프트 상태가 아닌 RFQ는 아이템을 편집할 수 없습니다.'}
</DialogDescription>
</DialogHeader>
@@ -393,6 +393,7 @@ export function RfqsItemsDialog({
<div key={field.id} className="flex items-center gap-2 group hover:bg-gray-50 p-1 rounded-md transition-colors">
{/* -- itemCode + Popover(Select) -- */}
{isEditable ? (
+ // 전체 FormField 컴포넌트와 아이템 선택 로직 개선
<FormField
control={form.control}
name={`items.${index}.itemCode`}
@@ -401,7 +402,7 @@ export function RfqsItemsDialog({
const selected = filteredItems.find(it => it.code === field.value);
return (
- <FormItem className="flex items-center gap-2 w-[250px]">
+ <FormItem className="flex items-center gap-2 w-[250px]" style={{width:250}}>
<FormControl>
<Popover open={popoverOpen} onOpenChange={setPopoverOpen}>
<PopoverTrigger asChild>
@@ -413,12 +414,17 @@ export function RfqsItemsDialog({
variant="outline"
role="combobox"
aria-expanded={popoverOpen}
- className="w-full justify-between"
+ className="flex items-center"
data-error={!!form.formState.errors.items?.[index]?.itemCode}
data-state={selected ? "filled" : "empty"}
+ style={{width:250}}
>
- {selected ? `${selected.code} - ${selected.name}` : "아이템 선택..."}
- <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
+ <div className="flex-1 overflow-hidden mr-2 text-left">
+ <span className="block truncate" style={{width:200}}>
+ {selected ? `${selected.code} - ${selected.name}` : "아이템 선택..."}
+ </span>
+ </div>
+ <ChevronsUpDown className="h-4 w-4 flex-shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-[400px] p-0">
@@ -440,7 +446,9 @@ export function RfqsItemsDialog({
focusField(`input[name="items.${index}.description"]`);
}}
>
- {label}
+ <div className="flex-1 overflow-hidden">
+ <span className="block truncate">{label}</span>
+ </div>
<Check
className={
"ml-auto h-4 w-4" +
@@ -486,9 +494,9 @@ export function RfqsItemsDialog({
render={({ field }) => (
<FormItem className="w-[400px]">
<FormControl>
- <Input
- className="w-full"
- placeholder="아이템 상세 정보"
+ <Input
+ className="w-full"
+ placeholder="아이템 상세 정보"
{...field}
onKeyDown={(e) => {
if (e.key === 'Enter') {
@@ -650,7 +658,7 @@ export function RfqsItemsDialog({
</span>
)}
</div>
-
+
{isEditable && (
<div className="text-xs text-muted-foreground">
<span className="inline-flex items-center gap-1 mr-2">
@@ -680,12 +688,12 @@ export function RfqsItemsDialog({
<TooltipContent>변경사항을 저장하지 않고 나가기</TooltipContent>
</Tooltip>
</TooltipProvider>
-
+
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
- <Button
- type="submit"
+ <Button
+ type="submit"
disabled={isSubmitting || (!form.formState.isDirty && deletedItemIds.length === 0) || !form.formState.isValid}
>
{isSubmitting ? (
diff --git a/lib/rfqs/table/add-rfq-dialog.tsx b/lib/rfqs/table/add-rfq-dialog.tsx
index 1d824bc0..45390cd0 100644
--- a/lib/rfqs/table/add-rfq-dialog.tsx
+++ b/lib/rfqs/table/add-rfq-dialog.tsx
@@ -128,7 +128,11 @@ export function AddRfqDialog({ rfqType = RfqType.PURCHASE }: AddRfqDialogProps)
}, [budgetaryRfqs, budgetarySearchTerm]);
// 프로젝트 선택 처리
- const handleProjectSelect = (project: Project) => {
+ const handleProjectSelect = (project: Project | null) => {
+ if (project === null) {
+ return;
+ }
+
form.setValue("projectId", project.id);
};