+ So often you see in ads that their subscription is just the price of a coffee a week
+ or something similar. So just how many coffees a day are you really spending on all these
+ subscriptions?
+
Cost in Coffee
See the true cost of your subscriptions in terms of cups of coffee a day.
@@ -31,7 +36,7 @@
cups of coffee a day.
That's $0.00 of coffee.
-
+
Your Subscriptions
@@ -47,6 +52,11 @@
+
+
\ No newline at end of file
diff --git a/main.js b/main.js
index 1d218b0..2b4f64f 100644
--- a/main.js
+++ b/main.js
@@ -1,4 +1,13 @@
+/*
+* TODO:
+* - (maybe) include coffee price somewhere
+* - (maybe) coffee price selector
+*/
+
/********* CONSTANTS AND GLOBAL VARS *********/
+
+// List of subscription services I can think of, for the
+// form to auto-fill
const SubList = [
'LinkedIn Premium',
'Netflix',
@@ -6,31 +15,106 @@ const SubList = [
'Crunchyroll',
'Youtube Premium',
'Skillshare',
+ 'Max',
+ 'Spotify',
+ 'Tidal',
+ 'Paramount+',
+ 'Peacock',
+ 'Disney+',
+ 'Amazon Prime',
+ 'Audible',
+ 'Scribd',
+ 'Apple TV+',
+ 'Curiosity Stream',
+ 'Kindle Unlimited'
]
+// list of prices to be auto-suggested in the form
+const PriceList = [
+ '$5',
+ '$10',
+ '$12',
+ '$15',
+ '$20',
+ '$40',
+]
+
+// array of current subscriptions
let subscriptions = []
+// pretty much just for IDing the HTML in the sub list. dunno why its global tbh
let numSubs = 0
+// matches $40.00, 40.00, 40, but not €40, 4.000, 4., 50c, etc.
const pricePattern = /^(\$)?(\d+(\.\d{1,2})?)$/
+// these two are for calculating the cost in coffee
const avgDaysInMonth = 30.475
let costOfCoffee = 2.50
+// total cost of all the user's subscriptions
let costTotal = 0
+// for opening/closing form
let addFormOpen = false
-const PriceList = [
- '$5',
- '$10',
- '$12',
- '$15',
- '$20'
-]
-
/***************** FUNCTION DEFINITIONS **************/
+// rebuilds the sub list after one gets removed
+// if we dont do this, the IDs in the HTML that we use for removing
+// items starts to drift from the actual JS array
+function updateSubList() {
+ numSubs = 0
+ console.log('rebuilding sub list...')
+ for (let sub of subscriptions) {
+ // add subscription to page
+ $('#addSubFormContainer').after(`
+
+
+
${sub.name}
+
${formatPrice(sub.price)}
+
+
`)
+ numSubs++
+ }
+}
+
+// handler for form
+function addSubFormHandler() {
+ if (!validatePrice()) {
+ // disallow submit
+ console.log('nope')
+ return
+ }
+
+ let price = processSubValue()
+
+ addSubscription($('#subName').val(), price)
+
+ subscriptions.push({
+ name: $('#subName').val(),
+ price: price,
+ })
+ localStorage.setItem('subscriptions', JSON.stringify(subscriptions))
+
+ $('#subPrice').val('')
+ $('#subName').val('')
+
+ $('#addSubFormContainer').hide()
+ addFormOpen = !addFormOpen
+}
+
+// updates the coffee and price displays
+function updateDisplay() {
+ // update display
+ let dailyCost = costTotal / avgDaysInMonth
+ $('#cost-display').text(`${formatPrice(dailyCost)}`)
+ $('#coffee-count').text(`${(dailyCost / costOfCoffee).toFixed(2)}`)
+}
+
+// handles logic for adding a new subscription
+// note: not just UI logic, also updating total costs etc.
function addSubscription(name, price) {
+
// make sure no sub text is hidden
$('.no-subs-text').hide()
- // inc. sub count
- numSubs++
+
+ // add subscription to page
$('#addSubFormContainer').after(`
@@ -38,14 +122,36 @@ function addSubscription(name, price) {
${formatPrice(price)}
`)
+
+ // inc. sub count
+ numSubs++
+
costTotal += price
+ updateDisplay()
+}
+// handles logic for removing a subscription
+// note: not just UI logic, also updating total costs etc.
+function removeSubscription(index) {
+ // get price from sub array
+ let price = subscriptions[index].price
+ // remove sub from array
+ subscriptions.splice(index, 1)
+ // if no subs are left, show no-sub text
+ if (subscriptions.length == 0) {
+ $('.no-subs-text').show()
+ }
+ // remove sub from localstorage
+ localStorage.setItem('subscriptions', JSON.stringify(subscriptions))
+ // remove sub from ui
+ $(`#subscription-${index}`).remove()
+ // subtract price from totalcost
+ costTotal -= price
// update display
- let dailyCost = costTotal / avgDaysInMonth
- $('#cost-display').text(`${formatPrice(dailyCost)}`)
- $('#coffee-count').text(`${(dailyCost / costOfCoffee).toFixed(2)}`)
+ updateDisplay()
}
+// takes sub cost from form and returns it as an actual number
function processSubValue() {
let priceString = $('#subPrice').val()
if (priceString[0] == '$') {
@@ -54,10 +160,12 @@ function processSubValue() {
return Number(priceString)
}
+// takes number and returns it as $X.XX string
function formatPrice(num) {
return '$' + (Math.round(num * 100) / 100).toFixed(2)
}
+// checks cost in form against regex, updates UI for error
function validatePrice() {
let priceString = $('#subPrice').val()
if (!priceString.match(pricePattern)) {
@@ -85,8 +193,12 @@ if (localStorage.getItem('subscriptions') != null) {
$('#addSubButton').click(() => {
if (!addFormOpen) {
+ $('#addSubButton').css('background-color', 'var(--brown)')
+ $('#addSubButton').css('color', 'var(--main-bg)')
$('#addSubFormContainer').show()
} else {
+ $('#addSubButton').css('background-color', '')
+ $('#addSubButton').css('color', '')
$('#addSubFormContainer').hide()
}
addFormOpen = !addFormOpen
@@ -95,13 +207,14 @@ $('#addSubButton').click(() => {
$('#subName').autocomplete({
source: SubList,
delay: 0,
- minLength: 0,
+ autoFocus: true,
})
$('#subPrice').autocomplete({
source: PriceList,
delay: 0,
minLength: 0,
+ autoFocus: true,
})
$('#subPrice').on('focus', () => {
@@ -109,25 +222,36 @@ $('#subPrice').on('focus', () => {
})
$('#confirmSubButton').click(() => {
- if (!validatePrice()) {
- // disallow submit
- console.log('nope')
- return
- }
+ addSubFormHandler()
+})
- let price = processSubValue()
-
- addSubscription($('#subName').val(), price)
+$('#add-sub-form').keypress((e) => {
+ if (e.which == 13) {
+ addSubFormHandler()
+ return false
+ }
+})
- subscriptions.push({
- name: $('#subName').val(),
- price: price,
+// these two are UI logic for the remove subscription button
+$('#subscriptions').on('mouseenter', '.sub-item-container', function() {
+ $(this).append(``);
+ $(`#${$(this).attr('id') + '-del'}`).animate({
+ left: "-=40",
+ }, 200, function() {
})
- localStorage.setItem('subscriptions', JSON.stringify(subscriptions))
-
- $('#subPrice').val('')
- $('#subName').val('')
+})
+$('#subscriptions').on('mouseleave', '.sub-item-container', function() {
+ $(`#${$(this).attr('id') + '-del'}`).animate({
+ left: "+=40",
+ }, 200, function() {
+ $(`#${$(this).attr('id') + '-del'}`).remove()
+ })
+})
- $('#addSubFormContainer').hide()
- addFormOpen = !addFormOpen
+$('#subscriptions').on('click', '.del-sub-button', function() {
+ // get index of sub to delete
+ let subIndex = $(this).attr('id').split('-')[1]
+ removeSubscription(subIndex)
+ $('.sub-item-container').remove()
+ updateSubList()
})