refactor: use types for props, not interfaces + update enum values + remove unnecessary useCallbacks from List.tsx
This commit is contained in:
parent
1b673d1333
commit
66b72dcb77
|
@ -5,27 +5,28 @@ import buildURL from 'utils/buildURL';
|
||||||
import { Linking, StyleSheet } from 'react-native';
|
import { Linking, StyleSheet } from 'react-native';
|
||||||
import { ActionSheet, Icon, NativeBase } from 'native-base';
|
import { ActionSheet, Icon, NativeBase } from 'native-base';
|
||||||
|
|
||||||
export interface MenuProps
|
|
||||||
extends Omit<NativeBase.Icon, 'onPress' | 'type' | 'name'> {}
|
|
||||||
|
|
||||||
const OPTIONS = ['Strona internetowa', 'Kontakt', 'Anuluj'];
|
const OPTIONS = ['Strona internetowa', 'Kontakt', 'Anuluj'];
|
||||||
const WEBSITE_OPT_INDEX = 0;
|
enum OptionIndex {
|
||||||
const CONTACT_OPT_INDEX = 1;
|
WEBSITE,
|
||||||
const CANCEL_OPT_INDEX = OPTIONS.length - 1;
|
CONTACT,
|
||||||
|
CANCEL,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MenuProps = Omit<NativeBase.Icon, 'onPress' | 'type' | 'name'>;
|
||||||
|
|
||||||
const Menu = ({ style, ...rest }: MenuProps) => {
|
const Menu = ({ style, ...rest }: MenuProps) => {
|
||||||
const showMenu = () => {
|
const showMenu = () => {
|
||||||
ActionSheet.show(
|
ActionSheet.show(
|
||||||
{
|
{
|
||||||
options: OPTIONS,
|
options: OPTIONS,
|
||||||
cancelButtonIndex: CANCEL_OPT_INDEX,
|
cancelButtonIndex: OptionIndex.CANCEL,
|
||||||
},
|
},
|
||||||
index => {
|
index => {
|
||||||
switch (index) {
|
switch (index) {
|
||||||
case WEBSITE_OPT_INDEX:
|
case OptionIndex.WEBSITE:
|
||||||
Linking.openURL(WEBSITE);
|
Linking.openURL(WEBSITE);
|
||||||
break;
|
break;
|
||||||
case CONTACT_OPT_INDEX:
|
case OptionIndex.CONTACT:
|
||||||
Linking.openURL(buildURL('email', EMAIL));
|
Linking.openURL(buildURL('email', EMAIL));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
export enum Event {
|
export enum Event {
|
||||||
SaveQualification = 'save_qualification',
|
SAVE_QUALIFICATION = 'save_qualification',
|
||||||
UnSaveQualification = 'unsave_qualification',
|
UNSAVE_QUALIFICATION = 'unsave_qualification',
|
||||||
StartTest = 'start_test',
|
START_TEST = 'start_test',
|
||||||
FinishTest = 'finish_test',
|
FINISH_TEST = 'finish_test',
|
||||||
SelectAnswer = 'select_answer',
|
SELECT_ANSWER = 'select_answer',
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
export enum Screen {
|
export enum Screen {
|
||||||
Home = 'HomeScreen',
|
HOME = 'HomeScreen',
|
||||||
Test = 'TestScreen',
|
TEST = 'TestScreen',
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TestScreenParams = {
|
export type TestScreenParams = {
|
||||||
|
@ -9,6 +9,6 @@ export type TestScreenParams = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AppStackParamList = {
|
export type AppStackParamList = {
|
||||||
[Screen.Home]: undefined;
|
[Screen.HOME]: undefined;
|
||||||
[Screen.Test]: TestScreenParams;
|
[Screen.TEST]: TestScreenParams;
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,17 +15,20 @@ export const createClient = (
|
||||||
cache: new InMemoryCache(),
|
cache: new InMemoryCache(),
|
||||||
link: ApolloLink.from([
|
link: ApolloLink.from([
|
||||||
onError(({ graphQLErrors, networkError }) => {
|
onError(({ graphQLErrors, networkError }) => {
|
||||||
if (__DEV__) {
|
if (!__DEV__) {
|
||||||
if (graphQLErrors) {
|
return;
|
||||||
graphQLErrors.forEach(({ message, locations, path }) =>
|
}
|
||||||
console.log(
|
|
||||||
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
|
if (graphQLErrors) {
|
||||||
),
|
graphQLErrors.forEach(({ message, locations, path }) =>
|
||||||
);
|
console.log(
|
||||||
}
|
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
|
||||||
if (networkError) {
|
),
|
||||||
console.log(`[Network error]: ${networkError}`);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (networkError) {
|
||||||
|
console.log(`[Network error]: ${networkError}`);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
new HttpLink({
|
new HttpLink({
|
||||||
|
|
|
@ -66,7 +66,7 @@ export default (variables /* : * */ = variable) => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
Platform.OS === OS.Android ? variables.footerDefaultBg : undefined,
|
Platform.OS === OS.ANDROID ? variables.footerDefaultBg : undefined,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
|
|
@ -47,9 +47,9 @@ export default (variables /* : * */ = variable) => {
|
||||||
shadowOffset: null,
|
shadowOffset: null,
|
||||||
shadowRadius: null,
|
shadowRadius: null,
|
||||||
shadowOpacity: null,
|
shadowOpacity: null,
|
||||||
paddingTop: platform === OS.Android ? StatusBar.currentHeight : undefined,
|
paddingTop: platform === OS.ANDROID ? StatusBar.currentHeight : undefined,
|
||||||
height:
|
height:
|
||||||
platform === OS.Android
|
platform === OS.ANDROID
|
||||||
? variables.toolbarHeight + (StatusBar.currentHeight ?? 0)
|
? variables.toolbarHeight + (StatusBar.currentHeight ?? 0)
|
||||||
: variables.toolbarHeight,
|
: variables.toolbarHeight,
|
||||||
},
|
},
|
||||||
|
|
|
@ -97,7 +97,7 @@ export default (variables /* : * */ = variable) => {
|
||||||
paddingTop:
|
paddingTop:
|
||||||
platform === OS.IOS ? variables.listItemPadding + 25 : undefined,
|
platform === OS.IOS ? variables.listItemPadding + 25 : undefined,
|
||||||
paddingBottom:
|
paddingBottom:
|
||||||
platform === OS.Android ? variables.listItemPadding + 20 : undefined,
|
platform === OS.ANDROID ? variables.listItemPadding + 20 : undefined,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
borderColor: variables.listBorderColor,
|
borderColor: variables.listBorderColor,
|
||||||
'NativeBase.Text': {
|
'NativeBase.Text': {
|
||||||
|
|
|
@ -12,8 +12,8 @@ export default (variables /* : * */ = variable) => {
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
'.scrollable': {
|
'.scrollable': {
|
||||||
paddingHorizontal: 20,
|
paddingHorizontal: 20,
|
||||||
flex: platform === OS.Android ? 0 : 1,
|
flex: platform === OS.ANDROID ? 0 : 1,
|
||||||
minWidth: platform === OS.Android ? undefined : 60,
|
minWidth: platform === OS.ANDROID ? undefined : 60,
|
||||||
},
|
},
|
||||||
'NativeBase.Text': {
|
'NativeBase.Text': {
|
||||||
color: variables.topTabBarTextColor,
|
color: variables.topTabBarTextColor,
|
||||||
|
|
|
@ -211,6 +211,6 @@ export type Variables = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum OS {
|
export enum OS {
|
||||||
Android = 'android',
|
ANDROID = 'android',
|
||||||
IOS = 'ios',
|
IOS = 'ios',
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,15 @@
|
||||||
import React, { useCallback, useState } from 'react';
|
import React, { PropsWithChildren, useCallback, useState } from 'react';
|
||||||
import { useEffectOnce, useUpdateEffect } from 'react-use';
|
import { useEffectOnce, useUpdateEffect } from 'react-use';
|
||||||
import { useAsyncStorage } from '@react-native-async-storage/async-storage';
|
import { useAsyncStorage } from '@react-native-async-storage/async-storage';
|
||||||
import analytics from '@react-native-firebase/analytics';
|
import analytics from '@react-native-firebase/analytics';
|
||||||
import { context as Context } from './context';
|
import { context as Context } from './context';
|
||||||
import { Event } from 'config/analytics';
|
import { Event } from 'config/analytics';
|
||||||
|
|
||||||
export interface SavedQualificationsProviderProps {
|
|
||||||
children?: React.ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ASYNC_STORAGE_KEY = 'saved_qualifications';
|
const ASYNC_STORAGE_KEY = 'saved_qualifications';
|
||||||
|
|
||||||
export const SavedQualificationsProvider = ({
|
export const SavedQualificationsProvider = ({
|
||||||
children,
|
children,
|
||||||
}: SavedQualificationsProviderProps) => {
|
}: PropsWithChildren<{}>) => {
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [savedQualifications, setSavedQualifications] = useState<number[]>([]);
|
const [savedQualifications, setSavedQualifications] = useState<number[]>([]);
|
||||||
const asyncStorage = useAsyncStorage(ASYNC_STORAGE_KEY);
|
const asyncStorage = useAsyncStorage(ASYNC_STORAGE_KEY);
|
||||||
|
@ -54,7 +50,7 @@ export const SavedQualificationsProvider = ({
|
||||||
);
|
);
|
||||||
|
|
||||||
analytics().logEvent(
|
analytics().logEvent(
|
||||||
save ? Event.SaveQualification : Event.UnSaveQualification,
|
save ? Event.SAVE_QUALIFICATION : Event.UNSAVE_QUALIFICATION,
|
||||||
{
|
{
|
||||||
id: id.toString(),
|
id: id.toString(),
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import 'react-native-gesture-handler';
|
import 'react-native-gesture-handler';
|
||||||
import React, { useEffect, useRef } from 'react';
|
import React, { useEffect, useMemo, useRef } from 'react';
|
||||||
import { ApolloProvider } from '@apollo/client';
|
import { ApolloProvider } from '@apollo/client';
|
||||||
import RNBootSplash from 'react-native-bootsplash';
|
import RNBootSplash from 'react-native-bootsplash';
|
||||||
import { Root, StyleProvider } from 'native-base';
|
import { Root, StyleProvider } from 'native-base';
|
||||||
|
@ -18,8 +18,12 @@ const BaseApp = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const theme = useRef(createTheme(variables)).current;
|
const theme = useMemo(() => {
|
||||||
const client = useRef(createClient(API_URI)).current;
|
return createTheme(variables);
|
||||||
|
}, []);
|
||||||
|
const client = useMemo(() => {
|
||||||
|
return createClient(API_URI);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ApolloProvider client={client}>
|
<ApolloProvider client={client}>
|
||||||
|
|
|
@ -7,7 +7,7 @@ import Professions from './components/Professions/Professions';
|
||||||
|
|
||||||
const HomeScreen = () => {
|
const HomeScreen = () => {
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
const [mode, setMode] = useState(Mode.All);
|
const [mode, setMode] = useState(Mode.ALL);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
|
|
|
@ -5,9 +5,9 @@ import { Keyboard, StyleSheet, TextInput } from 'react-native';
|
||||||
import { Icon, Input, Item, Header as NBHeader, View } from 'native-base';
|
import { Icon, Input, Item, Header as NBHeader, View } from 'native-base';
|
||||||
import Menu from 'common/Menu/Menu';
|
import Menu from 'common/Menu/Menu';
|
||||||
|
|
||||||
export interface HeaderProps {
|
export type HeaderProps = {
|
||||||
onSearch?: (search: string) => void;
|
onSearch?: (search: string) => void;
|
||||||
}
|
};
|
||||||
|
|
||||||
const Header = ({ onSearch }: HeaderProps) => {
|
const Header = ({ onSearch }: HeaderProps) => {
|
||||||
const inputRef = useRef<Input>(null);
|
const inputRef = useRef<Input>(null);
|
||||||
|
|
|
@ -2,28 +2,28 @@ import React from 'react';
|
||||||
import { Button, Segment, Text } from 'native-base';
|
import { Button, Segment, Text } from 'native-base';
|
||||||
|
|
||||||
export enum Mode {
|
export enum Mode {
|
||||||
All,
|
ALL,
|
||||||
Saved,
|
SAVED,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ModeSelectorProps {
|
export type ModeSelectorProps = {
|
||||||
mode: Mode;
|
mode: Mode;
|
||||||
onChangeMode: (mode: Mode) => void;
|
onChangeMode: (mode: Mode) => void;
|
||||||
}
|
};
|
||||||
|
|
||||||
const ModeSelector = ({ mode, onChangeMode }: ModeSelectorProps) => {
|
const ModeSelector = ({ mode, onChangeMode }: ModeSelectorProps) => {
|
||||||
return (
|
return (
|
||||||
<Segment>
|
<Segment>
|
||||||
<Button
|
<Button
|
||||||
first
|
first
|
||||||
onPress={() => onChangeMode(Mode.All)}
|
onPress={() => onChangeMode(Mode.ALL)}
|
||||||
active={mode === Mode.All}
|
active={mode === Mode.ALL}
|
||||||
>
|
>
|
||||||
<Text allowFontScaling={false}>Wszystkie</Text>
|
<Text allowFontScaling={false}>Wszystkie</Text>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onPress={() => onChangeMode(Mode.Saved)}
|
onPress={() => onChangeMode(Mode.SAVED)}
|
||||||
active={mode === Mode.Saved}
|
active={mode === Mode.SAVED}
|
||||||
>
|
>
|
||||||
<Text allowFontScaling={false}>Zapisane</Text>
|
<Text allowFontScaling={false}>Zapisane</Text>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { forwardRef, useCallback } from 'react';
|
import React, { forwardRef } from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
FlatList,
|
FlatList,
|
||||||
|
@ -11,24 +11,20 @@ import ListEmpty from './ListEmpty';
|
||||||
import ListLoading from './ListLoading';
|
import ListLoading from './ListLoading';
|
||||||
|
|
||||||
export type Item = ListItemProps;
|
export type Item = ListItemProps;
|
||||||
export interface ListProps
|
export type ListProps = {
|
||||||
extends Pick<
|
|
||||||
FlatListProps<Item>,
|
|
||||||
'refreshing' | 'onRefresh' | 'contentContainerStyle'
|
|
||||||
> {
|
|
||||||
items: Item[];
|
items: Item[];
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
}
|
} & Pick<
|
||||||
|
FlatListProps<Item>,
|
||||||
|
'refreshing' | 'onRefresh' | 'contentContainerStyle'
|
||||||
|
>;
|
||||||
|
|
||||||
const noop = () => {};
|
const noop = () => {};
|
||||||
|
const keyExtractor = (item: ListItemProps) => item.id!;
|
||||||
|
const renderItem = ({ item }: { item: Item }) => <ListItem {...item} />;
|
||||||
|
|
||||||
const MyList = forwardRef<FlatList<Item>, ListProps>(
|
const List = forwardRef<FlatList<Item>, ListProps>(
|
||||||
({ items, refreshing, onRefresh, loading, ...rest }: ListProps, ref) => {
|
({ items, refreshing, onRefresh, loading, ...rest }: ListProps, ref) => {
|
||||||
const renderItem = useCallback(({ item }: { item: Item }) => {
|
|
||||||
return <ListItem {...item} />;
|
|
||||||
}, []);
|
|
||||||
const keyExtractor = useCallback(item => item.id, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FlatList
|
<FlatList
|
||||||
data={items}
|
data={items}
|
||||||
|
@ -61,4 +57,4 @@ const styles = StyleSheet.create({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default MyList;
|
export default List;
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import React, { Fragment, useMemo, memo } from 'react';
|
import React, { Fragment, useMemo, memo } from 'react';
|
||||||
import { Icon, Left, ListItem, NativeBase, Right, Text } from 'native-base';
|
import { Icon, Left, ListItem, NativeBase, Right, Text } from 'native-base';
|
||||||
|
|
||||||
export interface ListItemProps
|
export type ListItemProps = {
|
||||||
extends Pick<NativeBase.ListItem, 'itemDivider' | 'itemHeader'> {
|
|
||||||
onPress?: (id: string) => void;
|
onPress?: (id: string) => void;
|
||||||
id?: string;
|
id?: string;
|
||||||
text: string;
|
text: string;
|
||||||
}
|
} & Pick<NativeBase.ListItem, 'itemDivider' | 'itemHeader'>;
|
||||||
|
|
||||||
const MyListItem = ({
|
const MyListItem = ({
|
||||||
onPress,
|
onPress,
|
||||||
|
|
|
@ -4,25 +4,27 @@ import { Alert, Linking } from 'react-native';
|
||||||
import buildURL from 'utils/buildURL';
|
import buildURL from 'utils/buildURL';
|
||||||
import { EMAIL } from 'config/app';
|
import { EMAIL } from 'config/app';
|
||||||
|
|
||||||
export interface NetworkConnectionAlertProps {
|
export type NetworkConnectionAlertProps = {
|
||||||
error?: ApolloError;
|
error?: ApolloError;
|
||||||
}
|
};
|
||||||
|
|
||||||
const NetworkConnectionAlert = ({ error }: NetworkConnectionAlertProps) => {
|
const NetworkConnectionAlert = ({ error }: NetworkConnectionAlertProps) => {
|
||||||
useUpdateEffect(() => {
|
useUpdateEffect(() => {
|
||||||
if (error && error.networkError) {
|
if (!error || !error.networkError) {
|
||||||
Alert.alert(
|
return;
|
||||||
'Problem z połączeniem',
|
|
||||||
'Prosimy o sprawdzenie połączenia z internetem / spróbowanie ponownie później. Przepraszamy za utrudnienia.',
|
|
||||||
[
|
|
||||||
{
|
|
||||||
text: 'Zgłoś problem',
|
|
||||||
onPress: () => Linking.openURL(buildURL('email', EMAIL)),
|
|
||||||
},
|
|
||||||
{ text: 'OK' },
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Alert.alert(
|
||||||
|
'Problem z połączeniem',
|
||||||
|
'Prosimy o sprawdzenie połączenia z internetem / spróbowanie ponownie później. Przepraszamy za utrudnienia.',
|
||||||
|
[
|
||||||
|
{
|
||||||
|
text: 'Zgłoś problem',
|
||||||
|
onPress: () => Linking.openURL(buildURL('email', EMAIL)),
|
||||||
|
},
|
||||||
|
{ text: 'OK' },
|
||||||
|
],
|
||||||
|
);
|
||||||
}, [error]);
|
}, [error]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -11,10 +11,10 @@ import List, { Item } from './List/List';
|
||||||
import QualificationModal from './QualificationModal';
|
import QualificationModal from './QualificationModal';
|
||||||
import NetworkConnectionAlert from './NetworkConnectionAlert';
|
import NetworkConnectionAlert from './NetworkConnectionAlert';
|
||||||
|
|
||||||
export interface ProfessionsProps {
|
export type ProfessionsProps = {
|
||||||
mode: Mode;
|
mode: Mode;
|
||||||
search: string;
|
search: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
const ID_SEPARATOR = '.';
|
const ID_SEPARATOR = '.';
|
||||||
const getQualificationAndProfessionID = (str: string): [number, number] => {
|
const getQualificationAndProfessionID = (str: string): [number, number] => {
|
||||||
|
@ -44,16 +44,21 @@ const Professions = ({ mode, search }: ProfessionsProps) => {
|
||||||
const [professionID, qualificationID] = getQualificationAndProfessionID(
|
const [professionID, qualificationID] = getQualificationAndProfessionID(
|
||||||
id,
|
id,
|
||||||
);
|
);
|
||||||
|
|
||||||
const profession = professions.find(p => p.id === professionID);
|
const profession = professions.find(p => p.id === professionID);
|
||||||
if (profession) {
|
if (!profession) {
|
||||||
const qualification = profession.qualifications.find(
|
return;
|
||||||
q => q.id === qualificationID,
|
|
||||||
);
|
|
||||||
if (qualification) {
|
|
||||||
setSelectedQualification(qualification);
|
|
||||||
setIsModalVisible(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const qualification = profession.qualifications.find(
|
||||||
|
q => q.id === qualificationID,
|
||||||
|
);
|
||||||
|
if (!qualification) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelectedQualification(qualification);
|
||||||
|
setIsModalVisible(true);
|
||||||
},
|
},
|
||||||
[setIsModalVisible, setSelectedQualification, professions],
|
[setIsModalVisible, setSelectedQualification, professions],
|
||||||
);
|
);
|
||||||
|
@ -62,7 +67,9 @@ const Professions = ({ mode, search }: ProfessionsProps) => {
|
||||||
if (professionsLoading) {
|
if (professionsLoading) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let items: Item[] = [];
|
let items: Item[] = [];
|
||||||
|
|
||||||
professions.forEach(profession => {
|
professions.forEach(profession => {
|
||||||
const qualifications = profession.qualifications
|
const qualifications = profession.qualifications
|
||||||
.filter(
|
.filter(
|
||||||
|
@ -70,7 +77,7 @@ const Professions = ({ mode, search }: ProfessionsProps) => {
|
||||||
(!search ||
|
(!search ||
|
||||||
qualification.name.toLowerCase().includes(search) ||
|
qualification.name.toLowerCase().includes(search) ||
|
||||||
qualification.code.toLowerCase().includes(search)) &&
|
qualification.code.toLowerCase().includes(search)) &&
|
||||||
(mode === Mode.All || isSaved(qualification.id)),
|
(mode === Mode.ALL || isSaved(qualification.id)),
|
||||||
)
|
)
|
||||||
.map(
|
.map(
|
||||||
(qualification): Item => {
|
(qualification): Item => {
|
||||||
|
@ -83,18 +90,21 @@ const Professions = ({ mode, search }: ProfessionsProps) => {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (qualifications.length > 0) {
|
|
||||||
items = [
|
if (qualifications.length === 0) {
|
||||||
...items,
|
return;
|
||||||
{
|
|
||||||
text: profession.name,
|
|
||||||
itemHeader: true,
|
|
||||||
itemDivider: true,
|
|
||||||
id: 'P' + profession.id,
|
|
||||||
} as Item,
|
|
||||||
...qualifications,
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
items = [
|
||||||
|
...items,
|
||||||
|
{
|
||||||
|
text: profession.name,
|
||||||
|
itemHeader: true,
|
||||||
|
itemDivider: true,
|
||||||
|
id: 'P' + profession.id,
|
||||||
|
} as Item,
|
||||||
|
...qualifications,
|
||||||
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
setListItems(items);
|
setListItems(items);
|
||||||
|
|
|
@ -19,10 +19,9 @@ import {
|
||||||
} from 'native-base';
|
} from 'native-base';
|
||||||
import Modal, { ModalProps } from 'common/Modal/Modal';
|
import Modal, { ModalProps } from 'common/Modal/Modal';
|
||||||
|
|
||||||
export interface QualificationModalProps
|
export type QualificationModalProps = {
|
||||||
extends Pick<ModalProps, 'visible' | 'onPressBackdrop'> {
|
|
||||||
qualification: Maybe<Qualification>;
|
qualification: Maybe<Qualification>;
|
||||||
}
|
} & Pick<ModalProps, 'visible' | 'onPressBackdrop'>;
|
||||||
|
|
||||||
const QualificationModal = ({
|
const QualificationModal = ({
|
||||||
qualification,
|
qualification,
|
||||||
|
@ -81,7 +80,7 @@ const QualificationModal = ({
|
||||||
if (onPressBackdrop) {
|
if (onPressBackdrop) {
|
||||||
onPressBackdrop();
|
onPressBackdrop();
|
||||||
}
|
}
|
||||||
navigation.navigate(Screen.Test, {
|
navigation.navigate(Screen.TEST, {
|
||||||
qualificationID: qualification?.id ?? 0,
|
qualificationID: qualification?.id ?? 0,
|
||||||
limit: question,
|
limit: question,
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,8 +15,8 @@ const AppStack = createStackNavigator<AppStackParamList>();
|
||||||
|
|
||||||
const AppScreens = () => (
|
const AppScreens = () => (
|
||||||
<AppStack.Navigator screenOptions={{ headerShown: false }}>
|
<AppStack.Navigator screenOptions={{ headerShown: false }}>
|
||||||
<Stack.Screen name={Screen.Home} component={HomeScreen} />
|
<Stack.Screen name={Screen.HOME} component={HomeScreen} />
|
||||||
<Stack.Screen name={Screen.Test} component={TestScreen} />
|
<Stack.Screen name={Screen.TEST} component={TestScreen} />
|
||||||
</AppStack.Navigator>
|
</AppStack.Navigator>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -15,9 +15,9 @@ import Suggestions from './components/Suggestions/Suggestions';
|
||||||
import Test from './components/Test/Test';
|
import Test from './components/Test/Test';
|
||||||
import Content from './components/Content/Content';
|
import Content from './components/Content/Content';
|
||||||
|
|
||||||
export interface TestScreenProps {
|
export type TestScreenProps = {
|
||||||
route: RouteProp<AppStackParamList, Screen.Test>;
|
route: RouteProp<AppStackParamList, Screen.TEST>;
|
||||||
}
|
};
|
||||||
|
|
||||||
type QueryGenerateTestSimilarQualificationsQualificationArgs = {
|
type QueryGenerateTestSimilarQualificationsQualificationArgs = {
|
||||||
limitTest: Scalars['Int'];
|
limitTest: Scalars['Int'];
|
||||||
|
|
|
@ -2,10 +2,10 @@ import React from 'react';
|
||||||
import { StyleSheet } from 'react-native';
|
import { StyleSheet } from 'react-native';
|
||||||
import { Content as ContentNB, NativeBase } from 'native-base';
|
import { Content as ContentNB, NativeBase } from 'native-base';
|
||||||
|
|
||||||
export interface ContentProps {
|
export type ContentProps = {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
contentContainerStyle?: NativeBase.Content['contentContainerStyle'];
|
contentContainerStyle?: NativeBase.Content['contentContainerStyle'];
|
||||||
}
|
};
|
||||||
|
|
||||||
const Content = ({ children, contentContainerStyle }: ContentProps) => {
|
const Content = ({ children, contentContainerStyle }: ContentProps) => {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -12,9 +12,9 @@ import {
|
||||||
} from 'native-base';
|
} from 'native-base';
|
||||||
import Menu from 'common/Menu/Menu';
|
import Menu from 'common/Menu/Menu';
|
||||||
|
|
||||||
export interface HeaderProps {
|
export type HeaderProps = {
|
||||||
title: string;
|
title: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
const Header = ({ title }: HeaderProps) => {
|
const Header = ({ title }: HeaderProps) => {
|
||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
|
|
|
@ -9,9 +9,9 @@ import { StyleSheet } from 'react-native';
|
||||||
import { H1, View, H3, Card, CardItem, Text, Button, Body } from 'native-base';
|
import { H1, View, H3, Card, CardItem, Text, Button, Body } from 'native-base';
|
||||||
import Content from '../Content/Content';
|
import Content from '../Content/Content';
|
||||||
|
|
||||||
export interface SuggestionsProps {
|
export type SuggestionsProps = {
|
||||||
qualifications: Qualification[];
|
qualifications: Qualification[];
|
||||||
}
|
};
|
||||||
|
|
||||||
const Suggestions = ({ qualifications }: SuggestionsProps) => {
|
const Suggestions = ({ qualifications }: SuggestionsProps) => {
|
||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
|
@ -51,7 +51,7 @@ const Suggestions = ({ qualifications }: SuggestionsProps) => {
|
||||||
<Button
|
<Button
|
||||||
key={question}
|
key={question}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
navigation.navigate(Screen.Test, {
|
navigation.navigate(Screen.TEST, {
|
||||||
qualificationID: qualification.id,
|
qualificationID: qualification.id,
|
||||||
limit: question,
|
limit: question,
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,12 +9,12 @@ export enum AlertVariant {
|
||||||
Warning = 'warning',
|
Warning = 'warning',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AlertProps extends Pick<NativeBase.View, 'style'> {
|
export type AlertProps = {
|
||||||
variant?: AlertVariant;
|
variant?: AlertVariant;
|
||||||
title: React.ReactNode;
|
title: React.ReactNode;
|
||||||
description?: React.ReactNode;
|
description?: React.ReactNode;
|
||||||
actions?: React.ReactNode;
|
actions?: React.ReactNode;
|
||||||
}
|
} & Pick<NativeBase.View, 'style'>;
|
||||||
|
|
||||||
const Alert = ({
|
const Alert = ({
|
||||||
variant = AlertVariant.Info,
|
variant = AlertVariant.Info,
|
||||||
|
|
|
@ -9,9 +9,9 @@ import {
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { H3 } from 'native-base';
|
import { H3 } from 'native-base';
|
||||||
|
|
||||||
export interface ImageProps extends Pick<RNImageProps, 'style'> {
|
export type ImageProps = {
|
||||||
path: string;
|
path: string;
|
||||||
}
|
} & Pick<RNImageProps, 'style'>;
|
||||||
|
|
||||||
const MyImage = ({ path, style }: ImageProps) => {
|
const MyImage = ({ path, style }: ImageProps) => {
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
|
@ -10,12 +10,12 @@ import Content from '../Content/Content';
|
||||||
import Image from './Image';
|
import Image from './Image';
|
||||||
import Alert, { AlertVariant } from './Alert';
|
import Alert, { AlertVariant } from './Alert';
|
||||||
|
|
||||||
export interface QuestionProps {
|
export type QuestionProps = {
|
||||||
question: QuestionT;
|
question: QuestionT;
|
||||||
reviewMode: boolean;
|
reviewMode: boolean;
|
||||||
selectedAnswer: Answer;
|
selectedAnswer: Answer;
|
||||||
selectAnswer: (a: Answer) => void;
|
selectAnswer: (a: Answer) => void;
|
||||||
}
|
};
|
||||||
|
|
||||||
const ANSWERS = Object.values(Answer);
|
const ANSWERS = Object.values(Answer);
|
||||||
|
|
||||||
|
|
|
@ -8,14 +8,14 @@ import { Button, H1, H3, Text, View } from 'native-base';
|
||||||
import Content from '../Content/Content';
|
import Content from '../Content/Content';
|
||||||
import Alert from './Alert';
|
import Alert from './Alert';
|
||||||
|
|
||||||
export interface SummaryTabProps {
|
export type SummaryTabProps = {
|
||||||
reviewMode: boolean;
|
reviewMode: boolean;
|
||||||
answers: Answer[];
|
answers: Answer[];
|
||||||
questions: Question[];
|
questions: Question[];
|
||||||
finishTest: () => void;
|
finishTest: () => void;
|
||||||
resetTest: () => void;
|
resetTest: () => void;
|
||||||
qualificationID: number;
|
qualificationID: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
const SummaryTab = ({
|
const SummaryTab = ({
|
||||||
reviewMode,
|
reviewMode,
|
||||||
|
|
|
@ -7,11 +7,11 @@ import { ScrollableTab, Tab, Tabs } from 'native-base';
|
||||||
import Question from './Question';
|
import Question from './Question';
|
||||||
import SummaryTab from './SummaryTab';
|
import SummaryTab from './SummaryTab';
|
||||||
|
|
||||||
export interface TestProps {
|
export type TestProps = {
|
||||||
questions: QuestionT[];
|
questions: QuestionT[];
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
qualification: Qualification;
|
qualification: Qualification;
|
||||||
}
|
};
|
||||||
|
|
||||||
const Test = ({ questions, onReset, qualification }: TestProps) => {
|
const Test = ({ questions, onReset, qualification }: TestProps) => {
|
||||||
const [reviewMode, setReviewMode] = useState(false);
|
const [reviewMode, setReviewMode] = useState(false);
|
||||||
|
@ -28,7 +28,7 @@ const Test = ({ questions, onReset, qualification }: TestProps) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
analytics().logEvent(Event.StartTest, analyticsParams);
|
analytics().logEvent(Event.START_TEST, analyticsParams);
|
||||||
}, [analyticsParams]);
|
}, [analyticsParams]);
|
||||||
|
|
||||||
const handleSelectAnswer = (index: number, answer: Answer) => {
|
const handleSelectAnswer = (index: number, answer: Answer) => {
|
||||||
|
@ -40,7 +40,7 @@ const Test = ({ questions, onReset, qualification }: TestProps) => {
|
||||||
index === index2 ? answer : otherAnswer,
|
index === index2 ? answer : otherAnswer,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
analytics().logEvent(Event.SelectAnswer, {
|
analytics().logEvent(Event.SELECT_ANSWER, {
|
||||||
qualificationID: analyticsParams.qualificationID,
|
qualificationID: analyticsParams.qualificationID,
|
||||||
questionID: questions[index].id.toString(),
|
questionID: questions[index].id.toString(),
|
||||||
answer,
|
answer,
|
||||||
|
@ -50,7 +50,7 @@ const Test = ({ questions, onReset, qualification }: TestProps) => {
|
||||||
|
|
||||||
const handleFinishTest = () => {
|
const handleFinishTest = () => {
|
||||||
setReviewMode(true);
|
setReviewMode(true);
|
||||||
analytics().logEvent(Event.FinishTest, analyticsParams);
|
analytics().logEvent(Event.FINISH_TEST, analyticsParams);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
Reference in New Issue