mirror of
https://github.com/abhay-raizada/PeerScribe.git
synced 2026-04-26 16:24:03 +00:00
Merge pull request #1 from abhay-raizada/add_inputs
Add Inputs to main screen
This commit is contained in:
32
App.tsx
32
App.tsx
@@ -5,37 +5,35 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, {useEffect, useState} from 'react';
|
||||||
import { SafeAreaView, ScrollView, StatusBar, StyleSheet } from 'react-native';
|
import {
|
||||||
|
SafeAreaView,
|
||||||
|
ScrollView,
|
||||||
|
StatusBar,
|
||||||
|
StyleSheet,
|
||||||
|
Text,
|
||||||
|
} from 'react-native';
|
||||||
|
|
||||||
import { getFormTemplate } from './formstr/formstr';
|
import {Colors} from 'react-native/Libraries/NewAppScreen';
|
||||||
import { Colors } from 'react-native/Libraries/NewAppScreen';
|
import {PrescriptionCreator} from './components/PrescriptionCreator';
|
||||||
import { PrescriptionCreator } from './components/PrescriptionCreator';
|
|
||||||
import 'react-native-url-polyfill/auto';
|
import 'react-native-url-polyfill/auto';
|
||||||
|
import PolyfillCrypto from 'react-native-webview-crypto';
|
||||||
|
|
||||||
function App(): React.JSX.Element {
|
function App(): React.JSX.Element {
|
||||||
const backgroundStyle = {
|
const backgroundStyle = {
|
||||||
backgroundColor: Colors.darker,
|
backgroundColor: 'black',
|
||||||
|
color: 'white',
|
||||||
};
|
};
|
||||||
|
|
||||||
const [form, setForm] = useState<{} | null>(null);
|
const [form, setForm] = useState<{} | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('inside useeffect');
|
console.log('inside useeffect');
|
||||||
const fetchForm = async () => {
|
}, []);
|
||||||
if (!form) {
|
|
||||||
console.log('fetchiiiing forrmmm!!!');
|
|
||||||
let form = await getFormTemplate(
|
|
||||||
'eb3df1f89653475f0bcbd22da35f8d2f126db8a68a88a7abedc53535c76c39b4',
|
|
||||||
)
|
|
||||||
setForm(form);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
fetchForm();
|
|
||||||
}, [form]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={backgroundStyle}>
|
<SafeAreaView style={backgroundStyle}>
|
||||||
|
<PolyfillCrypto />
|
||||||
<StatusBar
|
<StatusBar
|
||||||
barStyle={'light-content'}
|
barStyle={'light-content'}
|
||||||
backgroundColor={backgroundStyle.backgroundColor}
|
backgroundColor={backgroundStyle.backgroundColor}
|
||||||
|
|||||||
969
android/app/src/main/assets/index.android.bundle
Normal file
969
android/app/src/main/assets/index.android.bundle
Normal file
File diff suppressed because one or more lines are too long
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 208 B |
Binary file not shown.
|
After Width: | Height: | Size: 186 B |
129
components/PharmacyPicker/AddPharmacy.tsx
Normal file
129
components/PharmacyPicker/AddPharmacy.tsx
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
import {useState} from 'react';
|
||||||
|
import {Alert, Button, Modal, Text, TextInput, View} from 'react-native';
|
||||||
|
import {Section} from '../common/Section';
|
||||||
|
import {getData, storeData} from '../../utils/localStorage';
|
||||||
|
|
||||||
|
export const AddPharmacy = ({
|
||||||
|
isVisible,
|
||||||
|
onClose,
|
||||||
|
onAdd,
|
||||||
|
}: {
|
||||||
|
isVisible: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
onAdd: (npub: string, relay: string, name: string) => void;
|
||||||
|
}) => {
|
||||||
|
const [npub, setNpub] = useState('');
|
||||||
|
const [relay, setRelay] = useState('');
|
||||||
|
const [name, setName] = useState('');
|
||||||
|
|
||||||
|
const handleNpub = (value: string) => {
|
||||||
|
setNpub(value);
|
||||||
|
};
|
||||||
|
const handleRelay = (value: string) => {
|
||||||
|
setRelay(value);
|
||||||
|
};
|
||||||
|
const handleName = (value: string) => {
|
||||||
|
setName(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
async function handleAddClick() {
|
||||||
|
if (!npub || !relay || !name) {
|
||||||
|
Alert.alert(
|
||||||
|
'Missing Inputs',
|
||||||
|
'Please enter name, npub and relay of the Pharmacy',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (npub.length !== 63 || !npub.startsWith('npub1')) {
|
||||||
|
Alert.alert('Invalid Npub');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let pharmacyListString = (await getData('pharmacyList')) || '[]';
|
||||||
|
let pharmacyList = JSON.parse(pharmacyListString) as Array<{
|
||||||
|
label: string;
|
||||||
|
npub: string;
|
||||||
|
relay: string;
|
||||||
|
}>;
|
||||||
|
pharmacyList = [...pharmacyList, {label: name, relay: relay, npub: npub}];
|
||||||
|
await storeData('pharmacyList', JSON.stringify(pharmacyList));
|
||||||
|
|
||||||
|
onAdd(npub, relay, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
visible={isVisible}
|
||||||
|
onRequestClose={() => {
|
||||||
|
console.log('closing....');
|
||||||
|
onClose();
|
||||||
|
return true;
|
||||||
|
}}
|
||||||
|
onDismiss={() => {
|
||||||
|
onClose();
|
||||||
|
}}
|
||||||
|
transparent={true}
|
||||||
|
style={{backgroundColor: 'black', margin: 0, padding: 0, height: '80%'}}
|
||||||
|
animationType="slide">
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'black',
|
||||||
|
justifyContent: 'center',
|
||||||
|
minHeight: '80%',
|
||||||
|
display: 'flex',
|
||||||
|
margin: 30,
|
||||||
|
alignItems: 'center',
|
||||||
|
}}>
|
||||||
|
<Section title="Add A Pharmacy">
|
||||||
|
<View style={{margin: 5}}>
|
||||||
|
<Text style={{color: 'white', margin: 5}}>Add Pharmacy Name</Text>
|
||||||
|
<TextInput
|
||||||
|
style={{
|
||||||
|
borderColor: '#000000',
|
||||||
|
borderWidth: 1,
|
||||||
|
borderRadius: 5,
|
||||||
|
color: 'white',
|
||||||
|
}}
|
||||||
|
placeholderTextColor="grey"
|
||||||
|
placeholder="Pharmacy X"
|
||||||
|
onChangeText={handleName}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Text style={{color: 'white', margin: 5}}>Add Pharmacy Npub</Text>
|
||||||
|
<TextInput
|
||||||
|
style={{
|
||||||
|
borderColor: '#000000',
|
||||||
|
borderWidth: 1,
|
||||||
|
borderRadius: 5,
|
||||||
|
color: 'white',
|
||||||
|
}}
|
||||||
|
placeholderTextColor="grey"
|
||||||
|
placeholder="npub1...."
|
||||||
|
onChangeText={handleNpub}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Text style={{color: 'white', margin: 5}}>Add Pharmacy Relay</Text>
|
||||||
|
<TextInput
|
||||||
|
style={{
|
||||||
|
borderColor: '#000000',
|
||||||
|
borderWidth: 1,
|
||||||
|
borderRadius: 5,
|
||||||
|
color: 'white',
|
||||||
|
}}
|
||||||
|
placeholderTextColor="grey"
|
||||||
|
placeholder="wss://<relay-url>"
|
||||||
|
onChangeText={handleRelay}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View style={{flexDirection: 'row'}}>
|
||||||
|
<View style={{margin: 10}}>
|
||||||
|
<Button title="Cancel" onPress={() => onClose()}></Button>
|
||||||
|
</View>
|
||||||
|
<View style={{margin: 10}}>
|
||||||
|
<Button title="Add" onPress={handleAddClick}></Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</Section>
|
||||||
|
</View>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
109
components/PharmacyPicker/index.tsx
Normal file
109
components/PharmacyPicker/index.tsx
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
import {Dropdown} from 'react-native-element-dropdown';
|
||||||
|
import {Section} from '../common/Section';
|
||||||
|
import {Button, Dimensions, Text, View} from 'react-native';
|
||||||
|
import React, {useEffect, useState} from 'react';
|
||||||
|
import {AddPharmacy} from './AddPharmacy';
|
||||||
|
import {getData} from '../../utils/localStorage';
|
||||||
|
|
||||||
|
export const pharmacyData = [
|
||||||
|
{
|
||||||
|
label: 'Default pharmacy',
|
||||||
|
value: 'default',
|
||||||
|
npub: 'npub1tea09rtjeuzgk4gjajzry37wuyv7h02d4zw38cpadcrkg5yt0qhqncr7km',
|
||||||
|
relay: 'wss://relay.damus.io',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: ' + Add Pharmacy',
|
||||||
|
value: 'custom',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let width = Dimensions.get('window').width;
|
||||||
|
|
||||||
|
interface PharmacyPickerProps {
|
||||||
|
handleLocationChange: (item: any) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PharmacyPicker: React.FC<PharmacyPickerProps> = ({
|
||||||
|
handleLocationChange,
|
||||||
|
}) => {
|
||||||
|
const [showAddPharmacyModal, setShowAddPharmacyModal] = useState(false);
|
||||||
|
const [pharmacyList, setPharmacyList] = useState(pharmacyData);
|
||||||
|
const [initialized, setInitialized] = useState(false);
|
||||||
|
|
||||||
|
const initialize = async () => {
|
||||||
|
let pharmacyListString = (await getData('pharmacyList')) || '[]';
|
||||||
|
let newPharmacyList = JSON.parse(pharmacyListString);
|
||||||
|
let storePharmacyList = [...newPharmacyList, ...pharmacyList];
|
||||||
|
setPharmacyList(storePharmacyList);
|
||||||
|
setInitialized(true);
|
||||||
|
handleLocationChange(storePharmacyList[0]);
|
||||||
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
if (!initialized) initialize();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const renderItem = (item: any) => {
|
||||||
|
if (item.value === 'custom') {
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<Button
|
||||||
|
title="Add Pharmacy"
|
||||||
|
onPress={() => {
|
||||||
|
setShowAddPharmacyModal(true);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
width: width,
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
padding: 10,
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
}}>
|
||||||
|
<Text style={{color: 'black', fontSize: 24}}>{item.label}</Text>
|
||||||
|
<View style={{width: width - 100}}>
|
||||||
|
<Text style={{color: 'grey', paddingBottom: 5}}>
|
||||||
|
Npub: {item.npub}
|
||||||
|
</Text>
|
||||||
|
<Text style={{color: 'grey'}}>Relay: {item.relay}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<Section title="Choose a Pharmacy">
|
||||||
|
<View style={{width: width - 40}}>
|
||||||
|
<Dropdown
|
||||||
|
data={pharmacyList}
|
||||||
|
labelField={'label'}
|
||||||
|
valueField={'label'}
|
||||||
|
onChange={handleLocationChange}
|
||||||
|
value={pharmacyList[0]}
|
||||||
|
renderItem={renderItem}
|
||||||
|
style={{width: '100%'}}
|
||||||
|
placeholderStyle={{color: 'white'}}
|
||||||
|
selectedTextStyle={{color: 'white'}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</Section>
|
||||||
|
<AddPharmacy
|
||||||
|
isVisible={showAddPharmacyModal}
|
||||||
|
onClose={() => {
|
||||||
|
setShowAddPharmacyModal(false);
|
||||||
|
}}
|
||||||
|
onAdd={() => {
|
||||||
|
initialize();
|
||||||
|
setShowAddPharmacyModal(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
90
components/PrescriptionCreator/AddressForm.tsx
Normal file
90
components/PrescriptionCreator/AddressForm.tsx
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import {Text, TextInput, View} from 'react-native';
|
||||||
|
import {styles, TextTheme} from '../common/styles';
|
||||||
|
import {useState} from 'react';
|
||||||
|
import {Section} from '../common/Section';
|
||||||
|
|
||||||
|
interface AddressForm {
|
||||||
|
address_line_1?: string;
|
||||||
|
city?: string;
|
||||||
|
state_province?: string;
|
||||||
|
postal_code?: string;
|
||||||
|
country_code?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AddressFormProps {
|
||||||
|
nestedFormCallback: (tag: string, form: Object) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AddressForm: React.FC<AddressFormProps> = ({
|
||||||
|
nestedFormCallback,
|
||||||
|
}) => {
|
||||||
|
const [form, setForm] = useState<AddressForm>({});
|
||||||
|
|
||||||
|
const handleTextChange = (tag: keyof AddressForm, text: string) => {
|
||||||
|
let newForm = {...form};
|
||||||
|
newForm[tag] = text;
|
||||||
|
setForm(newForm);
|
||||||
|
nestedFormCallback('Address', newForm);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<View>
|
||||||
|
<Text style={TextTheme}>Address Line 1</Text>
|
||||||
|
<TextInput
|
||||||
|
style={styles.input}
|
||||||
|
placeholder="Enter street"
|
||||||
|
value={form.address_line_1}
|
||||||
|
placeholderTextColor="white"
|
||||||
|
onChangeText={(text: string) =>
|
||||||
|
handleTextChange('address_line_1', text)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View>
|
||||||
|
<Text style={TextTheme}>City</Text>
|
||||||
|
<TextInput
|
||||||
|
style={styles.input}
|
||||||
|
placeholder="Enter city"
|
||||||
|
value={form.city}
|
||||||
|
placeholderTextColor="white"
|
||||||
|
onChangeText={(text: string) => handleTextChange('city', text)}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View>
|
||||||
|
<Text style={TextTheme}>State Provice</Text>
|
||||||
|
<TextInput
|
||||||
|
style={styles.input}
|
||||||
|
placeholder="enter state..."
|
||||||
|
value={form.state_province}
|
||||||
|
placeholderTextColor="white"
|
||||||
|
onChangeText={(text: string) =>
|
||||||
|
handleTextChange('state_province', text)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View>
|
||||||
|
<Text style={TextTheme}>Postal Code</Text>
|
||||||
|
<TextInput
|
||||||
|
style={styles.input}
|
||||||
|
placeholder="Enter postal code..."
|
||||||
|
value={form.postal_code}
|
||||||
|
placeholderTextColor="white"
|
||||||
|
onChangeText={(text: string) => handleTextChange('postal_code', text)}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View>
|
||||||
|
<Text style={TextTheme}>Country Code</Text>
|
||||||
|
<TextInput
|
||||||
|
style={styles.input}
|
||||||
|
placeholder="Enter Country Code..."
|
||||||
|
value={form.country_code}
|
||||||
|
placeholderTextColor="white"
|
||||||
|
onChangeText={(text: string) =>
|
||||||
|
handleTextChange('country_code', text)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
95
components/PrescriptionCreator/MedicineForm.tsx
Normal file
95
components/PrescriptionCreator/MedicineForm.tsx
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import {Text, TextInput, View} from 'react-native';
|
||||||
|
import {Section} from '../common/Section';
|
||||||
|
import {styles, TextTheme} from '../common/styles';
|
||||||
|
import {useState} from 'react';
|
||||||
|
|
||||||
|
interface MedicineForm {
|
||||||
|
name?: string;
|
||||||
|
dosage_form?: string;
|
||||||
|
strength?: string;
|
||||||
|
quantity?: string;
|
||||||
|
refills?: string;
|
||||||
|
directions?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MedicineFormProps {
|
||||||
|
nestedFormCallback: (tag: string, form: Object) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MedicineForm: React.FC<MedicineFormProps> = ({
|
||||||
|
nestedFormCallback,
|
||||||
|
}) => {
|
||||||
|
const [form, setForm] = useState<MedicineForm>({});
|
||||||
|
|
||||||
|
const handleTextChange = (tag: keyof MedicineForm, text: string) => {
|
||||||
|
let newForm = {...form};
|
||||||
|
newForm[tag] = text;
|
||||||
|
setForm(newForm);
|
||||||
|
nestedFormCallback('MedicationPrescribed', newForm);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<View>
|
||||||
|
<Text style={TextTheme}>Name of Medicine</Text>
|
||||||
|
<TextInput
|
||||||
|
style={styles.input}
|
||||||
|
placeholder="Enter name of medicine"
|
||||||
|
value={form.name}
|
||||||
|
placeholderTextColor="white"
|
||||||
|
onChangeText={(text: string) => handleTextChange('name', text)}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View>
|
||||||
|
<Text style={TextTheme}> Form of Dosage</Text>
|
||||||
|
<TextInput
|
||||||
|
style={styles.input}
|
||||||
|
placeholder="what is the dosage form"
|
||||||
|
value={form.dosage_form}
|
||||||
|
placeholderTextColor="white"
|
||||||
|
onChangeText={(text: string) => handleTextChange('dosage_form', text)}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View>
|
||||||
|
<Text style={TextTheme}>Strength</Text>
|
||||||
|
<TextInput
|
||||||
|
style={styles.input}
|
||||||
|
placeholder="enter strength..."
|
||||||
|
value={form.strength}
|
||||||
|
placeholderTextColor="white"
|
||||||
|
onChangeText={(text: string) => handleTextChange('strength', text)}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View>
|
||||||
|
<Text style={TextTheme}>Quantity</Text>
|
||||||
|
<TextInput
|
||||||
|
style={styles.input}
|
||||||
|
placeholder="Enter quantity..."
|
||||||
|
value={form.quantity}
|
||||||
|
placeholderTextColor="white"
|
||||||
|
onChangeText={(text: string) => handleTextChange('quantity', text)}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View>
|
||||||
|
<Text style={TextTheme}>Refills</Text>
|
||||||
|
<TextInput
|
||||||
|
style={styles.input}
|
||||||
|
placeholder="Refills"
|
||||||
|
value={form.refills}
|
||||||
|
placeholderTextColor="white"
|
||||||
|
onChangeText={(text: string) => handleTextChange('refills', text)}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View>
|
||||||
|
<Text style={TextTheme}>Directions</Text>
|
||||||
|
<TextInput
|
||||||
|
style={styles.input}
|
||||||
|
placeholder="Enter directions"
|
||||||
|
value={form.refills}
|
||||||
|
placeholderTextColor="white"
|
||||||
|
onChangeText={(text: string) => handleTextChange('directions', text)}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
75
components/PrescriptionCreator/PatientForm.tsx
Normal file
75
components/PrescriptionCreator/PatientForm.tsx
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import {Text, TextInput, View, Button} from 'react-native';
|
||||||
|
import {Section} from '../common/Section';
|
||||||
|
import {styles, TextTheme} from '../common/styles';
|
||||||
|
import {useState} from 'react';
|
||||||
|
import DatePicker from 'react-native-date-picker';
|
||||||
|
|
||||||
|
interface PatientForm {
|
||||||
|
name?: string;
|
||||||
|
date_of_birth?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PatientFormProps {
|
||||||
|
nestedFormCallback: (tag: string, form: Object) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PatientForm: React.FC<PatientFormProps> = ({
|
||||||
|
nestedFormCallback,
|
||||||
|
}) => {
|
||||||
|
const [form, setForm] = useState<PatientForm>({});
|
||||||
|
const [openDate, setOpenDate] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const handleTextChange = (tag: 'name' | 'date_of_birth', text: string) => {
|
||||||
|
let newForm = {...form};
|
||||||
|
newForm[tag] = text;
|
||||||
|
setForm(newForm);
|
||||||
|
nestedFormCallback('patient', {human_patient: newForm});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<View>
|
||||||
|
<Text style={TextTheme}>Name</Text>
|
||||||
|
<TextInput
|
||||||
|
style={styles.input}
|
||||||
|
placeholder="Enter Patients Name"
|
||||||
|
value={form.name}
|
||||||
|
placeholderTextColor="white"
|
||||||
|
onChangeText={(text: string) => handleTextChange('name', text)}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View>
|
||||||
|
<Text style={TextTheme}>Date of Birth</Text>
|
||||||
|
{form.date_of_birth ? (
|
||||||
|
<View>
|
||||||
|
<Text style={TextTheme}>{form.date_of_birth}</Text>
|
||||||
|
<Button
|
||||||
|
onPress={() => {
|
||||||
|
setOpenDate(true);
|
||||||
|
}}
|
||||||
|
title="Edit"
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
onPress={() => {
|
||||||
|
setOpenDate(true);
|
||||||
|
}}
|
||||||
|
title="Pick a date"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<DatePicker
|
||||||
|
modal
|
||||||
|
mode={'date'}
|
||||||
|
open={openDate}
|
||||||
|
date={new Date(form.date_of_birth || '01-01-1999')}
|
||||||
|
onCancel={() => setOpenDate(false)}
|
||||||
|
onConfirm={(date: Date) => {
|
||||||
|
handleTextChange('date_of_birth', date.toDateString());
|
||||||
|
setOpenDate(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,67 +1,150 @@
|
|||||||
import {Dimensions, Image, StyleSheet, Text, View} from 'react-native';
|
import {Alert, Dimensions, Image, View, Button} from 'react-native';
|
||||||
import SampleJSON from '../../formstr/sample.json';
|
|
||||||
import {Colors} from 'react-native/Libraries/NewAppScreen';
|
import {Colors} from 'react-native/Libraries/NewAppScreen';
|
||||||
import {PropsWithChildren} from 'react';
|
import {useEffect, useState} from 'react';
|
||||||
import {Card} from '@ant-design/react-native';
|
import {
|
||||||
|
SimplePool,
|
||||||
|
UnsignedEvent,
|
||||||
|
finalizeEvent,
|
||||||
|
getPublicKey,
|
||||||
|
nip04,
|
||||||
|
nip19,
|
||||||
|
} from 'nostr-tools';
|
||||||
|
import EncryptedStorage from 'react-native-encrypted-storage';
|
||||||
|
import {ImportNsec} from '../common/ImportNsec';
|
||||||
|
import {Section} from '../common/Section';
|
||||||
|
import {PatientForm} from './PatientForm';
|
||||||
|
import {AddressForm} from './AddressForm';
|
||||||
|
import {MedicineForm} from './MedicineForm';
|
||||||
|
import {PharmacyPicker, pharmacyData} from '../PharmacyPicker';
|
||||||
|
|
||||||
type SectionProps = PropsWithChildren<{
|
function OBJtoXML(obj: any) {
|
||||||
title: string;
|
var xml = '';
|
||||||
}>;
|
for (var prop in obj) {
|
||||||
|
xml += '<' + prop + '>';
|
||||||
|
if (Array.isArray(obj[prop])) {
|
||||||
|
for (var array of obj[prop]) {
|
||||||
|
// A real botch fix here
|
||||||
|
xml += '</' + prop + '>';
|
||||||
|
xml += '<' + prop + '>';
|
||||||
|
|
||||||
const backgroundStyle = {
|
xml += OBJtoXML(new Object(array));
|
||||||
backgroundColor: Colors.darker,
|
}
|
||||||
};
|
} else if (typeof obj[prop] == 'object') {
|
||||||
|
xml += OBJtoXML(new Object(obj[prop]));
|
||||||
const styles = StyleSheet.create({
|
} else {
|
||||||
sectionContainer: {
|
xml += obj[prop];
|
||||||
marginTop: 32,
|
}
|
||||||
paddingHorizontal: 24,
|
xml += '</' + prop + '>';
|
||||||
width: Dimensions.get('window').width - 80,
|
}
|
||||||
},
|
var xml = xml.replace(/<\/?[0-9]{1,}>/g, '');
|
||||||
sectionTitle: {
|
return xml;
|
||||||
fontSize: 24,
|
|
||||||
fontWeight: '600',
|
|
||||||
},
|
|
||||||
sectionDescription: {
|
|
||||||
marginTop: 8,
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: '400',
|
|
||||||
},
|
|
||||||
highlight: {
|
|
||||||
fontWeight: '500',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
function Section({children, title}: SectionProps): React.JSX.Element {
|
|
||||||
return (
|
|
||||||
<View style={styles.sectionContainer}>
|
|
||||||
<Text
|
|
||||||
style={[
|
|
||||||
styles.sectionTitle,
|
|
||||||
{
|
|
||||||
color: Colors.white,
|
|
||||||
},
|
|
||||||
]}>
|
|
||||||
{title}
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
style={[
|
|
||||||
styles.sectionDescription,
|
|
||||||
{
|
|
||||||
color: Colors.light,
|
|
||||||
},
|
|
||||||
]}>
|
|
||||||
{children}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PrescriptionCreator = () => {
|
export const PrescriptionCreator = () => {
|
||||||
|
const [showImportNsec, setShowImportNsec] = useState(false);
|
||||||
|
const [loggedInNpub, setLoggedInNpub] = useState('');
|
||||||
|
const [finalJSON, setFinalJson] = useState({});
|
||||||
|
const [selectedPharmacyId, setSelectedPharmacyId] = useState(
|
||||||
|
pharmacyData[0].npub,
|
||||||
|
);
|
||||||
|
const [selectedPharmacyRelays, setSelectedPharmacyRelays] = useState<
|
||||||
|
Array<string>
|
||||||
|
>([pharmacyData[0].relay!]);
|
||||||
|
|
||||||
|
const handleLocationChange = (item: {
|
||||||
|
name: string;
|
||||||
|
npub: string;
|
||||||
|
relay: string;
|
||||||
|
}) => {
|
||||||
|
setSelectedPharmacyId(item.npub);
|
||||||
|
setSelectedPharmacyRelays([item.relay]);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function initialize() {
|
||||||
|
let doctorCredentials = null;
|
||||||
|
try {
|
||||||
|
doctorCredentials = await EncryptedStorage.getItem('user_credentials');
|
||||||
|
if (!doctorCredentials) {
|
||||||
|
setShowImportNsec(true);
|
||||||
|
} else {
|
||||||
|
setLoggedInNpub(
|
||||||
|
nip19.npubEncode(
|
||||||
|
getPublicKey(nip19.decode(doctorCredentials).data as Uint8Array),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Error getting credentials', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
initialize();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const nestedFormCallback = (
|
||||||
|
xmlTag: string,
|
||||||
|
value: Object | Array<any> | string,
|
||||||
|
) => {
|
||||||
|
console.log('Filling', xmlTag, value);
|
||||||
|
setFinalJson({...finalJSON, [xmlTag]: value});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleImportNsec = (nsec: string) => {
|
||||||
|
EncryptedStorage.setItem('user_credentials', nsec);
|
||||||
|
if (nsec.startsWith('nsec1') && nsec.length !== 63) {
|
||||||
|
Alert.alert('not a valid nsec!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setLoggedInNpub(
|
||||||
|
nip19.npubEncode(getPublicKey(nip19.decode(nsec).data as Uint8Array)),
|
||||||
|
);
|
||||||
|
setShowImportNsec(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleButtonPress = () => {
|
||||||
|
console.log('Final JSON is', finalJSON);
|
||||||
|
const xml = OBJtoXML({prescription: finalJSON});
|
||||||
|
console.log('XML is...', xml, typeof xml);
|
||||||
|
sendPrescription(xml);
|
||||||
|
};
|
||||||
|
|
||||||
|
const sendPrescription = async (xml: string) => {
|
||||||
|
console.log('Will generate IDs');
|
||||||
|
const sk = nip19.decode(
|
||||||
|
(await EncryptedStorage.getItem('user_credentials')) as `nsec1${string}`,
|
||||||
|
).data as Uint8Array;
|
||||||
|
const pk = getPublicKey(sk);
|
||||||
|
const pharmacyId = nip19.decode(selectedPharmacyId!).data as string;
|
||||||
|
console.log('Got ids');
|
||||||
|
console.log('content is ', xml);
|
||||||
|
const baseKind4Event: UnsignedEvent = {
|
||||||
|
kind: 4,
|
||||||
|
tags: [['p', pharmacyId]],
|
||||||
|
content: await nip04.encrypt(sk, pharmacyId, `${xml}`),
|
||||||
|
created_at: Math.floor(Date.now() / 1000),
|
||||||
|
pubkey: pk,
|
||||||
|
};
|
||||||
|
const finalEvent = finalizeEvent(baseKind4Event, sk);
|
||||||
|
console.log(
|
||||||
|
'FINAL EVENT IS ',
|
||||||
|
finalEvent,
|
||||||
|
'relays are',
|
||||||
|
selectedPharmacyRelays,
|
||||||
|
);
|
||||||
|
const pool = new SimplePool();
|
||||||
|
console.log('publishing event');
|
||||||
|
let messages = await Promise.allSettled(
|
||||||
|
pool.publish(selectedPharmacyRelays, finalEvent),
|
||||||
|
);
|
||||||
|
console.log('Messages from relays', messages);
|
||||||
|
Alert.alert('Prescription Sent to the pharmacy!');
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: Colors.black,
|
backgroundColor: Colors.black,
|
||||||
|
minHeight: Dimensions.get('window').height,
|
||||||
}}>
|
}}>
|
||||||
<Image
|
<Image
|
||||||
style={{
|
style={{
|
||||||
@@ -69,45 +152,45 @@ export const PrescriptionCreator = () => {
|
|||||||
width: Dimensions.get('window').width,
|
width: Dimensions.get('window').width,
|
||||||
}}
|
}}
|
||||||
source={{
|
source={{
|
||||||
uri: SampleJSON.settings.titleImageUrl,
|
uri: 'https://www.studentdoctor.net/wp-content/uploads/2018/08/20180815_prescription.png',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Section title="PeerScribe">
|
<Section title="PeerScribe">
|
||||||
From the practice of {SampleJSON.name}
|
From the practice of {loggedInNpub}
|
||||||
|
<Button
|
||||||
|
onPress={() => {
|
||||||
|
setShowImportNsec(true);
|
||||||
|
}}
|
||||||
|
title="edit"
|
||||||
|
/>
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
<Section title="Prescription">
|
<PharmacyPicker handleLocationChange={handleLocationChange} />
|
||||||
<View style={{display: 'flex', flexDirection: 'column'}}>
|
|
||||||
{SampleJSON.fields.map(field => {
|
<Section title="Create Prescription">
|
||||||
return (
|
<View style={{display: 'flex', flexDirection: 'column', width: '100%'}}>
|
||||||
<Card
|
<Section title="Patient" collapsible={true}>
|
||||||
key={field.questionId}
|
{' '}
|
||||||
style={{
|
<PatientForm nestedFormCallback={nestedFormCallback} />
|
||||||
display: 'flex',
|
</Section>
|
||||||
flexDirection: 'column',
|
<Section title="Address" collapsible={true}>
|
||||||
padding: 10,
|
<AddressForm nestedFormCallback={nestedFormCallback} />
|
||||||
backgroundColor: Colors.white,
|
</Section>
|
||||||
margin: 10,
|
<Section title="Medicine" collapsible={true}>
|
||||||
width: 500,
|
<MedicineForm nestedFormCallback={nestedFormCallback} />
|
||||||
height: 'auto',
|
</Section>
|
||||||
}}>
|
<View style={{margin: 15}}>
|
||||||
<Text
|
<Button onPress={handleButtonPress} title="Create Rx" />
|
||||||
style={{
|
</View>
|
||||||
color: Colors.green,
|
|
||||||
}}>
|
|
||||||
{field.question}
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
color: Colors.light,
|
|
||||||
}}>
|
|
||||||
{field.answerType}
|
|
||||||
</Text>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</View>
|
</View>
|
||||||
</Section>
|
</Section>
|
||||||
|
<ImportNsec
|
||||||
|
isVisible={showImportNsec}
|
||||||
|
onClose={() => {
|
||||||
|
setShowImportNsec(false);
|
||||||
|
}}
|
||||||
|
onPress={handleImportNsec}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
68
components/common/ImportNsec.tsx
Normal file
68
components/common/ImportNsec.tsx
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import {useState} from 'react';
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Modal,
|
||||||
|
NativeSyntheticEvent,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
TextInputChangeEventData,
|
||||||
|
View,
|
||||||
|
} from 'react-native';
|
||||||
|
|
||||||
|
export const ImportNsec = ({
|
||||||
|
isVisible,
|
||||||
|
onClose,
|
||||||
|
onPress,
|
||||||
|
}: {
|
||||||
|
isVisible: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
onPress: (nsec: `nsec1${string}`) => void;
|
||||||
|
}) => {
|
||||||
|
const [nsec, setNsec] = useState('');
|
||||||
|
|
||||||
|
const handleNsec = (value: string) => {
|
||||||
|
setNsec(value);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
visible={isVisible}
|
||||||
|
onRequestClose={() => {
|
||||||
|
console.log('closing....');
|
||||||
|
onClose();
|
||||||
|
return true;
|
||||||
|
}}
|
||||||
|
onDismiss={() => {
|
||||||
|
onClose();
|
||||||
|
}}
|
||||||
|
presentationStyle="pageSheet">
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
backgroundColor: '#ffffff',
|
||||||
|
height: 500,
|
||||||
|
justifyContent: 'center',
|
||||||
|
display: 'flex',
|
||||||
|
margin: 30,
|
||||||
|
borderColor: 'red',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}>
|
||||||
|
<View style={{margin: 5}}>
|
||||||
|
<Text style={{color: '#000000', margin: 5}}>Import Your Nsec</Text>
|
||||||
|
<TextInput
|
||||||
|
style={{
|
||||||
|
borderColor: '#000000',
|
||||||
|
borderWidth: 1,
|
||||||
|
borderRadius: 5,
|
||||||
|
color: '#000000',
|
||||||
|
}}
|
||||||
|
onChangeText={handleNsec}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View>
|
||||||
|
<Button
|
||||||
|
title="Import"
|
||||||
|
onPress={() => onPress(nsec as `nsec1${string}`)}></Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
43
components/common/Section.tsx
Normal file
43
components/common/Section.tsx
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import {PropsWithChildren, useState} from 'react';
|
||||||
|
import {
|
||||||
|
Dimensions,
|
||||||
|
StyleSheet,
|
||||||
|
Text,
|
||||||
|
Touchable,
|
||||||
|
TouchableOpacity,
|
||||||
|
View,
|
||||||
|
} from 'react-native';
|
||||||
|
import {styles} from './styles';
|
||||||
|
|
||||||
|
type SectionProps = PropsWithChildren<{
|
||||||
|
title: string;
|
||||||
|
collapsible?: boolean;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export function Section({
|
||||||
|
children,
|
||||||
|
title,
|
||||||
|
collapsible,
|
||||||
|
}: SectionProps): React.JSX.Element {
|
||||||
|
const [collapsed, setCollapsed] = useState(true);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.sectionContainer}>
|
||||||
|
{collapsible ? (
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={() => {
|
||||||
|
setCollapsed(!collapsed);
|
||||||
|
}}>
|
||||||
|
<Text style={styles.sectionTitle}>
|
||||||
|
{title} {collapsed ? '→' : '↓'}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
) : (
|
||||||
|
<Text style={styles.sectionTitle}>{title}</Text>
|
||||||
|
)}
|
||||||
|
{!collapsible || !collapsed ? (
|
||||||
|
<Text style={[styles.sectionDescription]}>{children}</Text>
|
||||||
|
) : null}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
39
components/common/styles.ts
Normal file
39
components/common/styles.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import {Dimensions, StyleSheet} from 'react-native';
|
||||||
|
import {Colors} from 'react-native/Libraries/NewAppScreen';
|
||||||
|
|
||||||
|
export const styles = StyleSheet.create({
|
||||||
|
input: {
|
||||||
|
height: 40,
|
||||||
|
margin: 12,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderBottomColor: 'white',
|
||||||
|
padding: 10,
|
||||||
|
color: Colors.white,
|
||||||
|
},
|
||||||
|
sectionContainer: {
|
||||||
|
marginTop: 32,
|
||||||
|
paddingHorizontal: 24,
|
||||||
|
width: Dimensions.get('window').width - 80,
|
||||||
|
color: Colors.white,
|
||||||
|
},
|
||||||
|
sectionTitle: {
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: '600',
|
||||||
|
color: Colors.white,
|
||||||
|
},
|
||||||
|
sectionDescription: {
|
||||||
|
marginTop: 8,
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: '400',
|
||||||
|
color: Colors.white,
|
||||||
|
},
|
||||||
|
highlight: {
|
||||||
|
fontWeight: '500',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const TextTheme = [
|
||||||
|
{
|
||||||
|
color: Colors.white,
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
import {SimplePool} from 'nostr-tools';
|
|
||||||
import {V1FormSpec} from '@formstr/sdk/dist/interfaces';
|
|
||||||
|
|
||||||
const relayList = ['wss://relay.primal.net/', 'wss://relay.hllo.live/'];
|
|
||||||
|
|
||||||
export const getFormTemplate = async (formId: string): Promise<{}> => {
|
|
||||||
console.log('inside getFormTemplate');
|
|
||||||
const pool = new SimplePool();
|
|
||||||
let formIdPubkey = formId;
|
|
||||||
console.log('everything initialised');
|
|
||||||
const filter = {
|
|
||||||
kinds: [0],
|
|
||||||
authors: [formIdPubkey], //formId is the npub of the form
|
|
||||||
};
|
|
||||||
let kind0 = null
|
|
||||||
try {
|
|
||||||
console.log('inside trydasdsad');
|
|
||||||
kind0 = await pool.get(relayList, filter)
|
|
||||||
console.log('Main thread is working, got event', kind0);
|
|
||||||
} catch (e) {
|
|
||||||
console.log('inside catch');
|
|
||||||
console.log('error is', e);
|
|
||||||
}
|
|
||||||
|
|
||||||
pool.close(relayList);
|
|
||||||
let formTemplate;
|
|
||||||
if (kind0) {
|
|
||||||
formTemplate = JSON.parse(kind0.content);
|
|
||||||
} else {
|
|
||||||
throw Error('Form template not found');
|
|
||||||
}
|
|
||||||
return formTemplate;
|
|
||||||
};
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "X Pharmacy ",
|
|
||||||
"schemaVersion": "v1",
|
|
||||||
"settings": {
|
|
||||||
"titleImageUrl": "https://www.studentdoctor.net/wp-content/uploads/2018/08/20180815_prescription.png",
|
|
||||||
"description": "Testing formstr for getting prescriptions",
|
|
||||||
"thankYouPage": true
|
|
||||||
},
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"question": "Location",
|
|
||||||
"answerType": "dropdown",
|
|
||||||
"answerSettings": {
|
|
||||||
"choices": [
|
|
||||||
{
|
|
||||||
"label": "Narnia",
|
|
||||||
"choiceId": "ae9DZi"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Hogwarts",
|
|
||||||
"choiceId": "Dx4gyn"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Mordor",
|
|
||||||
"choiceId": "oWygzo"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Tattooine",
|
|
||||||
"choiceId": "dLgxns"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"questionId": "V7yEc2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"question": "Patient",
|
|
||||||
"answerType": "label",
|
|
||||||
"answerSettings": {},
|
|
||||||
"questionId": "BPfwxq"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"question": "Name",
|
|
||||||
"answerType": "shortText",
|
|
||||||
"answerSettings": {},
|
|
||||||
"questionId": "HLydbI"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"question": "Date Of Birth ",
|
|
||||||
"answerType": "date",
|
|
||||||
"answerSettings": {},
|
|
||||||
"questionId": "lzpBhA"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"question": "Address",
|
|
||||||
"answerType": "paragraph",
|
|
||||||
"answerSettings": {},
|
|
||||||
"questionId": "Mvajrv"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"question": "Phone",
|
|
||||||
"answerType": "shortText",
|
|
||||||
"answerSettings": {},
|
|
||||||
"questionId": "PjTnfM"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"question": "Allergies",
|
|
||||||
"answerType": "shortText",
|
|
||||||
"answerSettings": {},
|
|
||||||
"questionId": "UMfscG"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"question": "Current Meds ",
|
|
||||||
"answerType": "shortText",
|
|
||||||
"answerSettings": {},
|
|
||||||
"questionId": "Cc8lih"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"question": "Medications",
|
|
||||||
"answerType": "label",
|
|
||||||
"answerSettings": {},
|
|
||||||
"questionId": "0R7uhf"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"question": "Name",
|
|
||||||
"answerType": "shortText",
|
|
||||||
"answerSettings": {},
|
|
||||||
"questionId": "WXJScB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"question": "Dosage Form",
|
|
||||||
"answerType": "shortText",
|
|
||||||
"answerSettings": {},
|
|
||||||
"questionId": "BxEwHQ"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"question": "Strength",
|
|
||||||
"answerType": "shortText",
|
|
||||||
"answerSettings": {},
|
|
||||||
"questionId": "q2nC6e"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"question": "Quantiy ",
|
|
||||||
"answerType": "number",
|
|
||||||
"answerSettings": {},
|
|
||||||
"questionId": "fz68UV"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"question": "Re-fills",
|
|
||||||
"answerType": "shortText",
|
|
||||||
"answerSettings": {},
|
|
||||||
"questionId": "9SdbyN"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"question": "Directions",
|
|
||||||
"answerType": "paragraph",
|
|
||||||
"answerSettings": {},
|
|
||||||
"questionId": "6FrcPv"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
1
index.js
1
index.js
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
import {AppRegistry} from 'react-native';
|
import {AppRegistry} from 'react-native';
|
||||||
import 'text-encoding-polyfill';
|
import 'text-encoding-polyfill';
|
||||||
|
import 'react-native-get-random-values';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
import {name as appName} from './app.json';
|
import {name as appName} from './app.json';
|
||||||
|
|
||||||
|
|||||||
25
package.json
25
package.json
@@ -10,15 +10,33 @@
|
|||||||
"test": "jest"
|
"test": "jest"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/react-native": "^5.1.0",
|
"@react-native-async-storage/async-storage": "^1.24.0",
|
||||||
"@formstr/sdk": "^0.0.4-alpha",
|
"@react-native-community/datetimepicker": "^8.0.1",
|
||||||
"@react-native-community/segmented-control": "^2.2.2",
|
"@react-native-community/segmented-control": "^2.2.2",
|
||||||
"@react-native-community/slider": "^4.5.0",
|
"@react-native-community/slider": "^4.5.0",
|
||||||
|
"@react-native-picker/picker": "^2.6.1",
|
||||||
|
"@rneui/base": "^4.0.0-rc.8",
|
||||||
|
"@rneui/themed": "^4.0.0-rc.8",
|
||||||
|
"buffer": "^6.0.3",
|
||||||
|
"events": "^3.3.0",
|
||||||
"nostr-tools": "^2.3.1",
|
"nostr-tools": "^2.3.1",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-native": "0.73.4",
|
"react-native": "0.73.4",
|
||||||
|
"react-native-crypto": "^2.2.0",
|
||||||
|
"react-native-date-picker": "^5.0.3",
|
||||||
|
"react-native-element-dropdown": "^2.10.4",
|
||||||
|
"react-native-encrypted-storage": "^4.0.3",
|
||||||
|
"react-native-get-random-values": "^1.11.0",
|
||||||
|
"react-native-picker-select": "^9.0.1",
|
||||||
|
"react-native-randombytes": "^3.6.1",
|
||||||
|
"react-native-safe-area-context": "^4.5.0",
|
||||||
"react-native-url-polyfill": "^2.0.0",
|
"react-native-url-polyfill": "^2.0.0",
|
||||||
"text-encoding-polyfill": "^0.6.7"
|
"react-native-vector-icons": "^10.1.0",
|
||||||
|
"react-native-webview": "^13.8.4",
|
||||||
|
"react-native-webview-crypto": "^0.0.25",
|
||||||
|
"stream": "^0.0.2",
|
||||||
|
"text-encoding-polyfill": "^0.6.7",
|
||||||
|
"xml-js": "^1.6.11"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.20.0",
|
"@babel/core": "^7.20.0",
|
||||||
@@ -29,6 +47,7 @@
|
|||||||
"@react-native/metro-config": "0.73.5",
|
"@react-native/metro-config": "0.73.5",
|
||||||
"@react-native/typescript-config": "0.73.1",
|
"@react-native/typescript-config": "0.73.1",
|
||||||
"@types/react": "^18.2.6",
|
"@types/react": "^18.2.6",
|
||||||
|
"@types/react-native-vector-icons": "^6.4.18",
|
||||||
"@types/react-test-renderer": "^18.0.0",
|
"@types/react-test-renderer": "^18.0.0",
|
||||||
"babel-jest": "^29.6.3",
|
"babel-jest": "^29.6.3",
|
||||||
"eslint": "^8.19.0",
|
"eslint": "^8.19.0",
|
||||||
|
|||||||
721109
static/medicine_names.json
Normal file
721109
static/medicine_names.json
Normal file
File diff suppressed because it is too large
Load Diff
29
utils/localStorage.ts
Normal file
29
utils/localStorage.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||||
|
|
||||||
|
export const storeData = async (key: string, value: string): Promise<void> => {
|
||||||
|
try {
|
||||||
|
await AsyncStorage.setItem(key, value);
|
||||||
|
console.log('Data stored successfully');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to store data:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getData = async (key: string): Promise<string | null> => {
|
||||||
|
try {
|
||||||
|
const value = await AsyncStorage.getItem(key);
|
||||||
|
return value;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to retrieve data:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const removeData = async (key: string): Promise<void> => {
|
||||||
|
try {
|
||||||
|
await AsyncStorage.removeItem(key);
|
||||||
|
console.log('Data removed successfully');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to remove data:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user