Adding work item details to activity
This commit is contained in:
@@ -31,7 +31,7 @@ import { formatLatLng, workItemTypeEnum } from "../util"
|
|||||||
import moment from "moment"
|
import moment from "moment"
|
||||||
import "url-search-params-polyfill"
|
import "url-search-params-polyfill"
|
||||||
import { config } from "../config"
|
import { config } from "../config"
|
||||||
import { FormStaticOptionStrip } from "../ui/FormStaticOptionStrip"
|
import { FormStaticOptionStrip, FormStaticPhotoPanel } from "../ui"
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
@@ -127,6 +127,7 @@ export class Activity extends React.Component {
|
|||||||
dateTime: moment().format(),
|
dateTime: moment().format(),
|
||||||
details: workItem.details,
|
details: workItem.details,
|
||||||
workItemType: workItem.workItemType,
|
workItemType: workItem.workItemType,
|
||||||
|
workItemPhotos: workItem.photos,
|
||||||
})
|
})
|
||||||
|
|
||||||
this.region = {
|
this.region = {
|
||||||
@@ -294,6 +295,7 @@ export class Activity extends React.Component {
|
|||||||
details,
|
details,
|
||||||
workItemType,
|
workItemType,
|
||||||
uploadPercent,
|
uploadPercent,
|
||||||
|
workItemPhotos,
|
||||||
} = this.state
|
} = this.state
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -347,6 +349,10 @@ export class Activity extends React.Component {
|
|||||||
value={workItemType}
|
value={workItemType}
|
||||||
/>
|
/>
|
||||||
<FormStaticInput label="Details:" value={details} />
|
<FormStaticInput label="Details:" value={details} />
|
||||||
|
<FormStaticPhotoPanel
|
||||||
|
label="Work Item Photos:"
|
||||||
|
value={workItemPhotos}
|
||||||
|
/>
|
||||||
<FormStaticInput label="Location:" value={location} />
|
<FormStaticInput label="Location:" value={location} />
|
||||||
<View style={{ flexDirection: "column", justifyContent: "center" }}>
|
<View style={{ flexDirection: "column", justifyContent: "center" }}>
|
||||||
<MapView
|
<MapView
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export default class App extends React.Component {
|
|||||||
<Switch>
|
<Switch>
|
||||||
<Route exact path="/login" component={Login} />
|
<Route exact path="/login" component={Login} />
|
||||||
<Route exact path="/logout" component={Logout} />
|
<Route exact path="/logout" component={Logout} />
|
||||||
{/* <ProtectedRoute exact path="/home" component={Home} /> */}
|
<ProtectedRoute exact path="/home" component={Home} />
|
||||||
<ProtectedRoute
|
<ProtectedRoute
|
||||||
exact
|
exact
|
||||||
path="/arviewer"
|
path="/arviewer"
|
||||||
@@ -69,8 +69,7 @@ export default class App extends React.Component {
|
|||||||
path="/workItemList"
|
path="/workItemList"
|
||||||
component={WorkItemList}
|
component={WorkItemList}
|
||||||
/>
|
/>
|
||||||
{/* <DefaultRoute redirect="/home" /> */}
|
<DefaultRoute redirect="/home" />
|
||||||
<DefaultRoute redirect="/activity?workItemId=5b0c85995ebbed31c323cdf6" />
|
|
||||||
</Switch>
|
</Switch>
|
||||||
</View>
|
</View>
|
||||||
</NativeRouter>
|
</NativeRouter>
|
||||||
|
|||||||
@@ -14,17 +14,7 @@ import ImagePicker from "react-native-image-picker"
|
|||||||
import ImageResizer from "react-native-image-resizer"
|
import ImageResizer from "react-native-image-resizer"
|
||||||
import { reactAutoBind } from "auto-bind2"
|
import { reactAutoBind } from "auto-bind2"
|
||||||
import { api } from "../API"
|
import { api } from "../API"
|
||||||
|
import { PhotoPanel } from "."
|
||||||
const getScreenPortraitDimensions = () => {
|
|
||||||
const { width, height } = Dimensions.get("window")
|
|
||||||
|
|
||||||
return {
|
|
||||||
screenWidth: Math.min(width, height),
|
|
||||||
screenHeight: Math.max(width, height),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const photoSpacing = 10
|
|
||||||
|
|
||||||
export class BoundPhotoPanel extends Component {
|
export class BoundPhotoPanel extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@@ -95,56 +85,15 @@ export class BoundPhotoPanel extends Component {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLayout(e) {
|
|
||||||
if (!this.state.photoSize) {
|
|
||||||
const { layout } = e.nativeEvent
|
|
||||||
const ratio = 3 / 4
|
|
||||||
const width = (layout.width - photoSpacing) / 2
|
|
||||||
const height = width / ratio
|
|
||||||
|
|
||||||
this.setState({ photoSize: { width, height } })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { photoSize, value: assetIds } = this.state
|
const { photoSize, value } = this.state
|
||||||
|
|
||||||
const renderPhoto = (index) => {
|
|
||||||
const assetId = assetIds[index]
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TouchableOpacity
|
|
||||||
key={assetId || "blank" + index.toString()}
|
|
||||||
style={{
|
|
||||||
width: photoSize.width,
|
|
||||||
height: photoSize.height,
|
|
||||||
borderWidth: 2,
|
|
||||||
borderColor: "gray",
|
|
||||||
borderRadius: 4,
|
|
||||||
justifyContent: "center",
|
|
||||||
}}
|
|
||||||
onPress={() => this.handlePhotoPress(index)}>
|
|
||||||
{!assetId && (
|
|
||||||
<Icon name="add" size={24} style={{ alignSelf: "center" }} />
|
|
||||||
)}
|
|
||||||
{assetId && (
|
|
||||||
<Image
|
|
||||||
source={{ uri: api.makeImageUrl(assetId) }}
|
|
||||||
style={{ width: "100%", height: "100%" }}
|
|
||||||
resizeMode="contain"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</TouchableOpacity>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
}}
|
}}>
|
||||||
onLayout={this.handleLayout}>
|
|
||||||
<Text
|
<Text
|
||||||
style={{
|
style={{
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
@@ -152,18 +101,7 @@ export class BoundPhotoPanel extends Component {
|
|||||||
}}>
|
}}>
|
||||||
Pictures:
|
Pictures:
|
||||||
</Text>
|
</Text>
|
||||||
{photoSize && (
|
<PhotoPanel onPhotoPress={this.handlePhotoPress} value={value} />
|
||||||
<View style={styles.photoRow}>
|
|
||||||
{renderPhoto(0)}
|
|
||||||
{renderPhoto(1)}
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
{photoSize && (
|
|
||||||
<View style={styles.photoRow}>
|
|
||||||
{renderPhoto(2)}
|
|
||||||
{renderPhoto(3)}
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
30
mobile/src/ui/FormStaticPhotoPanel.js
Normal file
30
mobile/src/ui/FormStaticPhotoPanel.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import React from "react"
|
||||||
|
import PropTypes from "prop-types"
|
||||||
|
import { Text, View, Platform } from "react-native"
|
||||||
|
import { PhotoPanel } from "."
|
||||||
|
|
||||||
|
export class FormStaticPhotoPanel extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
label: PropTypes.string,
|
||||||
|
value: PropTypes.arrayOf(PropTypes.string),
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { label, value, options } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={{ width: "100%" }}>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
color: "black",
|
||||||
|
fontSize: 14,
|
||||||
|
marginBottom: 5,
|
||||||
|
}}>
|
||||||
|
{label}
|
||||||
|
</Text>
|
||||||
|
<PhotoPanel value={value} readOnly />
|
||||||
|
<Text> </Text>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
108
mobile/src/ui/PhotoPanel.js
Normal file
108
mobile/src/ui/PhotoPanel.js
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
import React, { Component } from "react"
|
||||||
|
import PropTypes from "prop-types"
|
||||||
|
import {
|
||||||
|
StyleSheet,
|
||||||
|
Text,
|
||||||
|
View,
|
||||||
|
Image,
|
||||||
|
TouchableOpacity,
|
||||||
|
Dimensions,
|
||||||
|
ActivityIndicator,
|
||||||
|
} from "react-native"
|
||||||
|
import { Icon } from "."
|
||||||
|
import { reactAutoBind } from "auto-bind2"
|
||||||
|
import { api } from "../API"
|
||||||
|
|
||||||
|
const photoSpacing = 10
|
||||||
|
|
||||||
|
export class PhotoPanel extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
onPhotoPress: PropTypes.func,
|
||||||
|
readOnly: PropTypes.bool,
|
||||||
|
value: PropTypes.arrayOf(PropTypes.string),
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
reactAutoBind(this)
|
||||||
|
this.state = {
|
||||||
|
photoSize: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleLayout(e) {
|
||||||
|
if (!this.state.photoSize) {
|
||||||
|
const { layout } = e.nativeEvent
|
||||||
|
const ratio = 3 / 4
|
||||||
|
const width = (layout.width - photoSpacing) / 2
|
||||||
|
const height = width / ratio
|
||||||
|
|
||||||
|
this.setState({ photoSize: { width, height } })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { photoSize } = this.state
|
||||||
|
const { value: assetIds, readOnly, onPhotoPress } = this.props
|
||||||
|
const renderPhoto = (index) => {
|
||||||
|
const assetId = assetIds && assetIds[index]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TouchableOpacity
|
||||||
|
key={assetId || "blank" + index.toString()}
|
||||||
|
disabled={readOnly}
|
||||||
|
style={{
|
||||||
|
width: photoSize.width,
|
||||||
|
height: photoSize.height,
|
||||||
|
borderWidth: 2,
|
||||||
|
borderColor: "gray",
|
||||||
|
borderRadius: 4,
|
||||||
|
justifyContent: "center",
|
||||||
|
}}
|
||||||
|
onPress={() => onPhotoPress(index)}>
|
||||||
|
{!assetId && (
|
||||||
|
<Icon
|
||||||
|
name={!readOnly ? "add" : "blank"}
|
||||||
|
size={24}
|
||||||
|
style={{ alignSelf: "center" }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{assetId && (
|
||||||
|
<Image
|
||||||
|
source={{ uri: api.makeImageUrl(assetId) }}
|
||||||
|
style={{ width: "100%", height: "100%" }}
|
||||||
|
resizeMode="contain"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</TouchableOpacity>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View onLayout={this.handleLayout}>
|
||||||
|
{photoSize && (
|
||||||
|
<View style={styles.photoRow}>
|
||||||
|
{renderPhoto(0)}
|
||||||
|
{renderPhoto(1)}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
{photoSize && (
|
||||||
|
<View style={styles.photoRow}>
|
||||||
|
{renderPhoto(2)}
|
||||||
|
{renderPhoto(3)}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
photoRow: {
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
width: "100%",
|
||||||
|
paddingTop: 5,
|
||||||
|
paddingBottom: 5,
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -3,10 +3,12 @@ export { Header } from "./Header"
|
|||||||
export { OptionStrip } from "./OptionStrip"
|
export { OptionStrip } from "./OptionStrip"
|
||||||
export { BubbleLoader } from "./BubbleLoader"
|
export { BubbleLoader } from "./BubbleLoader"
|
||||||
export { ProgressBar } from "./ProgressBar"
|
export { ProgressBar } from "./ProgressBar"
|
||||||
|
export { PhotoPanel } from "./PhotoPanel"
|
||||||
export { Geolocation } from "./Geolocation"
|
export { Geolocation } from "./Geolocation"
|
||||||
export { BoundSwitch } from "./BoundSwitch"
|
export { BoundSwitch } from "./BoundSwitch"
|
||||||
export { BoundInput } from "./BoundInput"
|
export { BoundInput } from "./BoundInput"
|
||||||
export { FormStaticInput } from "./FormStaticInput"
|
export { FormStaticInput } from "./FormStaticInput"
|
||||||
|
export { FormStaticPhotoPanel } from "./FormStaticPhotoPanel"
|
||||||
export { FormStaticOptionStrip } from "./FormStaticOptionStrip"
|
export { FormStaticOptionStrip } from "./FormStaticOptionStrip"
|
||||||
export { BoundButton } from "./BoundButton"
|
export { BoundButton } from "./BoundButton"
|
||||||
export { BoundOptionStrip } from "./BoundOptionStrip"
|
export { BoundOptionStrip } from "./BoundOptionStrip"
|
||||||
|
|||||||
Reference in New Issue
Block a user