Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25,405 changes: 7,765 additions & 17,640 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"private": true,
"dependencies": {
"@date-io/date-fns": "^1.3.13",
"@fortawesome/fontawesome-svg-core": "^7.2.0",
"@fortawesome/free-regular-svg-icons": "^6.1.1",
"@fortawesome/free-solid-svg-icons": "^6.1.1",
"@fortawesome/react-fontawesome": "^0.1.18",
Expand All @@ -14,16 +15,17 @@
"@types/node": "^17.0.41",
"@types/react": "^16.9.0",
"@types/react-redux": "^7.1.7",
"axios": "^0.27.2",
"axios": "^1.15.0",
"css-in-js-media": "^2.0.1",
"date-fns": "^2.28.0",
"emoji-mart": "^3.0.1",
"emoji-picker-react": "^4.18.0",
"file-loader": "^6.2.0",
"prettier": "^2.6.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-redux": "^7.2.0",
"react-scripts": "5.0.1",
"react-scripts": "^5.0.1",
"sass": "^1.52.3",
"styled-components": "^5.3.5",
"typescript": "^4.7.3",
Expand Down Expand Up @@ -59,4 +61,4 @@
"@types/react-dom": "^18.0.5",
"@types/styled-components": "^5.1.25"
}
}
}
1 change: 1 addition & 0 deletions src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface Goal {
accountId: string
transactionIds: string[]
tagIds: string[]
icon?: string;
}

export interface Tag {
Expand Down
70 changes: 70 additions & 0 deletions src/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { createTheme, ThemeProvider as ThemeProviderMui } from '@material-ui/core'
import React, { useEffect } from 'react'
import styled, { ThemeProvider } from 'styled-components'
import { getUser as getUserApi } from './api/lib'
import { useAppDispatch, useAppSelector } from './store/hooks'
import { selectIsOpen, setIsOpen as setIsOpenRedux } from './store/modalSlice'
import { selectMode } from './store/themeSlice'
import { setUser as setUserRedux } from './store/userSlice'
import { GlobalStyle } from './ui/components/GlobalStyles'
import { DarkTheme, LightTheme } from './ui/components/Theme'
import Main from './ui/pages/Main/Main'
import Modal from './ui/surfaces/modal/Modal'

export default function App() {
const mode = useAppSelector(selectMode)
const modalIsOpen = useAppSelector(selectIsOpen)
const dispatch = useAppDispatch()

const muiTheme = createTheme({ palette: { type: mode } })

useEffect(() => {
async function fetch() {
const user = await getUserApi()
if (user != null) {
dispatch(setUserRedux(user))
}
}

fetch()
}, [dispatch])

const onClick = (event: React.MouseEvent) => {
event.stopPropagation()
dispatch(setIsOpenRedux(false))
}

return (
<AppContainer onClick={onClick}>
<ThemeProviderMui theme={muiTheme}>
<ThemeProvider theme={mode === 'light' ? LightTheme : DarkTheme}>
<GlobalStyle />

<Main />

<ModalContainer isOpen={modalIsOpen}>
<Modal />
</ModalContainer>
</ThemeProvider>
</ThemeProviderMui>
</AppContainer>
)
}

const AppContainer = styled.div`
position: relative;
`
const ModalContainer = styled.div<ModalContainerProps>`
width: 100vw;
height: 100vh;
display: ${(props) => (props.isOpen ? 'flex' : 'none')};
flex-direction: row;
justify-content: center;
align-items: center;
background-color: ${({ theme }) => theme.overlay};
position: absolute;
top: 0;
left: 0;
`

type ModalContainerProps = { isOpen: boolean }
53 changes: 53 additions & 0 deletions src/src/api/lib.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import axios from 'axios'
import { user } from '../data/user'
import { Goal, Transaction, User } from './types'

export const API_ROOT = 'https://fencer-commbank.azurewebsites.net'

export async function getUser(): Promise<User | null> {
try {
const response = await axios.get(`${API_ROOT}/api/User/${user.id}`)
return response.data
} catch (error: any) {
return null
}
}

export async function getTransactions(): Promise<Transaction[] | null> {
try {
const response = await axios.get(`${API_ROOT}/api/Transaction/User/${user.id}`)
return response.data
} catch (error: any) {
return null
}
}

export async function getGoals(): Promise<Goal[] | null> {
try {
const response = await axios.get(`${API_ROOT}/api/Goal/User/${user.id}`)
return response.data
} catch (error: any) {
return null
}
}

export async function createGoal(): Promise<Goal | null> {
try {
const response = await axios.post(`${API_ROOT}/api/Goal`, {
userId: user.id,
targetDate: new Date(),
})
return response.data
} catch (error: any) {
return null
}
}

export async function updateGoal(goalId: string, updatedGoal: Goal): Promise<boolean> {
try {
await axios.put(`${API_ROOT}/api/Goal/${goalId}`, updatedGoal)
return true
} catch (error: any) {
return false
}
}
69 changes: 69 additions & 0 deletions src/src/api/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
export interface Account {
id: string
number: number
name: string
balance: number
accountType: AccountType
applicationId: string
transactionIds: string[]
}

export interface Application {
id: string
created: Date
modified: Date
accountType: AccountType
applicationStatus: ApplicationStatus
userId: string
}

export interface Goal {
id: string
name: string
targetAmount: number
balance: number
targetDate: Date
created: Date
accountId: string
transactionIds: string[]
tagIds: string[]
icon?: string;
}

export interface Tag {
id: string
name: string
}

export interface Transaction {
id: string
transactionType: 'Debit' | 'Credit' | 'Transfer'
amount: number
dateTime: Date
goalId?: string
description: string
tagIds: string[]
}

export interface User {
id: string
name: string
email: string
applicationIds: string[]
}

export enum AccountType {
GoalSaver,
NetBankSaver,
}

export enum ApplicationStatus {
Received,
Assigned,
UnderReview,
Approved,
Rejected,
}

export type ModalContent = Goal
export type ModalType = 'Goal'
1 change: 1 addition & 0 deletions src/src/assets/images/commbank.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/src/assets/images/commbank_card.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/src/assets/images/tag.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions src/src/data/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Hardcoding user for MVP
* TODO(Implement auth)
*/

export const user = {
id: '62a29c15f4605c4c9fa7f306',
}
21 changes: 21 additions & 0 deletions src/src/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@import url('https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap');

body {
margin: 0;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}


* {
font-family: Roboto;
}

a {
cursor: pointer;
}
17 changes: 17 additions & 0 deletions src/src/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import App from './App'
import './index.scss'
import { store } from './store/store'

ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root'),
)

export {}
1 change: 1 addition & 0 deletions src/src/react-app-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference types="react-scripts" />
40 changes: 40 additions & 0 deletions src/src/store/goalsSlice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import { Goal } from '../api/types'
import { RootState } from './store'

export interface GoalsState {
map: IdToGoal
list: string[]
}

export interface IdToGoal {
[id: string]: Goal
}

const initialState: GoalsState = {
map: {},
list: [],
}

export const goalsSlice = createSlice({
name: 'goal',
initialState,
reducers: {
createGoal: (state, action: PayloadAction<Goal>) => {
state.map[action.payload.id] = action.payload
state.list.push(action.payload.id)
},

updateGoal: (state, action: PayloadAction<Goal>) => {
state.map[action.payload.id] = action.payload
},
},
})

export const { createGoal, updateGoal } = goalsSlice.actions

export const selectGoalsMap = (state: RootState) => state.goals.map
export const selectGoalsList = (state: RootState) => state.goals.list

export default goalsSlice.reducer

5 changes: 5 additions & 0 deletions src/src/store/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
import type { AppDispatch, RootState } from './store'

export const useAppDispatch = () => useDispatch<AppDispatch>()
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
39 changes: 39 additions & 0 deletions src/src/store/modalSlice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import { ModalContent, ModalType } from '../api/types'
import { RootState } from './store'

export interface ModalState {
isOpen: boolean
type: ModalType | null
content: ModalContent | null
}

const initialState: ModalState = {
isOpen: false,
type: null,
content: null,
}

export const modalSlice = createSlice({
name: 'modal',
initialState,
reducers: {
setContent: (state, action: PayloadAction<ModalContent>) => {
state.content = action.payload
},
setIsOpen: (state, action: PayloadAction<boolean>) => {
state.isOpen = action.payload
},
setType: (state, action: PayloadAction<ModalType>) => {
state.type = action.payload
},
},
})

export const { setContent, setIsOpen, setType } = modalSlice.actions

export const selectIsOpen = (state: RootState) => state.modal.isOpen
export const selectContent = (state: RootState) => state.modal.content
export const selectType = (state: RootState) => state.modal.type

export default modalSlice.reducer
Loading