did some more cleanup of the interface and UI interactions.
This commit is contained in:
parent
466e005e4e
commit
bf3923b4b9
@ -2,7 +2,7 @@ import { Product } from "@/lib/product";
|
|||||||
|
|
||||||
export const products = [
|
export const products = [
|
||||||
// Sheet goods
|
// Sheet goods
|
||||||
new Product(25, {l: 4, w : 8, u: "ft"}, { name: "Plywood 1/4\"" }),
|
new Product(15, {l: 4, w : 8, u: "ft"}, { name: "Plywood 1/4\"" }),
|
||||||
new Product(20, {l: 4, w : 8, u: "ft"}, { name: "Plywood 1/2\"" }),
|
new Product(20, {l: 4, w : 8, u: "ft"}, { name: "Plywood 1/2\"" }),
|
||||||
new Product(25, {l: 4, w : 8, u: "ft"}, { name: "Plywood 3/4\"" }),
|
new Product(25, {l: 4, w : 8, u: "ft"}, { name: "Plywood 3/4\"" }),
|
||||||
new Product(5, {l: 4, w : 8, u: "ft"}, { name: "Thin Panel Board" }),
|
new Product(5, {l: 4, w : 8, u: "ft"}, { name: "Thin Panel Board" }),
|
||||||
|
3
app.json
3
app.json
@ -2,10 +2,11 @@
|
|||||||
"expo": {
|
"expo": {
|
||||||
"name": "PliWould",
|
"name": "PliWould",
|
||||||
"slug": "PliWould",
|
"slug": "PliWould",
|
||||||
|
"scheme": "tech.damngood.PliWould",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"orientation": "portrait",
|
"orientation": "portrait",
|
||||||
"icon": "./assets/images/icon.png",
|
"icon": "./assets/images/icon.png",
|
||||||
"scheme": "myapp",
|
"entrypoint": "./app/app.tsx",
|
||||||
"userInterfaceStyle": "automatic",
|
"userInterfaceStyle": "automatic",
|
||||||
"splash": {
|
"splash": {
|
||||||
"image": "./assets/images/splash.png",
|
"image": "./assets/images/splash.png",
|
||||||
|
@ -10,7 +10,8 @@ import { setupStore } from '../store';
|
|||||||
export default function TabLayout() {
|
export default function TabLayout() {
|
||||||
const colorScheme = useColorScheme();
|
const colorScheme = useColorScheme();
|
||||||
const store = setupStore({
|
const store = setupStore({
|
||||||
products: fixtures.map(p => p.asObject)
|
products: fixtures.map(p => p.asObject),
|
||||||
|
units: "ft",
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
@ -22,7 +23,7 @@ export default function TabLayout() {
|
|||||||
<Tabs.Screen
|
<Tabs.Screen
|
||||||
name="index"
|
name="index"
|
||||||
options={{
|
options={{
|
||||||
title: 'Home Screen',
|
title: 'Measure',
|
||||||
tabBarIcon: ({ color, focused }) => (
|
tabBarIcon: ({ color, focused }) => (
|
||||||
<TabBarIcon name={focused ? 'scale' : 'scale-outline'} color={color} />
|
<TabBarIcon name={focused ? 'scale' : 'scale-outline'} color={color} />
|
||||||
),
|
),
|
||||||
@ -31,7 +32,7 @@ export default function TabLayout() {
|
|||||||
<Tabs.Screen
|
<Tabs.Screen
|
||||||
name="product-editor"
|
name="product-editor"
|
||||||
options={{
|
options={{
|
||||||
title: 'Products',
|
title: 'Edit Products',
|
||||||
tabBarIcon: ({ color, focused }) => (
|
tabBarIcon: ({ color, focused }) => (
|
||||||
<TabBarIcon name={focused ? 'list' : 'list-outline'} color={color} />
|
<TabBarIcon name={focused ? 'list' : 'list-outline'} color={color} />
|
||||||
),
|
),
|
||||||
|
12
app/store.ts
12
app/store.ts
@ -3,25 +3,29 @@ import { configureStore } from '@reduxjs/toolkit';
|
|||||||
import { rememberReducer, rememberEnhancer } from 'redux-remember';
|
import { rememberReducer, rememberEnhancer } from 'redux-remember';
|
||||||
import reducers from "@/features/product/productSlice"
|
import reducers from "@/features/product/productSlice"
|
||||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||||
import { Product, ProductData, } from "@/lib/product";
|
import { ProductData, } from "@/lib/product";
|
||||||
|
import {Length} from "convert"
|
||||||
|
|
||||||
const rememberedKeys = ['products'];
|
const rememberedKeys = ['products'];
|
||||||
|
|
||||||
const rootReducer = reducers;
|
const rootReducer = reducers;
|
||||||
|
|
||||||
|
const isBrowser = (typeof window !== "undefined");
|
||||||
|
|
||||||
export function setupStore(preloadedState = {
|
export function setupStore(preloadedState = {
|
||||||
products: [] as ProductData[],
|
products: [] as ProductData[],
|
||||||
|
units: "ft" as Length,
|
||||||
}) {
|
}) {
|
||||||
return configureStore({
|
return configureStore({
|
||||||
reducer: rememberReducer(reducers),
|
reducer: rememberReducer(reducers),
|
||||||
preloadedState,
|
preloadedState,
|
||||||
enhancers: (getDefaultEnhancers) => getDefaultEnhancers().concat(
|
enhancers: (getDefaultEnhancers) => getDefaultEnhancers().concat(
|
||||||
rememberEnhancer(
|
rememberEnhancer(
|
||||||
AsyncStorage,
|
isBrowser ? window.localStorage : AsyncStorage,
|
||||||
rememberedKeys,
|
rememberedKeys,
|
||||||
{
|
{
|
||||||
persistWholeStore: true,
|
persistWholeStore: false,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
@ -1,18 +1,21 @@
|
|||||||
import { MeasurementInput } from "./MeasurementInput";
|
import { MeasurementInput } from "./MeasurementInput";
|
||||||
import { area_t, dimensions_t } from "@/lib/product";
|
import { area_t, dimensions_t } from "@/lib/product";
|
||||||
|
import { Length } from "convert";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { StyleSheet, View } from "react-native";
|
import { StyleSheet, Text, View } from "react-native";
|
||||||
|
|
||||||
export type AreaInputProps = {
|
export type AreaInputProps = {
|
||||||
onMeasurementSet?: (area : dimensions_t) => any,
|
onMeasurementSet?: (area : dimensions_t) => any,
|
||||||
defaultValue?: area_t,
|
defaultValue?: area_t,
|
||||||
lengthLabel?: string,
|
lengthLabel?: string,
|
||||||
widthLabel?: string,
|
widthLabel?: string,
|
||||||
|
units?: Length,
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AreaInput({onMeasurementSet, lengthLabel, widthLabel, defaultValue} : AreaInputProps) {
|
export function AreaInput({onMeasurementSet, lengthLabel, widthLabel, defaultValue, units} : AreaInputProps) {
|
||||||
|
|
||||||
defaultValue = defaultValue || {l: 0, w: 0, u: "ft"}
|
defaultValue = defaultValue || {l: 0, w: 0, u: "ft"}
|
||||||
|
units = units || "ft"
|
||||||
|
|
||||||
const [area, setArea] = useState(defaultValue)
|
const [area, setArea] = useState(defaultValue)
|
||||||
|
|
||||||
@ -44,11 +47,14 @@ export function AreaInput({onMeasurementSet, lengthLabel, widthLabel, defaultVal
|
|||||||
defaultValue={{l: area.l, u: area.u}}
|
defaultValue={{l: area.l, u: area.u}}
|
||||||
onValueSet={doOnLengthSet}
|
onValueSet={doOnLengthSet}
|
||||||
label={lengthLabel}
|
label={lengthLabel}
|
||||||
|
units={units}
|
||||||
/>
|
/>
|
||||||
|
<Text style={{fontSize: 30,}} > x </Text>
|
||||||
<MeasurementInput
|
<MeasurementInput
|
||||||
defaultValue={{l: area.w, u: area.u}}
|
defaultValue={{l: area.w, u: area.u}}
|
||||||
onValueSet={doOnWidthSet}
|
onValueSet={doOnWidthSet}
|
||||||
label={widthLabel}
|
label={widthLabel}
|
||||||
|
units={units}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
@ -56,6 +62,7 @@ export function AreaInput({onMeasurementSet, lengthLabel, widthLabel, defaultVal
|
|||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
areaInputWrapper: {
|
areaInputWrapper: {
|
||||||
flexDirection: "row"
|
flexDirection: "row",
|
||||||
|
verticalAlign: "middle",
|
||||||
}
|
}
|
||||||
})
|
})
|
@ -9,13 +9,16 @@ export type MeasurementInputProps = {
|
|||||||
onValueSet?: (d: dimensions_t) => any,
|
onValueSet?: (d: dimensions_t) => any,
|
||||||
defaultValue: length_t;
|
defaultValue: length_t;
|
||||||
label?: string,
|
label?: string,
|
||||||
|
units?: Length,
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MeasurementInput({onValueSet, defaultValue, label}: MeasurementInputProps) {
|
export function MeasurementInput({onValueSet, defaultValue, label, units}: MeasurementInputProps) {
|
||||||
|
|
||||||
const [mValue, setMValue] = useState(defaultValue)
|
const [mValue, setMValue] = useState(defaultValue)
|
||||||
const defValue = Number.isNaN(defaultValue.l) ? 0 : defaultValue.l
|
const defValue = Number.isNaN(defaultValue.l) ? 0 : defaultValue.l
|
||||||
|
|
||||||
|
units = units || "ft";
|
||||||
|
|
||||||
function doOnValueSet(value : string) {
|
function doOnValueSet(value : string) {
|
||||||
setMValue(mValue);
|
setMValue(mValue);
|
||||||
const iVal = parseFloat(value) || parseInt(value);
|
const iVal = parseFloat(value) || parseInt(value);
|
||||||
@ -37,7 +40,7 @@ export function MeasurementInput({onValueSet, defaultValue, label}: MeasurementI
|
|||||||
style={styles.lengthInput}
|
style={styles.lengthInput}
|
||||||
aria-label={label || "Enter measurement"}
|
aria-label={label || "Enter measurement"}
|
||||||
/>
|
/>
|
||||||
<Text style={styles.unitHints}>{mValue.u}</Text>
|
<Text style={styles.unitHints}>{units}</Text>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -45,10 +48,13 @@ export function MeasurementInput({onValueSet, defaultValue, label}: MeasurementI
|
|||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
inputWrapper: {
|
inputWrapper: {
|
||||||
alignItems: "flex-start",
|
alignItems: "flex-start",
|
||||||
flexDirection: "row"
|
flexDirection: "row",
|
||||||
|
verticalAlign: "middle"
|
||||||
},
|
},
|
||||||
unitHints: {
|
unitHints: {
|
||||||
padding: 10,
|
padding: 10,
|
||||||
|
fontSize: 20,
|
||||||
|
verticalAlign: "middle",
|
||||||
},
|
},
|
||||||
lengthInput: {
|
lengthInput: {
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
|
@ -1,27 +1,36 @@
|
|||||||
import { StyleSheet, Text, TextInput, View } from "react-native";
|
import { StyleSheet, Text, TextInput, View } from "react-native";
|
||||||
import Slider from '@react-native-community/slider';
|
import Slider from '@react-native-community/slider';
|
||||||
import { useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
type PercentDamageProps = {
|
type PercentDamageProps = {
|
||||||
onSetPercentage: (percent: number) => any;
|
onSetPercentage: (percent: number) => any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PercentDamage ({onSetPercentage} : PercentDamageProps) {
|
function getDamangeColor(damage : number) {
|
||||||
|
if (damage === 0) return "black";
|
||||||
|
if (damage <= 20) return "blue";
|
||||||
|
if (damage <= 50) return "orange";
|
||||||
|
return "red";
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PercentDamage({ onSetPercentage }: PercentDamageProps) {
|
||||||
const [damage, setDamage] = useState(0);
|
const [damage, setDamage] = useState(0);
|
||||||
function doOnChangeText (val : number) {
|
const [damageColor, setDamageColor] = useState("black");
|
||||||
setDamage(val);
|
function doOnChangeText(val: number) {
|
||||||
onSetPercentage(val / 100);
|
setDamage(val || 0);
|
||||||
|
onSetPercentage((val / 100) || 0);
|
||||||
|
setDamageColor(getDamangeColor(val || 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.wrapper}>
|
<View style={styles.wrapper}>
|
||||||
<Slider
|
<Slider
|
||||||
value={damage}
|
|
||||||
minimumValue={0}
|
minimumValue={0}
|
||||||
maximumValue={100}
|
maximumValue={100}
|
||||||
step={5}
|
step={5}
|
||||||
onValueChange={doOnChangeText}
|
onValueChange={doOnChangeText}
|
||||||
/>
|
/>
|
||||||
<Text style={styles.label}> {damage}% Damage</Text>
|
<Text style={{ ...styles.label, color: damageColor }}> {damage}% Damage</Text>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -40,5 +49,9 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
margin: 5,
|
margin: 5,
|
||||||
|
alignSelf: "center",
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: "bold",
|
||||||
|
fontStyle: "italic",
|
||||||
}
|
}
|
||||||
})
|
})
|
@ -83,12 +83,14 @@ export default function ProductCalculatorSelector() {
|
|||||||
onMeasurementSet={onMeasurementSet}
|
onMeasurementSet={onMeasurementSet}
|
||||||
widthLabel='enter width'
|
widthLabel='enter width'
|
||||||
lengthLabel='enter length'
|
lengthLabel='enter length'
|
||||||
|
units={measurement.u}
|
||||||
/>
|
/>
|
||||||
:
|
:
|
||||||
<MeasurementInput
|
<MeasurementInput
|
||||||
defaultValue={activeProduct.dimensions}
|
defaultValue={activeProduct.dimensions}
|
||||||
onValueSet={onMeasurementSet}
|
onValueSet={onMeasurementSet}
|
||||||
label="enter length"
|
label="enter length"
|
||||||
|
units={measurement.u}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
) : (
|
) : (
|
||||||
@ -101,7 +103,7 @@ export default function ProductCalculatorSelector() {
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
{activeProduct &&
|
{activeProduct &&
|
||||||
(<View >
|
(<View style={styles.damageWrapper}>
|
||||||
<PercentDamage
|
<PercentDamage
|
||||||
onSetPercentage={onSetPercentDamage}
|
onSetPercentage={onSetPercentDamage}
|
||||||
/>
|
/>
|
||||||
@ -129,6 +131,7 @@ export const styles = StyleSheet.create({
|
|||||||
inputWrapper: {
|
inputWrapper: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
alignItems: "flex-start",
|
alignItems: "flex-start",
|
||||||
|
verticalAlign: "middle",
|
||||||
},
|
},
|
||||||
unitSelector: {
|
unitSelector: {
|
||||||
},
|
},
|
||||||
@ -195,4 +198,9 @@ export const styles = StyleSheet.create({
|
|||||||
padding: 4,
|
padding: 4,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
damageWrapper: {
|
||||||
|
paddingVertical: 10,
|
||||||
|
paddingHorizontal: 10,
|
||||||
|
},
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
import { Length } from "convert";
|
import { Length } from "convert";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Button, StyleSheet, View } from "react-native";
|
import { Button, StyleSheet, Text, TouchableHighlight, View } from "react-native";
|
||||||
|
|
||||||
export type UnitChooserProps = {
|
export type UnitChooserProps = {
|
||||||
choices: Length[],
|
choices: Length[],
|
||||||
onChoicePressed: (l: Length) => any,
|
onChoicePressed: (l: Length) => any,
|
||||||
activeColor?: string,
|
activeColor?: string,
|
||||||
defaultColor?: string,
|
defaultColor?: string,
|
||||||
|
defaultValue? : Length,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function UnitChooser({ choices, onChoicePressed, activeColor, defaultColor }: UnitChooserProps) {
|
export default function UnitChooser({ choices, onChoicePressed, activeColor, defaultColor, defaultValue }: UnitChooserProps) {
|
||||||
const [value, setValue] = useState(choices[0] as Length);
|
const [value, setValue] = useState(defaultValue || choices[0] as Length);
|
||||||
|
|
||||||
activeColor = activeColor || "lightblue";
|
activeColor = activeColor || "lightblue";
|
||||||
defaultColor = defaultColor || "lightgrey";
|
defaultColor = defaultColor || "lightgrey";
|
||||||
@ -24,11 +25,13 @@ export default function UnitChooser({ choices, onChoicePressed, activeColor, def
|
|||||||
<View style={styles.unitChooser}>
|
<View style={styles.unitChooser}>
|
||||||
{choices.map((ci) => {
|
{choices.map((ci) => {
|
||||||
return (
|
return (
|
||||||
<Button
|
<TouchableHighlight
|
||||||
title={ci}
|
|
||||||
onPress={() => doChoiceClicked(ci)}
|
onPress={() => doChoiceClicked(ci)}
|
||||||
color={value === ci ? activeColor : defaultColor}
|
style={value === ci ? styles.active : styles.default }
|
||||||
/>
|
key={ci}
|
||||||
|
>
|
||||||
|
<Text style={value === ci ? styles.textActive : styles.textDefault}>{ci}</Text>
|
||||||
|
</TouchableHighlight>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -38,12 +41,34 @@ export default function UnitChooser({ choices, onChoicePressed, activeColor, def
|
|||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
unitChooser: {
|
unitChooser: {
|
||||||
|
flexDirection: "row",
|
||||||
|
verticalAlign: "middle",
|
||||||
},
|
},
|
||||||
active: {
|
active: {
|
||||||
|
backgroundColor: "skyblue",
|
||||||
|
padding: 5,
|
||||||
|
borderRadius: 5,
|
||||||
},
|
},
|
||||||
default: {
|
default: {
|
||||||
|
backgroundColor: "lightgray",
|
||||||
}
|
padding: 5,
|
||||||
|
borderRadius: 5,
|
||||||
|
verticalAlign: "middle",
|
||||||
|
},
|
||||||
|
textActive: {
|
||||||
|
marginTop: 2,
|
||||||
|
marginBottom: 2,
|
||||||
|
marginLeft: 10,
|
||||||
|
marginRight: 10,
|
||||||
|
fontSize: 40,
|
||||||
|
},
|
||||||
|
textDefault: {
|
||||||
|
marginTop: 2,
|
||||||
|
marginBottom: 2,
|
||||||
|
marginLeft: 10,
|
||||||
|
marginRight: 10,
|
||||||
|
fontSize: 40,
|
||||||
|
},
|
||||||
|
unitButton: {
|
||||||
|
},
|
||||||
})
|
})
|
@ -1,12 +1,13 @@
|
|||||||
import { createSelector, createSlice, PayloadAction } from '@reduxjs/toolkit';
|
import { createSelector, createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||||
import { area_t, dimensions_t, Id, length_t, Product, ProductData } from '@/lib/product';
|
import { dimensions_t, Id, Product, ProductData } from '@/lib/product';
|
||||||
import uuid from "react-native-uuid";
|
import uuid from "react-native-uuid";
|
||||||
import { RootState } from '@/app/store';
|
import { RootState } from '@/app/store';
|
||||||
import { classToPlain, plainToClass } from 'class-transformer';
|
import { Length } from 'convert';
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
products: [] as ProductData[],
|
products: [] as ProductData[],
|
||||||
}
|
units: "ft",
|
||||||
|
};
|
||||||
|
|
||||||
export type UpdateAttribute = {
|
export type UpdateAttribute = {
|
||||||
product_id: Id,
|
product_id: Id,
|
||||||
@ -30,6 +31,9 @@ const productsState = createSlice({
|
|||||||
name: 'products-slice',
|
name: 'products-slice',
|
||||||
initialState,
|
initialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
|
setUnits(state, action : PayloadAction<Length>) {
|
||||||
|
state.units = action.payload;
|
||||||
|
},
|
||||||
createProduct(state, action: PayloadAction<ProductData>) {
|
createProduct(state, action: PayloadAction<ProductData>) {
|
||||||
if (!state) {
|
if (!state) {
|
||||||
return initialState
|
return initialState
|
||||||
@ -150,6 +154,10 @@ export const selectProductsDatas = (state: RootState) => {
|
|||||||
return state.products;
|
return state.products;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const selectUnits = (state : RootState) => {
|
||||||
|
return (state.units || "ft") as Length;
|
||||||
|
}
|
||||||
|
|
||||||
export const selectProducts = createSelector([selectProductsDatas], productsData => {
|
export const selectProducts = createSelector([selectProductsDatas], productsData => {
|
||||||
return productsData.map(d => Product.fromObject(d));
|
return productsData.map(d => Product.fromObject(d));
|
||||||
})
|
})
|
||||||
@ -172,6 +180,7 @@ export const actions = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const {
|
export const {
|
||||||
|
setUnits,
|
||||||
createProduct,
|
createProduct,
|
||||||
deleteProduct,
|
deleteProduct,
|
||||||
changeKey,
|
changeKey,
|
||||||
|
Loading…
Reference in New Issue
Block a user