Skip to content

Commit

Permalink
Merge pull request #6 from forevermatt/develop
Browse files Browse the repository at this point in the history
Release 0.3.0
  • Loading branch information
forevermatt authored Jun 5, 2020
2 parents 7cef047 + 7157ffc commit e58bf5d
Show file tree
Hide file tree
Showing 21 changed files with 346 additions and 46 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ https://forevermatt.github.io/svelte-budget/

- [x] See list of categories with budgeted amounts
- [x] Add a category
- [ ] Edit a category
- [ ] Add a financial account
- [x] Edit a category
- [x] Add a financial account
- [ ] Edit a financial account
- [ ] Record an expense
- [ ] ...
6 changes: 6 additions & 0 deletions assets/bootstrap-4.4.1.css

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions assets/bundle.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion assets/bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<title>Budget</title>

<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<link rel='stylesheet' href='assets/bootstrap-4.4.1.css'>
<link rel='stylesheet' href='assets/global.css'>
<link rel='stylesheet' href='assets/bundle.css'>

Expand Down
2 changes: 2 additions & 0 deletions src/App.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script>
import ErrorMessage from './components/ErrorMessage.svelte'
import { loadAccounts } from './data/accounts'
import { loadBudget } from './data/budget'
import { loadCategories } from './data/categories'
import { onMount } from 'svelte'
import Router from 'svelte-spa-router'
Expand All @@ -9,6 +10,7 @@ import routes from './views/routes'
onMount(async () => {
loadAccounts()
loadCategories()
loadBudget()
})
</script>

Expand Down
69 changes: 69 additions & 0 deletions src/components/BudgetOverview.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<script>
import CategoryGraph from './CategoryGraph.svelte'
import { budget, sortBudgetByCategory } from '../data/budget'
import { categories } from '../data/categories'
import { getCurrentYearMonthString } from '../helpers/dates'
import { dangerIfNegative, formatAmount, formatAmountAsWholeNumber } from '../helpers/numbers'
$: sortedBudget = sortBudgetByCategory($budget, $categories)
</script>

<style>
.category-amount {
white-space: nowrap;
text-align: right;
}
.category-available.danger {
color: red;
}
.category-available > sup {
margin-left: 0.5ex;
margin-right: 0.25ex;
}
.category-budgeted {
color: #999;
font-size: 0.8rem;
vertical-align: bottom;
}
.category-list td {
border: none;
vertical-align: middle;
}
.category-name {
white-space: nowrap;
}
.width-10 {
width: 10%;
}
.width-80 {
width: 80%;
}
</style>

<table class="category-list table table-sm">
<tbody>
{#each sortedBudget as {budgeted, remaining, name, uuid} }
<tr>
<td class="category-name width-10">
<a href="#/category/{ uuid }"
class="btn btn-outline-secondary">{ name }</a>
</td>
<td class="width-80">
<CategoryGraph {budgeted} {remaining} />
</td>
<td class="category-amount width-10">
<div class="category-available { dangerIfNegative(remaining) }">
<sup>$</sup>{ formatAmount(remaining) }
</div>
<div class="category-budgeted"><span>/ { formatAmountAsWholeNumber(budgeted) }</span></div>
</td>
</tr>
{/each}
</tbody>
</table>
61 changes: 61 additions & 0 deletions src/components/CategoryGraph.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<script>
import { dangerIfNegative } from '../helpers/numbers'
export let budgeted
export let remaining
const getStatus = (remaining, total) => {
if (remaining == undefined) {
return '';
} else if (remaining < 0) {
return 'danger';
} else if (remaining < (total / 4)) {
return 'warning';
}
return 'success';
}
const calculageWidth = (remaining, total) => {
if (remaining < 0) {
return 0;
} else if (total === 0) {
return (remaining > 0) ? 100 : 0;
}
return (remaining / total) * 100;
}
</script>

<style>
.category-graph {
box-shadow: 0px 0px 1px 0px rgba(0, 0, 0, 0.75);
}
.category-graph.danger {
box-shadow: 0px 0px 0px 1px rgba(255, 0, 0, 0.75);
}
.category-graph,
.category-graph-line {
border-radius: 4px;
}
.category-graph .danger {
background-color: red;
}
.category-graph .success {
background-color: green;
}
.category-graph .warning {
background-color: orange;
}
.category-graph-line {
height: 6px;
margin: auto 0;
}
</style>

<div class="category-graph { dangerIfNegative(remaining) }">
<div class="category-graph-line { getStatus(remaining, budgeted) }"
style="width: { calculageWidth(remaining, budgeted) }%;"></div>
</div>
4 changes: 2 additions & 2 deletions src/data/accounts.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { addToList, updateInList } from './helpers'
import { addToList, updateInList } from '../helpers/data-store-helpers'
import { getListFromStorage, saveToStorage } from './storage'
import { get, writable } from 'svelte/store'
import { v4 as uuidv4 } from 'uuid'
Expand All @@ -24,6 +24,6 @@ export const loadAccounts = () => {
const saveAccounts = () => saveToStorage(ACCOUNTS, get(accounts))

export const updateAccount = (uuid, changes) => {
updateInList(uuid, changes, accounts)
updateInList('uuid', uuid, changes, accounts)
saveAccounts()
}
65 changes: 65 additions & 0 deletions src/data/budget.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { getCategoryFrom } from './categories'
import { addToList, updateInObject } from '../helpers/data-store-helpers'
import { getCurrentYearMonthString } from '../helpers/dates'
import { getObjectFromStorage, saveToStorage } from './storage'
import { get, writable } from 'svelte/store'

const BUDGET = 'budget'

const budgetStore = writable({})
export {budgetStore as budget}

const addCategoryToBudget = (categoryUuid, budgeted) => {
updateBudget(categoryUuid, {
budgeted: budgeted,
remaining: budgeted,
refilled: getCurrentYearMonthString()
})
}

const isExistingCategory = uuid => get(budgetStore).hasOwnProperty(uuid)

export const loadBudget = () => {
budgetStore.set(getObjectFromStorage(BUDGET))
}

const saveBudget = () => saveToStorage(BUDGET, get(budgetStore))

export const setBudgetedForCategory = (uuid, budgeted) => {
const budget = get(budgetStore)
if (isExistingCategory(uuid)) {
updateBudgetedForExistingCategory(uuid, budgeted)
} else {
addCategoryToBudget(uuid, budgeted)
}
}

export const sortBudgetByCategory = (budget, categories) => {
let list = []
for (var uuid in budget) {
if (budget.hasOwnProperty(uuid)) {
let category = getCategoryFrom(uuid, categories)
list.push({
budgeted: budget[uuid].budgeted,
remaining: budget[uuid].remaining,
name: category.name,
uuid: category.uuid,
});
}
}
return list.sort((a, b) => (a.name || '').localeCompare(b.name || ''));
}

export const updateBudget = (categoryUuid, changes) => {
updateInObject(categoryUuid, changes, budgetStore)
saveBudget()
}

const updateBudgetedForExistingCategory = (categoryUuid, budgeted) => {
const budget = get(budgetStore)
let categoryAmounts = budget[categoryUuid]
let previousBudgeted = categoryAmounts.budgeted
let previousRemaining = categoryAmounts.remaining
let remaining = previousRemaining + (budgeted - previousBudgeted)
updateBudget(categoryUuid, {budgeted, remaining})
}
25 changes: 17 additions & 8 deletions src/data/categories.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { addToList, updateInList } from './helpers'
import { addToList, updateInList } from '../helpers/data-store-helpers'
import { getListFromStorage, saveToStorage } from './storage'
import { get, writable } from 'svelte/store'
import { v4 as uuidv4 } from 'uuid'
Expand All @@ -8,13 +8,22 @@ const CATEGORIES = 'categories'
export const categories = writable([])

export const createCategory = name => {
const newCategory = {
uuid: uuidv4(),
name: name,
const existingCategory = get(categories).find(c => c.name === name)
if (existingCategory) {
return existingCategory
} else {
const newCategory = {
uuid: uuidv4(),
name: name,
}
addToList(newCategory, categories)
saveCategories()
return newCategory
}
addToList(newCategory, categories)
saveCategories()
return newCategory
}

export const getCategoryFrom = (uuid, list) => {
return list.find(item => item.uuid === uuid) || {}
}

export const loadCategories = () => {
Expand All @@ -24,6 +33,6 @@ export const loadCategories = () => {
const saveCategories = () => saveToStorage(CATEGORIES, get(categories))

export const updateCategory = (uuid, changes) => {
updateInList(uuid, changes, categories)
updateInList('uuid', uuid, changes, categories)
saveCategories()
}
2 changes: 1 addition & 1 deletion src/data/errors.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { addToList, updateInList } from './helpers'
import { addToList, updateInList } from '../helpers/data-store-helpers'
import { getListFromStorage, saveToStorage } from './storage'
import { get, writable } from 'svelte/store'
import { v4 as uuidv4 } from 'uuid'
Expand Down
20 changes: 0 additions & 20 deletions src/data/helpers.js

This file was deleted.

15 changes: 12 additions & 3 deletions src/data/storage.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
const isArray = Array.isArray

export const getListFromStorage = listName => {
const stringFromStorage = localStorage.getItem(listName)
const dataFromStorage = JSON.parse(stringFromStorage)
return isArray(dataFromStorage) ? dataFromStorage : []
const list = parseJsonFromStorage(listName)
return isArray(list) ? list : []
}

export const getObjectFromStorage = objectName => {
const data = parseJsonFromStorage(objectName)
return (data !== null) ? data : {}
}

const parseJsonFromStorage = itemName => {
const stringFromStorage = localStorage.getItem(itemName)
return JSON.parse(stringFromStorage)
}

export const saveToStorage = (name, data) => {
Expand Down
37 changes: 37 additions & 0 deletions src/helpers/data-store-helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@

export const addToList = (item, store) => {
store.update(list => [item, ...list])
}

export const updateInList = (idField, id, changes, store) => {
store.update(list => {
const itemToUpdate = list.find(item => item[idField] === id)
const updatedItem = Object.assign({}, itemToUpdate, changes)

let found = false;
for (let i = 0; i < list.length; i++) {
if (list[i][idField] === id) {
found = true
list[i] = updatedItem
break
}
}

if (!found) {
list.push(updatedItem)
}

return list
})
}

export const updateInObject = (property, changes, store) => {
store.update(data => {
if (data.hasOwnProperty(property)) {
data[property] = Object.assign(data[property], changes)
} else {
data[property] = changes
}
return data
})
}
13 changes: 13 additions & 0 deletions src/helpers/dates.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

export const getCurrentYearMonthString = () => {
return getYearMonthStringForMonthsBefore(0, new Date())
}

const getYearMonthStringForMonthsBefore = (numMonthsAgo, when) => {
var currentYear = when.getFullYear()
var currentMonth = when.getMonth() // 0 to 11
var desiredDate = new Date(currentYear, currentMonth - numMonthsAgo)
var fullYear = desiredDate.getFullYear()
var desiredMonth = (desiredDate.getMonth() + 1) // 1 to 12
return fullYear + '-' + String('0' + desiredMonth).slice(-2)
}
Loading

0 comments on commit e58bf5d

Please sign in to comment.