Refactor to using RNP & UI Updates.
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FlatList, RefreshControl } from "react-native";
|
||||
import { Button, List, Modal, Portal, Provider } from "react-native-paper";
|
||||
import { FlatList, RefreshControl, View } from "react-native";
|
||||
import { Button, List, Modal, Portal, Searchbar } from "react-native-paper";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { QUERY_ALL_ACTIVE_JOBS } from "../../graphql/jobs.queries";
|
||||
@@ -47,23 +48,80 @@ export function CameraSelectJob({
|
||||
|
||||
const showModal = () => setVisible(true);
|
||||
const hideModal = () => setVisible(false);
|
||||
const containerStyle = { backgroundColor: "white", padding: 20 };
|
||||
|
||||
const onRefresh = async () => {
|
||||
return refetch();
|
||||
};
|
||||
const [searchQuery, setSearchQuery] = React.useState("");
|
||||
|
||||
const onChangeSearch = (query) => setSearchQuery(query);
|
||||
|
||||
const jobs = data
|
||||
? searchQuery === ""
|
||||
? data.jobs
|
||||
: data.jobs.filter(
|
||||
(j) =>
|
||||
(j.ro_number || "")
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.includes(searchQuery.toLowerCase()) ||
|
||||
(j.ownr_co_nm || "")
|
||||
.toLowerCase()
|
||||
.includes(searchQuery.toLowerCase()) ||
|
||||
(j.ownr_fn || "")
|
||||
.toLowerCase()
|
||||
.includes(searchQuery.toLowerCase()) ||
|
||||
(j.ownr_ln || "")
|
||||
.toLowerCase()
|
||||
.includes(searchQuery.toLowerCase()) ||
|
||||
(j.plate_no || "")
|
||||
.toLowerCase()
|
||||
.includes(searchQuery.toLowerCase()) ||
|
||||
(j.v_model_desc || "")
|
||||
.toLowerCase()
|
||||
.includes(searchQuery.toLowerCase()) ||
|
||||
(j.v_make_desc || "")
|
||||
.toLowerCase()
|
||||
.includes(searchQuery.toLowerCase())
|
||||
)
|
||||
: [];
|
||||
|
||||
return (
|
||||
<Provider>
|
||||
<>
|
||||
<Portal>
|
||||
<Modal
|
||||
visible={visible}
|
||||
onDismiss={hideModal}
|
||||
contentContainerStyle={containerStyle}
|
||||
// eslint-disable-next-line react-native/no-color-literals
|
||||
contentContainerStyle={{
|
||||
paddingTop: 20,
|
||||
paddingBottom: 20,
|
||||
flex: 1,
|
||||
backgroundColor: "white",
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
margin: 8,
|
||||
}}
|
||||
>
|
||||
<Button onPress={() => hideModal()}>
|
||||
<Ionicons name="ios-arrow-back" size={32} color="dodgerblue" />
|
||||
</Button>
|
||||
<Searchbar
|
||||
style={{ flex: 1 }}
|
||||
onChangeText={onChangeSearch}
|
||||
value={searchQuery}
|
||||
/>
|
||||
</View>
|
||||
<FlatList
|
||||
refreshControl={
|
||||
<RefreshControl refreshing={loading} onRefresh={onRefresh} />
|
||||
}
|
||||
data={data.jobs}
|
||||
data={[{ id: "temp", ro_number: "Temporary Storage" }, ...jobs]}
|
||||
keyExtractor={(item) => item.id}
|
||||
renderItem={(object) => (
|
||||
<List.Item
|
||||
@@ -71,8 +129,25 @@ export function CameraSelectJob({
|
||||
setCameraJobId(object.item.id);
|
||||
setCameraJob(object.item);
|
||||
hideModal();
|
||||
setSearchQuery("");
|
||||
}}
|
||||
description={`${
|
||||
left={() => {
|
||||
if (object.item.id !== cameraJobId) return null;
|
||||
return (
|
||||
<Ionicons
|
||||
name="ios-checkmark-circle"
|
||||
size={24}
|
||||
color="dodgerblue"
|
||||
style={{ alignSelf: "center" }}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
titleStyle={{
|
||||
...(object.item.id === cameraJobId
|
||||
? { color: "dodgerblue" }
|
||||
: {}),
|
||||
}}
|
||||
title={`${
|
||||
object.item.ro_number ? `${object.item.ro_number} - ` : ``
|
||||
}${object.item.ownr_fn || ""} ${object.item.ownr_ln || ""} ${
|
||||
object.item.ownr_co_nm || ""
|
||||
@@ -85,41 +160,19 @@ export function CameraSelectJob({
|
||||
/>
|
||||
</Modal>
|
||||
</Portal>
|
||||
<Button style={{ marginTop: 30 }} onPress={showModal}>
|
||||
<Button mode="outlined" style={{ margin: 8 }} onPress={showModal}>
|
||||
{cameraJobId
|
||||
? `${cameraJob.ro_number ? `${cameraJob.ro_number} - ` : ``}${
|
||||
cameraJob.ownr_fn || ""
|
||||
} ${cameraJob.ownr_ln || ""} ${cameraJob.ownr_co_nm || ""} - ${
|
||||
cameraJob.v_model_yr || ""
|
||||
} ${cameraJob.v_make_desc || ""} ${cameraJob.v_model_desc || ""}`
|
||||
? cameraJobId === "temp"
|
||||
? t("mediabrowser.labels.temporarystorage")
|
||||
: `${cameraJob.ro_number ? `${cameraJob.ro_number} - ` : ``}${
|
||||
cameraJob.ownr_fn || ""
|
||||
} ${cameraJob.ownr_ln || ""} ${cameraJob.ownr_co_nm || ""} - ${
|
||||
cameraJob.v_model_yr || ""
|
||||
} ${cameraJob.v_make_desc || ""} ${cameraJob.v_model_desc || ""}`
|
||||
: t("mediabrowser.labels.selectjob")}
|
||||
</Button>
|
||||
</Provider>
|
||||
</>
|
||||
);
|
||||
|
||||
// return (
|
||||
// <View
|
||||
// style={{
|
||||
// marginHorizontal: 10,
|
||||
// }}
|
||||
// >
|
||||
// <Picker
|
||||
// selectedValue={cameraJobId}
|
||||
// onValueChange={(value, idx) => {
|
||||
// logImEXEvent("imexmobile_setcamerajobid");
|
||||
// setCameraJobId(value);
|
||||
// setCameraJob(data.jobs[idx]);
|
||||
// }}
|
||||
// >
|
||||
// <Picker.Item
|
||||
// label={t("mediabrowser.labels.selectjob")}
|
||||
// value={null}
|
||||
// key="null"
|
||||
// />
|
||||
|
||||
// </Picker>
|
||||
// </View>
|
||||
// );
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(CameraSelectJob);
|
||||
|
||||
@@ -1,55 +1,78 @@
|
||||
import React, { useState } from "react";
|
||||
import React, { useState, useMemo } from "react";
|
||||
import {
|
||||
FlatList,
|
||||
Image,
|
||||
RefreshControl,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from "react-native";
|
||||
import env from "../../env";
|
||||
import { DetermineFileType } from "../../util/document-upload.utility";
|
||||
import MediaCacheOverlay from "../media-cache-overlay/media-cache-overlay.component";
|
||||
|
||||
const REACT_APP_CLOUDINARY_IMAGE_ENDPOINT =
|
||||
"https://res.cloudinary.com/bodyshop/image/upload";
|
||||
const REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS = "c_fill,f_auto,h_250,w_250";
|
||||
|
||||
export default function JobDocumentsComponent({ job, loading, refetch }) {
|
||||
const [previewVisible, setPreviewVisible] = useState(false);
|
||||
const [imgIndex, setImgIndex] = useState(0);
|
||||
const onRefresh = async () => {
|
||||
return refetch();
|
||||
};
|
||||
|
||||
const fullphotos = useMemo(
|
||||
() =>
|
||||
job.documents.map((doc) => {
|
||||
return {
|
||||
source: {
|
||||
uri: `${env.REACT_APP_CLOUDINARY_ENDPOINT}/${DetermineFileType(
|
||||
doc.type
|
||||
)}/upload/${doc.key}`,
|
||||
},
|
||||
};
|
||||
}),
|
||||
[job.documents]
|
||||
);
|
||||
|
||||
return (
|
||||
<View>
|
||||
<View style={{ flex: 1 }}>
|
||||
<FlatList
|
||||
refreshControl={
|
||||
<RefreshControl refreshing={loading} onRefresh={onRefresh} />
|
||||
}
|
||||
data={job.documents}
|
||||
contentContainerStyle={styles.listContentContainer}
|
||||
keyExtractor={(item) => item.id}
|
||||
numColumns={4}
|
||||
keyExtractor={(item) => item.id}
|
||||
renderItem={(object) => (
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
setImgIndex(object.index);
|
||||
setPreviewVisible(true);
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
flexDirection: "column",
|
||||
margin: 5,
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
style={{ margin: 5 }}
|
||||
source={{
|
||||
width: 100,
|
||||
height: 100,
|
||||
uri: `${REACT_APP_CLOUDINARY_IMAGE_ENDPOINT}/${REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS}/${object.item.key}`,
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
setImgIndex(object.index);
|
||||
setPreviewVisible(true);
|
||||
}}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
>
|
||||
<Image
|
||||
source={{
|
||||
width: 100,
|
||||
height: 100,
|
||||
uri: `${
|
||||
env.REACT_APP_CLOUDINARY_ENDPOINT
|
||||
}/${DetermineFileType(object.item.type)}/upload/${
|
||||
env.REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS
|
||||
}/${object.item.key}`,
|
||||
}}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
/>
|
||||
<Text>{job.documents.length}</Text>
|
||||
<MediaCacheOverlay
|
||||
photos={fullphotos}
|
||||
imgIndex={imgIndex}
|
||||
setImgIndex={setImgIndex}
|
||||
previewVisible={previewVisible}
|
||||
@@ -58,22 +81,3 @@ export default function JobDocumentsComponent({ job, loading, refetch }) {
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
actions: {
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-evenly",
|
||||
},
|
||||
listContentContainer: {
|
||||
flex: 1,
|
||||
},
|
||||
thumbnail: {
|
||||
width: 10,
|
||||
height: 10,
|
||||
backgroundColor: "tomato",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import Dinero from "dinero.js";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FlatList, RefreshControl, StyleSheet, Text, View } from "react-native";
|
||||
import { Card } from "react-native-paper";
|
||||
import { Card, DataTable } from "react-native-paper";
|
||||
|
||||
export default function JobLines({ job, loading, refetch }) {
|
||||
const { t } = useTranslation();
|
||||
@@ -18,60 +18,64 @@ export default function JobLines({ job, loading, refetch }) {
|
||||
|
||||
return (
|
||||
<View>
|
||||
<DataTable>
|
||||
<DataTable.Header>
|
||||
<DataTable.Title style={{ flex: 4 }}>
|
||||
{t("jobdetail.labels.lines_desc")}
|
||||
</DataTable.Title>
|
||||
<DataTable.Title style={{ flex: 2 }}>
|
||||
{t("jobdetail.labels.lines_lbr_ty")}
|
||||
</DataTable.Title>
|
||||
<DataTable.Title style={{ flex: 1 }}>
|
||||
{t("jobdetail.labels.lines_lb_hrs")}
|
||||
</DataTable.Title>
|
||||
<DataTable.Title style={{ flex: 2 }}>
|
||||
{t("jobdetail.labels.lines_part_type")}
|
||||
</DataTable.Title>
|
||||
<DataTable.Title style={{ flex: 1 }}>
|
||||
{t("jobdetail.labels.lines_qty")}
|
||||
</DataTable.Title>
|
||||
<DataTable.Title style={{ flex: 1 }}>
|
||||
{t("jobdetail.labels.lines_price")}
|
||||
</DataTable.Title>
|
||||
</DataTable.Header>
|
||||
</DataTable>
|
||||
|
||||
<FlatList
|
||||
data={job.joblines}
|
||||
refreshControl={
|
||||
<RefreshControl refreshing={loading} onRefresh={onRefresh} />
|
||||
}
|
||||
contentContainerStyle={localStyles.listContentContainer}
|
||||
keyExtractor={(item) => item.id}
|
||||
renderItem={(object) => (
|
||||
<Card>
|
||||
<Card.Content style={localStyles.flexRow}>
|
||||
<Text style={localStyles.growWithEllipsis}>{`${
|
||||
object.item.line_desc
|
||||
}${
|
||||
object.item.part_qty > 1 ? ` x ${object.item.part_qty}` : ""
|
||||
}`}</Text>
|
||||
{object.item.part_type && (
|
||||
<Text style={localStyles.sideMargins}>
|
||||
{t(`jobdetail.part_types.${object.item.part_type}`)}
|
||||
</Text>
|
||||
)}
|
||||
<Text style={localStyles.sideMargins}>
|
||||
{Dinero({
|
||||
amount: Math.round((object.item.act_price || 0) * 100),
|
||||
}).toFormat()}
|
||||
</Text>
|
||||
</Card.Content>
|
||||
<Card.Content style={localStyles.flexRow}>
|
||||
{object.item.mod_lbr_ty && (
|
||||
<Text>
|
||||
{t(`jobdetail.lbr_types.${object.item.mod_lbr_ty}`)}
|
||||
</Text>
|
||||
)}
|
||||
</Card.Content>
|
||||
</Card>
|
||||
<DataTable.Row>
|
||||
<DataTable.Cell style={{ flex: 4 }}>
|
||||
{object.item.line_desc}
|
||||
</DataTable.Cell>
|
||||
<DataTable.Cell style={{ flex: 2 }}>
|
||||
{object.item.mod_lbr_ty &&
|
||||
t(`jobdetail.lbr_types.${object.item.mod_lbr_ty}`)}
|
||||
</DataTable.Cell>
|
||||
<DataTable.Cell style={{ flex: 1 }}>
|
||||
{object.item.mod_lb_hrs}
|
||||
</DataTable.Cell>
|
||||
<DataTable.Cell style={{ flex: 2 }}>
|
||||
{object.item.part_type &&
|
||||
t(`jobdetail.part_types.${object.item.part_type}`)}
|
||||
</DataTable.Cell>
|
||||
<DataTable.Cell style={{ flex: 1 }}>
|
||||
{object.item.part_qty}
|
||||
</DataTable.Cell>
|
||||
<DataTable.Cell style={{ flex: 1 }}>
|
||||
{Dinero({
|
||||
amount: Math.round((object.item.act_price || 0) * 100),
|
||||
}).toFormat()}
|
||||
</DataTable.Cell>
|
||||
</DataTable.Row>
|
||||
)}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const localStyles = StyleSheet.create({
|
||||
listContentContainer: {
|
||||
flex: 1,
|
||||
},
|
||||
flexRow: {
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
},
|
||||
sideMargins: {
|
||||
marginLeft: 5,
|
||||
marginRight: 5,
|
||||
},
|
||||
growWithEllipsis: {
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
const localStyles = StyleSheet.create({});
|
||||
|
||||
@@ -4,8 +4,7 @@ import React, { useRef } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Animated } from "react-native";
|
||||
import { TouchableOpacity } from "react-native-gesture-handler";
|
||||
import Swipeable from "react-native-gesture-handler/Swipeable";
|
||||
import { List } from "react-native-paper";
|
||||
import { Button, List, Title } from "react-native-paper";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
@@ -60,21 +59,35 @@ export function JobListItem({ setCameraJob, setCameraJobId, item }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<Swipeable
|
||||
ref={_swipeableRow}
|
||||
renderRightActions={RenderRightAction}
|
||||
shouldCancelWhenOutside
|
||||
>
|
||||
<TouchableOpacity onPress={onPress}>
|
||||
<List.Item
|
||||
onPress={onPress}
|
||||
title={item.ro_number || t("general.labels.na")}
|
||||
title={<Title>{item.ro_number || t("general.labels.na")}</Title>}
|
||||
description={`${item.ownr_fn || ""} ${item.ownr_ln || ""} ${
|
||||
item.ownr_co_nm || ""
|
||||
} - ${item.v_model_yr || ""} ${item.v_make_desc || ""} ${
|
||||
item.v_model_desc || ""
|
||||
}`}
|
||||
right={({ style }) => (
|
||||
<Button
|
||||
style={style}
|
||||
onPress={() => {
|
||||
logImEXEvent("imexmobile_setcamerajobid_swipe");
|
||||
setCameraJobId(item.id);
|
||||
setCameraJob(item);
|
||||
navigation.navigate("MediaBrowserTab");
|
||||
_swipeableRow.current.close();
|
||||
}}
|
||||
>
|
||||
<Ionicons
|
||||
style={style}
|
||||
name="ios-add"
|
||||
size={32}
|
||||
color="dodgerblue"
|
||||
/>
|
||||
</Button>
|
||||
)}
|
||||
/>
|
||||
</Swipeable>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Text, View } from "react-native";
|
||||
import { Card } from "react-native-paper";
|
||||
export default function NoteListItem({ item }) {
|
||||
return (
|
||||
<Card>
|
||||
<Card style={{ margin: 8 }}>
|
||||
<Card.Content>
|
||||
<View style={{ display: "flex", flex: 1 }}>
|
||||
<Text>{item.text}</Text>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FlatList, RefreshControl, Text } from "react-native";
|
||||
import JobNotesItem from "../job-notes-item/job-notes-item.component";
|
||||
import { Card } from "react-native-paper";
|
||||
import JobNotesItem from "../job-notes-item/job-notes-item.component";
|
||||
export default function JobNotes({ job, loading, refetch }) {
|
||||
const { t } = useTranslation();
|
||||
if (!job) {
|
||||
@@ -14,6 +14,7 @@ export default function JobNotes({ job, loading, refetch }) {
|
||||
const onRefresh = async () => {
|
||||
return refetch();
|
||||
};
|
||||
|
||||
if (job.notes.length === 0)
|
||||
return (
|
||||
<Card>
|
||||
@@ -31,7 +32,6 @@ export default function JobNotes({ job, loading, refetch }) {
|
||||
style={{ flex: 1 }}
|
||||
data={job.notes}
|
||||
renderItem={(object) => <JobNotesItem item={object.item} />}
|
||||
//ItemSeparatorComponent={FlatListItemSeparator}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { Card } from "react-native-paper";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
RefreshControl,
|
||||
StyleSheet,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} from "react-native";
|
||||
import { Card, Headline, Subheading } from "react-native-paper";
|
||||
import DataLabelComponent from "../data-label/data-label.component";
|
||||
import StyleRepeater from "../style-repeater/style-repeater";
|
||||
import styles from "../styles";
|
||||
|
||||
export default function JobTombstone({ job, loading, refetch }) {
|
||||
const { t } = useTranslation();
|
||||
@@ -23,114 +25,121 @@ export default function JobTombstone({ job, loading, refetch }) {
|
||||
|
||||
return (
|
||||
<ScrollView
|
||||
padder
|
||||
style={styles.cardBackground}
|
||||
refreshControl={
|
||||
<RefreshControl refreshing={loading} onRefresh={onRefresh} />
|
||||
}
|
||||
>
|
||||
<Card>
|
||||
<Card.Content bordered style={localStyles.status}>
|
||||
<Text>{job.status}</Text>
|
||||
</Card.Content>
|
||||
{job.inproduction && (
|
||||
<Card.Content bordered style={localStyles.inproduction}>
|
||||
<Text>{t("objects.jobs.labels.inproduction")}</Text>
|
||||
<StyleRepeater childStyle={{ margin: 4 }}>
|
||||
<Card>
|
||||
<Card.Title title={t("jobdetail.labels.jobinfo")} />
|
||||
<Card.Content>
|
||||
<Headline>{job.status}</Headline>
|
||||
{job.inproduction && (
|
||||
<Subheading>{t("objects.jobs.labels.inproduction")}</Subheading>
|
||||
)}
|
||||
{job.inproduction &&
|
||||
job.production_vars &&
|
||||
!!job.production_vars.note && (
|
||||
<Subheading>{job.production_vars.note}</Subheading>
|
||||
)}
|
||||
</Card.Content>
|
||||
)}
|
||||
{job.inproduction && job.production_vars && !!job.production_vars.note && (
|
||||
<Card.Content bordered style={localStyles.inproduction}>
|
||||
<Text>{job.production_vars.note}</Text>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<Card.Title title={t("jobdetail.labels.claiminformation")} />
|
||||
<Card.Content style={localStyles.twoColumnCard}>
|
||||
<View style={localStyles.twoColumnCardColumn}>
|
||||
<DataLabelComponent
|
||||
label={t("objects.jobs.fields.owner")}
|
||||
content={`${job.ownr_fn || ""} ${job.ownr_ln || ""} ${
|
||||
job.ownr_co_nm || ""
|
||||
}`}
|
||||
/>
|
||||
<DataLabelComponent
|
||||
label={t("objects.jobs.fields.vehicle")}
|
||||
content={`${job.v_model_yr || ""} ${job.v_make_desc || ""} ${
|
||||
job.v_model_desc || ""
|
||||
}`}
|
||||
/>
|
||||
</View>
|
||||
<View style={localStyles.twoColumnCardColumn}>
|
||||
<DataLabelComponent
|
||||
label={t("objects.jobs.fields.ins_co_nm")}
|
||||
content={job.ins_co_nm}
|
||||
/>
|
||||
<DataLabelComponent
|
||||
label={t("objects.jobs.fields.clm_no")}
|
||||
content={job.clm_no}
|
||||
/>
|
||||
</View>
|
||||
</Card.Content>
|
||||
)}
|
||||
</Card>
|
||||
<Card>
|
||||
<Card.Content bordered style={localStyles.status}>
|
||||
<Text>{t("jobdetail.labels.claiminformation")}</Text>
|
||||
</Card.Content>
|
||||
<View>
|
||||
<DataLabelComponent
|
||||
label={t("objects.jobs.fields.owner")}
|
||||
content={`${job.ownr_fn || ""} ${job.ownr_ln || ""} ${
|
||||
job.ownr_co_nm || ""
|
||||
}`}
|
||||
/>
|
||||
<DataLabelComponent
|
||||
label={t("objects.jobs.fields.vehicle")}
|
||||
content={`${job.v_model_yr || ""} ${job.v_make_desc || ""} ${
|
||||
job.v_model_desc || ""
|
||||
}`}
|
||||
/>
|
||||
<DataLabelComponent
|
||||
label={t("objects.jobs.fields.ins_co_nm")}
|
||||
content={job.ins_co_nm}
|
||||
/>
|
||||
<DataLabelComponent
|
||||
label={t("objects.jobs.fields.clm_no")}
|
||||
content={job.clm_no}
|
||||
/>
|
||||
</View>
|
||||
</Card>
|
||||
<Card>
|
||||
<Card.Content bordered style={localStyles.status}>
|
||||
<Text>{t("jobdetail.labels.employeeassignments")}</Text>
|
||||
</Card.Content>
|
||||
<View>
|
||||
<DataLabelComponent
|
||||
label={t("objects.jobs.fields.employee_body")}
|
||||
content={`${
|
||||
(job.employee_body_rel && job.employee_body_rel.first_name) || ""
|
||||
} ${
|
||||
(job.employee_body_rel && job.employee_body_rel.last_name) || ""
|
||||
}`}
|
||||
/>
|
||||
<DataLabelComponent
|
||||
label={t("objects.jobs.fields.employee_prep")}
|
||||
content={`${
|
||||
(job.employee_prep_rel && job.employee_prep_rel.first_name) || ""
|
||||
} ${
|
||||
(job.employee_prep_rel && job.employee_prep_rel.last_name) || ""
|
||||
}`}
|
||||
/>
|
||||
<DataLabelComponent
|
||||
label={t("objects.jobs.fields.employee_refinish")}
|
||||
content={`${
|
||||
(job.employee_refinish_rel &&
|
||||
job.employee_refinish_rel.first_name) ||
|
||||
""
|
||||
} ${
|
||||
(job.employee_refinish_rel &&
|
||||
job.employee_refinish_rel.last_name) ||
|
||||
""
|
||||
}`}
|
||||
/>
|
||||
</View>
|
||||
</Card>
|
||||
<Card style={localStyles.twoColumnCard}>
|
||||
<View style={localStyles.twoColumnCardColumn}>
|
||||
<DataLabelComponent
|
||||
label={t("objects.jobs.fields.scheduled_in")}
|
||||
content={job.scheduled_in}
|
||||
dateTime
|
||||
/>
|
||||
<DataLabelComponent
|
||||
label={t("objects.jobs.fields.actual_in")}
|
||||
content={job.actual_in}
|
||||
dateTime
|
||||
/>
|
||||
</View>
|
||||
<View style={localStyles.twoColumnCardColumn}>
|
||||
<DataLabelComponent
|
||||
label={t("objects.jobs.fields.scheduled_completion")}
|
||||
content={job.scheduled_completion}
|
||||
dateTime
|
||||
/>
|
||||
<DataLabelComponent
|
||||
label={t("objects.jobs.fields.scheduled_delivery")}
|
||||
content={job.scheduled_delivery}
|
||||
dateTime
|
||||
/>
|
||||
</View>
|
||||
</Card>
|
||||
</Card>
|
||||
<Card>
|
||||
<Card.Title title={t("jobdetail.labels.employeeassignments")} />
|
||||
<Card.Content>
|
||||
<DataLabelComponent
|
||||
label={t("objects.jobs.fields.employee_body")}
|
||||
content={`${
|
||||
(job.employee_body_rel && job.employee_body_rel.first_name) ||
|
||||
""
|
||||
} ${
|
||||
(job.employee_body_rel && job.employee_body_rel.last_name) || ""
|
||||
}`}
|
||||
/>
|
||||
<DataLabelComponent
|
||||
label={t("objects.jobs.fields.employee_prep")}
|
||||
content={`${
|
||||
(job.employee_prep_rel && job.employee_prep_rel.first_name) ||
|
||||
""
|
||||
} ${
|
||||
(job.employee_prep_rel && job.employee_prep_rel.last_name) || ""
|
||||
}`}
|
||||
/>
|
||||
<DataLabelComponent
|
||||
label={t("objects.jobs.fields.employee_refinish")}
|
||||
content={`${
|
||||
(job.employee_refinish_rel &&
|
||||
job.employee_refinish_rel.first_name) ||
|
||||
""
|
||||
} ${
|
||||
(job.employee_refinish_rel &&
|
||||
job.employee_refinish_rel.last_name) ||
|
||||
""
|
||||
}`}
|
||||
/>
|
||||
</Card.Content>
|
||||
</Card>
|
||||
<Card>
|
||||
<Card.Title title={t("jobdetail.labels.dates")} />
|
||||
<Card.Content style={localStyles.twoColumnCard}>
|
||||
<View style={localStyles.twoColumnCardColumn}>
|
||||
<DataLabelComponent
|
||||
label={t("objects.jobs.fields.scheduled_in")}
|
||||
content={job.scheduled_in}
|
||||
dateTime
|
||||
/>
|
||||
<DataLabelComponent
|
||||
label={t("objects.jobs.fields.actual_in")}
|
||||
content={job.actual_in}
|
||||
dateTime
|
||||
/>
|
||||
</View>
|
||||
<View style={localStyles.twoColumnCardColumn}>
|
||||
<DataLabelComponent
|
||||
label={t("objects.jobs.fields.scheduled_completion")}
|
||||
content={job.scheduled_completion}
|
||||
dateTime
|
||||
/>
|
||||
<DataLabelComponent
|
||||
label={t("objects.jobs.fields.scheduled_delivery")}
|
||||
content={job.scheduled_delivery}
|
||||
dateTime
|
||||
/>
|
||||
</View>
|
||||
</Card.Content>
|
||||
</Card>
|
||||
</StyleRepeater>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
@@ -144,7 +153,6 @@ const localStyles = StyleSheet.create({
|
||||
justifyContent: "center",
|
||||
},
|
||||
inproduction: {
|
||||
backgroundColor: "tomato",
|
||||
textAlign: "center",
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
|
||||
@@ -1,84 +1,23 @@
|
||||
import React from "react";
|
||||
import { Alert, Modal, StyleSheet, Text, Button, View } from "react-native";
|
||||
import ImageViewer from "react-native-image-zoom-viewer";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { removePhotos } from "../../redux/photos/photos.actions";
|
||||
import { selectPhotos } from "../../redux/photos/photos.selectors";
|
||||
import { Modal } from "react-native";
|
||||
import Gallery from "react-native-image-gallery";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
photos: selectPhotos,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
removePhotos: (ids) => dispatch(removePhotos(ids)),
|
||||
});
|
||||
|
||||
export function MediaCacheOverlay({
|
||||
export default function MediaCacheOverlay({
|
||||
photos,
|
||||
removePhotos,
|
||||
previewVisible,
|
||||
setPreviewVisible,
|
||||
imgIndex,
|
||||
setImgIndex,
|
||||
}) {
|
||||
console.log("photos :>> ", photos);
|
||||
return (
|
||||
<Modal
|
||||
animationType="slide"
|
||||
onRequestClose={() => {
|
||||
Alert.alert("Modal has been closed.");
|
||||
}}
|
||||
onDismiss={() => setPreviewVisible(false)}
|
||||
onRequestClose={() => setPreviewVisible(false)}
|
||||
visible={previewVisible}
|
||||
transparent={true}
|
||||
transparent={false}
|
||||
>
|
||||
<ImageViewer
|
||||
onCancel={() => setPreviewVisible(false)}
|
||||
index={imgIndex}
|
||||
onChange={(index) => setImgIndex(index)}
|
||||
style={{ display: "flex" }}
|
||||
renderFooter={(index) => (
|
||||
<View
|
||||
style={{
|
||||
marginleft: "auto",
|
||||
backgroundColor: "tomato",
|
||||
}}
|
||||
>
|
||||
<Text>{index} This is the thing.</Text>
|
||||
<Button
|
||||
onPress={() => {
|
||||
removePhotos([photos[index].id]);
|
||||
}}
|
||||
>
|
||||
<Text>Delete</Text>
|
||||
</Button>
|
||||
</View>
|
||||
)}
|
||||
enableSwipeDown
|
||||
enablePreload
|
||||
imageUrls={photos.map((p) => {
|
||||
return { url: p.uri };
|
||||
})}
|
||||
/>
|
||||
<Gallery initialPage={imgIndex} style={{ flex: 1 }} images={photos} />
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
actions: {
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-evenly",
|
||||
},
|
||||
listContentContainer: {
|
||||
flex: 1,
|
||||
},
|
||||
thumbnail: {
|
||||
width: 10,
|
||||
height: 10,
|
||||
backgroundColor: "tomato",
|
||||
},
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(MediaCacheOverlay);
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useQuery } from "@apollo/client";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useWindowDimensions } from "react-native";
|
||||
import { SceneMap, TabView } from "react-native-tab-view";
|
||||
import { SceneMap, TabView, TabBar } from "react-native-tab-view";
|
||||
import { GET_JOB_BY_PK } from "../../graphql/jobs.queries";
|
||||
import ErrorDisplay from "../error-display/error-display.component";
|
||||
import JobDocuments from "../job-documents/job-documents.component";
|
||||
@@ -24,6 +24,14 @@ export default function ScreenJobDetail({ route }) {
|
||||
skip: !jobId,
|
||||
});
|
||||
|
||||
const renderTabBar = (props) => (
|
||||
<TabBar
|
||||
{...props}
|
||||
indicatorStyle={{ backgroundColor: "white" }}
|
||||
style={{ backgroundColor: "dodgerblue" }}
|
||||
/>
|
||||
);
|
||||
|
||||
const renderScene = SceneMap({
|
||||
job: () =>
|
||||
JobTombstone({
|
||||
@@ -68,6 +76,7 @@ export default function ScreenJobDetail({ route }) {
|
||||
renderScene={renderScene}
|
||||
onIndexChange={setIndex}
|
||||
initialLayout={{ width: layout.width }}
|
||||
renderTabBar={renderTabBar}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -64,7 +64,6 @@ export function ImageBrowserScreen({
|
||||
}
|
||||
|
||||
const onDone = async (data) => {
|
||||
console.log("Assets :>> ", data);
|
||||
logImEXEvent("imexmobile_upload_documents", { count: data.length });
|
||||
const actions = [];
|
||||
data.forEach(function (p) {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Formik } from "formik";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ActivityIndicator, Image, StyleSheet, View, Text } from "react-native";
|
||||
import { Image, StyleSheet, Text, View } from "react-native";
|
||||
import { Button, TextInput, Title } from "react-native-paper";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import Logo from "../../assets/logo192.png";
|
||||
@@ -11,8 +12,6 @@ import {
|
||||
selectSigningIn,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import SignInErrorAlertComponent from "../sign-in-error-alert/sign-in-error-alert.component";
|
||||
import styles from "../styles";
|
||||
import { TextInput, Button, Subheading } from "react-native-paper";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
currentUser: selectCurrentUser,
|
||||
@@ -33,41 +32,46 @@ export function SignIn({ emailSignInStart, signingIn }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<View
|
||||
scrollEnabled={false}
|
||||
contentContainerStyle={styles.contentContainer__centered}
|
||||
style={localStyles.content}
|
||||
>
|
||||
<View style={styles.evenlySpacedRow}>
|
||||
<View style={localStyles.content}>
|
||||
<View
|
||||
style={{
|
||||
display: "flex",
|
||||
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-evenly",
|
||||
}}
|
||||
>
|
||||
<Image style={localStyles.logo} source={Logo} />
|
||||
<Text>{t("app.title")}</Text>
|
||||
<Title>{t("app.title")}</Title>
|
||||
</View>
|
||||
<Formik initialValues={{ email: "", password: "" }} onSubmit={formSubmit}>
|
||||
{({ handleChange, handleBlur, handleSubmit, values }) => (
|
||||
<View>
|
||||
<View>
|
||||
<Subheading>{t("signin.fields.email")}</Subheading>
|
||||
<TextInput
|
||||
autoCapitalize="none"
|
||||
keyboardType="email-address"
|
||||
onChangeText={handleChange("email")}
|
||||
onBlur={handleBlur("email")}
|
||||
value={values.email}
|
||||
/>
|
||||
</View>
|
||||
<View>
|
||||
<Subheading>{t("signin.fields.password")}</Subheading>
|
||||
<TextInput
|
||||
secureTextEntry={true}
|
||||
onChangeText={handleChange("password")}
|
||||
onBlur={handleBlur("password")}
|
||||
value={values.password}
|
||||
/>
|
||||
</View>
|
||||
<TextInput
|
||||
label={t("signin.fields.email")}
|
||||
mode="outlined"
|
||||
autoCapitalize="none"
|
||||
keyboardType="email-address"
|
||||
onChangeText={handleChange("email")}
|
||||
onBlur={handleBlur("email")}
|
||||
value={values.email}
|
||||
style={[localStyles.input]}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
label={t("signin.fields.password")}
|
||||
mode="outlined"
|
||||
secureTextEntry={true}
|
||||
onChangeText={handleChange("password")}
|
||||
onBlur={handleBlur("password")}
|
||||
value={values.password}
|
||||
style={[localStyles.input]}
|
||||
/>
|
||||
|
||||
<SignInErrorAlertComponent />
|
||||
<Button full onPress={handleSubmit}>
|
||||
<Button mode="outlined" loading={signingIn} onPress={handleSubmit}>
|
||||
<Text>{t("signin.actions.signin")}</Text>
|
||||
{signingIn ? <ActivityIndicator size="large" /> : null}
|
||||
</Button>
|
||||
</View>
|
||||
)}
|
||||
@@ -78,9 +82,13 @@ export function SignIn({ emailSignInStart, signingIn }) {
|
||||
|
||||
const localStyles = StyleSheet.create({
|
||||
content: {
|
||||
paddingBottom: 200,
|
||||
display: "flex",
|
||||
flex: 1,
|
||||
},
|
||||
logo: { width: 100, height: 100 },
|
||||
input: {
|
||||
margin: 12,
|
||||
},
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(SignIn);
|
||||
|
||||
@@ -1,29 +1,35 @@
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Image, StyleSheet, View } from "react-native";
|
||||
import { BarIndicator } from "react-native-indicators";
|
||||
import { ActivityIndicator, Image, StyleSheet, View } from "react-native";
|
||||
import { Title } from "react-native-paper";
|
||||
import Logo from "../../assets/logo192.png";
|
||||
import styles from "../styles";
|
||||
|
||||
export default function ScreenSplash() {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<View
|
||||
contentContainerStyle={[
|
||||
styles.contentContainer__centered,
|
||||
localStyles.middleAlign,
|
||||
]}
|
||||
>
|
||||
<Image style={localStyles.logo} source={Logo} />
|
||||
<Title>{t("app.title")}</Title>
|
||||
<BarIndicator count={5} color="dodgerblue" />
|
||||
<View style={[localStyles.container]}>
|
||||
<View style={[localStyles.logoContainer]}>
|
||||
<Image style={localStyles.logo} source={Logo} />
|
||||
<Title>{t("app.title")}</Title>
|
||||
</View>
|
||||
|
||||
<ActivityIndicator color="dodgerblue" size="large" />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
const localStyles = StyleSheet.create({
|
||||
middleAlign: {
|
||||
container: {
|
||||
display: "flex",
|
||||
flex: 1,
|
||||
flexDirection: "column",
|
||||
alignContent: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
logoContainer: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
|
||||
alignItems: "center",
|
||||
},
|
||||
logo: { width: 100, height: 100, margin: 20 },
|
||||
logo: { width: 175, height: 175, margin: 20 },
|
||||
});
|
||||
|
||||
13
components/style-repeater/style-repeater.jsx
Normal file
13
components/style-repeater/style-repeater.jsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import React from "react";
|
||||
|
||||
export default function StyleRepeater({ childStyle, children }) {
|
||||
return (
|
||||
<>
|
||||
{React.Children.map(children, (child) =>
|
||||
React.cloneElement(child, {
|
||||
style: [child.props.style, childStyle],
|
||||
})
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,30 +1,12 @@
|
||||
import { StyleSheet } from "react-native";
|
||||
|
||||
const cardBackgroundColor = "gainsboro";
|
||||
|
||||
export default StyleSheet.create({
|
||||
contentContainer__centered: {
|
||||
justifyContent: "center",
|
||||
cardBackground: {
|
||||
padding: 5,
|
||||
backgroundColor: cardBackgroundColor,
|
||||
display: "flex",
|
||||
flex: 1,
|
||||
},
|
||||
|
||||
evenlySpacedRow: {
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-evenly",
|
||||
alignItems: "center",
|
||||
},
|
||||
|
||||
swipe_view: {
|
||||
flex: 1,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
width: 100,
|
||||
// marginTop: 5,
|
||||
//marginBottom: 5,
|
||||
},
|
||||
swipe_view_blue: {
|
||||
backgroundColor: "dodgerblue",
|
||||
},
|
||||
swipe_text: {
|
||||
textAlign: "center",
|
||||
color: "white",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import React from "react";
|
||||
import React, { useMemo } from "react";
|
||||
import {
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
@@ -10,10 +10,15 @@ import {
|
||||
import * as Progress from "react-native-progress";
|
||||
import _ from "lodash";
|
||||
export default function UploadProgress({ uploads, setUploads }) {
|
||||
const uploadKeys = useMemo(() => {
|
||||
if (uploads) return Object.keys(uploads);
|
||||
return [];
|
||||
}, [uploads]);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<ScrollView>
|
||||
{Object.keys(uploads).map((key) => (
|
||||
{uploadKeys.map((key) => (
|
||||
<View key={key} style={styles.progressItem}>
|
||||
<Text style={styles.progressText}>{key}</Text>
|
||||
<View style={styles.progressBarContainer}>
|
||||
|
||||
Reference in New Issue
Block a user