diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/README.md b/README.md new file mode 100644 index 0000000..b4d31b4 --- /dev/null +++ b/README.md @@ -0,0 +1,40 @@ +stackerForm +======== + +Progressively enhanced fork of [Thinkful-Ed/stackerAJAX](https://github.com/Thinkful-Ed/stackerAJAX). + +## Preview +Have a look at the synchronous and asynchronous endpoints in action. + +![](http://j4p.us/0E243Q2G0r3z/stackerform.gif) + +## Getting Started + +In order to build our front end assets, you need to have Node.js/npm latest and git 1.7 or later. +(Earlier versions might work OK, but are not tested.) + +For Windows you have to download and install [git](http://git-scm.com/downloads) and [Node.js](http://nodejs.org/download/). + +Mac OS users should install [Homebrew](http://mxcl.github.com/homebrew/). Once Homebrew is installed, run `brew install git` to install git, +and `brew install node` to install Node.js. + +Linux/BSD users should use their appropriate package managers to install git and Node.js, or build from source +if you swing that way. Easy-peasy. + +## Getting Started +First, clone a copy of this git repo by running: + +```bash +git clone -b grunt git@github.com:jpdevries/stackerForm.git +``` + +Then cd into the `stackerForm` folder and install the Node dependencies: +```bash +cd stackerForm +npm install +``` + +You should now be able to run the Node server! +```bash +npm run start +``` diff --git a/css/main.css b/css/main.css index 0fedd8d..686a6b8 100644 --- a/css/main.css +++ b/css/main.css @@ -1,26 +1,68 @@ +* { + box-sizing: border-box; +} + +input[type="text"] { + width: 100%; +} + +@media (min-width: 641px) { + input[type="text"] { + width: auto; + } +} + +img { + max-width: 100%; +} + +input[type="submit"] { + display: block; + margin-top: 1em; +} + +@media (min-width: 641px) { + input[type="submit"] { + display: inline-block; + } +} + .container { - margin: 3em auto; - width: 600px; + margin: 1em auto; } -.hidden { +[hidden] { display: none; } -.left { - float: left; +@media (min-width: 641px) { + .container { + margin: 3em auto; + max-width: 600px; + } +} + +.hidden { + display: none; } .stack-image { - background: url('../images/stack-icon.png') no-repeat center center; - width: 200px; - height: 200px; - -webkit-background-size: cover; - -moz-background-size: cover; - -o-background-size: cover; - background-size: cover; - margin-right: 1em; border-radius: 5px; + text-align: center; +} + +@media (min-width: 641px) { + .stack-image { + width: 200px; + height: 200px; + margin-right: 1em; + float: left; + } +} + +.stack-image img { + margin-left: auto; + margin-right: auto; } .intro { @@ -31,7 +73,7 @@ clear: both; } -.inspiration { +.inspiration { margin-left: 3em; } diff --git a/index.html b/index.html deleted file mode 100644 index 17c6029..0000000 --- a/index.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - - Stack Overflow AJAX Demo - - - -
-
-
-
-

StackOverflow Reputation Builder

-

This app lets you search by topic for unanswered questions on Stack Overflow to help you build your reputation. Find unanswered questions for a topic you know about, write quality answers, and watch your reputation go up.

-

Sometimes, you also need some inspiration. This page also lets you search for the top answerers for a tag. If you want to rise to the top ranks for a topic, see how many reputation points you'll need to aim for!

-
-
-
-
-

Get Unanswered Questions

-

Find unanswered questions by tag. For multiple tags, use a semi-colon to separate.

-
- - -
-

View the Top Answerers for a Tag

-
- - -
-
-
-
-
-
-
- - - - - diff --git a/js/app.js b/js/app.js index 18599b4..4da1478 100644 --- a/js/app.js +++ b/js/app.js @@ -1,10 +1,10 @@ // this function takes the question object returned by the StackOverflow request // and returns new result to be appended to DOM var showQuestion = function(question) { - + // clone our result template code var result = $('.templates .question').clone(); - + // Set the question properties in result var questionElem = result.find('.question-text a'); questionElem.attr('href', question.link); @@ -31,6 +31,16 @@ var showQuestion = function(question) { return result; }; +var showAnswer = function(answer) { + var result = $('.templates .answerer').clone(); + + result.find('.answerer-name a').text(answer.user.display_name); + result.find('.answerer-count').text(answer.post_count); + result.find('.answerer-count').text(answer.score); + + return result; +}; + // this function takes the results object from StackOverflow // and returns the number of results and tags to be appended to DOM @@ -49,15 +59,15 @@ var showError = function(error){ // takes a string of semi-colon separated tags to be searched // for on StackOverflow var getUnanswered = function(tags) { - + // the parameters we need to pass in our request to StackOverflow's API - var request = { + var request = { tagged: tags, site: 'stackoverflow', order: 'desc', sort: 'creation' }; - + $.ajax({ url: "http://api.stackexchange.com/2.2/questions/unanswered", data: request, @@ -81,6 +91,40 @@ var getUnanswered = function(tags) { }); }; +// takes a string of semi-colon separated tags to be searched +// for on StackOverflow +var getTopAnswerers = function(tags) { + + // the parameters we need to pass in our request to StackOverflow's API + var request = { + tagged: tags, + site: 'stackoverflow', + order: 'desc', + sort: 'creation' + }; + + $.ajax({ + url: 'http://api.stackexchange.com/2.2/tags/' + tags + '/top-answerers/all_time', + data: request, + dataType: "jsonp",//use jsonp to avoid cross origin issues + type: "GET", + }) + .done(function(result){ //this waits for the ajax to return with a succesful promise object + var searchResults = showSearchResults(request.tagged, result.items.length); + + $('.search-results').html(searchResults); + //$.each is a higher order function. It takes an array and a function as an argument. + //The function is executed once for each item in the array. + $.each(result.items, function(i, item) { + var answer = showAnswer(item); + $('.results').append(answer); + }); + }) + .fail(function(jqXHR, error){ //this waits for the ajax to return with an error promise object + var errorElem = showError(error); + $('.search-results').append(errorElem); + }); +}; $(document).ready( function() { $('.unanswered-getter').submit( function(e){ @@ -88,7 +132,15 @@ $(document).ready( function() { // zero out results if previous search has run $('.results').html(''); // get the value of the tags the user submitted - var tags = $(this).find("input[name='tags']").val(); + var tags = $(this).find("input[name='tagged']").val(); getUnanswered(tags); }); + $('.inspiration-getter').submit( function(e){ + e.preventDefault(); + // zero out results if previous search has run + $('.results').html(''); + // get the value of the tags the user submitted + var tags = $(this).find("input[name='tagged']").val(); + getTopAnswerers(tags); + }); }); diff --git a/package.json b/package.json new file mode 100644 index 0000000..6b83643 --- /dev/null +++ b/package.json @@ -0,0 +1,30 @@ +{ + "name": "stackerajax", + "version": "0.0.0", + "description": "", + "main": "server.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "nodemon --watch ./ --watch ./views -e js,twig server.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/jpdevries/stackerAJAX.git" + }, + "author": "JP de Vries", + "license": "MIT", + "bugs": { + "url": "https://github.com/jpdevries/stackerAJAX/issues" + }, + "homepage": "https://github.com/jpdevries/stackerAJAX#readme", + "dependencies": { + "express": "^4.14.0", + "formidable": "^1.0.17", + "request": "^2.74.0", + "serve-static": "^1.11.1", + "twig": "^0.9.5" + }, + "devDependencies": { + "nodemon": "^1.10.0" + } +} diff --git a/server.js b/server.js new file mode 100644 index 0000000..9f7f635 --- /dev/null +++ b/server.js @@ -0,0 +1,100 @@ +var http = require('http'), +fs = require('fs'), +Twig = require("twig"), +express = require('express'), +formidable = require("formidable"), +app = express(), +request = require("request"); + +app.get('/', function(req, res) { + res.render('index.twig', { + + }); +}); + +app.post('/', function(req, res) { + var form = new formidable.IncomingForm(); + form.parse(req, function (err, fields, files) { + console.log(fields); + switch(fields.action) { + case 'answerers': + getTopAnswerers(fields).then(function(data) { + res.render('index.twig', Object.assign({},data,fields)); + }, function(error) { + res.render('index.twig', { error: error }); + }); + break; + + case 'unanswered': + default: + getUnanswered(fields).then(function(data) { + res.render('index.twig', Object.assign({},data,fields)); + }, function(err) { + res.render('index.twig', { error: error }); + }); + + break; + } + + }); +}); + +function getUnanswered(fields) { + return new Promise(function(resolve, reject) { + + request.get("http://api.stackexchange.com/2.2/questions/unanswered", { + form: Object.assign({},{ + site: 'stackoverflow', + order: 'desc', + sort: 'creation' + },fields), + gzip: true, + json: true + }, function(error, response, body) { + if (!error && response.statusCode == 200) { + resolve(body); + } else { + reject(error); + } + }); + + }); +} + +function getTopAnswerers(fields) { + return new Promise(function(resolve, reject) { + + request.get(`http://api.stackexchange.com/2.2/tags/${fields.tagged}/top-answerers/all_time`, { + form: Object.assign({},{ + site: 'stackoverflow', + order: 'desc', + sort: 'creation' + },fields), + gzip: true, + json: true + }, function(error, response, body) { + if (!error && response.statusCode == 200) { + resolve(body); + } else { + reject(error); + } + }); + + }); +} + + +/* __ +/\ \__ /\ \__ __ +____\ \ ,_\ __ \ \ ,_\/\_\ ___ ____ __ _ __ __ __ __ _ __ +/',__\\ \ \/ /'__`\ \ \ \/\/\ \ /'___\ /',__\ /'__`\/\`'__\/\ \/\ \ /'__`\/\`'__\ +/\__, `\\ \ \_/\ \L\.\_\ \ \_\ \ \/\ \__/ /\__, `\/\ __/\ \ \/ \ \ \_/ |/\ __/\ \ \/ +\/\____/ \ \__\ \__/.\_\\ \__\\ \_\ \____\ \/\____/\ \____\\ \_\ \ \___/ \ \____\\ \_\ +\/___/ \/__/\/__/\/_/ \/__/ \/_/\/____/ \/___/ \/____/ \/_/ \/__/ \/____/ \/*/ + +app.use(express.static(__dirname)); + +app.listen(process.env.PORT || 1188); + +console.log("server listening on " + (process.env.PORT || 1188)); +console.log("Visit http://localhost:" + (process.env.PORT || 1188) + " in your browser"); diff --git a/views/answerer.twig b/views/answerer.twig new file mode 100644 index 0000000..05b7d69 --- /dev/null +++ b/views/answerer.twig @@ -0,0 +1,8 @@ +
+
Answerer
+
{{ user.display_name }}
+
Post Count
+
{{ post_count }}
+
Score
+
{{ score }}
+
diff --git a/views/error.twig b/views/error.twig new file mode 100644 index 0000000..da1d98e --- /dev/null +++ b/views/error.twig @@ -0,0 +1,3 @@ +
+

Uh-oh! Something went wrong with your request. Here's what we know:

+
diff --git a/views/index.twig b/views/index.twig new file mode 100644 index 0000000..e2afa4e --- /dev/null +++ b/views/index.twig @@ -0,0 +1,70 @@ + + + + + Stack Overflow AJAX Demo + + + +
+
+
+ Stack Overflow Logo +
+
+

StackOverflow Reputation Builder

+

This app lets you search by topic for unanswered questions on Stack Overflow to help you build your reputation. Find unanswered questions for a topic you know about, write quality answers, and watch your reputation go up.

+

Sometimes, you also need some inspiration. This page also lets you search for the top answerers for a tag. If you want to rise to the top ranks for a topic, see how many reputation points you'll need to aim for!

+
+
+
+
+
+

Get Unanswered Questions

+

Find unanswered questions by tag. For multiple tags, use a semi-colon to separate.

+
+ + + + +
+
+
+

View the Top Answerers for a Tag

+
+ + + + +
+
+
+
+ {% if error %} + {% include 'error.twig' %} + {% endif %} +
{% if action %}{{ items|length }} results for {{ tagged }}{% endif %}
+
+

{% if action == 'unanswered' %}Unanswered Questions{% elseif action == 'answerers'%}Top Answerers{% endif %}

+ {% for item in items %} + {% if action == 'unanswered' %} + {% include 'question.twig' with item %} + {% elseif action == 'answerers' %} + {% include 'answerer.twig' with item %} + {% endif %} + {% endfor %} +
+
+
+ + + + + + + + diff --git a/views/question.twig b/views/question.twig new file mode 100644 index 0000000..c336fd5 --- /dev/null +++ b/views/question.twig @@ -0,0 +1,12 @@ +
+
Question
+
{{ title }}
+
Asked
+
{{ creation_date }}
+
Viewed
+
{{ view_count }}
+
Asker
+
+

Name: {{ owner.display_name }}

+
+