Skip to content

Commit

Permalink
Merge pull request #1 from mrowe009/admin-overhaul
Browse files Browse the repository at this point in the history
Admin overhaul
  • Loading branch information
srowe0091 authored Apr 19, 2020
2 parents 6f0e944 + ec51fe9 commit 893b976
Show file tree
Hide file tree
Showing 54 changed files with 348 additions and 114 deletions.
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
### TODO:
- (Feature): Better manage Group Feature
- (Feature): Enhance toolbar to scroll away animate
- (Feature): Edit transactions - long hold
- (Feature): update splash screen
- (Feature): Edit transactions - long hold
Binary file modified android/app/src/main/ic_launcher-playstore.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/drawable-land-hdpi/splash.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/drawable-land-mdpi/splash.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/drawable-land-xhdpi/splash.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/drawable-land-xxhdpi/splash.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/drawable-land-xxxhdpi/splash.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/drawable-port-hdpi/splash.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/drawable-port-mdpi/splash.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/drawable-port-xhdpi/splash.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/drawable-port-xxhdpi/splash.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/drawable-port-xxxhdpi/splash.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Binary file added designs/Finance Logo.psd
Diff not rendered.
Binary file added designs/splash.png
Binary file added designs/splash.psd
Diff not rendered.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"react-router-dom": "^5.1.2",
"react-scripts": "3.4.1",
"react-use": "^14.1.1",
"rxjs": "^6.5.5",
"yup": "^0.28.3"
},
"devDependencies": {
Expand Down
2 changes: 2 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import theme from './styles/theme'
import routes from 'routes'
import { AuthorizedRoute } from 'components'
import { DrawerMenu } from 'modules/drawer'
import { ToastNotification } from 'modules/notification'
import { AuthProvider } from 'modules/authentication/context'

import LoginView from 'modules/authentication/views/LoginView'
Expand All @@ -36,6 +37,7 @@ const App = () => (
<AuthProvider>
<ThemeProvider theme={theme}>
<IonReactRouter>
<ToastNotification />
<DrawerMenu />
<IonRouterOutlet>
<AuthorizedRoute path={routes.home} component={HomeView} />
Expand Down
35 changes: 30 additions & 5 deletions src/components/FullPageLoader.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import React from 'react'
import { createUseStyles } from 'react-jss'
import { IonSpinner } from '@ionic/react'
import { IonSpinner, IonContent } from '@ionic/react'

const useStyles = createUseStyles({
const useFullPageLoaderStyles = createUseStyles({
container: {
width: '100%',
height: '100%',
top: '0',
top: 0,
position: 'fixed',
zIndex: '100',
zIndex: 100,
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
Expand All @@ -20,10 +20,35 @@ const useStyles = createUseStyles({
})

export const FullPageLoader = () => {
const classes = useStyles()
const classes = useFullPageLoaderStyles()
return (
<div className={classes.container}>
<IonSpinner color="primary" className={classes.loading} />
</div>
)
}

const useRelativeLoaderStyles = createUseStyles({
container: {
top: 0,
left: 0,
right: 0,
bottom: 0,
position: 'absolute',
zIndex: 100,
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}
})

export const RelativeLoader = () => {
const classes = useRelativeLoaderStyles()
return (
<IonContent color="dark">
<div className={classes.container}>
<IonSpinner color="primary" className={classes.loading} />
</div>
</IonContent>
)
}
6 changes: 4 additions & 2 deletions src/components/Toolbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const useToolbarStyles = createUseStyles(theme => ({
}
}))

export const Toolbar = ({ children, translucent, color = 'primary', title, back, transition }) => {
export const Toolbar = ({ children, translucent, color = 'primary', title, back, transition, extraToolbar }) => {
const classes = useToolbarStyles(color)
const transitionClass = useMemo(() => {
if (!isNil(transition)) {
Expand All @@ -49,6 +49,7 @@ export const Toolbar = ({ children, translucent, color = 'primary', title, back,
{title && <IonTitle>{title}</IonTitle>}
{children}
</IonToolbar>
{extraToolbar}
</IonHeader>
)
}
Expand All @@ -59,5 +60,6 @@ Toolbar.propTypes = {
color: PropTypes.string,
title: PropTypes.string,
back: PropTypes.bool,
transition: PropTypes.bool
transition: PropTypes.bool,
extraToolbar: PropTypes.node
}
5 changes: 2 additions & 3 deletions src/components/Transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ const useTransactionStyles = createUseStyles(theme => ({
marginBottom: theme.spacing(2),
borderRadius: theme.spacing(1),
boxShadow: '0px 2px 5px -2px var(--black)',
'--background': 'var(--themeGray1)'
},
label: {
display: 'flex',
Expand All @@ -38,8 +37,8 @@ const useTransactionStyles = createUseStyles(theme => ({
export const TransactionEntry = ({ id, amount, description, createdAt, onCheckboxClick, checked, group }) => {
const classes = useTransactionStyles()
return (
<IonItem className={clsx(classes.transaction, { [classes.group]: group })}>
<IonLabel color="light">
<IonItem color="medium" lines="none" className={clsx(classes.transaction, { [classes.group]: group })}>
<IonLabel>
<span className={classes.label}>
<span>
<p>{description || (<span color="textSecondary">(blank)</span>)}</p>
Expand Down
24 changes: 19 additions & 5 deletions src/elements/Input.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import React, { useCallback, useMemo } from 'react'
import React, { useCallback, useMemo, useRef } from 'react'
import { useIonViewWillEnter } from '@ionic/react'
import PropTypes from 'prop-types'
import clsx from 'clsx'
import { createUseStyles } from 'react-jss'

const useInputStyles = createUseStyles(theme => ({
input: {
flex: 'none',
width: '100%',
color: 'var(--white)',
outline: 'none',
Expand All @@ -24,19 +26,30 @@ const useInputStyles = createUseStyles(theme => ({
}
}))

export const Input = ({ className, onChange, onBlur, ...rest }) => {
const useAutoFocus = autoFocus => {
const ele = useRef(null)

useIonViewWillEnter(() => autoFocus && ele?.current?.focus?.())

return ele
}

export const Input = ({ className, onChange, onBlur, autoFocus, ...rest }) => {
const classes = useInputStyles()
return <input className={clsx(className, classes.input)} onBlur={onBlur} onChange={onChange} {...rest} />
const element = useAutoFocus(autoFocus)
return <input ref={element} className={clsx(className, classes.input)} onBlur={onBlur} onChange={onChange} {...rest} />
}

Input.propTypes = {
className: PropTypes.string,
autoFocus: PropTypes.bool,
onChange: PropTypes.func.isRequired,
onBlur: PropTypes.func.isRequired
}

export const MaskedInput = ({ className, onChange, onBlur, format, value, ...rest }) => {
export const MaskedInput = ({ className, onChange, onBlur, format, value, autoFocus, ...rest }) => {
const classes = useInputStyles()
const element = useAutoFocus(autoFocus)

const _value = useMemo(() => format(value)[0], [format, value])

Expand All @@ -50,13 +63,14 @@ export const MaskedInput = ({ className, onChange, onBlur, format, value, ...res
)

return (
<input className={clsx(className, classes.input)} onBlur={onBlur} onChange={_onChange} value={_value} {...rest} />
<input ref={element} className={clsx(className, classes.input)} onBlur={onBlur} onChange={_onChange} value={_value} {...rest} />
)
}

MaskedInput.propTypes = {
className: PropTypes.string,
value: PropTypes.any,
autoFocus: PropTypes.bool,
format: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
onBlur: PropTypes.func.isRequired
Expand Down
Binary file modified src/media/logo.png
12 changes: 12 additions & 0 deletions src/modules/admin/admin.gql.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import gql from 'graphql-tag'

import { TransactionFragment } from 'modules/transaction'
import { UserFragment } from 'modules/user'

export const GroupTransactions = gql`
query GroupTransactions {
Expand All @@ -14,6 +15,17 @@ export const GroupTransactions = gql`
${TransactionFragment}
`

export const UsersInGroup = gql`
query UsersInGroup {
admin {
usersInGroup {
...UserFragment
}
}
}
${UserFragment}
`

export const PayTransactions = gql`
mutation PayTransactions($transactionIds: [ID]!) {
admin {
Expand Down
15 changes: 15 additions & 0 deletions src/modules/admin/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,20 @@ export const useAdminViewStyles = createUseStyles(theme => ({
},
button: {
margin: theme.spacing(2, 0)
},
emptyView: {
marginTop: theme.spacing(8)
}
}))

export const useUsersTabStyles = createUseStyles(theme => ({
card: {
marginBottom: theme.spacing(2)
},
icon: {
marginRight: theme.spacing(1)
},
wrapper: {
margin: theme.spacing(2, 2, 0)
}
}))
107 changes: 27 additions & 80 deletions src/modules/admin/views/AdminView.js
Original file line number Diff line number Diff line change
@@ -1,90 +1,37 @@
import React, { useCallback, useState, useMemo, Fragment } from 'react'
import PropTypes from 'prop-types'
import { IonContent, IonText, useIonViewWillEnter } from '@ionic/react'
import { useQuery, useMutation } from '@apollo/react-hooks'
import useMountedState from 'react-use/lib/useMountedState'
import get from 'lodash/fp/get'
import map from 'lodash/fp/map'
import concat from 'lodash/fp/concat'
import reject from 'lodash/fp/reject'
import groupBy from 'lodash/fp/groupBy'
import includes from 'lodash/fp/includes'
import React, { useCallback, useState } from 'react'
import { IonToolbar, IonSegment, IonSegmentButton, IonLabel } from '@ionic/react'

import { useAdminViewStyles } from '../util'
import { GroupTransactions, PayTransactions } from '../admin.gql'
import { Button } from 'elements'
import { Toolbar, TransactionEntry, FullPageLoader, PullToRefresh } from 'components'
import routes from 'routes'
import PayTransaction from './PayTab'
import Users from './UsersTab'
import { Toolbar } from 'components'

const Admin = ({ history }) => {
const isMounted = useMountedState()
const classes = useAdminViewStyles()
const [transactionIds, handleIds] = useState([])
const [payTransaction, { loading: ptLoading }] = useMutation(PayTransactions, {
variables: { transactionIds },
awaitRefetchQueries: true,
refetchQueries: ['UserTransactions'],
onCompleted: () => history.push(routes.home)
})
const { data, loading, refetch } = useQuery(GroupTransactions)
const checkboxClick = useCallback(
id => e => handleIds(_ids => (e.target.checked ? concat(_ids)(id) : reject(_id => _id === id)(_ids))),
[]
)
const onRefresh = useCallback(e => refetch().then(e.detail.complete), [refetch])
const transactions = useMemo(() => groupBy('owner')(get('admin.groupTransactions')(data)), [data])

useIonViewWillEnter(() => {
if (isMounted()) {
handleIds([])
refetch()
}
})

if (loading) {
return <FullPageLoader />
}
const Admin = () => {
const [activeTab, changeTab] = useState('pay')
const handleTabChange = useCallback(e => changeTab(e.target.value), [])

return (
<>
<Toolbar color="medium" title="Pay Transactions" />
<IonContent color="dark">
<PullToRefresh onRefresh={onRefresh} />
<div className={classes.wrapper}>
<div className={classes.transactions}>
{Object.keys(transactions).map(email => (
<Fragment key={email}>
<IonText color="textSecondary">
<h5 className={classes.headers}>{email}</h5>
</IonText>
{map(t => (
<TransactionEntry
key={t.id}
onCheckboxClick={checkboxClick}
checked={includes(t.id)(transactionIds)}
{...t}
/>
))(transactions[email])}
</Fragment>
))}
</div>
<Button
type="button"
className={classes.button}
disabled={ptLoading || !transactionIds.length}
loading={ptLoading}
onClick={payTransaction}
>
Pay
</Button>
</div>
</IonContent>
<Toolbar
color="medium"
title="Admin"
extraToolbar={(
<IonToolbar color="medium">
<IonSegment onIonChange={handleTabChange} value={activeTab}>
<IonSegmentButton value="pay">
<IonLabel>Pay</IonLabel>
</IonSegmentButton>

<IonSegmentButton value="users">
<IonLabel>Users</IonLabel>
</IonSegmentButton>
</IonSegment>
</IonToolbar>
)}
/>
{activeTab === 'pay' && <PayTransaction />}
{activeTab === 'users' && <Users />}
</>
)
}

Admin.propTypes = {
history: PropTypes.object
}

export default Admin
Loading

0 comments on commit 893b976

Please sign in to comment.