diff --git a/README.md b/README.md index 3dcac70..b37bd72 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,177 @@ -# react_native_starter_kit -react_native_starter_kit +# React Native Starter Kit +Simple react native kit, working as an expense manager for android/ios devices. ## Environment Versions - Node: 10.15.3 - React Native : 0.63.3 -- Cocopod: 1.10.1 \ No newline at end of file +- Cocopod: 1.10.1 + +# Installation + +## Ruby & Fastlane + +### Install rvm +Install rvm and select Ruby version + +``` bash +# Install RVM +curl -sSL https://get.rvm.io | bash -s stable --ruby + +# Install the version used by Fastlane +rvm install ruby-$(cat .ruby-version) + +``` + +### Install Fastlane +So that we can build like cool humans, we use [Fastlane](https://fastlane.tools/) + +```bash +gem install fastlane -NV +``` + +## Node + +Node versions are managed via nvm. + +### nvm +To install + +``` +# Install NVM +curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.34.0/install.sh | bash + +# Install Required Node version +nvm install + +# Select node version +nvm use +``` + +## React Native + +```bash +brew install watchman +npm install -g react-native-cli +``` + + +### Platform specific dependencies +``` +npm run install-deps +``` + +### Android tools + +- Android Studio 3.2 +- Android SDK 8.1(oreo) +- Androik SDK Build Tools 29 +- Android Sdk platform-tools 28.0.0 +- Android sdk tools 26.1.1 +- Android support repository 47.0.0 +- Google repository 58 + +### Xcode + +10.1 + +# Build + +TL;DR + +```bash +# In terminal 1 +npm run start + +# In terminal 2 +npm run start-ios #switch npm run start-android +``` + +## Targeting specific platforms + +You can build out for a particular platform via the appropriate npm command +```bash +npm run build-ios +npm run build-android +``` + +# Deployment + +## Generating artefacts + + + +## Scripts + +``` +npm run script_name +``` + +the available scripts are + +```bash +# start the metro interface +npm run start + +npm run start-ios +npm run start-android + +npm run build-ios +npm run build-android + +npm run alpha-release + +npm run reinstall + +``` + +# Common setup issues + +## iOS + +You may receive errors about Podfile not matching Podfile.lock being out of date +or Gemfile being out of date Gemfile.lock + +``` +cd ios +pod update +pod install +gem update +``` + +# Trouble shooting in the development Process + +## Remove dead metro thread + +If you get an error like "something is already running" +follow these steps + +- get PID `sudo lsof -i :8081` +- remember the PID +- kill it `kill pid_number` ex: `kill 4532` + +## Fastlane doesnt not reconize node + +`ln -s $(which node) /usr/local/bin/node` + + +### White screen + +disable CORS `open -a Google\ Chrome --args --disable-web-security --user-data-dir` + +if http://some_stuff/debugger-ui/ Change to http://localhost:8081/debugger-ui/ + +### + +### The simulator IOS is crashing without any error message + +rm -rf ./ios/build +watchman watch-del-al +rm -rf $TMPDIR/react-* +lsof -ti :8081 | xargs kill -9 +cd ios/ && pod install +react-native run-ios + + +This command should create a symlink to `/uer` +`ln -s $(which node) /usr/local/bin/node` diff --git a/android/app/build.gradle b/android/app/build.gradle index 195ddf8..9604c35 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -133,7 +133,7 @@ android { applicationId "io.newplanet.reactnativestarterkit" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 27 + versionCode 28 versionName "0.1.1" } signingConfigs { diff --git a/package-lock.json b/package-lock.json index c828e90..b868540 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "react_native_starter_kit", - "version": "0.1.0", + "version": "0.1.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1509,6 +1509,11 @@ "resolved": "https://registry.npmjs.org/@react-native-community/picker/-/picker-1.8.1.tgz", "integrity": "sha512-Sj9DzX1CSnmYiuEQ5fQhExoo4XjSKoZkqLPAAybycq6RHtCuWppf+eJXRMCOJki25BlKSSt+qVqg0fIe//ujNQ==" }, + "@react-native-community/viewpager": { + "version": "5.0.11", + "resolved": "https://registry.npmjs.org/@react-native-community/viewpager/-/viewpager-5.0.11.tgz", + "integrity": "sha512-eboJwbDQjP1qJP3LFzVspgh88IGNF07S2qpU296j+4kL0inVuL+HXs81SuczMGtJmDZ4u19RNEBVq79of/h+jQ==" + }, "@react-navigation/bottom-tabs": { "version": "5.11.2", "resolved": "https://registry.npmjs.org/@react-navigation/bottom-tabs/-/bottom-tabs-5.11.2.tgz", @@ -9460,6 +9465,15 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "react-mixin": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/react-mixin/-/react-mixin-3.1.1.tgz", + "integrity": "sha512-z9fZ0aCRDjlgxLdMeWkJ9TwhmVLhQ09r8RFpin/cEPA2T6jsb7YHNWcIe0Oii+hhJNyMymdy91CSya5mRkuCkg==", + "requires": { + "object-assign": "^4.0.1", + "smart-mixin": "^2.0.0" + } + }, "react-native": { "version": "0.63.3", "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.63.3.tgz", @@ -9701,6 +9715,15 @@ } } }, + "react-native-carousel-view": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/react-native-carousel-view/-/react-native-carousel-view-0.5.1.tgz", + "integrity": "sha1-Kw1k0y1xvgn/dVWqjBmx2smMWWk=", + "requires": { + "react-mixin": "^3.0.5", + "react-timer-mixin": "^0.13.3" + } + }, "react-native-drawer": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/react-native-drawer/-/react-native-drawer-2.5.1.tgz", @@ -9969,6 +9992,11 @@ "scheduler": "^0.19.1" } }, + "react-timer-mixin": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/react-timer-mixin/-/react-timer-mixin-0.13.4.tgz", + "integrity": "sha512-4+ow23tp/Tv7hBM5Az5/Be/eKKF7DIvJ09voz5LyHGQaqqz9WV8YMs31eFvcYQs7d451LSg7kDJV70XYN/Ug/Q==" + }, "react-tween-state": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/react-tween-state/-/react-tween-state-0.1.5.tgz", @@ -10753,6 +10781,11 @@ "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=" }, + "smart-mixin": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/smart-mixin/-/smart-mixin-2.0.0.tgz", + "integrity": "sha1-o0oQVeMqdbMNK048oyPcmctT9Dc=" + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", diff --git a/package.json b/package.json index 3caa037..940f7d6 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "install-deps": "npm run install-deps-android && npm run install-deps-ios", "android": "react-native run-android", "alpha-release": "cd android && bundle exec fastlane alpha", + "build-android": "cd android && ./gradlew assembleRelease", "ios": "react-native run-ios", "start": "react-native start", "test": "jest", @@ -32,6 +33,7 @@ "dependencies": { "@react-native-community/masked-view": "^0.1.10", "@react-native-community/picker": "^1.8.1", + "@react-native-community/viewpager": "^5.0.11", "@react-navigation/bottom-tabs": "^5.11.2", "@react-navigation/drawer": "^5.11.4", "@react-navigation/material-bottom-tabs": "^5.3.10", @@ -45,6 +47,7 @@ "native-base": "^2.13.14", "react": "16.13.1", "react-native": "0.63.3", + "react-native-carousel-view": "^0.5.1", "react-native-elements": "^3.0.0-alpha.1", "react-native-gesture-handler": "^1.9.0", "react-native-get-random-values": "^1.5.0", diff --git a/src/actions/Record/index.js b/src/actions/Record/index.js index 4997f8e..755c2d9 100644 --- a/src/actions/Record/index.js +++ b/src/actions/Record/index.js @@ -1,13 +1,16 @@ // import 'react-native-get-random-values'; -import {fetchRecord, insertRecord} from '../../helpers/db'; +import { + fetchRecord, + insertRecord, + updateRecord, + removeRecord, +} from '../../helpers/db'; import { RECORD_FETCH_SUCCESS, RECORD_CREATE, RECORD_DELETE, RECORD_UPDATE, - RECORD_UPDATE_ROLLBACK, - RECORD_UPDATE_SUCCESS, } from '../types'; import {DEV_URL} from '../../config'; @@ -80,62 +83,50 @@ export const editRecord = ({ attachment, callback, }) => { - var recordData = { - id: id, - amount: amount, - date: date, - categoryId: categoryId, - payFrom: payFrom, - payTo: payTo, - description: description, - place: place, - attachment: attachment, - }; - - if (description === '') { - return (dispatch) => { - dispatch({ - type: RECORD_UPDATE_ROLLBACK, - payload: 'Record title is required!', - }); - }; - } - return (dispatch) => { - dispatch({ - type: RECORD_UPDATE, - payload: recordData, - meta: { - offline: { - effect: { - url: `${DEV_URL}/records/${id}`, - method: 'PUT', - data: recordData, - }, - commit: {type: RECORD_UPDATE_SUCCESS, meta: {id}}, - rollback: {type: RECORD_UPDATE_ROLLBACK, meta: {id}}, - }, - }, - }); - callback(); + return async (dispatch) => { + try { + const dbResult = await updateRecord( + amount, + date, + categoryId, + payFrom, + payTo, + description, + place, + attachment, + id, + ); + const recordData = { + id, + amount, + date, + categoryId, + payFrom, + payTo, + description, + place, + attachment, + }; + dispatch({type: RECORD_UPDATE, payload: recordData}); + callback(); + } catch (error) { + throw error; + } }; }; export const deleteRecord = ({id, callback}) => { - return (dispatch) => { - dispatch({ - type: RECORD_DELETE, - payload: id, - meta: { - offline: { - effect: { - url: `${DEV_URL}/records/${id}`, - method: 'DELETE', - }, - commit: {}, - rollback: {}, - }, - }, - }); - callback(); + return async (dispatch) => { + try { + await removeRecord(id); + + dispatch({ + type: RECORD_DELETE, + payload: id, + }); + callback(); + } catch (error) { + throw error; + } }; }; diff --git a/src/components/ReportPage/ReportDetail.js b/src/components/ReportPage/ReportDetail.js index 2facc65..6f7cd4c 100644 --- a/src/components/ReportPage/ReportDetail.js +++ b/src/components/ReportPage/ReportDetail.js @@ -1,71 +1,87 @@ /* eslint-disable react-native/no-inline-styles */ -import React from 'react'; +import React, {useState, useEffect} from 'react'; import {connect} from 'react-redux'; -import {Text, View} from 'react-native'; +import {Text, View, Dimensions, Platform} from 'react-native'; import {VictoryPie, VictoryTheme} from 'victory-native'; -import {Dimensions} from 'react-native'; +import Carousel from 'react-native-carousel-view'; + +import {} from 'react-native'; import cs from '../../styles/common'; +import styles from './styles'; const deviceHeight = Dimensions.get('window').height; const deviceWidth = Dimensions.get('window').width; const ReportDetail = (props) => { + const [data, setData] = useState([]); + + useEffect(() => { + setData(props.data); + }, [props.data]); + let myData = props.data; let expenseData = props.expenseData; return ( myData && ( - - - - Income - - datum.y * 0.01} - width={deviceWidth - 100} - data={myData} - events={[]} - style={{ - labels: {fontSize: '12', fill: 'white'}, - }} - animate={{ - duration: 1000, - }} - /> - - - - - Expense - - datum.y * 0.01} - width={deviceWidth - 10} - data={expenseData} - events={[]} + + + + + Income + + datum.y * 0.01} + width={deviceWidth - 10} + data={myData} + events={[]} + style={{ + labels: styles.labels, + }} + animate={{ + duration: 1000, + }} + /> + + - + alignItems: 'center', + padding: 20, + height: deviceHeight, + width: deviceWidth, + }}> + + Expense + + datum.y * 0.01} + width={deviceWidth - 10} + data={expenseData} + events={[]} + style={{ + labels: styles.labels, + }} + animate={{ + duration: 1000, + }} + /> + + ) ); diff --git a/src/components/ReportPage/index.js b/src/components/ReportPage/index.js index 9e24c51..69e8bb7 100644 --- a/src/components/ReportPage/index.js +++ b/src/components/ReportPage/index.js @@ -3,6 +3,7 @@ import React from 'react'; import moment from 'moment'; import {connect} from 'react-redux'; import {Container, Content, Button, Segment, Text} from 'native-base'; + import cs from '../../styles/common'; import {selectReportType} from '../../actions'; @@ -10,76 +11,20 @@ import ReportDetail from './ReportDetail'; const WEEK_TO_DATE = { WEEKLY: 7, - MONTHLY: 12, + MONTHLY: 30, YEARLY: 365, }; const ReportPage = (props) => { - const {selectedReportType} = props; - - let myCatgories = props.categories.filter((obj) => { + const {records, selectReportType, selectedReportType} = props; + const incomeCatgories = props.categories.filter((obj) => { return obj.type === 'INCOME'; }); - let finalResult = []; - - myCatgories.map((category) => { - let recordByCategory = props.records.filter((record) => { - let a = moment(); - let b = moment(record.date); - let dateDiff = a.diff(b, 'days'); - - return ( - record.categoryId === category.id && - dateDiff < WEEK_TO_DATE[selectedReportType] - ); - }); - - let mySum = 0; - - _.each(recordByCategory, (record) => { - mySum += parseFloat(record.amount); - }); - - mySum > 0 && - finalResult.push({ - label: category.title + '\n $' + mySum + '', - y: mySum, - }); - }); - - let myCatgories2 = props.categories.filter((obj) => { + const expenseCategories = props.categories.filter((obj) => { return obj.type === 'EXPENSE'; }); - let finalResult2 = []; - - myCatgories2.map((category) => { - let recordByCategory = props.records.filter((record) => { - let a = moment(); - let b = moment(record.date); - let dateDiff = a.diff(b, 'days'); - - return ( - record.categoryId === category.id && - dateDiff < WEEK_TO_DATE[selectedReportType] - ); - }); - - let mySum = 0; - - _.each(recordByCategory, (record) => { - mySum += parseFloat(record.amount); - }); - - mySum > 0 && - finalResult2.push({ - label: category.title + '\n $' + mySum + '', - x: 20, - y: mySum, - }); - }); - let yearWiseData = [ {x: 'Jan', y: 20}, {x: 'Feb', y: 3}, @@ -95,6 +40,32 @@ const ReportPage = (props) => { {x: 'Dec', y: 7}, ]; + const getData = (categories, allRecords) => { + return categories.map((category) => { + let recordByCategory = allRecords.filter((record) => { + const todayDate = moment(); + const recordDate = moment(record.date); + const dateDiff = todayDate.diff(recordDate, 'days'); + + return ( + record.categoryId === category.id && + dateDiff < WEEK_TO_DATE[selectedReportType] + ); + }); + + let sumOfIncome = 0; + + _.each(recordByCategory, (record) => { + sumOfIncome += parseFloat(record.amount); + }); + + return { + label: sumOfIncome > 0 ? category.title + '\n $' + sumOfIncome : ' ', + y: sumOfIncome > 0 ? parseInt(sumOfIncome, 10) : 0, + }; + }); + }; + return ( @@ -127,8 +98,8 @@ const ReportPage = (props) => { diff --git a/src/components/ReportPage/styles.js b/src/components/ReportPage/styles.js new file mode 100644 index 0000000..95310b8 --- /dev/null +++ b/src/components/ReportPage/styles.js @@ -0,0 +1,16 @@ +import {Dimensions} from 'react-native'; +const deviceHeight = Dimensions.get('window').height; +const deviceWidth = Dimensions.get('window').width; + +export default { + slides: { + alignItems: 'center', + padding: 20, + height: deviceHeight, + width: deviceWidth, + }, + labels: { + fontSize: '12', + fill: 'white', + }, +}; diff --git a/src/helpers/db.js b/src/helpers/db.js index 8018e09..f7421bc 100644 --- a/src/helpers/db.js +++ b/src/helpers/db.js @@ -284,20 +284,21 @@ export const removeRecord = (id) => { }; export const updateRecord = ( - title, - date, amount, - description, - payTo, - payFrom, + date, categoryId, + payFrom, + payTo, + description, + place, camera, + id, ) => { const promise = new Promise((resolve, reject) => { db.transaction((tx) => { tx.executeSql( - 'UPDATE categories SET title=?, date=?, amount=?, description=?, payTo=?, payFrom=?, categoryId=?, camera=? WHERE id=?', - [title, date, amount, description, payTo, payFrom, categoryId, camera], + 'UPDATE records SET date=?, amount=?, description=?, payTo=?, payFrom=?, categoryId=?, camera=? WHERE id=?', + [date, amount, description, payTo, payFrom, categoryId, camera, id], (_, result) => { resolve(result); }, diff --git a/src/navigation.js b/src/navigation.js index 9b1bfd1..4c7183b 100644 --- a/src/navigation.js +++ b/src/navigation.js @@ -192,7 +192,7 @@ export default function App() { initialRouteName="Home" overlayColor="transparent" drawerType="slide" - drawerStyle={{width: '50%'}}> + drawerStyle={{width: '56%'}}>