PliWould/components/ProductEditorItem.tsx

276 lines
7.8 KiB
TypeScript
Raw Normal View History

import { Id, Product, product_type_t } from "@/lib/product";
import { useState } from "react";
import {
Button,
FlatList,
Pressable,
StyleSheet,
Text,
TextInput,
Touchable,
TouchableHighlight,
View,
} from "react-native";
import { ProductAttributeEditor } from "./ProductAttributeEditor";
import { Dropdown } from "react-native-element-dropdown";
import { Ionicons } from "@expo/vector-icons";
import { Length } from "convert";
import { dimensions_t } from "@/lib/dimensions";
2024-06-27 23:31:59 +02:00
export type ProductAddedFunc = () => any;
export type ProductDeletedFunc = (product_id: Id) => any;
export type AttributeAddedFunc = (product_id: Id) => any;
export type AttributeKeyUpdatedFunc = (
product_id: Id,
oldKey: string,
newKey: string
) => any;
export type AttributeUpdatedFunc = (
product_id: Id,
attribute: string,
value: string
) => any;
export type AttributeDeletedFunc = (product_id: Id, attribute: string) => any;
export type PriceUpdatedFunc = (product_id: Id, price: number) => any;
export type DimensionUpdatedFunc = (
product_id: Id,
dimension: dimensions_t
) => any;
export type ProductTypeChangedFunc = (
product_id: Id,
product_type: product_type_t
) => any;
2024-06-27 23:31:59 +02:00
export type ProductEditorItemProps = {
product: Product;
onProductAdded?: ProductAddedFunc;
onProductDeleted?: ProductDeletedFunc;
onAttributeAdded?: AttributeAddedFunc;
onAttributeKeyChanged?: AttributeKeyUpdatedFunc;
onAttributeUpdated?: AttributeUpdatedFunc;
onAttributeDeleted?: AttributeDeletedFunc;
onPriceUpdated?: PriceUpdatedFunc;
onDimensionsUpdated?: DimensionUpdatedFunc;
onProductTypeChanged?: ProductTypeChangedFunc;
};
2024-06-27 23:31:59 +02:00
export const ProductEditorItem = (props: ProductEditorItemProps) => {
const [showAttributes, setShowAttributes] = useState(false);
const product = props.product;
2024-06-27 23:31:59 +02:00
function onProductTypeChange(id: Id, newProductType: product_type_t) {
props.onProductTypeChanged &&
props.onProductTypeChanged(product.id as Id, newProductType);
}
function onAttributeChanged(key: string, newValue: string) {
props.onAttributeUpdated &&
props.onAttributeUpdated(product.id as Id, key, newValue);
}
2024-06-27 23:31:59 +02:00
function onAttributeKeyChanged(oldKey: string, newKey: string) {
props.onAttributeKeyChanged &&
props.onAttributeKeyChanged(product.id as Id, oldKey, newKey);
}
function onAttributeDelete(key: string) {
props.onAttributeDeleted && props.onAttributeDeleted(product.id as Id, key);
}
function onPricePerUnitChange(pricePerUnit: string) {
props.onPriceUpdated &&
props.onPriceUpdated(
product.id as Id,
parseFloat(pricePerUnit) || parseInt(pricePerUnit)
);
}
function onUnitsChanged(newUnits: Length) {
props.onDimensionsUpdated &&
props.onDimensionsUpdated(product.id as Id, {
...(product.dimensions as dimensions_t),
u: newUnits,
});
}
function onChangeLength(len: string) {
const l = parseFloat(len) || parseInt(len);
props.onDimensionsUpdated &&
props.onDimensionsUpdated(product.id as Id, {
...(product.dimensions as dimensions_t),
l,
});
}
2024-06-27 23:31:59 +02:00
function onChangeWidth(width: string) {
const w = width.length == 0 ? null : parseFloat(width) || parseInt(width);
props.onDimensionsUpdated &&
props.onDimensionsUpdated(product.id as Id, {
...(product.dimensions as dimensions_t),
...(w ? { w } : {}),
});
}
2024-06-27 23:31:59 +02:00
function onDeleteProduct() {
props.onProductDeleted && props.onProductDeleted(product.id as Id);
}
2024-06-27 23:31:59 +02:00
const length = new String(
product.dimensions.l || product.dimensions.l || "0"
) as string;
const width = new String(product.dimensions.w || "") as string;
const dimension = product.dimensions.u || product.dimensions.u || "foot";
return (
<View>
<View style={styles.productListHeader}>
<TouchableHighlight
onPress={() => setShowAttributes(!showAttributes)}
aria-label="Product Item"
style={styles.productItemName}
>
{product.attributes && (
<Text style={styles.productNameText}>
{product.attributes.name || `Product ${product.id}`}
</Text>
)}
</TouchableHighlight>
<Pressable
onPress={() => onDeleteProduct()}
aria-label="delete product"
style={styles.deleteProductHighlight}
>
<Ionicons style={styles.deleteProductButton} name="trash-outline" />
</Pressable>
</View>
{showAttributes && (
<View style={styles.detailsWrapper}>
<View style={styles.priceSpecWrapper}>
<Text style={styles.priceLabel}>$</Text>
<TextInput
inputMode="decimal"
defaultValue={new String(product.pricePerUnit).valueOf()}
aria-label="price per unit"
onChangeText={onPricePerUnitChange}
style={styles.priceInput}
/>
<Text style={styles.per}>per</Text>
<Dropdown
data={[
{ label: "feet", value: "ft" },
{ label: "inches", value: "in" },
]}
style={styles.unitsSelect}
mode="modal"
labelField="label"
valueField="value"
value={product.dimensions.u || "ft"}
onChange={(item) => onUnitsChanged(item.value as Length)}
/>
<TextInput
inputMode="decimal"
defaultValue={length}
onChangeText={onChangeLength}
style={styles.lengthInput}
aria-label="length"
/>
<Text style={{ flex: 1 }}>x</Text>
<TextInput
inputMode="decimal"
defaultValue={width}
onChangeText={onChangeWidth}
style={styles.widthInput}
aria-label="width"
/>
</View>
<Button
title="+ Add Attribute"
onPress={() =>
props.onAttributeAdded && props.onAttributeAdded(product.id as Id)
2024-06-27 23:31:59 +02:00
}
/>
{product.attributes && (
<FlatList
style={styles.productAttributesList}
data={Object.entries(product.attributes)}
renderItem={({ item }) => (
<ProductAttributeEditor
onProductTypeChange={onProductTypeChange}
attributeKey={item[0] || "some key"}
attributeValue={item[1]}
onChangeAttributeKey={onAttributeKeyChanged}
onChangeAttribute={onAttributeChanged}
onDelete={onAttributeDelete}
/>
)}
keyExtractor={(item, i) => `${product.id}-${i}`}
/>
)}
2024-06-28 04:00:29 +02:00
</View>
)}
</View>
);
};
2024-06-27 23:31:59 +02:00
const styles = StyleSheet.create({
deleteProductHighlight: {
padding: 5,
borderWidth: 1,
},
deleteProductButton: {
fontSize: 20,
},
detailsWrapper: {},
priceSpecWrapper: {
flexDirection: "row",
},
priceLabel: {},
priceInput: {
flex: 1,
borderWidth: 2,
borderColor: "lightgrey",
borderStyle: "solid",
},
per: {
padding: 5,
},
unitsLabel: {},
unitsSelect: {
flex: 1,
padding: 5,
},
lengthInput: {
flex: 1,
borderWidth: 2,
borderColor: "lightgrey",
borderStyle: "solid",
},
widthInput: {
flex: 1,
borderWidth: 2,
borderColor: "lightgrey",
borderStyle: "solid",
},
productListHeader: {
flexDirection: "row",
padding: 5,
},
productNameText: {
paddingLeft: 10,
paddingRight: 10,
},
productItemName: {
flex: 1,
backgroundColor: "lightgrey",
padding: 4,
margin: 4,
},
productAttributesList: {
margin: 10,
padding: 10,
borderWidth: 1,
borderStyle: "solid",
borderColor: "black",
},
});