update netListTable, CreateOrUpdateHaulModal

This commit is contained in:
2025-11-10 16:11:02 +07:00
parent f3b0e7b7eb
commit c26de5aefc
2 changed files with 149 additions and 88 deletions

View File

@@ -36,6 +36,14 @@ const UNITS_OPTIONS = UNITS.map((unit) => ({
value: unit.toString(),
}));
const SIZE_UNITS = ["cm", "m"] as const;
type SizeUnit = (typeof SIZE_UNITS)[number];
const SIZE_UNITS_OPTIONS = SIZE_UNITS.map((unit) => ({
label: unit,
value: unit,
}));
// Zod schema cho 1 dòng cá
const fishItemSchema = z.object({
id: z.number().min(1, "Chọn loài cá"),
@@ -47,6 +55,7 @@ const fishItemSchema = z.object({
.number({ invalid_type_error: "Kích thước phải là số" })
.positive("Kích thước > 0")
.optional(),
sizeUnit: z.enum(SIZE_UNITS),
});
// Schema tổng: mảng các item
@@ -60,6 +69,7 @@ const defaultItem = (): FormValues["fish"][number] => ({
quantity: 1,
unit: "con",
size: undefined,
sizeUnit: "cm",
});
const CreateOrUpdateHaulModal: React.FC<CreateOrUpdateHaulModalProps> = ({
@@ -210,6 +220,7 @@ const CreateOrUpdateHaulModal: React.FC<CreateOrUpdateHaulModalProps> = ({
quantity: (h.catch_number as number) ?? 1,
unit: (h.catch_unit as Unit) ?? (defaultItem().unit as Unit),
size: (h.fish_size as number) ?? undefined,
sizeUnit: "cm" as SizeUnit,
}));
reset({ fish: mapped as any });
setIsCreateMode(false);
@@ -225,9 +236,11 @@ const CreateOrUpdateHaulModal: React.FC<CreateOrUpdateHaulModalProps> = ({
}, [isVisible, fishingLog?.info, reset]);
const renderRow = (item: any, index: number) => {
const isExpanded = expandedFishIndices.includes(index);
// Give expanded card highest zIndex, others get decreasing zIndex based on position
const cardZIndex = isExpanded ? 1000 : 100 - index;
return (
<View key={item._id} style={styles.fishCard}>
<View key={item._id} style={[styles.fishCard, { zIndex: cardZIndex }]}>
{/* Delete + Chevron buttons - top right corner */}
<View
style={{
@@ -315,13 +328,13 @@ const CreateOrUpdateHaulModal: React.FC<CreateOrUpdateHaulModalProps> = ({
{/* Form - visible when expanded */}
{isExpanded && (
<View style={{ paddingRight: 100 }}>
<View style={{ paddingRight: 10 }}>
{/* Species dropdown */}
<Controller
control={control}
name={`fish.${index}.id`}
render={({ field: { value, onChange } }) => (
<View style={styles.fieldGroup}>
<View style={[styles.fieldGroup, { marginTop: 20 }]}>
<Text style={styles.label}>Tên </Text>
<Select
options={fishSpecies!.map((fish) => ({
@@ -342,84 +355,117 @@ const CreateOrUpdateHaulModal: React.FC<CreateOrUpdateHaulModalProps> = ({
)}
/>
{/* Quantity */}
<Controller
control={control}
name={`fish.${index}.quantity`}
render={({ field: { value, onChange, onBlur } }) => (
<View style={styles.fieldGroup}>
<Text style={styles.label}>Số lượng</Text>
<TextInput
keyboardType="numeric"
value={String(value ?? "")}
onBlur={onBlur}
onChangeText={(t) =>
onChange(Number(t.replace(/,/g, ".")) || 0)
}
style={[styles.input, !isEditing && styles.inputDisabled]}
editable={isEditing}
/>
{errors.fish?.[index]?.quantity && (
<Text style={styles.errorText}>
{errors.fish[index]?.quantity?.message as string}
</Text>
{/* Số lượng & Đơn vị cùng hàng */}
<View style={{ flexDirection: "row", gap: 12 }}>
<View style={{ flex: 1 }}>
<Controller
control={control}
name={`fish.${index}.quantity`}
render={({ field: { value, onChange, onBlur } }) => (
<View style={styles.fieldGroup}>
<Text style={styles.label}>Số lượng</Text>
<TextInput
keyboardType="numeric"
value={String(value ?? "")}
onBlur={onBlur}
onChangeText={(t) =>
onChange(Number(t.replace(/,/g, ".")) || 0)
}
style={[
styles.input,
!isEditing && styles.inputDisabled,
]}
editable={isEditing}
/>
{errors.fish?.[index]?.quantity && (
<Text style={styles.errorText}>
{errors.fish[index]?.quantity?.message as string}
</Text>
)}
</View>
)}
</View>
)}
/>
/>
</View>
<View style={{ flex: 1 }}>
<Controller
control={control}
name={`fish.${index}.unit`}
render={({ field: { value, onChange } }) => (
<View style={styles.fieldGroup}>
<Text style={styles.label}>Đơn vị</Text>
<Select
options={UNITS_OPTIONS.map((unit) => ({
label: unit.label,
value: unit.value,
}))}
value={value}
onChange={onChange}
placeholder="Chọn đơn vị"
disabled={!isEditing}
listStyle={{ maxHeight: 100 }}
/>
{errors.fish?.[index]?.unit && (
<Text style={styles.errorText}>
{errors.fish[index]?.unit?.message as string}
</Text>
)}
</View>
)}
/>
</View>
</View>
{/* Unit dropdown */}
<Controller
control={control}
name={`fish.${index}.unit`}
render={({ field: { value, onChange } }) => (
<View style={styles.fieldGroup}>
<Text style={styles.label}>Đơn vị</Text>
<Select
options={UNITS_OPTIONS.map((unit) => ({
label: unit.label,
value: unit.value,
}))}
value={value}
onChange={onChange}
placeholder="Chọn đơn vị"
disabled={!isEditing}
listStyle={{ maxHeight: 100 }}
/>
{errors.fish?.[index]?.unit && (
<Text style={styles.errorText}>
{errors.fish[index]?.unit?.message as string}
</Text>
{/* Size (optional) + Unit dropdown */}
<View style={{ flexDirection: "row", gap: 12 }}>
<View style={{ flex: 1 }}>
<Controller
control={control}
name={`fish.${index}.size`}
render={({ field: { value, onChange, onBlur } }) => (
<View style={styles.fieldGroup}>
<Text style={styles.label}>Kích thước</Text>
<TextInput
keyboardType="numeric"
value={value ? String(value) : ""}
onBlur={onBlur}
onChangeText={(t) =>
onChange(t ? Number(t.replace(/,/g, ".")) : undefined)
}
style={[
styles.input,
!isEditing && styles.inputDisabled,
]}
editable={isEditing}
/>
{errors.fish?.[index]?.size && (
<Text style={styles.errorText}>
{errors.fish[index]?.size?.message as string}
</Text>
)}
</View>
)}
</View>
)}
/>
{/* Size (optional) */}
<Controller
control={control}
name={`fish.${index}.size`}
render={({ field: { value, onChange, onBlur } }) => (
<View style={styles.fieldGroup}>
<Text style={styles.label}>Kích thước (cm) tùy chọn</Text>
<TextInput
keyboardType="numeric"
value={value ? String(value) : ""}
onBlur={onBlur}
onChangeText={(t) =>
onChange(t ? Number(t.replace(/,/g, ".")) : undefined)
}
style={[styles.input, !isEditing && styles.inputDisabled]}
editable={isEditing}
/>
{errors.fish?.[index]?.size && (
<Text style={styles.errorText}>
{errors.fish[index]?.size?.message as string}
</Text>
/>
</View>
<View style={{ flex: 1 }}>
<Controller
control={control}
name={`fish.${index}.sizeUnit`}
render={({ field: { value, onChange } }) => (
<View style={styles.fieldGroup}>
<Text style={styles.label}>Đơn vị</Text>
<Select
options={SIZE_UNITS_OPTIONS}
value={value}
onChange={onChange}
placeholder="Chọn đơn vị"
disabled={!isEditing}
listStyle={{ maxHeight: 80 }}
/>
</View>
)}
</View>
)}
/>
/>
</View>
</View>
</View>
)}
</View>
@@ -447,15 +493,20 @@ const CreateOrUpdateHaulModal: React.FC<CreateOrUpdateHaulModalProps> = ({
<View style={styles.headerButtons}>
{isEditing ? (
<>
<TouchableOpacity
onPress={() => {
setIsEditing(false);
reset(); // reset to previous values
}}
style={[styles.saveButton, { backgroundColor: "#6c757d" }]}
>
<Text style={styles.saveButtonText}>Hủy</Text>
</TouchableOpacity>
{!isCreateMode && (
<TouchableOpacity
onPress={() => {
setIsEditing(false);
reset(); // reset to previous values
}}
style={[
styles.saveButton,
{ backgroundColor: "#6c757d" },
]}
>
<Text style={styles.saveButtonText}>Hủy</Text>
</TouchableOpacity>
)}
<TouchableOpacity
onPress={handleSubmit(onSubmit)}
style={styles.saveButton}