diff --git a/app.rb b/app.rb index 2bcfdd2..cf3545a 100644 --- a/app.rb +++ b/app.rb @@ -98,8 +98,17 @@ get '/services/:slug' do end get '/services/:slug/payments' do + @FILTER_VALUES = %w[ 500 1000 2500 5000 10000 ] @service = Service.first(:slug => params[:slug]) - @payments = Payment.all(:service_id => @service.id, :amount.gte => PAYMENTS_FILTER_MIN, :order => [ 'd' ]) + # payments_filter_min cookie persists user selection of filter value + unless @min = request.cookies["payments_filter_min"] + @min = PAYMENTS_FILTER_MIN + response.set_cookie( + "payments_filter_min", + { :value => @min, :expires => Time.now + (60 * 24 * 60 * 60) } + ) # 60 days + end + @payments = Payment.all(:service_id => @service.id, :amount.gte => @min, :order => [ 'd' ]) @total = @payments.sum(:amount) haml :servicepayments end diff --git a/public/js/jquery.cookie.js b/public/js/jquery.cookie.js new file mode 100644 index 0000000..55ec3f7 --- /dev/null +++ b/public/js/jquery.cookie.js @@ -0,0 +1,191 @@ +/** + * Cookie plugin + * + * Copyright (c) 2006 Klaus Hartl (stilbuero.de) + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + */ + +/** + * Create a cookie with the given name and value and other optional parameters. + * + * @example $.cookie('the_cookie', 'the_value'); + * @desc Set the value of a cookie. + * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true }); + * @desc Create a cookie with all available options. + * @example $.cookie('the_cookie', 'the_value'); + * @desc Create a session cookie. + * @example $.cookie('the_cookie', null); + * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain + * used when the cookie was set. + * + * @param String name The name of the cookie. + * @param String value The value of the cookie. + * @param Object options An object literal containing key/value pairs to provide optional cookie attributes. + * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object. + * If a negative value is specified (e.g. a date in the past), the cookie will be deleted. + * If set to null or omitted, the cookie will be a session cookie and will not be retained + * when the the browser exits. + * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie). + * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie). + * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will + * require a secure protocol (like HTTPS). + * @type undefined + * + * @name $.cookie + * @cat Plugins/Cookie + * @author Klaus Hartl/klaus.hartl@stilbuero.de + */ + +/** + * Get the value of a cookie with the given name. + * + * @example $.cookie('the_cookie'); + * @desc Get the value of a cookie. + * + * @param String name The name of the cookie. + * @return The value of the cookie. + * @type String + * + * @name $.cookie + * @cat Plugins/Cookie + * @author Klaus Hartl/klaus.hartl@stilbuero.de + */ +jQuery.cookie = function(name, value, options) { + if (typeof value != 'undefined') { // name and value given, set cookie + options = options || {}; + if (value === null) { + value = ''; + options.expires = -1; + } + var expires = ''; + if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) { + var date; + if (typeof options.expires == 'number') { + date = new Date(); + date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000)); + } else { + date = options.expires; + } + expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE + } + // CAUTION: Needed to parenthesize options.path and options.domain + // in the following expressions, otherwise they evaluate to undefined + // in the packed version for some reason... + var path = options.path ? '; path=' + (options.path) : ''; + var domain = options.domain ? '; domain=' + (options.domain) : ''; + var secure = options.secure ? '; secure' : ''; + document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join(''); + } else { // only name given, get cookie + var cookieValue = null; + if (document.cookie && document.cookie != '') { + var cookies = document.cookie.split(';'); + for (var i = 0; i < cookies.length; i++) { + var cookie = jQuery.trim(cookies[i]); + // Does this cookie string begin with the name we want? + if (cookie.substring(0, name.length + 1) == (name + '=')) { + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); + break; + } + } + } + return cookieValue; + } +};/** + * Cookie plugin + * + * Copyright (c) 2006 Klaus Hartl (stilbuero.de) + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + */ + +/** + * Create a cookie with the given name and value and other optional parameters. + * + * @example $.cookie('the_cookie', 'the_value'); + * @desc Set the value of a cookie. + * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true }); + * @desc Create a cookie with all available options. + * @example $.cookie('the_cookie', 'the_value'); + * @desc Create a session cookie. + * @example $.cookie('the_cookie', null); + * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain + * used when the cookie was set. + * + * @param String name The name of the cookie. + * @param String value The value of the cookie. + * @param Object options An object literal containing key/value pairs to provide optional cookie attributes. + * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object. + * If a negative value is specified (e.g. a date in the past), the cookie will be deleted. + * If set to null or omitted, the cookie will be a session cookie and will not be retained + * when the the browser exits. + * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie). + * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie). + * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will + * require a secure protocol (like HTTPS). + * @type undefined + * + * @name $.cookie + * @cat Plugins/Cookie + * @author Klaus Hartl/klaus.hartl@stilbuero.de + */ + +/** + * Get the value of a cookie with the given name. + * + * @example $.cookie('the_cookie'); + * @desc Get the value of a cookie. + * + * @param String name The name of the cookie. + * @return The value of the cookie. + * @type String + * + * @name $.cookie + * @cat Plugins/Cookie + * @author Klaus Hartl/klaus.hartl@stilbuero.de + */ +jQuery.cookie = function(name, value, options) { + if (typeof value != 'undefined') { // name and value given, set cookie + options = options || {}; + if (value === null) { + value = ''; + options.expires = -1; + } + var expires = ''; + if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) { + var date; + if (typeof options.expires == 'number') { + date = new Date(); + date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000)); + } else { + date = options.expires; + } + expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE + } + // CAUTION: Needed to parenthesize options.path and options.domain + // in the following expressions, otherwise they evaluate to undefined + // in the packed version for some reason... + var path = options.path ? '; path=' + (options.path) : ''; + var domain = options.domain ? '; domain=' + (options.domain) : ''; + var secure = options.secure ? '; secure' : ''; + document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join(''); + } else { // only name given, get cookie + var cookieValue = null; + if (document.cookie && document.cookie != '') { + var cookies = document.cookie.split(';'); + for (var i = 0; i < cookies.length; i++) { + var cookie = jQuery.trim(cookies[i]); + // Does this cookie string begin with the name we want? + if (cookie.substring(0, name.length + 1) == (name + '=')) { + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); + break; + } + } + } + return cookieValue; + } +}; diff --git a/public/style.css b/public/style.css index 87e1d0f..809d334 100644 --- a/public/style.css +++ b/public/style.css @@ -29,10 +29,15 @@ input { font-size: 100%; background-color: #fff; - text-align: left; + text-align: right; margin: 40px 0 40px 0; } +.top_border +{ + border-top: 1px solid #eee; +} + a { background-color: #dce9b0; @@ -57,11 +62,20 @@ a:hover h1 { - margin-top: 20px; + margin: 0px 0 0 0; + line-height: 1.4em; + font-weight: bold; + color: #86a11d; + font-size: 170%; +} + +h1.logo +{ + margin: 20px 0 0 0; line-height: 1.4em; font-weight: bold; color: #86a11d; - font-size: 180%; + font-size: 230%; } h2 @@ -139,9 +153,9 @@ tr .callout { - background-color: beige; + background-color: #eee; padding: 0px; - font-size: 150%; + font-size: 120%; line-height: 1.5em; } @@ -159,8 +173,6 @@ tr { margin: 0 0px 0 0; padding: 5px 10px; - background-color: #333; - color: #fff; } .filter a:hover @@ -170,6 +182,45 @@ tr a.filter_selected { - background-color: red; + widows: 0; + background-color: black; + color: #fff; + font-size: 100%; +} + +#nav +{ + background-color: #777; + margin: 0 0 20px 0; + padding: 2px 10px; +} + +#nav a, #nav a:visited +{ + background-color: #777; color: #fff; +} + +#nav a:hover +{ + background-color: #fff; + color: #777; +} + +#breadcrumb +{ + display: none; +} + +.hidden +{ + display: none; +} + +.home_subheads +{ + margin: 0; + padding: 0; + background-color: #fff; + vertical-align: bottom; } \ No newline at end of file diff --git a/views/about.haml b/views/about.haml index de227ef..5da22a9 100644 --- a/views/about.haml +++ b/views/about.haml @@ -8,6 +8,8 @@ + %img{ :src => "http://www.gravatar.com/avatar/4a92f0a6447839dc0a9a2b850fe9ed86?s=80&d=http%3A%2F%2Fgithub.com%2Fimages%2Fgravatars%2Fgravatar-80.png", :alt => "Adrian Short", :width => 80, :height => 80 } + %p.vcard This website was designed and written by %a.fn.url{ :href => 'http://adrianshort.co.uk' }< diff --git a/views/directorate.haml b/views/directorate.haml index 925e8ec..6227b71 100644 --- a/views/directorate.haml +++ b/views/directorate.haml @@ -20,12 +20,3 @@ %p %a{ :href => "/services/#{service.slug}" } = service.name - -.grid_6 - - %h3 Suppliers - - - for supplier in @directorate.suppliers - %p - %a{ :href => "/suppliers/#{supplier.slug}" } - = supplier.name diff --git a/views/home.haml b/views/home.haml index 4a8e027..1190258 100644 --- a/views/home.haml +++ b/views/home.haml @@ -7,7 +7,7 @@ .clear .grid_6 - %h2 Directorates + %h2 Council Directorates - for directorate in @directorates %p @@ -15,18 +15,63 @@ = directorate.name .grid_6 - .callout + .callout{ :style => "padding: 5px 30px;" } %p Armchair Auditor lets you see how your council spends your money. %p - We've got - = @payments_count - payments from - %a{ :href => "/services" } - = @services_count - services - at the Royal Borough of Windsor and Maidenhead to - %a{ :href => "/suppliers" } - = @suppliers_count - suppliers. \ No newline at end of file + For the Royal Borough of Windsor and Maidenhead we've got: + %ul + %li + %a{ :href => "/services" } + = commify(@services_count) + services + %li + %a{ :href => "/suppliers" } + = commify(@suppliers_count) + suppliers + %li + = commify(@payments_count) + payments +.clear + +.grid_3.home_subheads + %h3 + Big picture, + %br + small details + +.grid_3.home_subheads + %h3 Talk about it + +.grid_3.home_subheads + %h3 Open data + +.grid_3.home_subheads + %h3 Open source + +.clear + +.grid_3 + %p Sometimes you want a high-level view of how much money is being spent by each council service or paid to each supplier. Other times you want to examine the details right down to individual payments. + + %p Armchair Auditor lets you do both. + +.grid_3 + %p Armchair Auditor doesn't just let you see how your council spends its money — it lets you talk about it too. + + %p There's a comments thread for every council service, supplier and even each individual payment so you can add more information and your opinions. + +.grid_3 + %p Open data is free for you to use in any way you choose. Armchair Auditor lets you download your council's spending data into an Excel spreadsheet and any other program that can read CSV files. + + %p Every page prints beautifully, too. + +.grid_3 + %p If you want to use Armchair Auditor's software to publish your own council's data, it's free. Or you can use it as the starting point for your own custom project. + %p + %a{ :href => "http://github.com/adrianshort/Armchair-Auditor" } + Get the code on Github. + + + diff --git a/views/layout.haml b/views/layout.haml index 5866fc8..e86fdf0 100644 --- a/views/layout.haml +++ b/views/layout.haml @@ -4,10 +4,12 @@ %head %title= @page_title ? @page_title + " - Armchair Auditor" : "Armchair Auditor" %link{ :rel => 'stylesheet', :type => 'text/css', :href => '/style.css' } - %link{ :rel => 'stylesheet', :type => 'text/css', :href => '/breadcrumb/breadcrumb.css' } + -# + %link{ :rel => 'stylesheet', :type => 'text/css', :href => '/breadcrumb/breadcrumb.css' } %link{ :rel => 'stylesheet', :type => 'text/css', :href => '/print.css', :media => 'print' } %link{ :rel => 'stylesheet', :type => 'text/css', :href => '/grid.css' } %script{:type => 'text/javascript', :src => 'http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js'} + %script{:type => 'text/javascript', :src => '/js/jquery.cookie.js'} - -
- +-# +