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

@@ -96,7 +96,12 @@ const NetListTable: React.FC = () => {
{/* Cột Trạng thái */} {/* Cột Trạng thái */}
<View style={[styles.cell, styles.statusContainer]}> <View style={[styles.cell, styles.statusContainer]}>
<View style={styles.statusDot} /> <View
style={[
styles.statusDot,
{ backgroundColor: item.status ? "#2ecc71" : "#FFD600" },
]}
/>
<TouchableOpacity <TouchableOpacity
onPress={() => handleStatusPress(item.fishing_log_id)} onPress={() => handleStatusPress(item.fishing_log_id)}
> >
@@ -125,7 +130,12 @@ const NetListTable: React.FC = () => {
{/* Cột Trạng thái */} {/* Cột Trạng thái */}
<View style={[styles.cell, styles.statusContainer]}> <View style={[styles.cell, styles.statusContainer]}>
<View style={styles.statusDot} /> <View
style={[
styles.statusDot,
{ backgroundColor: item.status ? "#2ecc71" : "#FFD600" },
]}
/>
<TouchableOpacity <TouchableOpacity
onPress={() => handleStatusPress(item.fishing_log_id)} onPress={() => handleStatusPress(item.fishing_log_id)}
> >

View File

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