2021-02-20 08:38:08 +00:00
|
|
|
import AsyncStorage from '@react-native-async-storage/async-storage'
|
2021-03-26 09:34:11 +00:00
|
|
|
import { NavigationProp, RouteProp } from '@react-navigation/native'
|
2021-02-19 08:18:22 +00:00
|
|
|
import {
|
|
|
|
Button,
|
|
|
|
CheckBox,
|
|
|
|
Divider,
|
|
|
|
Input,
|
2021-02-20 08:38:08 +00:00
|
|
|
Layout,
|
2021-02-19 08:18:22 +00:00
|
|
|
Text,
|
|
|
|
TopNavigation,
|
|
|
|
TopNavigationAction,
|
|
|
|
useTheme,
|
|
|
|
} from '@ui-kitten/components'
|
2021-03-26 09:34:11 +00:00
|
|
|
import { ErrorMessage, Formik } from 'formik'
|
2021-02-20 08:38:08 +00:00
|
|
|
import moment from 'moment'
|
2021-02-19 08:18:22 +00:00
|
|
|
import Personnummer from 'personnummer'
|
2021-02-20 08:38:08 +00:00
|
|
|
import React from 'react'
|
|
|
|
import { SafeAreaView, StyleSheet, View } from 'react-native'
|
2021-02-19 08:18:22 +00:00
|
|
|
import DateTimePickerModal from 'react-native-modal-datetime-picker'
|
2021-02-20 08:38:08 +00:00
|
|
|
import * as Yup from 'yup'
|
2021-02-19 14:30:48 +00:00
|
|
|
import { studentName } from '../utils/peopleHelpers'
|
2021-02-20 08:38:08 +00:00
|
|
|
import { useSMS } from '../utils/SMS'
|
|
|
|
import { BackIcon } from './icon.component'
|
2021-03-26 09:34:11 +00:00
|
|
|
import { RootStackParamList } from './navigation.component'
|
|
|
|
|
|
|
|
interface AbsenceProps {
|
|
|
|
navigation: NavigationProp<RootStackParamList, 'Absence'>
|
|
|
|
route: RouteProp<RootStackParamList, 'Absence'>
|
|
|
|
}
|
|
|
|
|
|
|
|
interface AbsenceFormValues {
|
|
|
|
displayStartTimePicker: boolean
|
|
|
|
displayEndTimePicker: boolean
|
|
|
|
socialSecurityNumber: string
|
|
|
|
isFullDay: boolean
|
|
|
|
startTime: moment.Moment
|
|
|
|
endTime: moment.Moment
|
|
|
|
}
|
2021-02-19 08:18:22 +00:00
|
|
|
|
|
|
|
const AbsenceSchema = Yup.object().shape({
|
|
|
|
socialSecurityNumber: Yup.string()
|
|
|
|
.required('Personnummer saknas')
|
|
|
|
.test('is-valid', 'Personnumret är ogiltigt', (value) =>
|
2021-03-26 09:34:11 +00:00
|
|
|
value ? Personnummer.valid(value) : true
|
2021-02-19 08:18:22 +00:00
|
|
|
),
|
|
|
|
isFullDay: Yup.bool().required(),
|
|
|
|
})
|
|
|
|
|
2021-03-26 09:34:11 +00:00
|
|
|
const Absence = ({ route, navigation }: AbsenceProps) => {
|
2021-02-19 08:18:22 +00:00
|
|
|
const { sendSMS } = useSMS()
|
|
|
|
const { child } = route.params
|
|
|
|
const theme = useTheme()
|
|
|
|
const [socialSecurityNumber, setSocialSecurityNumber] = React.useState('')
|
|
|
|
const minumumDate = moment().hours(8).minute(0)
|
|
|
|
const maximumDate = moment().hours(17).minute(0)
|
|
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
const getSocialSecurityNumber = async () => {
|
|
|
|
const ssn = await AsyncStorage.getItem(`@childssn.${child.id}`)
|
2021-03-26 09:34:11 +00:00
|
|
|
setSocialSecurityNumber(ssn || '')
|
2021-02-19 08:18:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
getSocialSecurityNumber()
|
|
|
|
}, [child])
|
|
|
|
|
2021-03-26 09:34:11 +00:00
|
|
|
const initialValues: AbsenceFormValues = {
|
|
|
|
displayStartTimePicker: false,
|
|
|
|
displayEndTimePicker: false,
|
|
|
|
socialSecurityNumber: socialSecurityNumber || '',
|
|
|
|
isFullDay: true,
|
|
|
|
startTime: moment().hours(Math.max(8, new Date().getHours())).minute(0),
|
|
|
|
endTime: maximumDate,
|
|
|
|
}
|
|
|
|
|
2021-02-19 08:18:22 +00:00
|
|
|
return (
|
|
|
|
<SafeAreaView style={styles.safeArea}>
|
|
|
|
<TopNavigation
|
|
|
|
accessoryLeft={() => (
|
|
|
|
<TopNavigationAction
|
2021-02-20 08:38:08 +00:00
|
|
|
icon={BackIcon}
|
2021-02-19 08:18:22 +00:00
|
|
|
onPress={() => navigation.goBack()}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
alignment="center"
|
|
|
|
style={styles.topBar}
|
|
|
|
title="Anmäl frånvaro"
|
2021-02-19 14:30:48 +00:00
|
|
|
subtitle={studentName(child.name)}
|
2021-02-19 08:18:22 +00:00
|
|
|
/>
|
|
|
|
<Divider />
|
|
|
|
<Layout style={styles.wrap}>
|
|
|
|
<Formik
|
|
|
|
enableReinitialize
|
|
|
|
validationSchema={AbsenceSchema}
|
2021-03-26 09:34:11 +00:00
|
|
|
initialValues={initialValues}
|
2021-02-19 08:18:22 +00:00
|
|
|
onSubmit={async (values) => {
|
|
|
|
const ssn = Personnummer.parse(values.socialSecurityNumber).format()
|
|
|
|
|
|
|
|
if (values.isFullDay) {
|
|
|
|
sendSMS(ssn)
|
|
|
|
} else {
|
|
|
|
sendSMS(
|
|
|
|
`${ssn} ${moment(values.startTime).format('HHmm')}-${moment(
|
|
|
|
values.endTime
|
|
|
|
).format('HHmm')}`
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
await AsyncStorage.setItem(
|
|
|
|
`@childssn.${child.id}`,
|
|
|
|
values.socialSecurityNumber
|
|
|
|
)
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
{({
|
|
|
|
handleChange,
|
|
|
|
handleBlur,
|
|
|
|
handleSubmit,
|
|
|
|
setFieldValue,
|
|
|
|
values,
|
|
|
|
touched,
|
|
|
|
errors,
|
|
|
|
}) => {
|
2021-03-26 09:34:11 +00:00
|
|
|
const hasError = (field: keyof typeof values) =>
|
|
|
|
errors[field] && touched[field]
|
2021-02-19 08:18:22 +00:00
|
|
|
|
|
|
|
return (
|
|
|
|
<View>
|
|
|
|
<View style={styles.field}>
|
|
|
|
<Input
|
|
|
|
accessibilityLabel="Personnummer"
|
|
|
|
label="Personnummer"
|
|
|
|
keyboardType="number-pad"
|
|
|
|
onChangeText={handleChange('socialSecurityNumber')}
|
|
|
|
onBlur={handleBlur('socialSecurityNumber')}
|
|
|
|
status={
|
|
|
|
hasError('socialSecurityNumber') ? 'danger' : 'basic'
|
|
|
|
}
|
|
|
|
value={values.socialSecurityNumber}
|
|
|
|
/>
|
|
|
|
{hasError('socialSecurityNumber') && (
|
|
|
|
<Text style={{ color: theme['color-danger-700'] }}>
|
|
|
|
{errors.socialSecurityNumber}
|
|
|
|
</Text>
|
|
|
|
)}
|
|
|
|
</View>
|
|
|
|
<View style={styles.field}>
|
|
|
|
<CheckBox
|
|
|
|
checked={values.isFullDay}
|
|
|
|
onChange={(checked) => setFieldValue('isFullDay', checked)}
|
|
|
|
>
|
|
|
|
Heldag
|
|
|
|
</CheckBox>
|
|
|
|
</View>
|
|
|
|
{!values.isFullDay && (
|
|
|
|
<View style={styles.partOfDay}>
|
|
|
|
<View style={styles.inputHalf}>
|
|
|
|
<Text style={styles.label}>Starttid</Text>
|
|
|
|
<Button
|
|
|
|
status="basic"
|
|
|
|
onPress={() =>
|
|
|
|
setFieldValue('displayStartTimePicker', true)
|
|
|
|
}
|
|
|
|
>
|
|
|
|
{moment(values.startTime).format('HH:mm')}
|
|
|
|
</Button>
|
|
|
|
<DateTimePickerModal
|
|
|
|
cancelTextIOS="Avbryt"
|
|
|
|
confirmTextIOS="Bekräfta"
|
|
|
|
date={moment(values.startTime).toDate()}
|
|
|
|
isVisible={values.displayStartTimePicker}
|
|
|
|
headerTextIOS="Välj en starttid"
|
|
|
|
locale="sv-SE"
|
|
|
|
maximumDate={maximumDate.toDate()}
|
|
|
|
minimumDate={minumumDate.toDate()}
|
|
|
|
minuteInterval={10}
|
|
|
|
mode="time"
|
|
|
|
onConfirm={(date) => {
|
|
|
|
setFieldValue('startTime', date)
|
|
|
|
setFieldValue('displayStartTimePicker', false)
|
|
|
|
}}
|
|
|
|
onCancel={() =>
|
|
|
|
setFieldValue('displayStartTimePicker', false)
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
</View>
|
|
|
|
<View style={styles.spacer} />
|
|
|
|
<View style={styles.inputHalf}>
|
|
|
|
<Text style={styles.label}>Sluttid</Text>
|
|
|
|
<Button
|
|
|
|
status="basic"
|
|
|
|
onPress={() =>
|
|
|
|
setFieldValue('displayEndTimePicker', true)
|
|
|
|
}
|
|
|
|
>
|
|
|
|
{moment(values.endTime).format('HH:mm')}
|
|
|
|
</Button>
|
|
|
|
<DateTimePickerModal
|
|
|
|
cancelTextIOS="Avbryt"
|
|
|
|
confirmTextIOS="Bekräfta"
|
|
|
|
date={moment(values.endTime).toDate()}
|
|
|
|
isVisible={values.displayEndTimePicker}
|
|
|
|
headerTextIOS="Välj en sluttid"
|
|
|
|
locale="sv-SE"
|
|
|
|
maximumDate={maximumDate.toDate()}
|
|
|
|
minimumDate={minumumDate.toDate()}
|
|
|
|
minuteInterval={10}
|
|
|
|
mode="time"
|
|
|
|
onConfirm={(date) => {
|
|
|
|
setFieldValue('endTime', date)
|
|
|
|
setFieldValue('displayEndTimePicker', false)
|
|
|
|
}}
|
|
|
|
onCancel={() =>
|
|
|
|
setFieldValue('displayEndTimePicker', false)
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
)}
|
|
|
|
<Button onPress={handleSubmit} status="primary">
|
|
|
|
Skicka
|
|
|
|
</Button>
|
|
|
|
</View>
|
|
|
|
)
|
|
|
|
}}
|
|
|
|
</Formik>
|
|
|
|
</Layout>
|
|
|
|
</SafeAreaView>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
export default Absence
|
|
|
|
|
|
|
|
const styles = StyleSheet.create({
|
|
|
|
safeArea: { backgroundColor: '#fff', flex: 1 },
|
|
|
|
topBar: { backgroundColor: '#fff' },
|
|
|
|
wrap: { flex: 1, padding: 20 },
|
|
|
|
field: { marginBottom: 16 },
|
|
|
|
partOfDay: { flexDirection: 'row', marginBottom: 16 },
|
|
|
|
spacer: { width: 8 },
|
|
|
|
inputHalf: { flex: 1 },
|
|
|
|
label: {
|
|
|
|
color: 'rgb(150,161,184)',
|
|
|
|
fontWeight: '700',
|
|
|
|
fontSize: 12,
|
|
|
|
marginBottom: 4,
|
|
|
|
},
|
|
|
|
})
|