mirror of
https://github.com/abhay-raizada/PeerScribe.git
synced 2026-04-26 16:24:03 +00:00
Add nested xml
This commit is contained in:
9
App.tsx
9
App.tsx
@@ -12,7 +12,7 @@ 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'
|
import PolyfillCrypto from 'react-native-webview-crypto';
|
||||||
|
|
||||||
function App(): React.JSX.Element {
|
function App(): React.JSX.Element {
|
||||||
const backgroundStyle = {
|
const backgroundStyle = {
|
||||||
@@ -27,12 +27,11 @@ function App(): React.JSX.Element {
|
|||||||
if (!form) {
|
if (!form) {
|
||||||
let form = await getFormTemplate(
|
let form = await getFormTemplate(
|
||||||
'eb3df1f89653475f0bcbd22da35f8d2f126db8a68a88a7abedc53535c76c39b4',
|
'eb3df1f89653475f0bcbd22da35f8d2f126db8a68a88a7abedc53535c76c39b4',
|
||||||
)
|
);
|
||||||
setForm(form);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
fetchForm();
|
fetchForm();
|
||||||
}, [form]);
|
}, [form, setForm]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={backgroundStyle}>
|
<SafeAreaView style={backgroundStyle}>
|
||||||
@@ -44,7 +43,7 @@ function App(): React.JSX.Element {
|
|||||||
<ScrollView
|
<ScrollView
|
||||||
contentInsetAdjustmentBehavior="automatic"
|
contentInsetAdjustmentBehavior="automatic"
|
||||||
style={backgroundStyle}>
|
style={backgroundStyle}>
|
||||||
<PrescriptionCreator form={form}/>
|
<PrescriptionCreator />
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,15 @@
|
|||||||
import { DatePicker, DatePickerView, InputItem, List, Text, TextareaItem, View } from "@ant-design/react-native";
|
import {
|
||||||
import { V1AnswerSettings, AnswerTypes } from "@formstr/sdk/dist/interfaces";
|
DatePicker,
|
||||||
import { useState } from "react";
|
DatePickerView,
|
||||||
import RNPickerSelect from "react-native-picker-select"
|
InputItem,
|
||||||
|
List,
|
||||||
|
Text,
|
||||||
|
TextareaItem,
|
||||||
|
View,
|
||||||
|
} from '@ant-design/react-native';
|
||||||
|
import {V1AnswerSettings, AnswerTypes} from '@formstr/sdk/dist/interfaces';
|
||||||
|
import {useState} from 'react';
|
||||||
|
import RNPickerSelect from 'react-native-picker-select';
|
||||||
|
|
||||||
interface InputFillerProps {
|
interface InputFillerProps {
|
||||||
answerType: AnswerTypes;
|
answerType: AnswerTypes;
|
||||||
@@ -16,30 +24,30 @@ export const InputFiller: React.FC<InputFillerProps> = ({
|
|||||||
onChange,
|
onChange,
|
||||||
defaultValue,
|
defaultValue,
|
||||||
}) => {
|
}) => {
|
||||||
|
const [inputValue, setInputValue] = useState('');
|
||||||
const [inputValue, setInputValue] = useState("");
|
const handleInputChange = (e: any) => {
|
||||||
const handleInputChange = (
|
console.log('E is', e);
|
||||||
e: any
|
|
||||||
) => {
|
|
||||||
console.log("E is", e)
|
|
||||||
setInputValue(e);
|
setInputValue(e);
|
||||||
onChange(e)
|
onChange(e);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleValueChange = (value: string) => {
|
const handleValueChange = (value: string) => {
|
||||||
if (!value) return;
|
if (!value) return;
|
||||||
setInputValue(value)
|
setInputValue(value);
|
||||||
onChange(value);
|
onChange(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getInput = (
|
const getInput = (
|
||||||
answerType: AnswerTypes,
|
answerType: AnswerTypes,
|
||||||
answerSettings: V1AnswerSettings
|
answerSettings: V1AnswerSettings,
|
||||||
) => {
|
) => {
|
||||||
const dropdownItems = (answerSettings.choices || []).map((choice) => {
|
const dropdownItems = (answerSettings.choices || []).map(choice => {
|
||||||
return {
|
return {
|
||||||
label: choice.label, value: choice.choiceId, key: choice.choiceId
|
label: choice.label,
|
||||||
}})
|
value: choice.choiceId,
|
||||||
|
key: choice.choiceId,
|
||||||
|
};
|
||||||
|
});
|
||||||
const INPUT_TYPE_COMPONENT_MAP: {[key in AnswerTypes]?: JSX.Element} = {
|
const INPUT_TYPE_COMPONENT_MAP: {[key in AnswerTypes]?: JSX.Element} = {
|
||||||
[AnswerTypes.label]: <></>,
|
[AnswerTypes.label]: <></>,
|
||||||
[AnswerTypes.shortText]: (
|
[AnswerTypes.shortText]: (
|
||||||
@@ -47,8 +55,7 @@ export const InputFiller: React.FC<InputFillerProps> = ({
|
|||||||
onChange={handleValueChange}
|
onChange={handleValueChange}
|
||||||
defaultValue={defaultValue as string}
|
defaultValue={defaultValue as string}
|
||||||
placeholder="enter a value..."
|
placeholder="enter a value..."
|
||||||
placeholderTextColor="#aaaaaa">
|
placeholderTextColor="#aaaaaa"></InputItem>
|
||||||
</InputItem>
|
|
||||||
),
|
),
|
||||||
[AnswerTypes.paragraph]: (
|
[AnswerTypes.paragraph]: (
|
||||||
<TextareaItem
|
<TextareaItem
|
||||||
@@ -60,41 +67,24 @@ export const InputFiller: React.FC<InputFillerProps> = ({
|
|||||||
style={{paddingVertical: 5}}
|
style={{paddingVertical: 5}}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
[AnswerTypes.number]: (
|
[AnswerTypes.number]: <View></View>,
|
||||||
// <InputNumber
|
[AnswerTypes.radioButton]: <View></View>,
|
||||||
// defaultValue={defaultValue as string}
|
[AnswerTypes.checkboxes]: <View></View>,
|
||||||
// onChange={handleValueChange}
|
|
||||||
// style={{ width: "100%" }}
|
|
||||||
// placeholder="Please enter your response"
|
|
||||||
// />
|
|
||||||
<View></View>
|
|
||||||
),
|
|
||||||
[AnswerTypes.radioButton]: (
|
|
||||||
// <ChoiceFiller
|
|
||||||
// answerType={answerType as AnswerTypes.radioButton}
|
|
||||||
// answerSettings={answerSettings}
|
|
||||||
// defaultValue={defaultValue as string}
|
|
||||||
// onChange={handleValueChange}
|
|
||||||
// />
|
|
||||||
<View></View>
|
|
||||||
),
|
|
||||||
[AnswerTypes.checkboxes]: (
|
|
||||||
// <ChoiceFiller
|
|
||||||
// defaultValue={defaultValue as string}
|
|
||||||
// answerType={answerType as AnswerTypes.checkboxes}
|
|
||||||
// answerSettings={answerSettings}
|
|
||||||
// onChange={handleValueChange}
|
|
||||||
// />
|
|
||||||
<View></View>
|
|
||||||
),
|
|
||||||
[AnswerTypes.dropdown]: (
|
[AnswerTypes.dropdown]: (
|
||||||
<RNPickerSelect
|
<RNPickerSelect
|
||||||
onValueChange={handleValueChange}
|
onValueChange={handleValueChange}
|
||||||
items={dropdownItems}
|
items={dropdownItems}
|
||||||
placeholder={{}}
|
placeholder={{}}
|
||||||
key="picker"
|
key="picker"
|
||||||
value={inputValue}
|
value={inputValue}>
|
||||||
><Text>{inputValue ? answerSettings.choices?.filter((choice) => { return choice.choiceId === inputValue})[0].label : "Select an option"}</Text></RNPickerSelect>
|
<Text>
|
||||||
|
{inputValue
|
||||||
|
? answerSettings.choices?.filter(choice => {
|
||||||
|
return choice.choiceId === inputValue;
|
||||||
|
})[0].label
|
||||||
|
: 'Select an option'}
|
||||||
|
</Text>
|
||||||
|
</RNPickerSelect>
|
||||||
),
|
),
|
||||||
[AnswerTypes.date]: (
|
[AnswerTypes.date]: (
|
||||||
<List>
|
<List>
|
||||||
|
|||||||
51
components/PrescriptionCreator/AddressForm.tsx
Normal file
51
components/PrescriptionCreator/AddressForm.tsx
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import {Text, TextInput, View} from 'react-native';
|
||||||
|
import {Section} from './Section';
|
||||||
|
import {styles, TextTheme} from './styles';
|
||||||
|
import {useState} from 'react';
|
||||||
|
import DatePicker from 'react-native-date-picker';
|
||||||
|
import {Button} from '@ant-design/react-native';
|
||||||
|
|
||||||
|
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 [openDate, setOpenDate] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const handleTextChange = (tag: keyof AddressForm, text: string) => {
|
||||||
|
let newForm = {...form};
|
||||||
|
newForm[tag] = text;
|
||||||
|
setForm(newForm);
|
||||||
|
nestedFormCallback('Address', newForm);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Section title="Address">
|
||||||
|
<View>
|
||||||
|
<View>
|
||||||
|
<Text style={TextTheme}>Name</Text>
|
||||||
|
<TextInput
|
||||||
|
style={styles.input}
|
||||||
|
placeholder="Address Line 1"
|
||||||
|
value={form.address_line_1}
|
||||||
|
placeholderTextColor="white"
|
||||||
|
onChangeText={(text: string) =>
|
||||||
|
handleTextChange('address_line_1', text)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</Section>
|
||||||
|
);
|
||||||
|
};
|
||||||
80
components/PrescriptionCreator/PatientForm.tsx
Normal file
80
components/PrescriptionCreator/PatientForm.tsx
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import {Text, TextInput, View} from 'react-native';
|
||||||
|
import {Section} from './Section';
|
||||||
|
import {styles, TextTheme} from './styles';
|
||||||
|
import {useState} from 'react';
|
||||||
|
import DatePicker from 'react-native-date-picker';
|
||||||
|
import {Button} from '@ant-design/react-native';
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<Section title="Patient">
|
||||||
|
<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
|
||||||
|
size="small"
|
||||||
|
onPress={() => {
|
||||||
|
setOpenDate(true);
|
||||||
|
}}>
|
||||||
|
Edit
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
onPress={() => {
|
||||||
|
setOpenDate(true);
|
||||||
|
}}
|
||||||
|
size="small">
|
||||||
|
Pick a date
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<DatePicker
|
||||||
|
modal
|
||||||
|
mode={'date'}
|
||||||
|
open={openDate}
|
||||||
|
date={new Date(form.date_of_birth || '01-01-1990')}
|
||||||
|
onCancel={() => setOpenDate(false)}
|
||||||
|
onConfirm={(date: Date) => {
|
||||||
|
handleTextChange('date_of_birth', date.toDateString());
|
||||||
|
setOpenDate(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</Section>
|
||||||
|
);
|
||||||
|
};
|
||||||
17
components/PrescriptionCreator/Section.tsx
Normal file
17
components/PrescriptionCreator/Section.tsx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import {PropsWithChildren} from 'react';
|
||||||
|
import {Dimensions, StyleSheet, Text, View} from 'react-native';
|
||||||
|
import {Colors} from 'react-native/Libraries/NewAppScreen';
|
||||||
|
import {styles} from './styles';
|
||||||
|
|
||||||
|
type SectionProps = PropsWithChildren<{
|
||||||
|
title: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export function Section({children, title}: SectionProps): React.JSX.Element {
|
||||||
|
return (
|
||||||
|
<View style={styles.sectionContainer}>
|
||||||
|
<Text style={styles.sectionTitle}>{title}</Text>
|
||||||
|
<Text style={[styles.sectionDescription]}>{children}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,192 +1,213 @@
|
|||||||
import { Alert, Appearance, Dimensions, Image, StyleSheet, Text, View } from 'react-native';
|
import {Alert, Appearance, Dimensions, Image, Text, View} from 'react-native';
|
||||||
import {Colors} from 'react-native/Libraries/NewAppScreen';
|
import {Colors} from 'react-native/Libraries/NewAppScreen';
|
||||||
import {PropsWithChildren, useEffect, useState} from 'react';
|
import {PropsWithChildren, useEffect, useState} from 'react';
|
||||||
import {Button, Card, Modal} from '@ant-design/react-native';
|
import {Button, Card, Modal} from '@ant-design/react-native';
|
||||||
import { V1Field } from '@formstr/sdk/dist/interfaces';
|
|
||||||
import { InputFiller } from '../Inputs/Inputs';
|
|
||||||
// import { SendPrescription } from './sendPrescription';
|
// import { SendPrescription } from './sendPrescription';
|
||||||
import {Dropdown} from 'react-native-element-dropdown';
|
import {Dropdown} from 'react-native-element-dropdown';
|
||||||
import { SimplePool, UnsignedEvent, finalizeEvent, generateSecretKey, getPublicKey, nip04, nip19 } from 'nostr-tools';
|
import {
|
||||||
|
SimplePool,
|
||||||
|
UnsignedEvent,
|
||||||
|
finalizeEvent,
|
||||||
|
generateSecretKey,
|
||||||
|
getPublicKey,
|
||||||
|
nip04,
|
||||||
|
nip19,
|
||||||
|
} from 'nostr-tools';
|
||||||
import EncryptedStorage from 'react-native-encrypted-storage';
|
import EncryptedStorage from 'react-native-encrypted-storage';
|
||||||
import {ImportNsec} from './ImportNsec';
|
import {ImportNsec} from './ImportNsec';
|
||||||
import {json2xml} from 'xml-js';
|
import {json2xml} from 'xml-js';
|
||||||
|
import {Section} from './Section';
|
||||||
|
import {PatientForm} from './PatientForm';
|
||||||
|
import {AddressForm} from './AddressForm';
|
||||||
|
|
||||||
|
/*
|
||||||
|
Patient
|
||||||
|
- Name
|
||||||
|
- Date Of Birth
|
||||||
|
|
||||||
|
Address
|
||||||
|
- Address Line 1
|
||||||
|
- City
|
||||||
|
- StateProvince
|
||||||
|
- Postal Code
|
||||||
|
- Country Code
|
||||||
|
|
||||||
|
Medicine
|
||||||
|
- Name
|
||||||
|
- Dosage Form
|
||||||
|
- Strength
|
||||||
|
- Quantity
|
||||||
|
- Re-fills
|
||||||
|
- Directions
|
||||||
|
`
|
||||||
|
*/
|
||||||
|
|
||||||
function OBJtoXML(obj: any) {
|
function OBJtoXML(obj: any) {
|
||||||
var xml = '';
|
var xml = '';
|
||||||
for (var prop in obj) {
|
for (var prop in obj) {
|
||||||
xml += "<" + prop + ">";
|
xml += '<' + prop + '>';
|
||||||
if (Array.isArray(obj[prop])) {
|
if (Array.isArray(obj[prop])) {
|
||||||
for (var array of obj[prop]) {
|
for (var array of obj[prop]) {
|
||||||
|
|
||||||
// A real botch fix here
|
// A real botch fix here
|
||||||
xml += "</" + prop + ">";
|
xml += '</' + prop + '>';
|
||||||
xml += "<" + prop + ">";
|
xml += '<' + prop + '>';
|
||||||
|
|
||||||
xml += OBJtoXML(new Object(array));
|
xml += OBJtoXML(new Object(array));
|
||||||
}
|
}
|
||||||
} else if (typeof obj[prop] == "object") {
|
} else if (typeof obj[prop] == 'object') {
|
||||||
xml += OBJtoXML(new Object(obj[prop]));
|
xml += OBJtoXML(new Object(obj[prop]));
|
||||||
} else {
|
} else {
|
||||||
xml += obj[prop];
|
xml += obj[prop];
|
||||||
}
|
}
|
||||||
xml += "</" + prop + ">";
|
xml += '</' + prop + '>';
|
||||||
}
|
}
|
||||||
var xml = xml.replace(/<\/?[0-9]{1,}>/g, '');
|
var xml = xml.replace(/<\/?[0-9]{1,}>/g, '');
|
||||||
return xml
|
return xml;
|
||||||
}
|
}
|
||||||
|
|
||||||
type SectionProps = PropsWithChildren<{
|
|
||||||
title: string;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
const colorScheme = Appearance.getColorScheme();
|
const colorScheme = Appearance.getColorScheme();
|
||||||
|
|
||||||
const backgroundStyle = {
|
const backgroundStyle = {
|
||||||
backgroundColor: Colors.darker,
|
backgroundColor: Colors.darker,
|
||||||
};
|
};
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
sectionContainer: {
|
|
||||||
marginTop: 32,
|
|
||||||
paddingHorizontal: 24,
|
|
||||||
width: Dimensions.get('window').width - 80,
|
|
||||||
},
|
|
||||||
sectionTitle: {
|
|
||||||
fontSize: 24,
|
|
||||||
fontWeight: '600',
|
|
||||||
},
|
|
||||||
sectionDescription: {
|
|
||||||
marginTop: 8,
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: '400',
|
|
||||||
},
|
|
||||||
highlight: {
|
|
||||||
fontWeight: '500',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
const width = Dimensions.get('window').width; //full width
|
const width = Dimensions.get('window').width; //full width
|
||||||
const height = Dimensions.get('window').height
|
const height = Dimensions.get('window').height;
|
||||||
|
|
||||||
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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const locationData = [
|
const locationData = [
|
||||||
{ label: 'Pharmacy A', value: 'A', npub: 'npub1tea09rtjeuzgk4gjajzry37wuyv7h02d4zw38cpadcrkg5yt0qhqncr7km', relays: ["wss://relay.damus.io"] },
|
{
|
||||||
{ label: 'Pharmacy B', value: 'B', npub: 'npub1tea09rtjeuzgk4gjajzry37wuyv7h02d4zw38cpadcrkg5yt0qhqncr7km', relays: ["wss://relay.primal.net"] },
|
label: 'Pharmacy A',
|
||||||
{ label: 'Pharmacy C', value: 'C', npub: 'npub1tea09rtjeuzgk4gjajzry37wuyv7h02d4zw38cpadcrkg5yt0qhqncr7km', relays: ["wss://relay.hllo.live"] },
|
value: 'A',
|
||||||
{ label: 'Pharmacy D', value: 'D', npub: 'npub1tea09rtjeuzgk4gjajzry37wuyv7h02d4zw38cpadcrkg5yt0qhqncr7km', relays: ["wss://nos.lol", "wss://relay.damus.io"] }
|
npub: 'npub1tea09rtjeuzgk4gjajzry37wuyv7h02d4zw38cpadcrkg5yt0qhqncr7km',
|
||||||
]
|
relays: ['wss://relay.damus.io'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Pharmacy B',
|
||||||
|
value: 'B',
|
||||||
|
npub: 'npub1tea09rtjeuzgk4gjajzry37wuyv7h02d4zw38cpadcrkg5yt0qhqncr7km',
|
||||||
|
relays: ['wss://relay.primal.net'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Pharmacy C',
|
||||||
|
value: 'C',
|
||||||
|
npub: 'npub1tea09rtjeuzgk4gjajzry37wuyv7h02d4zw38cpadcrkg5yt0qhqncr7km',
|
||||||
|
relays: ['wss://relay.hllo.live'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Pharmacy D',
|
||||||
|
value: 'D',
|
||||||
|
npub: 'npub1tea09rtjeuzgk4gjajzry37wuyv7h02d4zw38cpadcrkg5yt0qhqncr7km',
|
||||||
|
relays: ['wss://nos.lol', 'wss://relay.damus.io'],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const locationDummyData = [
|
const locationDummyData = [{label: 'Pharmacy A', value: 'A'}];
|
||||||
{ label: 'Pharmacy A', value: 'A' }
|
|
||||||
]
|
|
||||||
|
|
||||||
export const PrescriptionCreator = ({ form }: { form: any }) => {
|
export const PrescriptionCreator = () => {
|
||||||
if (form === null) return <View style={{ backgroundColor: "#ffffff" }}><Text style={{ color: "#000000" }}>Loading...</Text></View>
|
|
||||||
const [showImportNsec, setShowImportNsec] = useState(false);
|
const [showImportNsec, setShowImportNsec] = useState(false);
|
||||||
const [loggedInNpub, setLoggedInNpub] = useState("")
|
const [loggedInNpub, setLoggedInNpub] = useState('');
|
||||||
const [selectedPharmacyId, setSelectedPharmacyId] = useState(locationData[0].npub);
|
const [selectedPharmacyId, setSelectedPharmacyId] = useState(
|
||||||
|
locationData[0].npub,
|
||||||
|
);
|
||||||
const [selectedPharmacyRelays, setSelectedPharmacyRelays] = useState([]);
|
const [selectedPharmacyRelays, setSelectedPharmacyRelays] = useState([]);
|
||||||
const [finalJSON, setFinalJson] = useState({})
|
const [finalJSON, setFinalJson] = useState({});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function initialize() {
|
async function initialize() {
|
||||||
let doctorCredentials = null;
|
let doctorCredentials = null;
|
||||||
try {
|
try {
|
||||||
doctorCredentials = await EncryptedStorage.getItem("user_credentials");
|
doctorCredentials = await EncryptedStorage.getItem('user_credentials');
|
||||||
if (!doctorCredentials) {
|
if (!doctorCredentials) {
|
||||||
setShowImportNsec(true)
|
setShowImportNsec(true);
|
||||||
|
} else {
|
||||||
|
setLoggedInNpub(
|
||||||
|
nip19.npubEncode(
|
||||||
|
getPublicKey(nip19.decode(doctorCredentials).data as Uint8Array),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
else {
|
} catch (e) {
|
||||||
setLoggedInNpub(nip19.npubEncode(getPublicKey(nip19.decode(doctorCredentials).data as Uint8Array)))
|
console.log('Error getting credentials', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(e) {
|
initialize();
|
||||||
console.log("Error getting credentials", e)
|
}, []);
|
||||||
}
|
|
||||||
}
|
|
||||||
initialize()
|
|
||||||
}, [])
|
|
||||||
const renderItem = (item: any) => {
|
const renderItem = (item: any) => {
|
||||||
return <View style={{ width: width, display: 'flex', flexDirection: 'column', padding: 10, flexWrap: "wrap" }}>
|
return (
|
||||||
<Text style={{ color: "black", fontSize: 24 }}>{item.label}</Text>
|
<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}}>
|
<View style={{width: width - 100}}>
|
||||||
<Text style={{ color: 'grey', paddingBottom: 5 }}>Npub: {item.npub}</Text>
|
<Text style={{color: 'grey', paddingBottom: 5}}>
|
||||||
|
Npub: {item.npub}
|
||||||
|
</Text>
|
||||||
<Text style={{color: 'grey'}}>Relays: {item.relays.join(', ')}</Text>
|
<Text style={{color: 'grey'}}>Relays: {item.relays.join(', ')}</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
}
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const handleFormItemChange = (questionId: string, value: string) => {
|
const nestedFormCallback = (
|
||||||
console.log("Filling", questionId, value)
|
xmlTag: string,
|
||||||
setFinalJson({...finalJSON, [questionId]: value})
|
value: Object | Array<any> | string,
|
||||||
}
|
) => {
|
||||||
|
console.log('Filling', xmlTag, value);
|
||||||
|
setFinalJson({...finalJSON, [xmlTag]: value});
|
||||||
|
};
|
||||||
|
|
||||||
const handleLocationChange = (item: any) => {
|
const handleLocationChange = (item: any) => {
|
||||||
setSelectedPharmacyId(item.npub)
|
setSelectedPharmacyId(item.npub);
|
||||||
setSelectedPharmacyRelays(item.relays)
|
setSelectedPharmacyRelays(item.relays);
|
||||||
}
|
};
|
||||||
|
|
||||||
const handleImportNsec = (nsec: string) => {
|
const handleImportNsec = (nsec: string) => {
|
||||||
EncryptedStorage.setItem("user_credentials", nsec)
|
EncryptedStorage.setItem('user_credentials', nsec);
|
||||||
if (nsec.startsWith('nsec1') && nsec.length !== 63) {
|
if (nsec.startsWith('nsec1') && nsec.length !== 63) {
|
||||||
Alert.alert("not a valid nsec!")
|
Alert.alert('not a valid nsec!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setLoggedInNpub(nip19.npubEncode(getPublicKey(nip19.decode(nsec).data as Uint8Array)))
|
setLoggedInNpub(
|
||||||
setShowImportNsec(false)
|
nip19.npubEncode(getPublicKey(nip19.decode(nsec).data as Uint8Array)),
|
||||||
}
|
);
|
||||||
|
setShowImportNsec(false);
|
||||||
|
};
|
||||||
|
|
||||||
const handleButtonPress = () => {
|
const handleButtonPress = () => {
|
||||||
console.log("Final JSON is", finalJSON)
|
console.log('Final JSON is', finalJSON);
|
||||||
const xml = OBJtoXML({form: finalJSON })
|
const xml = OBJtoXML({prescription: finalJSON});
|
||||||
console.log("XML is...", xml, typeof xml )
|
console.log('XML is...', xml, typeof xml);
|
||||||
sendPrescription(xml);
|
sendPrescription(xml);
|
||||||
}
|
};
|
||||||
|
|
||||||
const sendPrescription = async (xml: string) => {
|
const sendPrescription = async (xml: string) => {
|
||||||
console.log("Will generate IDs")
|
console.log('Will generate IDs');
|
||||||
const sk = nip19.decode(await EncryptedStorage.getItem("user_credentials") as `nsec1${string}`).data as Uint8Array
|
const sk = nip19.decode(
|
||||||
const pk = getPublicKey(sk)
|
(await EncryptedStorage.getItem('user_credentials')) as `nsec1${string}`,
|
||||||
const pharmacyId = nip19.decode(selectedPharmacyId).data as string
|
).data as Uint8Array;
|
||||||
console.log("Got ids")
|
const pk = getPublicKey(sk);
|
||||||
|
const pharmacyId = nip19.decode(selectedPharmacyId).data as string;
|
||||||
|
console.log('Got ids');
|
||||||
|
console.log('content is ', xml);
|
||||||
const baseKind4Event: UnsignedEvent = {
|
const baseKind4Event: UnsignedEvent = {
|
||||||
kind: 4,
|
kind: 4,
|
||||||
tags: [["p", pharmacyId]],
|
tags: [['p', pharmacyId]],
|
||||||
content: await nip04.encrypt(sk, pharmacyId, `This is a test prescription from PeerScribe ${xml}`),
|
content: await nip04.encrypt(sk, pharmacyId, `${xml}`),
|
||||||
created_at: Math.floor(Date.now() / 1000),
|
created_at: Math.floor(Date.now() / 1000),
|
||||||
pubkey: pk
|
pubkey: pk,
|
||||||
}
|
};
|
||||||
const finalEvent = finalizeEvent(baseKind4Event, sk)
|
const finalEvent = finalizeEvent(baseKind4Event, sk);
|
||||||
const pool = new SimplePool()
|
const pool = new SimplePool();
|
||||||
console.log("publishing event")
|
console.log('publishing event');
|
||||||
await Promise.any(pool.publish(selectedPharmacyRelays, finalEvent))
|
await Promise.any(pool.publish(selectedPharmacyRelays, finalEvent));
|
||||||
console.log("Event Published")
|
console.log('Event Published');
|
||||||
Alert.alert("Prescription Sent to the pharmacy!")
|
Alert.alert('Prescription Sent to the pharmacy!');
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
@@ -199,58 +220,60 @@ export const PrescriptionCreator = ({ form }: { form: any }) => {
|
|||||||
width: Dimensions.get('window').width,
|
width: Dimensions.get('window').width,
|
||||||
}}
|
}}
|
||||||
source={{
|
source={{
|
||||||
uri: form.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 {loggedInNpub}
|
From the practice of {loggedInNpub}
|
||||||
<Button size="small" onPress={() => {setShowImportNsec(true)}} >Edit!</Button>
|
<Button
|
||||||
|
size="small"
|
||||||
|
onPress={() => {
|
||||||
|
setShowImportNsec(true);
|
||||||
|
}}>
|
||||||
|
Edit!
|
||||||
|
</Button>
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
<Section title="Choose a Pharmacy">
|
<Section title="Choose a Pharmacy">
|
||||||
<View style={{width: width - 40}}>
|
<View style={{width: width - 40}}>
|
||||||
<Dropdown data={locationData} labelField={'label'} valueField={'label'} onChange={handleLocationChange} value={locationData[0]}
|
<Dropdown
|
||||||
renderItem={renderItem} style={{ width: "100%" }} placeholderStyle={{ color: "white" }} selectedTextStyle={{ color: 'white' }} />
|
data={locationData}
|
||||||
|
labelField={'label'}
|
||||||
|
valueField={'label'}
|
||||||
|
onChange={handleLocationChange}
|
||||||
|
value={locationData[0]}
|
||||||
|
renderItem={renderItem}
|
||||||
|
style={{width: '100%'}}
|
||||||
|
placeholderStyle={{color: 'white'}}
|
||||||
|
selectedTextStyle={{color: 'white'}}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
<Section title="Prescription">
|
<Section title="Prescription">
|
||||||
<View style={{ display: 'flex', flexDirection: 'column', width: "100%" }}>
|
<View style={{display: 'flex', flexDirection: 'column', width: '100%'}}>
|
||||||
{form.fields.map((field: V1Field) => {
|
<PatientForm nestedFormCallback={nestedFormCallback} />
|
||||||
return (
|
<AddressForm nestedFormCallback={nestedFormCallback} />
|
||||||
<Card
|
<Button type="primary" onPress={handleButtonPress}>
|
||||||
key={field.questionId}
|
|
||||||
style={{
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
padding: 10,
|
|
||||||
backgroundColor: Colors.white,
|
|
||||||
margin: 10,
|
|
||||||
width: 350,
|
|
||||||
height: 'auto',
|
|
||||||
}}>
|
|
||||||
<Text
|
<Text
|
||||||
style={{
|
style={[
|
||||||
color: "#000000",
|
{
|
||||||
}}>
|
|
||||||
{field.question}
|
|
||||||
</Text>
|
|
||||||
{/* <Text
|
|
||||||
style={{
|
|
||||||
color: Colors.light,
|
color: Colors.light,
|
||||||
}}>
|
},
|
||||||
{field.answerType}
|
]}>
|
||||||
</Text> */}
|
{' '}
|
||||||
<InputFiller answerSettings={field.answerSettings} answerType={field.answerType} onChange={
|
Create RX{' '}
|
||||||
(answer) => handleFormItemChange(field.questionId, answer)} />
|
</Text>
|
||||||
</Card>
|
</Button>
|
||||||
);
|
|
||||||
})}
|
|
||||||
<Button type='primary' onPress={handleButtonPress}> Create RX </Button>
|
|
||||||
</View>
|
</View>
|
||||||
</Section>
|
</Section>
|
||||||
<ImportNsec isVisible={showImportNsec} onClose={() => { setShowImportNsec(false)}} onPress={handleImportNsec}/>
|
<ImportNsec
|
||||||
|
isVisible={showImportNsec}
|
||||||
|
onClose={() => {
|
||||||
|
setShowImportNsec(false);
|
||||||
|
}}
|
||||||
|
onPress={handleImportNsec}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
39
components/PrescriptionCreator/styles.ts
Normal file
39
components/PrescriptionCreator/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,
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/react-native": "^5.1.0",
|
"@ant-design/react-native": "^5.1.0",
|
||||||
"@formstr/sdk": "^0.0.4-alpha",
|
"@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",
|
"@react-native-picker/picker": "^2.6.1",
|
||||||
@@ -21,6 +22,7 @@
|
|||||||
"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-crypto": "^2.2.0",
|
||||||
|
"react-native-date-picker": "^5.0.3",
|
||||||
"react-native-element-dropdown": "^2.10.4",
|
"react-native-element-dropdown": "^2.10.4",
|
||||||
"react-native-encrypted-storage": "^4.0.3",
|
"react-native-encrypted-storage": "^4.0.3",
|
||||||
"react-native-get-random-values": "^1.11.0",
|
"react-native-get-random-values": "^1.11.0",
|
||||||
|
|||||||
12
yarn.lock
12
yarn.lock
@@ -1775,6 +1775,13 @@
|
|||||||
prompts "^2.4.2"
|
prompts "^2.4.2"
|
||||||
semver "^7.5.2"
|
semver "^7.5.2"
|
||||||
|
|
||||||
|
"@react-native-community/datetimepicker@^8.0.1":
|
||||||
|
version "8.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@react-native-community/datetimepicker/-/datetimepicker-8.0.1.tgz#047f27566fb21b5095fa54f558bffd8ab6472b46"
|
||||||
|
integrity sha512-4BO0t3geMNNw9cIIm9p9FNUzwMXexdzD4pAH0AaUAycs3BS71HLrX8jHbrI7nzq/+8O7cLAXn5Gudte+YpTV8Q==
|
||||||
|
dependencies:
|
||||||
|
invariant "^2.2.4"
|
||||||
|
|
||||||
"@react-native-community/segmented-control@^2.2.2":
|
"@react-native-community/segmented-control@^2.2.2":
|
||||||
version "2.2.2"
|
version "2.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/@react-native-community/segmented-control/-/segmented-control-2.2.2.tgz#4014256819ab8f40f6bc3a3929ff14a9d149cf04"
|
resolved "https://registry.yarnpkg.com/@react-native-community/segmented-control/-/segmented-control-2.2.2.tgz#4014256819ab8f40f6bc3a3929ff14a9d149cf04"
|
||||||
@@ -6587,6 +6594,11 @@ react-native-crypto@^2.2.0:
|
|||||||
public-encrypt "^4.0.0"
|
public-encrypt "^4.0.0"
|
||||||
randomfill "^1.0.3"
|
randomfill "^1.0.3"
|
||||||
|
|
||||||
|
react-native-date-picker@^5.0.3:
|
||||||
|
version "5.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-native-date-picker/-/react-native-date-picker-5.0.3.tgz#8fc5a3e2dc62ad689cb54f7a1a8083db269212ae"
|
||||||
|
integrity sha512-pA/HcnB7RBac/CigQcqvM95JiD4c7mN5DPie3c/LFBol55ntvpxyxxr5ixAonB5PYLTjop21EIajAHKAP7/JeQ==
|
||||||
|
|
||||||
react-native-element-dropdown@^2.10.4:
|
react-native-element-dropdown@^2.10.4:
|
||||||
version "2.10.4"
|
version "2.10.4"
|
||||||
resolved "https://registry.yarnpkg.com/react-native-element-dropdown/-/react-native-element-dropdown-2.10.4.tgz#58d8a5e38d2a3fb74498c195bd775d9e1536e6bd"
|
resolved "https://registry.yarnpkg.com/react-native-element-dropdown/-/react-native-element-dropdown-2.10.4.tgz#58d8a5e38d2a3fb74498c195bd775d9e1536e6bd"
|
||||||
|
|||||||
Reference in New Issue
Block a user