260 lines
7.5 KiB
TypeScript
260 lines
7.5 KiB
TypeScript
import React, { useState } from "react";
|
|
import {
|
|
TouchableOpacity,
|
|
Text,
|
|
Image,
|
|
View,
|
|
ScrollView,
|
|
TextInput,
|
|
Appearance,
|
|
Alert,
|
|
} from "react-native";
|
|
import { Stack, useLocalSearchParams, router } from "expo-router";
|
|
import utmObj from "utm-latlng";
|
|
import { styled } from "nativewind";
|
|
import mime from "mime";
|
|
|
|
import uploadImage from "./uploadImage";
|
|
|
|
declare function isNaN(x: string | number): boolean;
|
|
|
|
export default function () {
|
|
const utm = new utmObj();
|
|
const [image, setImage] = useState(null);
|
|
const [storeName, onstoreName] = useState("");
|
|
const [itemName, onitemName] = useState("");
|
|
const [itemVolume, onitemVolume] = useState("");
|
|
const [itemPrice, onitemPrice] = useState("");
|
|
|
|
let { coords } = useLocalSearchParams();
|
|
|
|
const handleSubmit = async () => {
|
|
coords = coords.split("%2B");
|
|
const lon = coords[0];
|
|
const lat = coords[1];
|
|
const utmCoords = utm.convertLatLngToUtm(lat, lon, 5);
|
|
if (
|
|
itemPrice === "" ||
|
|
itemVolume === "" ||
|
|
itemName === "" ||
|
|
storeName === ""
|
|
) {
|
|
Alert.alert(
|
|
"Insufficient information!",
|
|
"Please double-check to ensure all fields have been filled"
|
|
);
|
|
} else {
|
|
let res;
|
|
// add image if image
|
|
if (image) {
|
|
const formData = new FormData();
|
|
formData.append("file", {
|
|
uri: image.uri,
|
|
type: mime.getType(image.uri),
|
|
name: "file",
|
|
} as unknown as File);
|
|
res = await fetch(
|
|
`${process.env.EXPO_PUBLIC_BACKEND_URL}/?` +
|
|
new URLSearchParams({
|
|
easting: utmCoords.Easting,
|
|
northing: utmCoords.Northing,
|
|
zone: utmCoords.ZoneNumber,
|
|
zoneLetter: utmCoords.ZoneLetter,
|
|
name: storeName,
|
|
itemName: itemName,
|
|
itemPrice: itemPrice,
|
|
itemVolume: itemVolume,
|
|
}),
|
|
{
|
|
method: "POST",
|
|
body: formData,
|
|
mode: "cors",
|
|
redirect: "follow",
|
|
}
|
|
);
|
|
} else {
|
|
res = await fetch(
|
|
`${process.env.EXPO_PUBLIC_BACKEND_URL}/?` +
|
|
new URLSearchParams({
|
|
easting: utmCoords.Easting,
|
|
northing: utmCoords.Northing,
|
|
zone: utmCoords.ZoneNumber,
|
|
zoneLetter: utmCoords.ZoneLetter,
|
|
name: storeName,
|
|
itemName: itemName,
|
|
itemPrice: itemPrice,
|
|
itemVolume: itemVolume,
|
|
}),
|
|
{
|
|
method: "POST",
|
|
mode: "cors",
|
|
redirect: "follow",
|
|
}
|
|
);
|
|
}
|
|
|
|
if (res.ok) {
|
|
Alert.alert(
|
|
"Success!",
|
|
"Store has been successfully added. Thank you!",
|
|
[
|
|
{
|
|
text: "OK",
|
|
onPress: () => {
|
|
router.push("/");
|
|
},
|
|
},
|
|
]
|
|
);
|
|
} else {
|
|
switch (res.status) {
|
|
case 400:
|
|
Alert.alert(
|
|
"Error!",
|
|
"Please double-check all fields are filled in."
|
|
);
|
|
break;
|
|
|
|
case 409:
|
|
Alert.alert(
|
|
"Error!",
|
|
"This store already exists! Please click 'More' on its pin and upload an item there.",
|
|
[
|
|
{
|
|
text: "OK",
|
|
onPress: () => {
|
|
router.push("/");
|
|
},
|
|
},
|
|
]
|
|
);
|
|
break;
|
|
|
|
case 500:
|
|
Alert.alert(
|
|
"Error!",
|
|
"Backend server error. Please report to ak95@riseup.net"
|
|
);
|
|
break;
|
|
|
|
default:
|
|
Alert.alert(
|
|
"Error!",
|
|
"Unspecified error. Please report to ak95@riseup.net"
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
const StyledText = styled(Text);
|
|
|
|
return (
|
|
<ScrollView contentContainerStyle={{ flexGrow: 1 }}>
|
|
<View
|
|
style={{
|
|
flex: 1,
|
|
alignItems: "center",
|
|
height: "100%",
|
|
paddingBottom: "5%",
|
|
}}
|
|
>
|
|
<Stack.Screen
|
|
options={{
|
|
title: "New Store",
|
|
}}
|
|
/>
|
|
<TouchableOpacity onPress={() => setImage(uploadImage())}>
|
|
{image ? (
|
|
<Image source={{ uri: image.uri }} className="h-[33vh] w-[100vw]" />
|
|
) : (
|
|
<Image
|
|
source={require("../../../assets/images/photo_placeholder.png")}
|
|
className="h-[33vh] w-[100vw]"
|
|
/>
|
|
)}
|
|
</TouchableOpacity>
|
|
<View
|
|
style={{
|
|
flex: 1,
|
|
justifyContent: "space-between",
|
|
}}
|
|
>
|
|
<View
|
|
style={{
|
|
display: "flex",
|
|
}}
|
|
>
|
|
<TextInput
|
|
className="w-[90vw] mt-4 h-14 text-2xl border rounded text-center dark:border-white dark:text-white"
|
|
onChangeText={onstoreName}
|
|
placeholder="Store Name"
|
|
placeholderTextColor={
|
|
Appearance.getColorScheme() === "dark" ? "#fff" : "#000"
|
|
}
|
|
value={storeName}
|
|
/>
|
|
<TextInput
|
|
className="w-[90vw] mt-4 h-12 text-xl border rounded text-center dark:border-white dark:text-white"
|
|
onChangeText={onitemName}
|
|
placeholder="Item Name"
|
|
placeholderTextColor={
|
|
Appearance.getColorScheme() === "dark" ? "#fff" : "#000"
|
|
}
|
|
value={itemName}
|
|
/>
|
|
<TextInput
|
|
className="w-[90vw] mt-4 h-12 text-xl border rounded text-center dark:border-white dark:text-white"
|
|
onChangeText={(text) => {
|
|
const split = text.split("");
|
|
let numeric = true;
|
|
split.forEach((char: string | number) => {
|
|
if (isNaN(char)) {
|
|
numeric = false;
|
|
}
|
|
});
|
|
if (numeric) onitemVolume(text);
|
|
}}
|
|
placeholder="Item Volume (fl. oz.)"
|
|
placeholderTextColor={
|
|
Appearance.getColorScheme() === "dark" ? "#fff" : "#000"
|
|
}
|
|
inputMode="numeric"
|
|
value={itemVolume}
|
|
/>
|
|
<TextInput
|
|
className="w-[90vw] mt-4 h-12 text-xl border rounded text-center dark:border-white dark:text-white"
|
|
onChangeText={(text) => {
|
|
text = text.slice(2);
|
|
if (text.includes(".")) {
|
|
const split = text.split(".");
|
|
if (split.length < 3) {
|
|
const righthand = split[1];
|
|
if (righthand.length <= 2) {
|
|
onitemPrice(text);
|
|
}
|
|
}
|
|
} else onitemPrice(text);
|
|
}}
|
|
placeholder="Item Price"
|
|
inputMode="decimal"
|
|
placeholderTextColor={
|
|
Appearance.getColorScheme() === "dark" ? "#fff" : "#000"
|
|
}
|
|
value={`$ ${itemPrice}`}
|
|
/>
|
|
</View>
|
|
<TouchableOpacity
|
|
className="px-8 py-4 my-4 bg-green-700 rounded"
|
|
onPress={handleSubmit}
|
|
>
|
|
<StyledText className="text-white text-center text-2xl">
|
|
Submit
|
|
</StyledText>
|
|
</TouchableOpacity>
|
|
</View>
|
|
</View>
|
|
</ScrollView>
|
|
);
|
|
}
|