diff --git a/README.md b/README.md index 69d29a7..14a6432 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # Welcome Take control of your budget, download it today. It is free - forever. +![MyBudget](images/readme.png) + # Features - Completely offline - we are not saving any information of yours, it remains on your computer. - Simple - only contains what you need to budget. diff --git a/app/actions/createTransaction.js b/app/actions/createTransaction.js index 3de7a2b..5ec6222 100644 --- a/app/actions/createTransaction.js +++ b/app/actions/createTransaction.js @@ -94,7 +94,7 @@ export function modifyAmount(amount: number){ } export function modifyDay(day: number){ return (dispatch: Dispatch, store: Store) => { - dispatch(modify_day(day)); + dispatch(modify_day(parseInt(day))); } } export function modifySelectedCategory(categoryId: string, categoryName: string){ diff --git a/app/app.global.css b/app/app.global.css index 8248ec4..c8f33b5 100644 --- a/app/app.global.css +++ b/app/app.global.css @@ -9,7 +9,7 @@ body { position: relative; color: #000000; height: 100vh; - background-color: #ffffff; + background-color: #eceaea; overflow-y:hidden; /*checkin*/ /*background-image: linear-gradient(45deg, rgba(0, 216, 255, 0.5) 10%, rgba(0, 1, 127, 0.7)); */ font-family: Arial, Helvetica, Helvetica Neue, serif; @@ -27,9 +27,9 @@ h1,h2 { margin-bottom:10px; } -* { +/* * { overflow-wrap:break-word; -} +} */ p { font-size: 24px; @@ -53,4 +53,10 @@ a:hover { #root { height:100%; +} + +/* spectre overrides */ +.card { + box-shadow: 0 0.25rem 1rem rgba(48,55,66,.15); + padding:10px; } \ No newline at end of file diff --git a/app/components/Category/Category.js b/app/components/Category/Category.js index 1693d67..ee7481e 100644 --- a/app/components/Category/Category.js +++ b/app/components/Category/Category.js @@ -142,6 +142,9 @@ class Category extends Component { let code = event.keyCode || event.which; if (code === 13){ this.renameCategory(); + } else if (code === 27){ + this.toggleRenameActive(); + event.target.blur(); } } @@ -149,6 +152,9 @@ class Category extends Component { let code = event.keyCode || event.which; if (code === 13){ this.createNewItem(); + } else if (code === 27){ + this.toggleAddActive(); + event.target.blur(); } } @@ -191,7 +197,7 @@ class Category extends Component {
-
this.props.delete(this.props.id, this.props.name)}> +
this.props.delete(this.props.id, this.props.name)}>
@@ -202,8 +208,8 @@ class Category extends Component { renderNewSubcategory(){ if (!this.state.addActive){ return ( -
this.toggleAddActive()}> - +
this.toggleAddActive()}> + sub-category
); } else { @@ -255,7 +261,8 @@ class Category extends Component { function mapStateToProps(state, props){ return { - items: state.items.filter(i => i.dateId === props.dateId && i.categoryId === props.id) + items: state.items.filter(i => i.dateId === props.dateId && i.categoryId === props.id), + transactions: state.transactions.filter(t => t.dateId === props.dateId && t.categoryId === props.id) } } diff --git a/app/components/CategoryCollection/CategoryCollection.css b/app/components/CategoryCollection/CategoryCollection.css index eea36d5..cf320b1 100644 --- a/app/components/CategoryCollection/CategoryCollection.css +++ b/app/components/CategoryCollection/CategoryCollection.css @@ -7,5 +7,6 @@ } .category-container { - + overflow-y:auto; + max-height:52vh; } \ No newline at end of file diff --git a/app/components/CategoryCollection/CategoryCollection.js b/app/components/CategoryCollection/CategoryCollection.js index 8473a08..0418b41 100644 --- a/app/components/CategoryCollection/CategoryCollection.js +++ b/app/components/CategoryCollection/CategoryCollection.js @@ -7,6 +7,7 @@ import * as ItemCollectionActions from "../../actions/itemCollection"; import * as ModifyActions from "../../actions/modify"; import styles from "./CategoryCollection.css"; import Category from "../Category/Category"; +import { dateToReadble } from "../../utils/readableDate"; class CategoryCollection extends Component { props: Props; @@ -14,13 +15,21 @@ class CategoryCollection extends Component { constructor(){ super(); this.state = { - newCategoryName: "" + newCategoryName: "", + copyPreviousCategoriesActive: false, + previousCategoryDates: [], + selectedPreviousDateForCategories: "" }; this.modifyNewCategoryName = this.modifyNewCategoryName.bind(this); this.createNewCategory = this.createNewCategory.bind(this); this.renameCategory = this.renameCategory.bind(this); this.deleteCategory = this.deleteCategory.bind(this); + this.prepPreviousCategories = this.prepPreviousCategories.bind(this); + this.modifySelectedDateForCategories = this.modifySelectedDateForCategories.bind(this); + this.toggleCopyPreviousCategories = this.toggleCopyPreviousCategories.bind(this); + this.copyPreviousCategories = this.copyPreviousCategories.bind(this); + this.handleEscapeKey = this.handleEscapeKey.bind(this); } modifyNewCategoryName(event){ @@ -33,8 +42,7 @@ class CategoryCollection extends Component { if (this.state.newCategoryName !== "") { // Don't create duplicate categories - if (typeof this.props.categories.find(c => c.dateId === this.props.date.id && - c.name === this.state.newCategoryName) === "undefined") { + if (typeof this.props.categories.filter(c => c.dateId === this.props.date.id).find(c => c.name === this.state.newCategoryName) === "undefined") { this.props.addCategory(this.state.newCategoryName); this.props.trueModify(); @@ -47,7 +55,7 @@ class CategoryCollection extends Component { } renameCategory(id, newName){ - let categories = this.props.categories; + let categories = this.props.categories.filter(c => c.dateId === this.props.date.id); if (typeof categories.find(c => c.name === newName) === "undefined"){ this.props.renameCategory(id, newName); @@ -81,6 +89,128 @@ class CategoryCollection extends Component { }); } + prepPreviousCategories(){ + var dates = []; + + for (var i = 0; i < this.props.categories.length; i++){ + if (dates.indexOf(this.props.categories[i].dateId) < 0){ + dates.push(this.props.categories[i].dateId); + } + } + + this.setState({ + previousCategoryDates: dates + }); + } + + modifySelectedDateForCategories(event){ + this.setState({ + selectedPreviousDateForCategories: event.target.value + }); + } + + toggleCopyPreviousCategories(event){ + let newState = !this.state.copyPreviousCategoriesActive; + if (newState){ + this.prepPreviousCategories(); + } + + this.setState({ + copyPreviousCategoriesActive: newState + }); + } + + copyPreviousCategories(event){ + let target = this.state.selectedPreviousDateForCategories; + + // copy all categories + for (var i = 0; i < this.props.categories.length; i++){ + if (this.props.categories[i].dateId === target){ + this.props.addCategory(this.props.categories[i].name); + } + } + + // copy all items + for (var i = 0; i < this.props.items.length; i++){ + if (this.props.items[i].dateId === target){ + this.props.addItem(this.props.items[i].categoryId, this.props.items[i].name); + } + } + + this.props.trueModify(); + this.setState({ + copyPreviousCategoriesActive: false, + previousCategoryDates: [], + selectedPreviousDateForCategories: "" + }); + } + + handleEscapeKey(event){ + let code = event.keyCode || event.which; + if (code === 27){ + event.target.blur(); + this.setState({ + newCategoryName: "" + }); + } + } + + createPreviousCategoriesDropdown(){ + let dates = this.state.previousCategoryDates; + + return dates.sort(function(a, b){ + var split1 = a.split('-'); + var split2 = b.split('-'); + var m1 = split1[0]; + var y1 = split1[1]; + var m2 = split2[0]; + var y2 = split2[1]; + + if (y1 > y2){ + return 1; + } else if (y2 > y1) { + return -1; + } else if (m1 > m2) { + return 1; + } else if (m2 > m1) { + return -1; + } + return 0; + }).map((date) => + + ); + } + + renderCopyPreviousCategories(){ + if (!this.state.copyPreviousCategoriesActive){ + return ( +
+
+
this.toggleCopyPreviousCategories()}> + +
+
+
+ ); + } else { + return ( +
+
+
this.copyPreviousCategories()}> +
+ + +
+
+
+
+ ); + } + } + render() { return ( @@ -91,28 +221,27 @@ class CategoryCollection extends Component {
this.createNewCategory()}>
- +
-
- {this.props.categories.sort(function(a, b){ +
+ {this.props.categories.filter(c => c.dateId === this.props.date.id).sort(function(a, b){ var a1 = a.name.toLowerCase(); var b1 = b.name.toLowerCase(); if (a1 > b1) return 1; if (a1 < b1) return -1; return 0; }).map((value, index, array) => { - return value.dateId === this.props.date.id ? + return value.dateId === this.props.date.id &&
- : - })} -
+
+ {this.props.categories.filter(c => c.dateId === this.props.date.id).length === 0 && this.renderCopyPreviousCategories()} ); } @@ -123,7 +252,7 @@ class CategoryCollection extends Component { function mapStateToProps(state){ return { date: state.date, - categories: state.categories.filter(c => c.dateId === state.date.id), + categories: state.categories, items: state.items } } diff --git a/app/components/Date/Date.css b/app/components/Date/Date.css index 174f7cc..e0952be 100644 --- a/app/components/Date/Date.css +++ b/app/components/Date/Date.css @@ -1,3 +1,7 @@ .red { color: red; +} + +.pt { + padding-top:6px; } \ No newline at end of file diff --git a/app/components/Date/Date.js b/app/components/Date/Date.js index 226ca93..9ed5fe7 100644 --- a/app/components/Date/Date.js +++ b/app/components/Date/Date.js @@ -3,30 +3,24 @@ import { bindActionCreators } from "redux"; import { connect } from "react-redux"; import * as DateActions from "../../actions/date"; import styles from "./Date.css"; +import { dateToReadble } from "../../utils/readableDate"; class Date extends Component { props: Props; render() { return ( - +
-

calendar

- {/* */} - {/* */} -
-
-
-
-
+
-
-
{this.props.date.month} / {this.props.date.year}
+
+
{dateToReadble(this.props.date)}
-
+
diff --git a/app/components/Entry/Entry.css b/app/components/Entry/Entry.css index 3752f57..83a8014 100644 --- a/app/components/Entry/Entry.css +++ b/app/components/Entry/Entry.css @@ -4,4 +4,8 @@ .less { margin-top:100px; +} + +.smaller { + margin-top:25px; } \ No newline at end of file diff --git a/app/components/Entry/Entry.js b/app/components/Entry/Entry.js index 2528744..6d3d23c 100644 --- a/app/components/Entry/Entry.js +++ b/app/components/Entry/Entry.js @@ -159,6 +159,23 @@ class Entry extends Component{
+
+
+
+ +
+
+
+ If this is your first time using MyBudget, you can choose to encrypt your data with a passphrase. If you do so, you must enter in your passphrase every time you use this app. You cannot change your passphrase once it's been set! +
+
+ If you don't choose a passphrase, your data will be saved unencrypted on your computer. +
+
+
+
+
+
this.fixBug()}>
diff --git a/app/components/Home/Home.js b/app/components/Home/Home.js index 22cf8db..bd0333c 100644 --- a/app/components/Home/Home.js +++ b/app/components/Home/Home.js @@ -18,23 +18,39 @@ export default class Home extends Component { return (
-
- +
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+
-
- +
+
+
+ +
+
+
+
+ +
+
-
- -
-
-
-
- -
-
- -
{/* to Counter */}
diff --git a/app/components/Income/Income.js b/app/components/Income/Income.js index 1bd212b..d703f68 100644 --- a/app/components/Income/Income.js +++ b/app/components/Income/Income.js @@ -80,7 +80,7 @@ class Income extends Component{ let spent = this.getPercentSpent(data); return ( - +

income

@@ -108,7 +108,7 @@ class Income extends Component{
- +
); } else { return ( diff --git a/app/components/Item/Item.js b/app/components/Item/Item.js index f735785..3e8aeb2 100644 --- a/app/components/Item/Item.js +++ b/app/components/Item/Item.js @@ -71,6 +71,9 @@ class Item extends Component { let code = event.keyCode || event.which; if (code === 13){ this.renameItem(); + } else if (code === 27){ + event.target.blur(); + this.toggleRenameActive(); } } diff --git a/app/components/Save/Save.js b/app/components/Save/Save.js index 50bc9f9..4d895d5 100644 --- a/app/components/Save/Save.js +++ b/app/components/Save/Save.js @@ -21,6 +21,10 @@ class Save extends Component{ this.props.save(); } + export(event){ + dialog.showOpenDialog({ properties: ['openFile', 'openDirectory', 'multiSelections'] }); + } + deleteAll(event){ dialog.showMessageBox({ @@ -41,10 +45,10 @@ class Save extends Component{ return (
-
-

data

- this.multi()}> - this.deleteAll()}> +
+ + {/* */} +
diff --git a/app/components/Transaction/Transaction.css b/app/components/Transaction/Transaction.css index e69de29..2faeb66 100644 --- a/app/components/Transaction/Transaction.css +++ b/app/components/Transaction/Transaction.css @@ -0,0 +1,20 @@ +.transactionrow { + min-height: 30px; + border: 0.05rem solid #b5bbc1; + border-top: none; +} + +.note { + overflow-x: auto; +} + +.icon { + cursor: pointer; + padding-top:3px; +} +.icon:hover{ + background-color:#ABAAEC; +} +.icon-fix { + padding-top:6px; +} \ No newline at end of file diff --git a/app/components/Transaction/Transaction.js b/app/components/Transaction/Transaction.js index 591e7db..d9eec54 100644 --- a/app/components/Transaction/Transaction.js +++ b/app/components/Transaction/Transaction.js @@ -3,6 +3,7 @@ import { bindActionCreators } from "redux"; import { connect } from "react-redux"; import * as TransactionActions from "../../actions/transaction"; import styles from "./Transaction.css"; +import { dateToShort } from "../../utils/readableDate"; class Transaction extends Component { props: Props; @@ -11,21 +12,55 @@ class Transaction extends Component { super(); } + renderControls(){ + return ( +
this.props.delete(this.props.categoryId, this.props.itemId, this.props.id, this.props.amount)}> + +
+ ); + } + render(){ return ( -
-
-
${this.props.amount} {this.props.note !== "" ? - - - {this.props.note} - : } + {/*
+
+
+
+ {this.props.name} +
+ {this.renderControls()} +
+ {this.props.items.sort(function(a, b){ + var a1 = a.name.toLowerCase(); + var b1 = b.name.toLowerCase(); + if (a1 > b1) return 1; + if (a1 < b1) return -1; + return 0; + }).map((value, index, array) => { + return
+ +
; + })} +
+ {this.renderNewSubcategory()}
-
-
this.props.delete(this.props.categoryId, this.props.itemId, this.props.id, this.props.amount)}> - -
+
*/} + +
+
+ {/*HACK TABLE*/} +
+
+ {`${this.props.dateId.split('-')[0]}/${this.props.day}`} +
+
+ ${this.props.amount} +
+
{this.props.note}
+ {this.renderControls()} +
diff --git a/app/components/TransactionCollection/TransactionCollection.js b/app/components/TransactionCollection/TransactionCollection.js index e0fbaec..4c6ba6c 100644 --- a/app/components/TransactionCollection/TransactionCollection.js +++ b/app/components/TransactionCollection/TransactionCollection.js @@ -159,9 +159,9 @@ class TransactionCollection extends Component { render() { return ( - +
-
+

transactions

@@ -217,11 +217,16 @@ class TransactionCollection extends Component {
- {this.props.transactions.map((value, index, array) => + {this.props.transactions.sort(function(a, b){ + + if (a.day > b.day) return 1; + if (b.day > a.day) return -1; + return 0; + }).map((value, index, array) => )}
- +
); } } diff --git a/app/utils/readableDate.js b/app/utils/readableDate.js new file mode 100644 index 0000000..6d68cac --- /dev/null +++ b/app/utils/readableDate.js @@ -0,0 +1,74 @@ +var split = function(dateId){ + var split = ""; + + // if passing date object in + if (typeof dateId["id"] !== "undefined" && dateId["id"].length > 0){ + split = dateId.id.split("-"); + } else if (dateId.length > 0){ + // if passing in dateId string + split = dateId.split("-"); + } + + return { + month: split[0], + year: split[1] + } +} + +export function dateToShort(dateId){ + var data = split(dateId); + var month = data.month; + var year = data.year; + + return `${month}/${year.substring(0,1)}`; +} + +export function dateToReadble(dateId){ + var data = split(dateId); + var month = data.month; + var year = data.year; + + var readableMonth = ""; + switch(month){ + case "1": + readableMonth = "january"; + break; + case "2": + readableMonth = "february"; + break; + case "3": + readableMonth = "march"; + break; + case "4": + readableMonth = "april"; + break; + case "5": + readableMonth = "may"; + break; + case "6": + readableMonth = "june"; + break; + case "7": + readableMonth = "july"; + break; + case "8": + readableMonth = "august"; + break; + case "9": + readableMonth = "september"; + break; + case "10": + readableMonth = "october"; + break; + case "11": + readableMonth = "november"; + break; + case "12": + readableMonth = "december"; + break; + default: + break; + } + + return `${readableMonth} ${year}`; +} \ No newline at end of file diff --git a/package.json b/package.json index 4c2dc3f..52116ad 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "my-budget", "productName": "MyBudget", - "version": "1.0.4-beta", + "version": "1.1.0-beta", "description": "Free, open source offline cross-platform budgeting solution built with Electron.", "scripts": { "build": "concurrently \"yarn build-main\" \"yarn build-renderer\"",