-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Detangle recoil state and add error boundary for API errors
Also add a home page that explains what this app does and how to get a personal access token
- Loading branch information
Showing
26 changed files
with
537 additions
and
420 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,40 @@ | ||
# Up Bank Transaction Tagger | ||
# Unofficial Up Bank Web App | ||
|
||
Web app using [Up Bank API](https://developer.up.com.au/) to allow bulk tagging of | ||
transactions. Everything happens in your browser, so a refresh will wipe any loaded | ||
transactions. | ||
Web app using [Up Bank API](https://developer.up.com.au/), currently primarily to | ||
allow bulk tagging of transactions. Everything happens in your browser, so a | ||
refresh will wipe any loaded transactions. | ||
|
||
Access the live version on GitHub Pages! [erfanio.github.io/up-transaction-tagger](https://erfanio.github.io/up-transaction-tagger/) | ||
|
||
![screenshot](./preview.png) | ||
|
||
## FAQ | ||
|
||
### What's an API key? | ||
### What's an API or Perosnal Access Token? | ||
|
||
Your API key uniquely identifies you with Up Bank. It allows anyone that knows your | ||
API key to read your transactions and add/update tags and categories. | ||
Up Bank provides an interface for third-party applications (such as this one) to | ||
read your transactions and add/update categories or tags to them. This is called | ||
an application programming interface (API) and your **personal access token** is | ||
a unique identifier that allows applications to access your trasaction data | ||
using the API. | ||
|
||
Go to [api.up.com.au](http://api.up.com.au) to get your API key! | ||
You can get your personal access token from [api.up.com.au](http://api.up.com.au). | ||
You can revoke the previous token by generating a new one any time. | ||
|
||
### I see "Covered from X" or "Forwarded to X" transactions... what gives? | ||
|
||
Covers and fowards are transactions under the hood. The Up app hides these transactions | ||
under pretty UI but they're still there. | ||
Covers and fowards are transactions under the hood. The Up app hides these | ||
transactions under pretty UI but they're still there. | ||
|
||
These transactions show up in the API but they don't mark which transaction they're | ||
covering. I have an [open GitHub issue](https://github.com/up-banking/api/issues/99) | ||
with Up to add this feature. | ||
These transactions show up in the API but they don't mark which transaction | ||
they're covering. I have an | ||
[open GitHub issue](https://github.com/up-banking/api/issues/99) with Up to add | ||
this feature. | ||
|
||
For now, this web app uses heuristics to find likely covers/forwards and displays them a similar | ||
UI to the app. | ||
For now, this web app uses heuristics to find likely covers/forwards and displays | ||
them a similar UI to the app. | ||
|
||
### Can I shift select? | ||
|
||
YES! The web app supports selecting multiple transactions in a row if you hold the shift key. | ||
YES! The web app supports selecting multiple transactions in a row if you hold the | ||
shift key. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
.api-form label > input { | ||
margin-left: 10px; | ||
} | ||
|
||
.api-form input[type='submit'] { | ||
margin-top: 20px; | ||
display: block; | ||
background-color: #ff7a64; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import React, { useState } from 'react'; | ||
import './ApiKeyForm.css'; | ||
|
||
type ApiKeyFormProps = { | ||
onSubmit: (apiKey: string) => void; | ||
}; | ||
export default function ApiKeyForm({ onSubmit }: ApiKeyFormProps) { | ||
const [apiKey, setApiKey] = useState(''); | ||
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => { | ||
setApiKey(event.target.value); | ||
}; | ||
|
||
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => { | ||
event.preventDefault(); | ||
onSubmit(apiKey); | ||
}; | ||
|
||
return ( | ||
<form onSubmit={handleSubmit} className="api-form"> | ||
<label> | ||
Personal Access Token | ||
<input | ||
type="text" | ||
name="api-key" | ||
placeholder="up:yeah:aBcDeFgHiJkLmNoP12345" | ||
value={apiKey} | ||
onChange={handleChange} | ||
/> | ||
</label> | ||
<input type="submit" value="Let's Get Started" /> | ||
</form> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,3 +6,11 @@ | |
.topbar { | ||
display: flex; | ||
} | ||
|
||
.card.text p { | ||
margin: 1em 0 1em 0; | ||
} | ||
|
||
.error pre { | ||
text-wrap: auto; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,70 +1,124 @@ | ||
import { apiKeyState } from './api_client'; | ||
import { useRecoilState } from 'recoil'; | ||
import React, { useState } from 'react'; | ||
import React, { Component } from 'react'; | ||
import Accounts from './Accounts'; | ||
import ActionBar from './ActionBar'; | ||
import Filters from './Filters'; | ||
import Search from './Search'; | ||
|
||
import ApiKeyForm from './ApiKeyForm'; | ||
import { apiKeyState } from './data/apiKey'; | ||
import { useRecoilState } from 'recoil'; | ||
import './App.css'; | ||
|
||
type ApiKeyFormProps = { | ||
onSubmit: (apiKey: string) => void; | ||
type DisplayErrorsProps = { | ||
setApiKey: (apiKey: string) => void; | ||
children: React.ReactElement; | ||
}; | ||
function ApiKeyForm({ onSubmit }: ApiKeyFormProps) { | ||
const [apiKey, setApiKey] = useState(''); | ||
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => { | ||
setApiKey(event.target.value); | ||
}; | ||
type DisplayErrorsState = { | ||
caughtError?: any; | ||
}; | ||
class DisplayErrors extends Component<DisplayErrorsProps, DisplayErrorsState> { | ||
state: DisplayErrorsState = { caughtError: undefined }; | ||
|
||
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => { | ||
event.preventDefault(); | ||
onSubmit(apiKey); | ||
}; | ||
static getDerivedStateFromError(error: any) { | ||
return { caughtError: error }; | ||
} | ||
|
||
return ( | ||
<form onSubmit={handleSubmit}> | ||
<input | ||
type="text" | ||
name="api-key" | ||
placeholder="API KEY" | ||
value={apiKey} | ||
onChange={handleChange} | ||
/> | ||
<input type="submit" /> | ||
</form> | ||
); | ||
} | ||
onApiKeySubmit = (apiKey: string) => { | ||
this.setState({ caughtError: undefined }); | ||
this.props.setApiKey(apiKey); | ||
} | ||
|
||
type WithApiKeyProps = { | ||
children: React.ReactElement; | ||
}; | ||
function WithApiKey({ children }: WithApiKeyProps) { | ||
const [apiKey, setApiKey] = useRecoilState(apiKeyState); | ||
render() { | ||
if (this.state.caughtError) { | ||
if (this.state.caughtError.status == '401') { | ||
return ( | ||
<div className="card text error"> | ||
<h1>Auth Error</h1> | ||
<p> | ||
Uh oh, looks like your personal access token isn't working. Up API | ||
returned this error. | ||
</p> | ||
<pre>{JSON.stringify(this.state.caughtError, null, 2)}</pre> | ||
<h2>Update Your Personal Access Token</h2> | ||
<p> | ||
Maybe there was a typo, try setting your personal access token | ||
again. If that still doesn't work, generate a new one from{' '} | ||
<a href="https://api.up.com.au/getting_started" target="_blank"> | ||
api.up.com.au | ||
</a> | ||
. | ||
</p> | ||
<ApiKeyForm onSubmit={this.onApiKeySubmit} /> | ||
</div> | ||
); | ||
} | ||
|
||
const handleSubmit = (newApiKey: string) => { | ||
setApiKey(newApiKey); | ||
}; | ||
return ( | ||
<div className="card text error"> | ||
<h1>Unknown Error</h1> | ||
<p>Uh oh, something went wrong. Refresh this page to reset.</p> | ||
<p> | ||
Maybe to take screenshot of this error and create an issue on Github,{' '} | ||
<a href="https://github.com/erfanio/up-transaction-tagger/issues" target="_blank"> | ||
github.com/erfanio/up-transaction-tagger/issues | ||
</a> | ||
</p> | ||
<pre>{JSON.stringify(this.state.caughtError, null, 2)}</pre> | ||
</div> | ||
); | ||
} | ||
|
||
if (apiKey) { | ||
return children; | ||
return this.props.children; | ||
} | ||
return <ApiKeyForm onSubmit={handleSubmit} />; | ||
} | ||
|
||
export default function App() { | ||
const [apiKey, setApiKey] = useRecoilState(apiKeyState); | ||
|
||
return ( | ||
<div className="app"> | ||
<WithApiKey> | ||
<React.Suspense fallback={<p>Loading accounts...</p>}> | ||
<div className="topbar"> | ||
<Search /> | ||
<Filters /> | ||
<DisplayErrors setApiKey={setApiKey}> | ||
{!apiKey ? ( | ||
<div className="card text"> | ||
<h1>Unofficial Up Bank Web App</h1> | ||
<p> | ||
The official app is amazing but it doesn't have every feature. | ||
This unofficial app uses the Up Bank API to access your | ||
transaction data and add some features that @erfanio wanted | ||
to have :) | ||
</p> | ||
<h2>How Does It Work?</h2> | ||
<p> | ||
Up Bank offers a Application Programming Interface (API) which is a | ||
convenient way to allow third-party apps to <b>only</b> access your | ||
transaction data and do minor changes like adding tags and categories. | ||
</p> | ||
<p>This app is fully local, your data never leaves your browser.</p> | ||
<h2>Get Your Personal Access Token</h2> | ||
<p> | ||
Personal access token authenticates this app with Up Bank, and | ||
allows this app to list your accounts, transactions, and tags | ||
and assign tags and categories to transactions. | ||
</p> | ||
<p> | ||
You can generate a new personal access token by going to{' '} | ||
<a href="https://api.up.com.au/getting_started" target="_blank"> | ||
api.up.com.au | ||
</a> | ||
. Once you have it, enter here to get started. | ||
</p> | ||
<ApiKeyForm onSubmit={setApiKey} /> | ||
</div> | ||
<Accounts /> | ||
<ActionBar /> | ||
</React.Suspense> | ||
</WithApiKey> | ||
) : ( | ||
<React.Suspense fallback={<p>Loading accounts...</p>}> | ||
<div className="topbar"> | ||
<Search /> | ||
<Filters /> | ||
</div> | ||
<Accounts /> | ||
<ActionBar /> | ||
</React.Suspense> | ||
)} | ||
</DisplayErrors> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.