Version 1 done

main
Gabe Farrell 2 years ago
parent 8d130b1e7b
commit 582a50ab19

@ -8,6 +8,7 @@
--brown-light: #52391e;
--blue: #05213f;
--red: #EF3E36;
--red-dark: #d73831;
--green: #08670a;
--green-light: #217623;
--purple: #750973;
@ -93,7 +94,7 @@ button:hover {
color: var(--green);
}
.subscriptions {
#subscriptions {
width: 400px;
}
@ -109,6 +110,7 @@ button:hover {
}
.no-subs-text {
color: var(--text-light);
font-style: italic;
font-size: 16px;
text-align: center;
@ -117,6 +119,10 @@ button:hover {
.sub-item-container:nth-child(odd) {
background-color: white;
}
.sub-item-container {
overflow-x:hidden;
position: relative;
}
.sub-item {
width: 350px;
@ -126,6 +132,21 @@ button:hover {
margin: auto;
}
.del-sub-button {
background-color: var(--red);
color: var(--main-bg);
border: none;
height: 100%;
width: 40px;
border-radius: 0px;
position: absolute;
right: -40px;
top: 0px;
}
.del-sub-button:hover {
background-color: var(--red-dark);
}
#add-sub-form * {
margin: 5px 0px;
}
@ -158,6 +179,9 @@ input:focus {
#subPrice {
width: 55px;
}
#subName {
width: 200px;
}
.ui-autocomplete {
background-color: white;
@ -169,7 +193,7 @@ input:focus {
padding: 0px;
}
.ui-autocomplete li {
padding: 10px 25px;
padding: 10px 0px 8px 25px;
}
.ui-autocomplete li:hover {
cursor: pointer;
@ -180,3 +204,18 @@ input:focus {
.error {
border: 2px solid var(--red);
}
.footer {
position: fixed;
bottom: 0px;
text-align: center;
font-size: 14px;
color: var(--text-light);
margin-bottom: 25px;
left: 50%;
transform: translate(-50%, 0);
}
.footer a {
font-style: italic;
color: inherit;
}

@ -2,7 +2,7 @@
<html lang="en">
<head>
<style>
@import url('https://fonts.googleapis.com/css2?family=Josefin+Sans:ital,wght@0,300;0,400;1,300&family=Orelega+One&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Josefin+Sans:ital,wght@0,300;0,400;1,300;1,400&family=Orelega+One&display=swap');
</style>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@ -21,6 +21,11 @@
</head>
<body>
<div class="container">
<div class="preface">
So often you see in ads that their subscription is just <i>the price of a coffee a week</i>
or something similar. So just how many coffees a day are you really spending on all these
subscriptions?
</div>
<div class="header">
<h1>Cost in Coffee</h1>
<h2>See the true cost of your subscriptions in terms of cups of coffee a day.</h2>
@ -31,7 +36,7 @@
<p>cups of coffee a day.</p>
<p>That's <span id="cost-display">$0.00</span> of coffee.</p>
</div>
<div class="subscriptions">
<div id="subscriptions">
<div id="subscription-header">
<p>Your Subscriptions</p>
<button type="button" id="addSubButton">Add</button>
@ -47,6 +52,11 @@
</div>
</div>
<div class="footer">
<p>&copy;2023 Gabe Farrell</p>
<a href="https://github.com/gabehf/CostInCoffee">View the source on GitHub</a>
</div>
<script src="main.js"></script>
</body>
</html>

@ -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(`
<div class="sub-item-container" id="subscription-${numSubs}">
<div class="sub-item">
<p>${sub.name}</p>
<p>${formatPrice(sub.price)}</p>
</div>
</div>`)
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(`
<div class="sub-item-container" id="subscription-${numSubs}">
<div class="sub-item">
@ -38,14 +122,36 @@ function addSubscription(name, price) {
<p>${formatPrice(price)}</p>
</div>
</div>`)
// 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
}
let price = processSubValue()
addSubscription($('#subName').val(), price)
addSubFormHandler()
})
subscriptions.push({
name: $('#subName').val(),
price: price,
$('#add-sub-form').keypress((e) => {
if (e.which == 13) {
addSubFormHandler()
return false
}
})
localStorage.setItem('subscriptions', JSON.stringify(subscriptions))
$('#subPrice').val('')
$('#subName').val('')
// these two are UI logic for the remove subscription button
$('#subscriptions').on('mouseenter', '.sub-item-container', function() {
$(this).append(`<button id="${$(this).attr('id') + '-del'}" class="del-sub-button">X</button>`);
$(`#${$(this).attr('id') + '-del'}`).animate({
left: "-=40",
}, 200, function() {
})
})
$('#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()
})

Loading…
Cancel
Save