diff --git a/docs/en/images/author-list.png b/docs/en/images/author-list.png
index b6a73d8f8d..80ee5e31f3 100644
Binary files a/docs/en/images/author-list.png and b/docs/en/images/author-list.png differ
diff --git a/docs/en/images/book-list-with-options.png b/docs/en/images/book-list-with-options.png
index a6bf87e71f..8baa1e06eb 100644
Binary files a/docs/en/images/book-list-with-options.png and b/docs/en/images/book-list-with-options.png differ
diff --git a/docs/en/images/book-list.png b/docs/en/images/book-list.png
index f14ac93a64..571e6e7677 100644
Binary files a/docs/en/images/book-list.png and b/docs/en/images/book-list.png differ
diff --git a/docs/en/images/book-store-menu-item.png b/docs/en/images/book-store-menu-item.png
index f190f176ce..f348c3af48 100644
Binary files a/docs/en/images/book-store-menu-item.png and b/docs/en/images/book-store-menu-item.png differ
diff --git a/docs/en/images/books-menu-item.png b/docs/en/images/books-menu-item.png
index eb9c1042fd..d6de012008 100644
Binary files a/docs/en/images/books-menu-item.png and b/docs/en/images/books-menu-item.png differ
diff --git a/docs/en/images/create-book-button-visibility.png b/docs/en/images/create-book-button-visibility.png
index 1b153f9f65..3ee915382f 100644
Binary files a/docs/en/images/create-book-button-visibility.png and b/docs/en/images/create-book-button-visibility.png differ
diff --git a/docs/en/images/create-book-icon.png b/docs/en/images/create-book-icon.png
index 4d4be5ef30..9590e3c996 100644
Binary files a/docs/en/images/create-book-icon.png and b/docs/en/images/create-book-icon.png differ
diff --git a/docs/en/images/create-book.png b/docs/en/images/create-book.png
index d7a9675c72..52e8ee7fd8 100644
Binary files a/docs/en/images/create-book.png and b/docs/en/images/create-book.png differ
diff --git a/docs/en/images/delete-book-alert.png b/docs/en/images/delete-book-alert.png
index 1676c37ce8..812f4460f0 100644
Binary files a/docs/en/images/delete-book-alert.png and b/docs/en/images/delete-book-alert.png differ
diff --git a/docs/en/images/delete-book.png b/docs/en/images/delete-book.png
index f6f554d6af..976e040ac3 100644
Binary files a/docs/en/images/delete-book.png and b/docs/en/images/delete-book.png differ
diff --git a/docs/en/images/update-book.png b/docs/en/images/update-book.png
index c746550939..38e831daec 100644
Binary files a/docs/en/images/update-book.png and b/docs/en/images/update-book.png differ
diff --git a/docs/en/images/update-delete-book-button-visibility.png b/docs/en/images/update-delete-book-button-visibility.png
index fc9f69e870..70e6506894 100644
Binary files a/docs/en/images/update-delete-book-button-visibility.png and b/docs/en/images/update-delete-book-button-visibility.png differ
diff --git a/docs/en/tutorials/mobile/react-native/index.md b/docs/en/tutorials/mobile/react-native/index.md
index 2dfa3df957..f03ba2073f 100644
--- a/docs/en/tutorials/mobile/react-native/index.md
+++ b/docs/en/tutorials/mobile/react-native/index.md
@@ -6,7 +6,7 @@ React Native mobile option is *available for* ***Team*** *or higher licenses*. T
> You must have an ABP Team or a higher license to be able to create a mobile application.
-- This tutorial assumes that you have completed the [Web Application Development tutorial](../../book-store/part-01.md) and built an ABP based application named `Acme.BookStore` with [React Native](../../../framework/ui/react-native) as the mobile option.. Therefore, if you haven't completed the [Web Application Development tutorial](../../book-store/part-01.md), you either need to complete it or download the source code from down below and follow this tutorial.
+- This tutorial assumes that you have completed the [Web Application Development tutorial](../../book-store/part-01.md) and built an ABP based application named `Acme.BookStore` with [React Native](../../../framework/ui/react-native) as the mobile option. Therefore, if you haven't completed the [Web Application Development tutorial](../../book-store/part-01.md), you either need to complete it or download the source code from down below and follow this tutorial.
- In this tutorial, we will only focus on the UI side of the `Acme.BookStore` application and will implement the CRUD operations.
- Before starting, please make sure that the [React Native Development Environment](../../../framework/ui/react-native/index.md) is ready on your machine.
@@ -20,33 +20,34 @@ You can use the following link to download the source code of the application de
## The Book List Page
-In react native there is no dynamic proxy generation, that's why we need to create the BookAPI proxy manually under the `./src/api` folder.
+There is no dynamic proxy generation for the react native application, that is why we need to create the BookAPI proxy manually under the `./src/api` folder.
-```js
-import api from "./API";
+```ts
+//./src/api/BookAPI.ts
+import api from './API';
-export const getList = () => api.get("/api/app/book").then(({ data }) => data);
+export const getList = () => api.get('/api/app/book').then(({ data }) => data);
-export const get = (id) =>
- api.get(`/api/app/book/${id}`).then(({ data }) => data);
+export const getAuthorLookup = () =>
+ api.get('/api/app/book/author-lookup').then(({ data }) => data);
-export const create = (input) =>
- api.post("/api/app/book", input).then(({ data }) => data);
+export const get = id => api.get(`/api/app/book/${id}`).then(({ data }) => data);
-export const update = (input, id) =>
- api.put(`/api/app/book/${id}`, input).then(({ data }) => data);
+export const create = input => api.post('/api/app/book', input).then(({ data }) => data);
+
+export const update = (input, id) => api.put(`/api/app/book/${id}`, input).then(({ data }) => data);
+
+export const remove = id => api.delete(`/api/app/book/${id}`).then(({ data }) => data);
-export const remove = (id) =>
- api.delete(`/api/app/book/${id}`).then(({ data }) => data);
```
### Add the `Book Store` menu item to the navigation
-For the create menu item, navigate to `./src/navigators/DrawerNavigator.js` file and add `BookStoreStack` to `Drawer.Navigator` component.
+For createing a menu item, navigate to `./src/navigators/DrawerNavigator.tsx` file and add `BookStoreStack` to `Drawer.Navigator` component.
-```js
+```tsx
//Other imports..
-import BookStoreStackNavigator from "./BookStoreNavigator";
+import BookStoreStackNavigator from './BookStoreNavigator';
const Drawer = createDrawerNavigator();
@@ -69,39 +70,59 @@ export default function DrawerNavigator() {
}
```
-Create the `BookStoreStackNavigator` in `./src/navigators/BookStoreNavigator.js`, this navigator will be used for the BookStore menu item.
+Create the `BookStoreStackNavigator` inside `./src/navigators/BookStoreNavigator.tsx`, this navigator will be used for the BookStore menu item.
-```js
-import React from "react";
-import { SafeAreaView } from "react-native-safe-area-context";
-import { createNativeStackNavigator } from "@react-navigation/native-stack";
-import i18n from "i18n-js";
-import HamburgerIcon from "../components/HamburgerIcon/HamburgerIcon";
-import BookStoreScreen from "../screens/Books/BookStoreScreen";
+```tsx
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import { Button } from 'react-native-paper';
+import i18n from 'i18n-js';
+
+import { BookStoreScreen, CreateUpdateAuthorScreen, CreateUpdateBookScreen } from '../screens';
+
+import { HamburgerIcon } from '../components';
+import { useThemeColors } from '../hooks';
const Stack = createNativeStackNavigator();
export default function BookStoreStackNavigator() {
+ const { background, onBackground } = useThemeColors();
+
return (
-
-
- ({
- title: i18n.t("BookStore::Menu:BookStore"),
- headerLeft: () => ,
- })}
- />
-
-
+
+ ({
+ title: i18n.t('BookStore::Menu:BookStore'),
+ headerLeft: () => ,
+ headerStyle: { backgroundColor: background },
+ headerTintColor: onBackground,
+ headerShadowVisible: false,
+ })}
+ />
+ ({
+ title: i18n.t(route.params?.bookId ? 'BookStore::Edit' : 'BookStore::NewBook'),
+ headerRight: () => (
+ navigation.navigate('BookStore')}>
+ {i18n.t('AbpUi::Cancel')}
+
+ ),
+ headerStyle: { backgroundColor: background },
+ headerTintColor: onBackground,
+ headerShadowVisible: false,
+ })}
+ />
+
);
}
```
- BookStoreScreen will be used to store the `books` and `authors` page
-Add the `BookStoreStack` to the screens object in the `./src/components/DrawerContent/DrawerContent.js` file. The DrawerContent component will be used to render the menu items.
+Add the `BookStoreStack` to the screens object in the `./src/components/DrawerContent/DrawerContent.tsx` file. The DrawerContent component will be used to render the menu items.
```js
// Imports..
@@ -141,15 +162,18 @@ const screens = {
### Create Book List page
-Before creating the book list page, we need to create the `BookStoreScreen.js` file under the `./src/screens/BookStore` folder. This file will be used to store the `books` and `authors` page.
+Before creating the book list page, we need to create the `BookStoreScreen.tsx` file under the `./src/screens/BookStore` folder. This file will be used to store the `books` and `authors` page.
-```js
-import React from "react";
-import i18n from "i18n-js";
-import { BottomNavigation } from "react-native-paper";
-import BooksScreen from "./Books/BooksScreen";
+```tsx
+import { useState, useEffect } from 'react';
+import { useSelector } from 'react-redux';
+import i18n from 'i18n-js';
+import { BottomNavigation } from 'react-native-paper';
-const BooksRoute = () => ;
+import { BooksScreen } from '../../screens';
+import { useThemeColors } from '../../hooks';
+
+const BooksRoute = nav => ;
function BookStoreScreen({ navigation }) {
const [index, setIndex] = React.useState(0);
@@ -177,24 +201,24 @@ function BookStoreScreen({ navigation }) {
export default BookStoreScreen;
```
-Create the `BooksScreen.js` file under the `./src/screens/BookStore/Books` folder.
+Create the `BooksScreen.tsx` file under the `./src/screens/BookStore/Books` folder.
-```js
-import React from "react";
+```tsx
import { useSelector } from "react-redux";
import { View } from "react-native";
-import { useTheme, List } from "react-native-paper";
+import { List } from "react-native-paper";
import { getBooks } from "../../api/BookAPI";
import i18n from "i18n-js";
import DataList from "../../components/DataList/DataList";
import { createAppConfigSelector } from "../../store/selectors/AppSelectors";
+import { useThemeColors } from '../../../hooks';
function BooksScreen({ navigation }) {
- const theme = useTheme();
+ const { background, primary } = useThemeColors();
const currentUser = useSelector(createAppConfigSelector())?.currentUser;
return (
-
+
{currentUser?.isAuthenticated && (
-
- {/*Other screens*/}
-
- {/* Added this screen */}
- ({
- title: i18n.t(
- route.params?.bookId ? "BookStore::Edit" : "BookStore::NewBook"
- ),
- headerRight: () => (
- navigation.navigate("BookStore")}
- >
- {i18n.t("AbpUi::Cancel")}
-
- ),
- })}
- />
-
-
+
+ {/*Other screens*/}
+ {/* Added this screen */}
+ ({
+ title: i18n.t(
+ route.params?.bookId ? "BookStore::Edit" : "BookStore::NewBook"
+ ),
+ headerRight: () => (
+ navigation.navigate("BookStore")}
+ >
+ {i18n.t("AbpUi::Cancel")}
+
+ ),
+ headerStyle: { backgroundColor: background },
+ headerTintColor: onBackground,
+ headerShadowVisible: false,
+ })}
+ />
+
);
}
```
-To navigate to the `CreateUpdateBookScreen`, we need to add the `CreateUpdateBook` button to the `BooksScreen.js` file.
+To navigate to the `CreateUpdateBookScreen`, we need to add the `CreateUpdateBook` button to the `BooksScreen.tsx` file.
-```js
+```tsx
//Other imports..
import {
@@ -294,7 +318,7 @@ function BooksScreen({ navigation }) {
//Other codes..
return (
-
+
{/* Other codes..*/}
{/* Included Code */}
@@ -308,7 +332,7 @@ function BooksScreen({ navigation }) {
visible={true}
animateFrom={"right"}
iconMode={"static"}
- style={[styles.fabStyle, { backgroundColor: theme.colors.primary }]}
+ style={[styles.fabStyle, { backgroundColor: primary }]}
/>
)}
{/* Included Code */}
@@ -332,11 +356,10 @@ const styles = StyleSheet.create({
export default BooksScreen;
```
-After adding the `CreateUpdateBook` button, we need to add the `CreateUpdateBookScreen.js` file under the `./src/screens/BookStore/Books/CreateUpdateBook` folder.
+After adding the `CreateUpdateBook` button, we need to add the `CreateUpdateBookScreen.tsx` file under the `./src/screens/BookStore/Books/CreateUpdateBook` folder.
-```js
+```tsx
import PropTypes from "prop-types";
-import React from "react";
import { create } from "../../../../api/BookAPI";
import LoadingActions from "../../../../store/actions/LoadingActions";
@@ -371,31 +394,24 @@ export default connectToRedux({
});
```
-- In this page we'll store logic, send post/put requests, get the selected book data and etc.
+- In this page we will store logic, send post/put requests, get the selected book data and etc.
- This page will wrap the `CreateUpdateBookFrom` component and pass the submit function with other properties.
-Create a `CreateUpdateBookForm.js` file under the `./src/screens/BookStore/Books/CreateUpdateBook` folder and add the following code to it.
+Create a `CreateUpdateBookForm.tsx` file under the `./src/screens/BookStore/Books/CreateUpdateBook` folder and add the following code to it.
-```js
-import React, { useRef, useState } from "react";
-import {
- Platform,
- KeyboardAvoidingView,
- StyleSheet,
- View,
- ScrollView,
-} from "react-native";
+```tsx
+import * as Yup from 'yup';
+import { useRef, useState } from 'react';
+import { Platform, KeyboardAvoidingView, StyleSheet, View, ScrollView } from 'react-native';
+import { useFormik } from 'formik';
+import i18n from 'i18n-js';
+import PropTypes from 'prop-types';
+import { TextInput, Portal, Modal, Text, Divider, Button } from 'react-native-paper';
+import DateTimePicker from '@react-native-community/datetimepicker';
-import { useFormik } from "formik";
-import i18n from "i18n-js";
-import PropTypes from "prop-types";
-import * as Yup from "yup";
-import { useTheme, TextInput } from "react-native-paper";
-import DateTimePicker from "@react-native-community/datetimepicker";
+import { FormButtons, ValidationMessage, AbpSelect } from '../../../../components';
+import { useThemeColors } from '../../../../hooks';
-import { FormButtons } from "../../../../components/FormButtons";
-import ValidationMessage from "../../../../components/ValidationMessage/ValidationMessage";
-import AbpSelect from "../../../../components/Select/Select";
const validations = {
name: Yup.string().required("AbpValidation::ThisFieldIsRequired."),
@@ -412,19 +428,19 @@ const props = {
};
function CreateUpdateBookForm({ submit }) {
- const theme = useTheme();
+ const { primaryContainer, background, onBackground } = useThemeColors();
const [bookTypeVisible, setBookTypeVisible] = useState(false);
const [publishDateVisible, setPublishDateVisible] = useState(false);
- const nameRef = useRef();
- const priceRef = useRef();
- const typeRef = useRef();
- const publishDateRef = useRef();
+ const nameRef = useRef(null);
+ const priceRef = useRef(null);
+ const typeRef = useRef(null);
+ const publishDateRef = useRef(null);
const inputStyle = {
...styles.input,
- backgroundColor: theme.colors.primaryContainer,
+ backgroundColor: primaryContainer,
};
const bookTypes = new Array(8).fill(0).map((_, i) => ({
id: i + 1,
@@ -479,7 +495,7 @@ function CreateUpdateBookForm({ submit }) {
};
return (
-
+
- {publishDateVisible && (
-
- )}
-
-
+
-
+
priceRef.current.focus()}
returnKeyType="next"
- onChangeText={bookForm.handleChange("name")}
- onBlur={bookForm.handleBlur("name")}
+ onChangeText={bookForm.handleChange('name')}
+ onBlur={bookForm.handleBlur('name')}
value={bookForm.values.name}
autoCapitalize="none"
- label={i18n.t("BookStore::Name")}
+ label={i18n.t('BookStore::Name')}
style={inputStyle}
{...props}
/>
- {isInvalidControl("name") && (
- {bookForm.errors.name}
+ {isInvalidControl('name') && (
+ {bookForm.errors.name as string}
)}
-
+
typeRef.current.focus()}
returnKeyType="next"
- onChangeText={bookForm.handleChange("price")}
- onBlur={bookForm.handleBlur("price")}
+ onChangeText={bookForm.handleChange('price')}
+ onBlur={bookForm.handleBlur('price')}
value={bookForm.values.price}
autoCapitalize="none"
- label={i18n.t("BookStore::Price")}
+ label={i18n.t('BookStore::Price')}
style={inputStyle}
{...props}
/>
- {isInvalidControl("price") && (
- {bookForm.errors.price}
+ {isInvalidControl('price') && (
+ {bookForm.errors.price as string}
)}
-
+
setBookTypeVisible(true)}
- icon="menu-down"
- />
- }
+ error={isInvalidControl('type')}
+ label={i18n.t('BookStore::Type')}
+ right={ setBookTypeVisible(true)} icon="menu-down" />}
style={inputStyle}
editable={false}
value={bookForm.values.typeDisplayName}
{...props}
/>
- {isInvalidControl("type") && (
- {bookForm.errors.type}
+ {isInvalidControl('type') && (
+ {bookForm.errors.type as string}
)}
-
+
setPublishDateVisible(true)}
- icon="menu-down"
+ setPublishDateVisible(true)}
+ icon="calendar"
+ iconColor={bookForm.values.publishDate ? '#4CAF50' : '#666'}
/>
}
style={inputStyle}
editable={false}
- value={bookForm.values.publishDate?.toLocaleDateString()}
+ value={formatDate(bookForm.values.publishDate)}
+ placeholder="Select publish date"
{...props}
/>
- {isInvalidControl("publishDate") && (
-
- {bookForm.errors.publishDate}
-
+ {isInvalidControl('publishDate') && (
+ {bookForm.errors.publishDate as string}
)}
+
+
+
+ {i18n.t('BookStore::PublishDate')}
+
+
+
+
+
+ {i18n.t('AbpUi::Cancel')}
+
+
+ {i18n.t('AbpUi::Ok')}
+
+
+
+
+
@@ -602,12 +632,12 @@ function CreateUpdateBookForm({ submit }) {
}
const styles = StyleSheet.create({
+ inputContainer: {
+ margin: 8,
+ marginLeft: 16,
+ marginRight: 16,
+ },
input: {
- container: {
- margin: 8,
- marginLeft: 16,
- marginRight: 16,
- },
borderRadius: 8,
borderTopLeftRadius: 8,
borderTopRightRadius: 8,
@@ -616,9 +646,38 @@ const styles = StyleSheet.create({
marginLeft: 16,
marginRight: 16,
},
+ dateModal: {
+ padding: 20,
+ margin: 20,
+ borderRadius: 12,
+ elevation: 5,
+ shadowColor: '#000',
+ shadowOffset: {
+ width: 0,
+ height: 2,
+ },
+ shadowOpacity: 0.25,
+ shadowRadius: 3.84,
+ },
+ modalTitle: {
+ textAlign: 'center',
+ marginBottom: 16,
+ fontWeight: '600',
+ },
+ divider: {
+ marginBottom: 16,
+ },
+ modalButtons: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ marginTop: 20,
+ paddingHorizontal: 8,
+ },
});
CreateUpdateBookForm.propTypes = {
+ book: PropTypes.object,
+ authors: PropTypes.array.isRequired,
submit: PropTypes.func.isRequired,
};
@@ -636,9 +695,9 @@ export default CreateUpdateBookForm;
## Update a Book
-We need the navigation parameter for the get bookId and then navigate it again after the Create & Update operation. That's why we'll pass the navigation parameter to the `BooksScreen` component.
+We need the navigation parameter for getting the bookId and then navigate it again after the create & update operations. That is why we will pass the navigation parameter to the `BooksScreen` component.
-```js
+```tsx
//Imports..
//Add navigation parameter
@@ -657,66 +716,102 @@ function BookStoreScreen({ navigation }) {
export default BookStoreScreen;
```
-Replace the code below in the `BookScreen.js` file under the `./src/screens/BookStore/Books` folder.
+Replace the code below in the `BookScreen.tsx` file under the `./src/screens/BookStore/Books` folder.
-```js
-import React from "react";
-import { useSelector } from "react-redux";
-import { Alert, View, StyleSheet } from "react-native";
-import { useTheme, List, IconButton, AnimatedFAB } from "react-native-paper";
-import { useActionSheet } from "@expo/react-native-action-sheet";
-import i18n from "i18n-js";
+```tsx
+import { useState } from 'react';
+import { useSelector } from 'react-redux';
+import { Alert, View, StyleSheet } from 'react-native';
+import { List, IconButton, AnimatedFAB } from 'react-native-paper';
+import { useActionSheet } from '@expo/react-native-action-sheet';
+import i18n from 'i18n-js';
-import { getList } from "../../../api/BookAPI";
-import DataList from "../../../components/DataList/DataList";
-import { createAppConfigSelector } from "../../../store/selectors/AppSelectors";
+import { getList, remove } from '../../../api/BookAPI';
+import { DataList } from '../../../components';
+import { createAppConfigSelector } from '../../../store/selectors/AppSelectors';
+import { useThemeColors } from '../../../hooks';
function BooksScreen({ navigation }) {
- const theme = useTheme();
+ const { background, primary } = useThemeColors();
const currentUser = useSelector(createAppConfigSelector())?.currentUser;
+ const policies = useSelector(createAppConfigSelector())?.auth?.grantedPolicies;
+
+ const [refresh, setRefresh] = useState(null);
const { showActionSheetWithOptions } = useActionSheet();
- const openContextMenu = (item) => {
+ const openContextMenu = (item: { id: string }) => {
const options = [];
- options.push(i18n.t("AbpUi::Edit"));
- options.push(i18n.t("AbpUi::Cancel"));
+ if (policies['BookStore.Books.Delete']) {
+ options.push(i18n.t('AbpUi::Delete'));
+ }
+
+ if (policies['BookStore.Books.Edit']) {
+ options.push(i18n.t('AbpUi::Edit'));
+ }
+
+ options.push(i18n.t('AbpUi::Cancel'));
showActionSheetWithOptions(
{
options,
cancelButtonIndex: options.length - 1,
+ destructiveButtonIndex: options.indexOf(i18n.t('AbpUi::Delete')),
},
- (index) => {
+ index => {
switch (options[index]) {
- case i18n.t("AbpUi::Edit"):
+ case i18n.t('AbpUi::Edit'):
edit(item);
break;
+ case i18n.t('AbpUi::Delete'):
+ removeOnClick(item);
+ break;
}
- }
+ },
);
};
- const edit = (item) => {
- navigation.navigate("CreateUpdateBook", { bookId: item.id });
+ const removeOnClick = (item: { id: string }) => {
+ Alert.alert('Warning', i18n.t('BookStore::AreYouSureToDelete'), [
+ {
+ text: i18n.t('AbpUi::Cancel'),
+ style: 'cancel',
+ },
+ {
+ style: 'default',
+ text: i18n.t('AbpUi::Ok'),
+ onPress: () => {
+ remove(item.id).then(() => {
+ setRefresh((refresh ?? 0) + 1);
+ });
+ },
+ },
+ ]);
+ };
+
+ const edit = (item: { id: string }) => {
+ navigation.navigate('CreateUpdateBook', { bookId: item.id });
};
return (
-
+
{currentUser?.isAuthenticated && (
(
(
+ description={`${item.authorName} | ${i18n.t(
+ 'BookStore::Enum:BookType.' + item.type,
+ )}`}
+ right={props => (
openContextMenu(item)}
/>
@@ -726,17 +821,17 @@ function BooksScreen({ navigation }) {
/>
)}
- {currentUser?.isAuthenticated && (
+ {currentUser?.isAuthenticated && !!policies['BookStore.Books.Create'] && (
navigation.navigate("CreateUpdateBook")}
+ onPress={() => navigation.navigate('CreateUpdateBook')}
visible={true}
- animateFrom={"right"}
- iconMode={"static"}
- style={[styles.fabStyle, { backgroundColor: theme.colors.primary }]}
+ animateFrom={'right'}
+ iconMode={'static'}
+ style={[styles.fabStyle, { backgroundColor: primary }]}
/>
)}
@@ -750,36 +845,32 @@ const styles = StyleSheet.create({
fabStyle: {
bottom: 16,
right: 16,
- position: "absolute",
+ position: 'absolute',
},
});
export default BooksScreen;
```
-Replace code below for `CreateUpdateBookScreen.js` file under the `./src/screens/BookStore/Books/CreateUpdateBook/`
+Replace code below for `CreateUpdateBookScreen.tsx` file under the `./src/screens/BookStore/Books/CreateUpdateBook/`
-```js
-import PropTypes from "prop-types";
-import React, { useEffect, useState } from "react";
+```tsx
+import PropTypes from 'prop-types';
+import { useEffect, useState } from 'react';
-import { get, create, update } from "../../../../api/BookAPI";
-import LoadingActions from "../../../../store/actions/LoadingActions";
-import { createLoadingSelector } from "../../../../store/selectors/LoadingSelectors";
-import { connectToRedux } from "../../../../utils/ReduxConnect";
-import CreateUpdateBookForm from "./CreateUpdateBookForm";
+import { getAuthorLookup, get, create, update } from '../../../../api/BookAPI';
+import LoadingActions from '../../../../store/actions/LoadingActions';
+import { createLoadingSelector } from '../../../../store/selectors/LoadingSelectors';
+import { connectToRedux } from '../../../../utils/ReduxConnect';
+import CreateUpdateBookForm from './CreateUpdateBookForm';
-function CreateUpdateBookScreen({
- navigation,
- route,
- startLoading,
- clearLoading,
-}) {
+function CreateUpdateBookScreen({ navigation, route, startLoading, clearLoading }) {
const { bookId } = route.params || {};
const [book, setBook] = useState(null);
+ const [authors, setAuthors] = useState([]);
- const submit = (data) => {
- startLoading({ key: "save" });
+ const submit = (data: any) => {
+ startLoading({ key: 'save' });
(data.id ? update(data, data.id) : create(data))
.then(() => navigation.goBack())
@@ -788,15 +879,19 @@ function CreateUpdateBookScreen({
useEffect(() => {
if (bookId) {
- startLoading({ key: "fetchBookDetail" });
+ startLoading({ key: 'fetchBookDetail' });
get(bookId)
- .then((response) => setBook(response))
+ .then((response: any) => setBook(response))
.finally(() => clearLoading());
}
}, [bookId]);
- return ;
+ useEffect(() => {
+ getAuthorLookup().then(({ items } = {}) => setAuthors(items));
+ }, []);
+
+ return ;
}
CreateUpdateBookScreen.propTypes = {
@@ -806,7 +901,7 @@ CreateUpdateBookScreen.propTypes = {
export default connectToRedux({
component: CreateUpdateBookScreen,
- stateProps: (state) => ({ loading: createLoadingSelector()(state) }),
+ stateProps: state => ({ loading: createLoadingSelector()(state) }),
dispatchProps: {
startLoading: LoadingActions.start,
clearLoading: LoadingActions.clear,
@@ -818,9 +913,9 @@ export default connectToRedux({
- `update` method is used to update the book on the server.
- `route` parameter will be used to get the bookId from the navigation.
-Replace the `CreateUpdateBookForm.js` file with the code below. We'll use this file for the create and update operations.
+Replace the `CreateUpdateBookForm.tsx` file with the code below. We will use this file for the create and update operations.
-```js
+```tsx
//Imports..
//validateSchema
@@ -859,7 +954,7 @@ function CreateUpdateBookForm({
//Other codes..
```
-- `book` is a nullable property. It'll store the selected book, if the book parameter is null then we'll create a new book.
+- `book` is a nullable property. It will store the selected book, if the book parameter is null then we will create a new book.

@@ -867,62 +962,70 @@ function CreateUpdateBookForm({
## Delete a Book
-Replace the code below in the `BooksScreen.js` file under the `./src/screens/BookStore/Books` folder.
+Replace the code below in the `BooksScreen.tsx` file under the `./src/screens/BookStore/Books` folder.
-```js
-import React, { useState } from "react";
-import { useSelector } from "react-redux";
-import { Alert, View, StyleSheet } from "react-native";
-import { useTheme, List, IconButton, AnimatedFAB } from "react-native-paper";
-import { useActionSheet } from "@expo/react-native-action-sheet";
-import i18n from "i18n-js";
+```tsx
+import { useState } from 'react';
+import { useSelector } from 'react-redux';
+import { Alert, View, StyleSheet } from 'react-native';
+import { List, IconButton, AnimatedFAB } from 'react-native-paper';
+import { useActionSheet } from '@expo/react-native-action-sheet';
+import i18n from 'i18n-js';
-import { getList, remove } from "../../../api/BookAPI";
-import DataList from "../../../components/DataList/DataList";
-import { createAppConfigSelector } from "../../../store/selectors/AppSelectors";
+import { getList, remove } from '../../../api/BookAPI';
+import { DataList } from '../../../components';
+import { createAppConfigSelector } from '../../../store/selectors/AppSelectors';
+import { useThemeColors } from '../../../hooks';
function BooksScreen({ navigation }) {
- const theme = useTheme();
+ const { background, primary } = useThemeColors();
const currentUser = useSelector(createAppConfigSelector())?.currentUser;
+ const policies = useSelector(createAppConfigSelector())?.auth?.grantedPolicies;
const [refresh, setRefresh] = useState(null);
const { showActionSheetWithOptions } = useActionSheet();
- const openContextMenu = (item) => {
+ const openContextMenu = (item: { id: string }) => {
const options = [];
- options.push(i18n.t("AbpUi::Delete"));
- options.push(i18n.t("AbpUi::Edit"));
- options.push(i18n.t("AbpUi::Cancel"));
+ if (policies['BookStore.Books.Delete']) {
+ options.push(i18n.t('AbpUi::Delete'));
+ }
+
+ if (policies['BookStore.Books.Edit']) {
+ options.push(i18n.t('AbpUi::Edit'));
+ }
+
+ options.push(i18n.t('AbpUi::Cancel'));
showActionSheetWithOptions(
{
options,
cancelButtonIndex: options.length - 1,
- destructiveButtonIndex: options.indexOf(i18n.t("AbpUi::Delete")),
+ destructiveButtonIndex: options.indexOf(i18n.t('AbpUi::Delete')),
},
- (index) => {
+ index => {
switch (options[index]) {
- case i18n.t("AbpUi::Edit"):
+ case i18n.t('AbpUi::Edit'):
edit(item);
break;
- case i18n.t("AbpUi::Delete"):
+ case i18n.t('AbpUi::Delete'):
removeOnClick(item);
break;
}
- }
+ },
);
};
- const removeOnClick = (item) => {
- Alert.alert("Warning", i18n.t("BookStore::AreYouSureToDelete"), [
+ const removeOnClick = (item: { id: string }) => {
+ Alert.alert('Warning', i18n.t('BookStore::AreYouSureToDelete'), [
{
- text: i18n.t("AbpUi::Cancel"),
- style: "cancel",
+ text: i18n.t('AbpUi::Cancel'),
+ style: 'cancel',
},
{
- style: "default",
- text: i18n.t("AbpUi::Ok"),
+ style: 'default',
+ text: i18n.t('AbpUi::Ok'),
onPress: () => {
remove(item.id).then(() => {
setRefresh((refresh ?? 0) + 1);
@@ -932,12 +1035,12 @@ function BooksScreen({ navigation }) {
]);
};
- const edit = (item) => {
- navigation.navigate("CreateUpdateBook", { bookId: item.id });
+ const edit = (item: { id: string }) => {
+ navigation.navigate('CreateUpdateBook', { bookId: item.id });
};
return (
-
+
{currentUser?.isAuthenticated && (
(
+ description={`${item.authorName} | ${i18n.t(
+ 'BookStore::Enum:BookType.' + item.type,
+ )}`}
+ right={props => (
openContextMenu(item)}
/>
@@ -962,17 +1067,17 @@ function BooksScreen({ navigation }) {
/>
)}
- {currentUser?.isAuthenticated && (
+ {currentUser?.isAuthenticated && !!policies['BookStore.Books.Create'] && (
navigation.navigate("CreateUpdateBook")}
+ onPress={() => navigation.navigate('CreateUpdateBook')}
visible={true}
- animateFrom={"right"}
- iconMode={"static"}
- style={[styles.fabStyle, { backgroundColor: theme.colors.primary }]}
+ animateFrom={'right'}
+ iconMode={'static'}
+ style={[styles.fabStyle, { backgroundColor: primary }]}
/>
)}
@@ -986,7 +1091,7 @@ const styles = StyleSheet.create({
fabStyle: {
bottom: 16,
right: 16,
- position: "absolute",
+ position: 'absolute',
},
});
@@ -1006,7 +1111,7 @@ export default BooksScreen;
Add `grantedPolicies` to the policies variable from the `appConfig` store
-```js
+```tsx
//Other imports..
import { useSelector } from "react-redux";
@@ -1065,9 +1170,9 @@ export default BookStoreScreen;
### Hide the New Book Button
-`New Book` button is placed in the BooksScreen as a `+` icon button. For the toggle visibility of the button, we need to add the `policies` variable to the `BooksScreen` component like the `BookStoreScreen` component. Open the `BooksScreen.js` file in the `./src/screens/BookStore/Books` folder and include the code below.
+`New Book` button is placed in the BooksScreen as a `+` icon button. For the toggle visibility of the button, we need to add the `policies` variable to the `BooksScreen` component like the `BookStoreScreen` component. Open the `BooksScreen.tsx` file in the `./src/screens/BookStore/Books` folder and include the code below.
-```js
+```tsx
//Imports..
function BooksScreen({ navigation }) {
@@ -1090,7 +1195,7 @@ function BooksScreen({ navigation }) {
visible={true}
animateFrom={'right'}
iconMode={'static'}
- style={[styles.fabStyle, { backgroundColor: theme.colors.primary }]}
+ style={[styles.fabStyle, { backgroundColor: primary }]}
/>
)
}
@@ -1104,9 +1209,9 @@ function BooksScreen({ navigation }) {
### Hide the Edit and Delete Actions
-Update your code as below in the `./src/screens/BookStore/Books/BooksScreen.js` file. We'll check the `policies` variables for the `Edit` and `Delete` actions.
+Update your code as below in the `./src/screens/BookStore/Books/BooksScreen.tsx` file. We'll check the `policies` variables for the `Edit` and `Delete` actions.
-```js
+```tsx
function BooksScreen() {
//...
@@ -1134,8 +1239,8 @@ function BooksScreen() {
### Create API Proxy
-```js
-./src/api/AuthorAPI.js
+```ts
+//./src/api/AuthorAPI.ts
import api from './API';
@@ -1154,9 +1259,9 @@ export const remove = id => api.delete(`/api/app/author/${id}`).then(({ data })
### Add Authors Tab to BookStoreScreen
-Open the `./src/screens/BookStore/BookStoreScreen.js` file and update it with the code below.
+Open the `./src/screens/BookStore/BookStoreScreen.tsx` file and update it with the code below.
-```js
+```tsx
//Other imports
import AuthorsScreen from "./Authors/AuthorsScreen";
@@ -1186,70 +1291,70 @@ function BookStoreScreen({ navigation }) {
export default BookStoreScreen;
```
-Create a `AuthorsScreen.js` file under the `./src/screens/BookStore/Authors` folder and add the code below to it.
+Create a `AuthorsScreen.tsx` file under the `./src/screens/BookStore/Authors` folder and add the code below to it.
-```js
-import React, { useState } from "react";
-import { useSelector } from "react-redux";
-import { Alert, View, StyleSheet } from "react-native";
-import { useTheme, List, IconButton, AnimatedFAB } from "react-native-paper";
-import { useActionSheet } from "@expo/react-native-action-sheet";
-import i18n from "i18n-js";
+```tsx
+import { useState } from 'react';
+import { useSelector } from 'react-redux';
+import { Alert, View, StyleSheet } from 'react-native';
+import { List, IconButton, AnimatedFAB } from 'react-native-paper';
+import { useActionSheet } from '@expo/react-native-action-sheet';
+import i18n from 'i18n-js';
-import { getList, remove } from "../../../api/AuthorAPI";
-import DataList from "../../../components/DataList/DataList";
-import { createAppConfigSelector } from "../../../store/selectors/AppSelectors";
+import { getList, remove } from '../../../api/AuthorAPI';
+import { DataList } from '../../../components';
+import { createAppConfigSelector } from '../../../store/selectors/AppSelectors';
+import { useThemeColors } from '../../../hooks';
function AuthorsScreen({ navigation }) {
- const theme = useTheme();
+ const { background, primary } = useThemeColors();
const currentUser = useSelector(createAppConfigSelector())?.currentUser;
- const policies = useSelector(createAppConfigSelector())?.auth
- ?.grantedPolicies;
+ const policies = useSelector(createAppConfigSelector())?.auth?.grantedPolicies;
const [refresh, setRefresh] = useState(null);
const { showActionSheetWithOptions } = useActionSheet();
- const openContextMenu = (item) => {
+ const openContextMenu = (item: { id: string }) => {
const options = [];
- if (policies["BookStore.Authors.Delete"]) {
- options.push(i18n.t("AbpUi::Delete"));
+ if (policies['BookStore.Authors.Delete']) {
+ options.push(i18n.t('AbpUi::Delete'));
}
- if (policies["BookStore.Authors.Edit"]) {
- options.push(i18n.t("AbpUi::Edit"));
+ if (policies['BookStore.Authors.Edit']) {
+ options.push(i18n.t('AbpUi::Edit'));
}
- options.push(i18n.t("AbpUi::Cancel"));
+ options.push(i18n.t('AbpUi::Cancel'));
showActionSheetWithOptions(
{
options,
cancelButtonIndex: options.length - 1,
- destructiveButtonIndex: options.indexOf(i18n.t("AbpUi::Delete")),
+ destructiveButtonIndex: options.indexOf(i18n.t('AbpUi::Delete')),
},
- (index) => {
+ (index: number) => {
switch (options[index]) {
- case i18n.t("AbpUi::Edit"):
+ case i18n.t('AbpUi::Edit'):
edit(item);
break;
- case i18n.t("AbpUi::Delete"):
+ case i18n.t('AbpUi::Delete'):
removeOnClick(item);
break;
}
- }
+ },
);
};
- const removeOnClick = ({ id } = {}) => {
- Alert.alert("Warning", i18n.t("BookStore::AreYouSureToDelete"), [
+ const removeOnClick = ({ id }: { id: string }) => {
+ Alert.alert('Warning', i18n.t('BookStore::AreYouSureToDelete'), [
{
- text: i18n.t("AbpUi::Cancel"),
- style: "cancel",
+ text: i18n.t('AbpUi::Cancel'),
+ style: 'cancel',
},
{
- style: "default",
- text: i18n.t("AbpUi::Ok"),
+ style: 'default',
+ text: i18n.t('AbpUi::Ok'),
onPress: () => {
remove(id).then(() => {
setRefresh((refresh ?? 0) + 1);
@@ -1259,12 +1364,12 @@ function AuthorsScreen({ navigation }) {
]);
};
- const edit = ({ id } = {}) => {
- navigation.navigate("CreateUpdateAuthor", { authorId: id });
+ const edit = ({ id }: { id: string }) => {
+ navigation.navigate('CreateUpdateAuthor', { authorId: id });
};
return (
-
+
{currentUser?.isAuthenticated && (
(
+ description={item.shortBio || new Date(item.birthDate)?.toLocaleDateString()}
+ right={(props: any) => (
openContextMenu(item)}
/>
@@ -1291,17 +1394,17 @@ function AuthorsScreen({ navigation }) {
/>
)}
- {currentUser?.isAuthenticated && policies["BookStore.Authors.Create"] && (
+ {currentUser?.isAuthenticated && policies['BookStore.Authors.Create'] && (
navigation.navigate("CreateUpdateAuthor")}
+ onPress={() => navigation.navigate('CreateUpdateAuthor')}
visible={true}
- animateFrom={"right"}
- iconMode={"static"}
- style={[styles.fabStyle, { backgroundColor: theme.colors.primary }]}
+ animateFrom={'right'}
+ iconMode={'static'}
+ style={[styles.fabStyle, { backgroundColor: primary }]}
/>
)}
@@ -1315,36 +1418,31 @@ const styles = StyleSheet.create({
fabStyle: {
bottom: 16,
right: 16,
- position: "absolute",
+ position: 'absolute',
},
});
export default AuthorsScreen;
```
-Create a `CreateUpdateAuthorScreen.js` file under the `./src/screens/BookStore/Authors/CreateUpdateAuthor` folder and add the code below to it.
+Create a `CreateUpdateAuthorScreen.tsx` file under the `./src/screens/BookStore/Authors/CreateUpdateAuthor` folder and add the code below to it.
-```js
-import PropTypes from "prop-types";
-import React, { useEffect, useState } from "react";
+```tsx
+import PropTypes from 'prop-types';
+import { useEffect, useState } from 'react';
-import { get, create, update } from "../../../../api/AuthorAPI";
-import LoadingActions from "../../../../store/actions/LoadingActions";
-import { createLoadingSelector } from "../../../../store/selectors/LoadingSelectors";
-import { connectToRedux } from "../../../../utils/ReduxConnect";
-import CreateUpdateAuthorForm from "./CreateUpdateAuthorForm";
+import { get, create, update } from '../../../../api/AuthorAPI';
+import LoadingActions from '../../../../store/actions/LoadingActions';
+import { createLoadingSelector } from '../../../../store/selectors/LoadingSelectors';
+import { connectToRedux } from '../../../../utils/ReduxConnect';
+import CreateUpdateAuthorForm from './CreateUpdateAuthorForm';
-function CreateUpdateAuthorScreen({
- navigation,
- route,
- startLoading,
- clearLoading,
-}) {
+function CreateUpdateAuthorScreen({ navigation, route, startLoading, clearLoading }) {
const { authorId } = route.params || {};
- const [author, setAuthor] = useState(null);
+ const [ author, setAuthor ] = useState(null);
- const submit = (data) => {
- startLoading({ key: "save" });
+ const submit = (data: any) => {
+ startLoading({ key: 'save' });
(data.id ? update(data, data.id) : create(data))
.then(() => navigation.goBack())
@@ -1353,10 +1451,10 @@ function CreateUpdateAuthorScreen({
useEffect(() => {
if (authorId) {
- startLoading({ key: "fetchAuthorDetail" });
+ startLoading({ key: 'fetchAuthorDetail' });
get(authorId)
- .then((response) => setAuthor(response))
+ .then((response: any) => setAuthor(response))
.finally(() => clearLoading());
}
}, [authorId]);
@@ -1371,7 +1469,7 @@ CreateUpdateAuthorScreen.propTypes = {
export default connectToRedux({
component: CreateUpdateAuthorScreen,
- stateProps: (state) => ({ loading: createLoadingSelector()(state) }),
+ stateProps: (state: any) => ({ loading: createLoadingSelector()(state) }),
dispatchProps: {
startLoading: LoadingActions.start,
clearLoading: LoadingActions.clear,
@@ -1379,55 +1477,44 @@ export default connectToRedux({
});
```
-Create a `CreateUpdateAuthorForm.js` file under the `./src/screens/BookStore/Authors/CreateUpdateAuthor` folder and add the code below to it.
+Create a `CreateUpdateAuthorForm.tsx` file under the `./src/screens/BookStore/Authors/CreateUpdateAuthor` folder and add the code below to it.
-```js
-import React, { useRef, useState } from "react";
-import {
- Platform,
- KeyboardAvoidingView,
- StyleSheet,
- View,
- ScrollView,
-} from "react-native";
+```tsx
+import { useRef, useState } from 'react';
+import { Platform, KeyboardAvoidingView, StyleSheet, View, ScrollView } from 'react-native';
-import { useFormik } from "formik";
-import i18n from "i18n-js";
-import PropTypes from "prop-types";
-import * as Yup from "yup";
-import { useTheme, TextInput } from "react-native-paper";
-import DateTimePicker from "@react-native-community/datetimepicker";
+import { useFormik } from 'formik';
+import i18n from 'i18n-js';
+import PropTypes from 'prop-types';
+import * as Yup from 'yup';
+import { Divider, Portal, TextInput, Text, Button, Modal } from 'react-native-paper';
+import DateTimePicker from '@react-native-community/datetimepicker';
-import { FormButtons } from "../../../../components/FormButtons";
-import ValidationMessage from "../../../../components/ValidationMessage/ValidationMessage";
+import { useThemeColors } from '../../../../hooks';
+import { FormButtons, ValidationMessage } from '../../../../components';
const validations = {
- name: Yup.string().required("AbpValidation::ThisFieldIsRequired."),
- birthDate: Yup.string()
- .nullable()
- .required("AbpValidation::ThisFieldIsRequired."),
+ name: Yup.string().required('AbpValidation::ThisFieldIsRequired.'),
+ birthDate: Yup.string().nullable().required('AbpValidation::ThisFieldIsRequired.'),
};
const props = {
- underlineStyle: { backgroundColor: "transparent" },
- underlineColor: "#333333bf",
+ underlineStyle: { backgroundColor: 'transparent' },
+ underlineColor: '#333333bf',
};
function CreateUpdateAuthorForm({ submit, author = null }) {
- const theme = useTheme();
+ const { primaryContainer, background, onBackground } = useThemeColors();
const [birthDateVisible, setPublishDateVisible] = useState(false);
- const nameRef = useRef();
- const birthDateRef = useRef();
- const shortBioRef = useRef();
+ const nameRef = useRef(null);
+ const birthDateRef = useRef(null);
+ const shortBioRef = useRef(null);
- const inputStyle = {
- ...styles.input,
- backgroundColor: theme.colors.primaryContainer,
- };
+ const inputStyle = { ...styles.input, backgroundColor: primaryContainer };
- const onSubmit = (values) => {
+ const onSubmit = (values: any) => {
if (!authorForm.isValid) {
return;
}
@@ -1443,9 +1530,9 @@ function CreateUpdateAuthorForm({ submit, author = null }) {
}),
initialValues: {
...author,
- name: author?.name || "",
+ name: author?.name || '',
birthDate: (author?.birthDate && new Date(author?.birthDate)) || null,
- shortBio: author?.shortBio || "",
+ shortBio: author?.shortBio || '',
},
onSubmit,
});
@@ -1462,89 +1549,110 @@ function CreateUpdateAuthorForm({ submit, author = null }) {
);
};
- const onChange = (event, selectedDate) => {
+ const onChange = (event: any, selectedDate: any) => {
if (!selectedDate) {
return;
}
setPublishDateVisible(false);
- if (event && event.type !== "dismissed") {
- authorForm.setFieldValue("birthDate", selectedDate, true);
+ if (event && event.type !== 'dismissed') {
+ authorForm.setFieldValue('birthDate', selectedDate, true);
}
};
return (
-
+
{birthDateVisible && (
)}
-
+
-
+
birthDateRef.current.focus()}
returnKeyType="next"
- onChangeText={authorForm.handleChange("name")}
- onBlur={authorForm.handleBlur("name")}
+ onChangeText={authorForm.handleChange('name')}
+ onBlur={authorForm.handleBlur('name')}
value={authorForm.values.name}
autoCapitalize="none"
- label={i18n.t("BookStore::Name")}
+ label={i18n.t('BookStore::Name')}
style={inputStyle}
{...props}
/>
- {isInvalidControl("name") && (
- {authorForm.errors.name}
+ {isInvalidControl('name') && (
+ {authorForm.errors.name as string}
)}
-
+
shortBioRef.current.focus()}
right={
- setPublishDateVisible(true)}
- icon="menu-down"
- />
+ setPublishDateVisible(true)} icon="calendar" />
}
style={inputStyle}
editable={false}
value={authorForm.values.birthDate?.toLocaleDateString()}
{...props}
/>
- {isInvalidControl("birthDate") && (
-
- {authorForm.errors.birthDate}
-
+ {isInvalidControl('birthDate') && (
+ {authorForm.errors.birthDate as string}
)}
-
+
+
+
+ {i18n.t('BookStore::BirthDate')}
+
+
+
+
+ setPublishDateVisible(false)} mode="text">
+ {i18n.t('AbpUi::Cancel')}
+
+ setPublishDateVisible(false)} mode="contained">
+ {i18n.t('AbpUi::Ok')}
+
+
+
+
+
+
authorForm.handleSubmit()}
returnKeyType="next"
- onChangeText={authorForm.handleChange("shortBio")}
- onBlur={authorForm.handleBlur("shortBio")}
+ onChangeText={authorForm.handleChange('shortBio')}
+ onBlur={authorForm.handleBlur('shortBio')}
value={authorForm.values.shortBio}
autoCapitalize="none"
- label={i18n.t("BookStore::ShortBio")}
+ label={i18n.t('BookStore::ShortBio')}
style={inputStyle}
{...props}
/>
@@ -1558,12 +1666,12 @@ function CreateUpdateAuthorForm({ submit, author = null }) {
}
const styles = StyleSheet.create({
- input: {
- container: {
- margin: 8,
+ inputContainer: {
+ margin: 8,
marginLeft: 16,
marginRight: 16,
- },
+ },
+ input: {
borderRadius: 8,
borderTopLeftRadius: 8,
borderTopRightRadius: 8,
@@ -1572,6 +1680,33 @@ const styles = StyleSheet.create({
marginLeft: 16,
marginRight: 16,
},
+ divider: {
+ marginBottom: 16,
+ },
+ modalButtons: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ marginTop: 20,
+ paddingHorizontal: 8,
+ },
+ dateModal: {
+ padding: 20,
+ margin: 20,
+ borderRadius: 12,
+ elevation: 5,
+ shadowColor: '#000',
+ shadowOffset: {
+ width: 0,
+ height: 2,
+ },
+ shadowOpacity: 0.25,
+ shadowRadius: 3.84,
+ },
+ modalTitle: {
+ textAlign: 'center',
+ marginBottom: 16,
+ fontWeight: '600',
+ },
});
CreateUpdateAuthorForm.propTypes = {
@@ -1734,7 +1869,7 @@ function CreateUpdateBookForm({ submit, book = null, authors = [] }) {
//Add `AbpSelect` component and TextInput for authors
return (
-
+