For a quick summary: I've created this internal web application, and I've hit a point where I can really see the mess I've made. I need some help separating the logic, the view, and the data.
More detail: Over the past few months, I've been doing all I can to learn more about JavaScript built web sites/applications. I've created the below code, and it seems as though any additions are just ruining the entire thing. This is the fourth time I've started from scratch on this, and I can't get a product I really like.
Now, it works as it should, I just don't like the way it's built. I tried using Angular.js, but that was over-kill for this single page app (plus working with the routing was nightmarish). Now I've just created this mess of a main.js file, and it needs refactoring.
It'd be great if we could avoid suggesting tools requiring node.js.
index.html
<!DOCTYPE html>
<html>
<head>
<!-- Basic Page Needs
–––––––––––––––––––––––––––––––––––––––––––––––––– -->
<meta charset="utf-8">
<title>Pies</title>
<meta name="description" content="">
<meta name="author" content="">
<!-- Mobile Specific Metas
–––––––––––––––––––––––––––––––––––––––––––––––––– -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- FONT
–––––––––––––––––––––––––––––––––––––––––––––––––– -->
<link href="//fonts.googleapis.com/css?family=Raleway:400,300,600" rel="stylesheet" type="text/css">
<!-- SCRIPTS
–––––––––––––––––––––––––––––––––––––––––––––––––– -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.js"></script>
<script src="scripts/main.js"></script>
<!-- CSS
–––––––––––––––––––––––––––––––––––––––––––––––––– -->
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/skeleton.css">
<link rel="stylesheet" href="css/main.css">
</head>
<body>
<div class="container">
<header class="row">
<div class="twelve columns">
<h1>Pies</h1>
</div>
</header>
<div class="row">
<div class="six columns" id="newOrderContainer">
<h2>New Order?</h2>
<span class="response success">Successfully created!</span>
<span class="response fail">Something went wrong, try again later.</span>
<form id="newOrderForm">
<label for="customerName">Customer name</label>
<input class="u-full-width" type="text" id="customerName">
<label for="dueDate">Due date (optional)</label>
<input type="date" id="dueDate"/>
<div class="flavorSelector u-full-width"></div>
</form>
</div>
<div class="six columns">
<h2>Payments</h2>
</div>
</div>
<div class="row">
<div class="twelve columns" id="ordersContainer">
<h2>Orders</h2>
<span class="response success">Successfully paid!</span>
<span class="response fail">Something went wrong, try again later.</span>
<div id="ordersTableContainer">
<table class="u-full-width">
<thead>
<tr>
<th>Name</th>
<th>Priority</th>
<th>Flavor</th>
<th>Payment</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
<div class="row">
<div class="u-full-width">
<h2>Settings</h2>
</div>
</div>
<div class="row">
<div class="four columns" id="newFlavorContainer">
<h3>New Flavor</h3>
<span class="response success">Successfully added!</span>
<span class="response fail">Something went wrong, try again later.</span>
<form id="newFlavorForm">
<label for="newFlavor">Flavor</label>
<input class="u-full-width" type="text" placeholder="Apple, pecan, etc." id="newFlavor">
<div class="button" style="display: block;">
Add New Flavor
</div>
</form>
</div>
<div class="eight columns" id="flavorEditContainer">
<h3>Existing Flavors</h3>
<div id="flavorsTableContainer">
<table class="u-full-width">
<thead>
<tr>
<th>Flavor</th>
<th>Delete</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
</body>
</html>
There's our main page. I've used the Skeleton CSS boilerplate.
main.js
$(document).ready(function() {
$.get("scripts/appdata.json", function(data) {
$(".flavorSelector").each(function() {
for (flavor in data.flavors) {
$(this).append('<div class="button" style="width: 60%">' + flavor + '</div>');
}
});
for (order in data.orders) {
var details = data.orders[order];
if (details.paid === false) {
$("#ordersTableContainer tbody").append(newOrderRow(order, details));
}
}
for (flavor in data.flavors) {
$("#flavorsTableContainer tbody").append(newFlavorRow(flavor));
}
$("#newOrderForm div.button").click(function() {
var pdata = {
"action" : "newOrder",
"name" : $("#customerName").val(),
"due" : $("#dueDate").val(),
"flavor" : $(this).text()
};
$.post("scripts/server.php", pdata, function(data) {
if (data != true) {
$("#newOrderContainer > span.fail").show().delay(5000).fadeOut(600, function() {
$(this).hide();
});
} else {
$("#customerName").val("");
$("#dueDate").val("");
fetchOrders();
$("#newOrderContainer > span.success").show().delay(5000).fadeOut(600, function() {
$(this).hide();
});
}
});
});
$(".payment-form div.button").click(function() {
var pdata = {
"action" : "payOrder",
"hash" : $(this).parents("tr").data("hash"),
"paid" : $(this).siblings("input").val()
};
$.post("scripts/server.php", pdata, function(data) {
if (data != true) {
$("#ordersContainer > span.fail").show().delay(5000).fadeOut(600, function() {
$(this).hide();
});
} else {
$("tr[data-hash=" + pdata["hash"] + "]").fadeOut(600, function() {
$(this).remove();
}).delay(1000);
$("#ordersContainer > span.success").show().delay(5000).fadeOut(600, function() {
$(this).hide();
});
}
});
});
}, 'json');
$("#newFlavorForm div.button").click(function() {
var pdata = {
"action" : "newFlavor",
"flavor" : $("#newFlavorForm input[type=text]").val()
};
$.post("scripts/server.php", pdata, function(data) {
if (data != true) {
$("#newFlavorContainer > span.fail").show().delay(5000).fadeOut(600, function() {
$(this).hide();
});
} else {
$("#newFlavorForm input[type=text]").val("");
$("#newFlavorContainer > span.success").show().delay(5000).fadeOut(600, function() {
$(this).hide();
});
}
});
});
});
function newOrderRow(hash, data) {
var name = data.name, flavor = data.flavor, paymentForm = "<form class='payment-form'><input type='text'/><div class='button paid'>Paid</div></form>";
var priority = "<div class='priority' style='background: " + getPriority(Math.floor(Date.now() / 1000), data.made, data.due) + "'></div>";
return "<tr data-hash='" + hash + "'><td>" + name + "</td><td>" + priority + "</td><td>" + flavor + "</td><td>" + paymentForm + "</td></tr>";
}
function newFlavorRow(flavor) {
return "<tr><td><input type='text' value='" + flavor + "'/></td><td><span>Delete</span></td></tr>";
}
function getPriority(now, made, due) {
var colors = ["#A30E0E", "#FF9401", "#6FBF0D"];
var marks = [172800, 64800, 0];
var elapsed = now - made;
if (due == "") {
for (var i = 0; i < marks.length; i++) {
if (elapsed > marks[i]) {
return colors[i];
}
}
}
var until = due - now;
var i = 0;
colors.reverse();
for (var i = 0; i < marks.length; i++) {
console.log(until, ">", marks[i], i);
if (until > marks[i]) {
return colors[i];
}
}
}
I know, it's bad. Everything is mixed together, and I don't know what to do! Suggestions for architectures would be great, but if you could help me fit this into some framework, then would be great too.
Right now, I've got the data stored in a JSON file. I'd prefer not to have it in an RDBMS like MySQL, but I've never worked with anything else so I'm open to suggestions!
The data I have is looking like this:
{
"flavors": {
"Berry": "",
"Apple": "",
"Pecan": ""
},
"orders": {
"43d133ecaed389cf527c93117fc29969": {
"name": "Customer1",
"flavor": "Berry",
"made": 1421471493,
"due": 1421884800,
"paid": false
},
"4bb7e6668a2a63d32a7487267128d406": {
"name": "Customer2",
"flavor": "Pecan",
"made": 1421471572,
"due": 1421884800,
"paid": false
}
}
}
I had some data being paired with flavors, but I got rid of that feature.
Is there any chance I can turn this project into something scalable, fast, and modern?