liquid_feedback_frontend
changeset 718:cc64a4fc6ab6
Cleaned up initiative page
author | bsw |
---|---|
date | Wed Jun 27 14:37:46 2012 +0200 (2012-06-27) |
parents | 46b94112781d |
children | 17a33bd0d48a |
files | app/main/draft/_show.lua app/main/initiative/_battles.lua app/main/initiative/_details.lua app/main/initiative/_show.lua app/main/initiative/_show_voting.lua app/main/initiative/_suggestions.lua app/main/initiative/show.lua app/main/issue/_details.lua app/main/member/_list.lua app/main/suggestion/_list.lua app/main/suggestion/_list_element.lua app/main/suggestion/_suggestion.lua app/main/suggestion/show.lua static/style.css |
line diff
1.1 --- a/app/main/draft/_show.lua Wed Jun 27 12:35:34 2012 +0200 1.2 +++ b/app/main/draft/_show.lua Wed Jun 27 14:37:46 2012 +0200 1.3 @@ -1,24 +1,16 @@ 1.4 local draft = param.get("draft", "table") 1.5 local source = param.get("source", atom.boolean) 1.6 1.7 -ui.form{ 1.8 - attr = { class = "vertical" }, 1.9 - record = draft, 1.10 - readonly = true, 1.11 +ui.container{ 1.12 + attr = { class = "draft_content wiki" }, 1.13 content = function() 1.14 - 1.15 - ui.container{ 1.16 - attr = { class = "draft_content wiki" }, 1.17 - content = function() 1.18 - if source then 1.19 - ui.tag{ 1.20 - tag = "pre", 1.21 - content = draft.content 1.22 - } 1.23 - else 1.24 - slot.put(draft:get_content("html")) 1.25 - end 1.26 - end 1.27 - } 1.28 + if source then 1.29 + ui.tag{ 1.30 + tag = "pre", 1.31 + content = draft.content 1.32 + } 1.33 + else 1.34 + slot.put(draft:get_content("html")) 1.35 + end 1.36 end 1.37 }
2.1 --- a/app/main/initiative/_battles.lua Wed Jun 27 12:35:34 2012 +0200 2.2 +++ b/app/main/initiative/_battles.lua Wed Jun 27 14:37:46 2012 +0200 2.3 @@ -19,11 +19,6 @@ 2.4 :count() 2.5 2.6 if number_of_initiatives > 1 then 2.7 - ui.container{ 2.8 - attr = { class = "heading first" }, 2.9 - content = _"This initiative compared to alternative initiatives" 2.10 - } 2.11 - 2.12 ui.list{ 2.13 records = battled_initiatives, 2.14 columns = {
3.1 --- a/app/main/initiative/_details.lua Wed Jun 27 12:35:34 2012 +0200 3.2 +++ b/app/main/initiative/_details.lua Wed Jun 27 14:37:46 2012 +0200 3.3 @@ -1,12 +1,30 @@ 3.4 local initiative = param.get("initiative", "table") 3.5 3.6 -ui.container{ content = _"Initiative details" } 3.7 - 3.8 ui.form{ 3.9 attr = { class = "vertical" }, 3.10 record = initiative, 3.11 readonly = true, 3.12 content = function() 3.13 + if initiative.issue.closed then 3.14 + ui.field.boolean{ label = _"Direct majority", value = initiative.direct_majority } 3.15 + ui.field.boolean{ label = _"Indirect majority", value = initiative.indirect_majority } 3.16 + ui.field.text{ label = _"Schulze rank", value = tostring(initiative.schulze_rank) .. " (" .. _("Status quo: #{rank}", { rank = initiative.issue.status_quo_schulze_rank }) .. ")" } 3.17 + local texts = {} 3.18 + if initiative.reverse_beat_path then 3.19 + texts[#texts+1] = _"reverse beat path to status quo (including ties)" 3.20 + end 3.21 + if initiative.multistage_majority then 3.22 + texts[#texts+1] = _"possibly instable result caused by multistage majority" 3.23 + end 3.24 + if #texts == 0 then 3.25 + texts[#texts+1] = _"none" 3.26 + end 3.27 + ui.field.text{ 3.28 + label = _"Other failures", 3.29 + value = table.concat(texts, ", ") 3.30 + } 3.31 + ui.field.boolean{ label = _"Eligible as winner", value = initiative.eligible } 3.32 + end 3.33 ui.field.text{ 3.34 label = _"Created at", 3.35 value = tostring(initiative.created) 3.36 @@ -17,10 +35,10 @@ 3.37 value = format.timestamp(initiative.revoked) 3.38 } 3.39 end 3.40 - ui.field.boolean{ label = _"Admitted", name = "admitted" } 3.41 + if initiative.admitted ~= nil then 3.42 + ui.field.boolean{ label = _"Admitted", name = "admitted" } 3.43 + end 3.44 end 3.45 } 3.46 3.47 -ui.container{ content = _"Issue details" } 3.48 - 3.49 execute.view{ module = "issue", view = "_details", params = { issue = initiative.issue } } 3.50 \ No newline at end of file
4.1 --- a/app/main/initiative/_show.lua Wed Jun 27 12:35:34 2012 +0200 4.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 4.3 @@ -1,309 +0,0 @@ 4.4 -local initiative = param.get("initiative", "table") 4.5 - 4.6 -local initiators_members_selector = initiative:get_reference_selector("initiating_members") 4.7 - :add_field("initiator.accepted", "accepted") 4.8 - :add_order_by("member.name") 4.9 -if initiator and initiator.accepted then 4.10 - initiators_members_selector:add_where("initiator.accepted ISNULL OR initiator.accepted") 4.11 -else 4.12 - initiators_members_selector:add_where("initiator.accepted") 4.13 -end 4.14 - 4.15 -local initiators = initiators_members_selector:exec() 4.16 - 4.17 - 4.18 -local initiatives_selector = initiative.issue:get_reference_selector("initiatives") 4.19 -slot.select("head", function() 4.20 - execute.view{ 4.21 - module = "issue", 4.22 - view = "_show", 4.23 - params = { 4.24 - issue = initiative.issue, 4.25 - initiative_limit = 3, 4.26 - for_initiative = initiative 4.27 - } 4.28 - } 4.29 -end) 4.30 - 4.31 -ui.container{ attr = { class = "initiative_head" }, content = function() 4.32 - 4.33 - ui.container{ 4.34 - attr = { class = "title" }, 4.35 - content = _("Initiative i#{id}: #{name}", { id = initiative.id, name = initiative.name }) 4.36 - } 4.37 - 4.38 - ui.container{ attr = { class = "content" }, content = function() 4.39 - if app.session.member_id or config.public_access == "pseudonym" or config.public_access == "full" then 4.40 - ui.tag{ 4.41 - attr = { class = "initiator_names" }, 4.42 - content = function() 4.43 - for i, initiator in ipairs(initiators) do 4.44 - slot.put(" ") 4.45 - if app.session.member_id or config.public_access == "full" then 4.46 - ui.link{ 4.47 - content = function () 4.48 - execute.view{ 4.49 - module = "member_image", 4.50 - view = "_show", 4.51 - params = { 4.52 - member = initiator, 4.53 - image_type = "avatar", 4.54 - show_dummy = true, 4.55 - class = "micro_avatar", 4.56 - popup_text = text 4.57 - } 4.58 - } 4.59 - end, 4.60 - module = "member", view = "show", id = initiator.id 4.61 - } 4.62 - slot.put(" ") 4.63 - end 4.64 - ui.link{ 4.65 - text = initiator.name, 4.66 - module = "member", view = "show", id = initiator.id 4.67 - } 4.68 - if not initiator.accepted then 4.69 - ui.tag{ attr = { title = _"Not accepted yet" }, content = "?" } 4.70 - end 4.71 - end 4.72 - end 4.73 - } 4.74 - end 4.75 - 4.76 - if initiator and initiator.accepted and not initiative.issue.fully_frozen and not initiative.issue.closed and not initiative.revoked then 4.77 - slot.put(" · ") 4.78 - ui.link{ 4.79 - attr = { class = "action" }, 4.80 - content = function() 4.81 - slot.put(_"Invite initiator") 4.82 - end, 4.83 - module = "initiative", 4.84 - view = "add_initiator", 4.85 - params = { initiative_id = initiative.id } 4.86 - } 4.87 - if #initiators > 1 then 4.88 - slot.put(" · ") 4.89 - ui.link{ 4.90 - content = function() 4.91 - slot.put(_"Remove initiator") 4.92 - end, 4.93 - module = "initiative", 4.94 - view = "remove_initiator", 4.95 - params = { initiative_id = initiative.id } 4.96 - } 4.97 - end 4.98 - end 4.99 - if initiator and initiator.accepted == false then 4.100 - slot.put(" · ") 4.101 - ui.link{ 4.102 - text = _"Cancel refuse of invitation", 4.103 - module = "initiative", 4.104 - action = "remove_initiator", 4.105 - params = { 4.106 - initiative_id = initiative.id, 4.107 - member_id = app.session.member.id 4.108 - }, 4.109 - routing = { 4.110 - ok = { 4.111 - mode = "redirect", 4.112 - module = "initiative", 4.113 - view = "show", 4.114 - id = initiative.id 4.115 - } 4.116 - } 4.117 - } 4.118 - end 4.119 - end } 4.120 - ui.container{ attr = { class = "content" }, content = function() 4.121 - if app.session.member_id then 4.122 - execute.view{ 4.123 - module = "supporter", 4.124 - view = "_show_box", 4.125 - params = { 4.126 - initiative = initiative 4.127 - } 4.128 - } 4.129 - end 4.130 - 4.131 - end } 4.132 - 4.133 - if initiative.issue.ranks_available and initiative.admitted then 4.134 - local class = initiative.winner and "admitted_info" or "not_admitted_info" 4.135 - ui.container{ 4.136 - attr = { class = class }, 4.137 - content = function() 4.138 - local max_value = initiative.issue.voter_count 4.139 - slot.put(" ") 4.140 - local positive_votes = initiative.positive_votes 4.141 - local negative_votes = initiative.negative_votes 4.142 - local sum_votes = initiative.positive_votes + initiative.negative_votes 4.143 - local function perc(votes, sum) 4.144 - if sum > 0 and votes > 0 then return " (" .. string.format( "%.f", votes * 100 / sum ) .. "%)" end 4.145 - return "" 4.146 - end 4.147 - slot.put(_"Yes" .. ": <b>" .. tostring(positive_votes) .. perc(positive_votes, sum_votes) .. "</b>") 4.148 - slot.put(" · ") 4.149 - slot.put(_"Abstention" .. ": <b>" .. tostring(max_value - initiative.negative_votes - initiative.positive_votes) .. "</b>") 4.150 - slot.put(" · ") 4.151 - slot.put(_"No" .. ": <b>" .. tostring(initiative.negative_votes) .. perc(negative_votes, sum_votes) .. "</b>") 4.152 - slot.put(" · ") 4.153 - slot.put("<b>") 4.154 - if initiative.winner then 4.155 - slot.put(_"Approved") 4.156 - elseif initiative.rank then 4.157 - slot.put(_("Not approved (rank #{rank})", { rank = initiative.rank })) 4.158 - else 4.159 - slot.put(_"Not approved") 4.160 - end 4.161 - slot.put("</b>") 4.162 - end 4.163 - } 4.164 - end 4.165 - 4.166 - if initiative.admitted == false then 4.167 - local policy = initiative.issue.policy 4.168 - ui.container{ 4.169 - attr = { class = "not_admitted_info" }, 4.170 - content = _("This initiative has not been admitted! It failed the quorum of #{quorum}.", { quorum = format.percentage(policy.initiative_quorum_num / policy.initiative_quorum_den) }) 4.171 - } 4.172 - end 4.173 - 4.174 - if initiative.revoked then 4.175 - ui.container{ 4.176 - attr = { class = "revoked_info" }, 4.177 - content = function() 4.178 - slot.put(_("This initiative has been revoked at #{revoked}", { revoked = format.timestamp(initiative.revoked) })) 4.179 - local suggested_initiative = initiative.suggested_initiative 4.180 - if suggested_initiative then 4.181 - slot.put("<br /><br />") 4.182 - slot.put(_("The initiators suggest to support the following initiative:")) 4.183 - slot.put(" ") 4.184 - ui.link{ 4.185 - content = _("Issue ##{id}", { id = suggested_initiative.issue.id } ) .. ": " .. encode.html(suggested_initiative.name), 4.186 - module = "initiative", 4.187 - view = "show", 4.188 - id = suggested_initiative.id 4.189 - } 4.190 - end 4.191 - end 4.192 - } 4.193 - end 4.194 - 4.195 -end } 4.196 - 4.197 -util.help("initiative.show") 4.198 - 4.199 - 4.200 -if initiator and initiator.accepted == nil and not initiative.issue.half_frozen and not initiative.issue.closed then 4.201 - ui.container{ 4.202 - attr = { class = "initiator_invite_info" }, 4.203 - content = function() 4.204 - slot.put(_"You are invited to become initiator of this initiative.") 4.205 - slot.put(" ") 4.206 - ui.link{ 4.207 - image = { static = "icons/16/tick.png" }, 4.208 - text = _"Accept invitation", 4.209 - module = "initiative", 4.210 - action = "accept_invitation", 4.211 - id = initiative.id, 4.212 - routing = { 4.213 - default = { 4.214 - mode = "redirect", 4.215 - module = request.get_module(), 4.216 - view = request.get_view(), 4.217 - id = param.get_id_cgi(), 4.218 - params = param.get_all_cgi() 4.219 - } 4.220 - } 4.221 - } 4.222 - slot.put(" ") 4.223 - ui.link{ 4.224 - image = { static = "icons/16/cross.png" }, 4.225 - text = _"Refuse invitation", 4.226 - module = "initiative", 4.227 - action = "reject_initiator_invitation", 4.228 - params = { 4.229 - initiative_id = initiative.id, 4.230 - member_id = app.session.member.id 4.231 - }, 4.232 - routing = { 4.233 - default = { 4.234 - mode = "redirect", 4.235 - module = request.get_module(), 4.236 - view = request.get_view(), 4.237 - id = param.get_id_cgi(), 4.238 - params = param.get_all_cgi() 4.239 - } 4.240 - } 4.241 - } 4.242 - end 4.243 - } 4.244 - slot.put("<br />") 4.245 -end 4.246 - 4.247 - 4.248 -local supporter 4.249 - 4.250 -if app.session.member_id then 4.251 - supporter = app.session.member:get_reference_selector("supporters") 4.252 - :add_where{ "initiative_id = ?", initiative.id } 4.253 - :optional_object_mode() 4.254 - :exec() 4.255 -end 4.256 - 4.257 -if supporter and not initiative.issue.closed then 4.258 - local old_draft_id = supporter.draft_id 4.259 - local new_draft_id = initiative.current_draft.id 4.260 - if old_draft_id ~= new_draft_id then 4.261 - ui.container{ 4.262 - attr = { class = "draft_updated_info" }, 4.263 - content = function() 4.264 - slot.put(_"The draft of this initiative has been updated!") 4.265 - slot.put(" ") 4.266 - ui.link{ 4.267 - content = _"Show diff", 4.268 - module = "draft", 4.269 - view = "diff", 4.270 - params = { 4.271 - old_draft_id = old_draft_id, 4.272 - new_draft_id = new_draft_id 4.273 - } 4.274 - } 4.275 - if not initiative.revoked then 4.276 - slot.put(" ") 4.277 - ui.link{ 4.278 - text = _"Refresh support to current draft", 4.279 - module = "initiative", 4.280 - action = "add_support", 4.281 - id = initiative.id, 4.282 - routing = { 4.283 - default = { 4.284 - mode = "redirect", 4.285 - module = "initiative", 4.286 - view = "show", 4.287 - id = initiative.id 4.288 - } 4.289 - } 4.290 - } 4.291 - end 4.292 - end 4.293 - } 4.294 - end 4.295 -end 4.296 - 4.297 -execute.view{ 4.298 - module = "initiative", 4.299 - view = "show_tab", 4.300 - params = { 4.301 - initiative = initiative, 4.302 - initiator = initiator 4.303 - } 4.304 -} 4.305 - 4.306 -if initiative.issue.snapshot then 4.307 - slot.put("<br />") 4.308 - ui.field.timestamp{ label = _"Last snapshot:", value = initiative.issue.snapshot } 4.309 -end 4.310 - 4.311 - 4.312 -
5.1 --- a/app/main/initiative/_show_voting.lua Wed Jun 27 12:35:34 2012 +0200 5.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 5.3 @@ -1,70 +0,0 @@ 5.4 -local initiative = param.get("initiative", "table") 5.5 - 5.6 -if initiative.revoked then 5.7 - slot.put(_"Not voted (revoked from initiator)") 5.8 -elseif initiative.admitted == false then 5.9 - slot.put(_"Not voted (not admitted)") 5.10 -else 5.11 - 5.12 - execute.view{ 5.13 - module = "initiative", 5.14 - view = "_battles", 5.15 - params = { initiative = initiative } 5.16 - } 5.17 - 5.18 - slot.put("<br />") 5.19 - 5.20 - ui.container{ 5.21 - attr = { class = "heading" }, 5.22 - content = _"Member voting" 5.23 - } 5.24 - 5.25 - execute.view{ 5.26 - module = "member", 5.27 - view = "_list", 5.28 - params = { 5.29 - initiative = initiative, 5.30 - for_votes = true, 5.31 - members_selector = initiative.issue:get_reference_selector("direct_voters") 5.32 - :left_join("vote", nil, { "vote.initiative_id = ? AND vote.member_id = member.id", initiative.id }) 5.33 - :add_field("direct_voter.weight as voter_weight") 5.34 - :add_field("coalesce(vote.grade, 0) as grade") 5.35 - :left_join("initiative", nil, "initiative.id = vote.initiative_id") 5.36 - :left_join("issue", nil, "issue.id = initiative.issue_id") 5.37 - } 5.38 - } 5.39 - 5.40 - slot.put("<br />") 5.41 - 5.42 - ui.container{ 5.43 - attr = { class = "heading" }, 5.44 - content = _"Voting details" 5.45 - } 5.46 - 5.47 - ui.form{ 5.48 - attr = { class = "vertical" }, 5.49 - content = function() 5.50 - 5.51 - ui.field.boolean{ label = _"Direct majority", value = initiative.direct_majority } 5.52 - ui.field.boolean{ label = _"Indirect majority", value = initiative.indirect_majority } 5.53 - ui.field.text{ label = _"Schulze rank", value = tostring(initiative.schulze_rank) .. " (" .. _("Status quo: #{rank}", { rank = initiative.issue.status_quo_schulze_rank }) .. ")" } 5.54 - local texts = {} 5.55 - if initiative.reverse_beat_path then 5.56 - texts[#texts+1] = _"reverse beat path to status quo (including ties)" 5.57 - end 5.58 - if initiative.multistage_majority then 5.59 - texts[#texts+1] = _"possibly instable result caused by multistage majority" 5.60 - end 5.61 - if #texts == 0 then 5.62 - texts[#texts+1] = _"none" 5.63 - end 5.64 - ui.field.text{ 5.65 - label = _"Other failures", 5.66 - value = table.concat(texts, ", ") 5.67 - } 5.68 - ui.field.boolean{ label = _"Eligible as winner", value = initiative.eligible } 5.69 - end 5.70 -} 5.71 - 5.72 - 5.73 -end
6.1 --- a/app/main/initiative/_suggestions.lua Wed Jun 27 12:35:34 2012 +0200 6.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 6.3 @@ -1,5 +0,0 @@ 6.4 -local initiative = param.get("initiative", "table") 6.5 - 6.6 - 6.7 - 6.8 -
7.1 --- a/app/main/initiative/show.lua Wed Jun 27 12:35:34 2012 +0200 7.2 +++ b/app/main/initiative/show.lua Wed Jun 27 14:37:46 2012 +0200 7.3 @@ -17,11 +17,447 @@ 7.4 } 7.5 end) 7.6 7.7 +local initiators_members_selector = initiative:get_reference_selector("initiating_members") 7.8 + :add_field("initiator.accepted", "accepted") 7.9 + :add_order_by("member.name") 7.10 +if initiator and initiator.accepted then 7.11 + initiators_members_selector:add_where("initiator.accepted ISNULL OR initiator.accepted") 7.12 +else 7.13 + initiators_members_selector:add_where("initiator.accepted") 7.14 +end 7.15 + 7.16 +local initiators = initiators_members_selector:exec() 7.17 + 7.18 + 7.19 +local initiatives_selector = initiative.issue:get_reference_selector("initiatives") 7.20 +slot.select("head", function() 7.21 + execute.view{ 7.22 + module = "issue", 7.23 + view = "_show", 7.24 + params = { 7.25 + issue = initiative.issue, 7.26 + initiative_limit = 3, 7.27 + for_initiative = initiative 7.28 + } 7.29 + } 7.30 +end) 7.31 + 7.32 +util.help("initiative.show") 7.33 + 7.34 +ui.container{ attr = { class = "initiative_head" }, content = function() 7.35 + 7.36 + ui.container{ 7.37 + attr = { class = "title" }, 7.38 + content = _("Initiative i#{id}: #{name}", { id = initiative.id, name = initiative.name }) 7.39 + } 7.40 + 7.41 + ui.container{ attr = { class = "content" }, content = function() 7.42 + if app.session.member_id or config.public_access == "pseudonym" or config.public_access == "full" then 7.43 + ui.tag{ 7.44 + attr = { class = "initiator_names" }, 7.45 + content = function() 7.46 + for i, initiator in ipairs(initiators) do 7.47 + slot.put(" ") 7.48 + if app.session.member_id or config.public_access == "full" then 7.49 + ui.link{ 7.50 + content = function () 7.51 + execute.view{ 7.52 + module = "member_image", 7.53 + view = "_show", 7.54 + params = { 7.55 + member = initiator, 7.56 + image_type = "avatar", 7.57 + show_dummy = true, 7.58 + class = "micro_avatar", 7.59 + popup_text = text 7.60 + } 7.61 + } 7.62 + end, 7.63 + module = "member", view = "show", id = initiator.id 7.64 + } 7.65 + slot.put(" ") 7.66 + end 7.67 + ui.link{ 7.68 + text = initiator.name, 7.69 + module = "member", view = "show", id = initiator.id 7.70 + } 7.71 + if not initiator.accepted then 7.72 + ui.tag{ attr = { title = _"Not accepted yet" }, content = "?" } 7.73 + end 7.74 + end 7.75 + end 7.76 + } 7.77 + end 7.78 + 7.79 + if initiator and initiator.accepted and not initiative.issue.fully_frozen and not initiative.issue.closed and not initiative.revoked then 7.80 + slot.put(" · ") 7.81 + ui.link{ 7.82 + attr = { class = "action" }, 7.83 + content = function() 7.84 + slot.put(_"Invite initiator") 7.85 + end, 7.86 + module = "initiative", 7.87 + view = "add_initiator", 7.88 + params = { initiative_id = initiative.id } 7.89 + } 7.90 + if #initiators > 1 then 7.91 + slot.put(" · ") 7.92 + ui.link{ 7.93 + content = function() 7.94 + slot.put(_"Remove initiator") 7.95 + end, 7.96 + module = "initiative", 7.97 + view = "remove_initiator", 7.98 + params = { initiative_id = initiative.id } 7.99 + } 7.100 + end 7.101 + end 7.102 + if initiator and initiator.accepted == false then 7.103 + slot.put(" · ") 7.104 + ui.link{ 7.105 + text = _"Cancel refuse of invitation", 7.106 + module = "initiative", 7.107 + action = "remove_initiator", 7.108 + params = { 7.109 + initiative_id = initiative.id, 7.110 + member_id = app.session.member.id 7.111 + }, 7.112 + routing = { 7.113 + ok = { 7.114 + mode = "redirect", 7.115 + module = "initiative", 7.116 + view = "show", 7.117 + id = initiative.id 7.118 + } 7.119 + } 7.120 + } 7.121 + end 7.122 + end } 7.123 + ui.container{ attr = { class = "content" }, content = function() 7.124 + if app.session.member_id then 7.125 + execute.view{ 7.126 + module = "supporter", 7.127 + view = "_show_box", 7.128 + params = { 7.129 + initiative = initiative 7.130 + } 7.131 + } 7.132 + end 7.133 + 7.134 + end } 7.135 + 7.136 + 7.137 + -- voting results 7.138 + if initiative.issue.ranks_available and initiative.admitted then 7.139 + local class = initiative.winner and "admitted_info" or "not_admitted_info" 7.140 + ui.container{ 7.141 + attr = { class = class }, 7.142 + content = function() 7.143 + local max_value = initiative.issue.voter_count 7.144 + slot.put(" ") 7.145 + local positive_votes = initiative.positive_votes 7.146 + local negative_votes = initiative.negative_votes 7.147 + local sum_votes = initiative.positive_votes + initiative.negative_votes 7.148 + local function perc(votes, sum) 7.149 + if sum > 0 and votes > 0 then return " (" .. string.format( "%.f", votes * 100 / sum ) .. "%)" end 7.150 + return "" 7.151 + end 7.152 + slot.put(_"Yes" .. ": <b>" .. tostring(positive_votes) .. perc(positive_votes, sum_votes) .. "</b>") 7.153 + slot.put(" · ") 7.154 + slot.put(_"Abstention" .. ": <b>" .. tostring(max_value - initiative.negative_votes - initiative.positive_votes) .. "</b>") 7.155 + slot.put(" · ") 7.156 + slot.put(_"No" .. ": <b>" .. tostring(initiative.negative_votes) .. perc(negative_votes, sum_votes) .. "</b>") 7.157 + slot.put(" · ") 7.158 + slot.put("<b>") 7.159 + if initiative.winner then 7.160 + slot.put(_"Approved") 7.161 + elseif initiative.rank then 7.162 + slot.put(_("Not approved (rank #{rank})", { rank = initiative.rank })) 7.163 + else 7.164 + slot.put(_"Not approved") 7.165 + end 7.166 + slot.put("</b>") 7.167 + end 7.168 + } 7.169 + end 7.170 + 7.171 + ui.container{ attr = { class = "content" }, content = function() 7.172 + execute.view{ 7.173 + module = "initiative", 7.174 + view = "_battles", 7.175 + params = { initiative = initiative } 7.176 + } 7.177 + end } 7.178 + 7.179 + -- initiative not admitted info 7.180 + if initiative.admitted == false then 7.181 + local policy = initiative.issue.policy 7.182 + ui.container{ 7.183 + attr = { class = "not_admitted_info" }, 7.184 + content = _("This initiative has not been admitted! It failed the quorum of #{quorum}.", { quorum = format.percentage(policy.initiative_quorum_num / policy.initiative_quorum_den) }) 7.185 + } 7.186 + end 7.187 + 7.188 + -- initiative revoked info 7.189 + if initiative.revoked then 7.190 + ui.container{ 7.191 + attr = { class = "revoked_info" }, 7.192 + content = function() 7.193 + slot.put(_("This initiative has been revoked at #{revoked}", { revoked = format.timestamp(initiative.revoked) })) 7.194 + local suggested_initiative = initiative.suggested_initiative 7.195 + if suggested_initiative then 7.196 + slot.put("<br /><br />") 7.197 + slot.put(_("The initiators suggest to support the following initiative:")) 7.198 + slot.put(" ") 7.199 + ui.link{ 7.200 + content = _("Issue ##{id}", { id = suggested_initiative.issue.id } ) .. ": " .. encode.html(suggested_initiative.name), 7.201 + module = "initiative", 7.202 + view = "show", 7.203 + id = suggested_initiative.id 7.204 + } 7.205 + end 7.206 + end 7.207 + } 7.208 + end 7.209 + 7.210 + 7.211 + -- invited as initiator 7.212 + if initiator and initiator.accepted == nil and not initiative.issue.half_frozen and not initiative.issue.closed then 7.213 + ui.container{ 7.214 + attr = { class = "initiator_invite_info" }, 7.215 + content = function() 7.216 + slot.put(_"You are invited to become initiator of this initiative.") 7.217 + slot.put(" ") 7.218 + ui.link{ 7.219 + image = { static = "icons/16/tick.png" }, 7.220 + text = _"Accept invitation", 7.221 + module = "initiative", 7.222 + action = "accept_invitation", 7.223 + id = initiative.id, 7.224 + routing = { 7.225 + default = { 7.226 + mode = "redirect", 7.227 + module = request.get_module(), 7.228 + view = request.get_view(), 7.229 + id = param.get_id_cgi(), 7.230 + params = param.get_all_cgi() 7.231 + } 7.232 + } 7.233 + } 7.234 + slot.put(" ") 7.235 + ui.link{ 7.236 + image = { static = "icons/16/cross.png" }, 7.237 + text = _"Refuse invitation", 7.238 + module = "initiative", 7.239 + action = "reject_initiator_invitation", 7.240 + params = { 7.241 + initiative_id = initiative.id, 7.242 + member_id = app.session.member.id 7.243 + }, 7.244 + routing = { 7.245 + default = { 7.246 + mode = "redirect", 7.247 + module = request.get_module(), 7.248 + view = request.get_view(), 7.249 + id = param.get_id_cgi(), 7.250 + params = param.get_all_cgi() 7.251 + } 7.252 + } 7.253 + } 7.254 + end 7.255 + } 7.256 + end 7.257 + 7.258 + -- draft updated 7.259 + local supporter 7.260 + 7.261 + if app.session.member_id then 7.262 + supporter = app.session.member:get_reference_selector("supporters") 7.263 + :add_where{ "initiative_id = ?", initiative.id } 7.264 + :optional_object_mode() 7.265 + :exec() 7.266 + end 7.267 + 7.268 + if supporter and not initiative.issue.closed then 7.269 + local old_draft_id = supporter.draft_id 7.270 + local new_draft_id = initiative.current_draft.id 7.271 + if old_draft_id ~= new_draft_id then 7.272 + ui.container{ 7.273 + attr = { class = "draft_updated_info" }, 7.274 + content = function() 7.275 + slot.put(_"The draft of this initiative has been updated!") 7.276 + slot.put(" ") 7.277 + ui.link{ 7.278 + content = _"Show diff", 7.279 + module = "draft", 7.280 + view = "diff", 7.281 + params = { 7.282 + old_draft_id = old_draft_id, 7.283 + new_draft_id = new_draft_id 7.284 + } 7.285 + } 7.286 + if not initiative.revoked then 7.287 + slot.put(" ") 7.288 + ui.link{ 7.289 + text = _"Refresh support to current draft", 7.290 + module = "initiative", 7.291 + action = "add_support", 7.292 + id = initiative.id, 7.293 + routing = { 7.294 + default = { 7.295 + mode = "redirect", 7.296 + module = "initiative", 7.297 + view = "show", 7.298 + id = initiative.id 7.299 + } 7.300 + } 7.301 + } 7.302 + end 7.303 + end 7.304 + } 7.305 + end 7.306 + end 7.307 + 7.308 + 7.309 + execute.view{ 7.310 + module = "draft", 7.311 + view = "_show", 7.312 + params = { 7.313 + draft = initiative.current_draft 7.314 + } 7.315 + } 7.316 + 7.317 +end } 7.318 + 7.319 +execute.view{ 7.320 + module = "suggestion", 7.321 + view = "_list", 7.322 + params = { 7.323 + initiative = initiative, 7.324 + suggestions_selector = initiative:get_reference_selector("suggestions"), 7.325 + tab_id = param.get("tab_id") 7.326 + } 7.327 +} 7.328 + 7.329 + 7.330 +if config.public_access == "full" or app.session.member_id then 7.331 + if initiative.issue.ranks_available then 7.332 + local members_selector = initiative.issue:get_reference_selector("direct_voters") 7.333 + :left_join("vote", nil, { "vote.initiative_id = ? AND vote.member_id = member.id", initiative.id }) 7.334 + :add_field("direct_voter.weight as voter_weight") 7.335 + :add_field("coalesce(vote.grade, 0) as grade") 7.336 + :left_join("initiative", nil, "initiative.id = vote.initiative_id") 7.337 + :left_join("issue", nil, "issue.id = initiative.issue_id") 7.338 + 7.339 + ui.container{ attr = { class = "heading"}, content = _"Member voting" } 7.340 + 7.341 + execute.view{ 7.342 + module = "member", 7.343 + view = "_list", 7.344 + params = { 7.345 + initiative = initiative, 7.346 + for_votes = true, 7.347 + members_selector = members_selector 7.348 + } 7.349 + } 7.350 + end 7.351 + 7.352 + local members_selector = initiative:get_reference_selector("supporting_members_snapshot") 7.353 + :join("issue", nil, "issue.id = direct_supporter_snapshot.issue_id") 7.354 + :join("direct_interest_snapshot", nil, "direct_interest_snapshot.event = issue.latest_snapshot_event AND direct_interest_snapshot.issue_id = issue.id AND direct_interest_snapshot.member_id = member.id") 7.355 + :add_field("direct_interest_snapshot.weight") 7.356 + :add_where("direct_supporter_snapshot.event = issue.latest_snapshot_event") 7.357 + :add_where("direct_supporter_snapshot.satisfied") 7.358 + :add_field("direct_supporter_snapshot.informed", "is_informed") 7.359 + 7.360 + 7.361 + 7.362 + if members_selector:count() > 0 then 7.363 + if issue.fully_frozen then 7.364 + ui.container{ attr = { class = "heading"}, content = _"Supporters (before begin of voting)" } 7.365 + else 7.366 + ui.container{ attr = { class = "heading"}, content = _"Supporters" } 7.367 + end 7.368 + 7.369 + execute.view{ 7.370 + module = "member", 7.371 + view = "_list", 7.372 + params = { 7.373 + 7.374 + initiative = initiative, 7.375 + members_selector = members_selector 7.376 + } 7.377 + } 7.378 + else 7.379 + if issue.fully_frozen then 7.380 + ui.container{ attr = { class = "heading"}, content = _"No supporters (before begin of voting)" } 7.381 + else 7.382 + ui.container{ attr = { class = "heading"}, content = _"No supporters" } 7.383 + end 7.384 + slot.put("<br />") 7.385 + end 7.386 + 7.387 + local members_selector = initiative:get_reference_selector("supporting_members_snapshot") 7.388 + :join("issue", nil, "issue.id = direct_supporter_snapshot.issue_id") 7.389 + :join("direct_interest_snapshot", nil, "direct_interest_snapshot.event = issue.latest_snapshot_event AND direct_interest_snapshot.issue_id = issue.id AND direct_interest_snapshot.member_id = member.id") 7.390 + :add_field("direct_interest_snapshot.weight") 7.391 + :add_where("direct_supporter_snapshot.event = issue.latest_snapshot_event") 7.392 + :add_where("NOT direct_supporter_snapshot.satisfied") 7.393 + :add_field("direct_supporter_snapshot.informed", "is_informed") 7.394 + 7.395 + 7.396 + if members_selector:count() > 0 then 7.397 + if issue.fully_frozen then 7.398 + ui.container{ attr = { class = "heading"}, content = _"Potential supporters (before begin of voting" } 7.399 + else 7.400 + ui.container{ attr = { class = "heading"}, content = _"Potential supporters" } 7.401 + end 7.402 + 7.403 + execute.view{ 7.404 + module = "member", 7.405 + view = "_list", 7.406 + params = { 7.407 + 7.408 + initiative = initiative, 7.409 + members_selector = members_selector 7.410 + } 7.411 + } 7.412 + else 7.413 + if issue.fully_frozen then 7.414 + ui.container{ attr = { class = "heading"}, content = _"No potential supporters (before begin of voting)" } 7.415 + else 7.416 + ui.container{ attr = { class = "heading"}, content = _"No potential supporters" } 7.417 + end 7.418 + slot.put("<br />") 7.419 + end 7.420 + 7.421 + ui.container{ attr = { class = "heading"}, content = _"Details" } 7.422 + execute.view { 7.423 + module = "initiative", 7.424 + view = "_details", 7.425 + params = { 7.426 + initiative = initiative, 7.427 + members_selector = members_selector 7.428 + } 7.429 + } 7.430 + 7.431 +end 7.432 + 7.433 + 7.434 +--[[ 7.435 execute.view{ 7.436 module = "initiative", 7.437 - view = "_show", 7.438 + view = "show_tab", 7.439 params = { 7.440 initiative = initiative, 7.441 initiator = initiator 7.442 } 7.443 } 7.444 + 7.445 +if initiative.issue.snapshot then 7.446 + slot.put("<br />") 7.447 + ui.field.timestamp{ label = _"Last snapshot:", value = initiative.issue.snapshot } 7.448 +end 7.449 + 7.450 + 7.451 +--]]
8.1 --- a/app/main/issue/_details.lua Wed Jun 27 12:35:34 2012 +0200 8.2 +++ b/app/main/issue/_details.lua Wed Jun 27 14:37:46 2012 +0200 8.3 @@ -7,7 +7,7 @@ 8.4 attr = { class = "vertical" }, 8.5 content = function() 8.6 ui.field.text{ label = _"Population", name = "population" } 8.7 - ui.field.text{ label = _"State", name = "state" } 8.8 + ui.field.text{ label = _"State", name = "state_name" } 8.9 ui.field.timestamp{ label = _"Created at", name = "created" } 8.10 ui.field.text{ label = _"Admission time", value = issue.admission_time } 8.11 ui.field.text{ 8.12 @@ -20,9 +20,13 @@ 8.13 value = math.ceil(issue.population * policy.issue_quorum_num / policy.issue_quorum_den) 8.14 } 8.15 end 8.16 - ui.field.timestamp{ label = _"Accepted at", name = "accepted" } 8.17 + if issue.accepted then 8.18 + ui.field.timestamp{ label = _"Accepted at", name = "accepted" } 8.19 + end 8.20 ui.field.text{ label = _"Discussion time", value = issue.discussion_time } 8.21 - ui.field.timestamp{ label = _"Half frozen at", name = "half_frozen" } 8.22 + if issue.half_frozen then 8.23 + ui.field.timestamp{ label = _"Half frozen at", name = "half_frozen" } 8.24 + end 8.25 ui.field.text{ label = _"Verification time", value = issue.verification_time } 8.26 ui.field.text{ 8.27 label = _"Initiative quorum", 8.28 @@ -34,9 +38,13 @@ 8.29 value = math.ceil(issue.population * (issue.policy.initiative_quorum_num / issue.policy.initiative_quorum_den)), 8.30 } 8.31 end 8.32 - ui.field.timestamp{ label = _"Fully frozen at", name = "fully_frozen" } 8.33 + if issue.fully_frozen then 8.34 + ui.field.timestamp{ label = _"Fully frozen at", name = "fully_frozen" } 8.35 + end 8.36 ui.field.text{ label = _"Voting time", value = issue.voting_time } 8.37 - ui.field.timestamp{ label = _"Closed", name = "closed" } 8.38 + if issue.closed then 8.39 + ui.field.timestamp{ label = _"Closed", name = "closed" } 8.40 + end 8.41 end 8.42 } 8.43 ui.form{
9.1 --- a/app/main/member/_list.lua Wed Jun 27 12:35:34 2012 +0200 9.2 +++ b/app/main/member/_list.lua Wed Jun 27 14:37:46 2012 +0200 9.3 @@ -22,19 +22,6 @@ 9.4 local filter = { name = "member_list" } 9.5 9.6 if issue or initiative then 9.7 - if for_votes then 9.8 - filter[#filter+1] = { 9.9 - name = "delegations", 9.10 - label = _"Delegations", 9.11 - selector_modifier = function(selector) selector:add_order_by("voter_weight DESC") end 9.12 - } 9.13 - else 9.14 - filter[#filter+1] = { 9.15 - name = "delegations", 9.16 - label = _"Delegations", 9.17 - selector_modifier = function(selector) selector:add_order_by("weight DESC") end 9.18 - } 9.19 - end 9.20 end 9.21 9.22 filter[#filter+1] = { 9.23 @@ -59,7 +46,17 @@ 9.24 selector_modifier = function(selector) selector:add_order_by("name DESC") end 9.25 } 9.26 9.27 -ui.filters{ 9.28 +local ui_filters = ui.filters 9.29 +if issue or initiative then 9.30 + ui_filters = function(args) args.content() end 9.31 + if for_votes then 9.32 + members_selector:add_order_by("voter_weight DESC, name, id") 9.33 + else 9.34 + members_selector:add_order_by("weight DESC, name, id") 9.35 + end 9.36 +end 9.37 + 9.38 +ui_filters{ 9.39 label = _"Change order", 9.40 selector = members_selector, 9.41 filter,
10.1 --- a/app/main/suggestion/_list.lua Wed Jun 27 12:35:34 2012 +0200 10.2 +++ b/app/main/suggestion/_list.lua Wed Jun 27 14:37:46 2012 +0200 10.3 @@ -4,319 +4,95 @@ 10.4 10.5 suggestions_selector:add_order_by("plus2_unfulfilled_count + plus1_unfulfilled_count DESC, id") 10.6 10.7 -local tab_id = param.get("tab_id") 10.8 -local show_name = param.get("show_name", atom.boolean) 10.9 -if show_name == nil then 10.10 - show_name = true 10.11 -end 10.12 -local show_filter = param.get("show_filter", atom.boolean) 10.13 -if show_filter == nil then 10.14 - show_filter = true 10.15 -end 10.16 - 10.17 -local partial = { 10.18 - routing = { 10.19 - default = { 10.20 - mode = "redirect", 10.21 - module = "initiative", 10.22 - view = "show_tab", 10.23 - params = { 10.24 - initiative_id = initiative.id, 10.25 - tab = "suggestions", 10.26 - tab_id = tab_id 10.27 - }, 10.28 - } 10.29 - } 10.30 -} 10.31 - 10.32 local ui_filters = ui.filters 10.33 if true or not show_filter then 10.34 ui_filters = function(args) args.content() end 10.35 end 10.36 10.37 -ui.container{ attr = { class = "box" }, 10.38 + 10.39 +ui.container{ attr = { class = "initiative_head" }, 10.40 content = function() 10.41 - ui.paginate{ 10.42 - selector = suggestions_selector, 10.43 - content = function() 10.44 - ui.list{ 10.45 - attr = { style = "table-layout: fixed;" }, 10.46 - records = suggestions_selector:exec(), 10.47 - columns = { 10.48 - { 10.49 - label = show_name and _"Suggestion" or nil, 10.50 - content = function(record) 10.51 - if show_name then 10.52 - ui.link{ 10.53 - text = record.name, 10.54 - module = "suggestion", 10.55 - view = "show", 10.56 - id = record.id 10.57 - } 10.58 - end 10.59 - end 10.60 - }, 10.61 - { 10.62 - label = _"Collective opinion of supporters", 10.63 - label_attr = { style = "width: 101px;" }, 10.64 - content = function(record) 10.65 - if record.minus2_unfulfilled_count then 10.66 - local max_value = record.initiative.supporter_count 10.67 - ui.bargraph{ 10.68 - max_value = max_value, 10.69 - width = 100, 10.70 - bars = { 10.71 - { color = "#0a0", value = record.plus2_unfulfilled_count + record.plus2_fulfilled_count }, 10.72 - { color = "#8f8", value = record.plus1_unfulfilled_count + record.plus1_fulfilled_count }, 10.73 - { color = "#eee", value = max_value - record.minus2_unfulfilled_count - record.minus1_unfulfilled_count - record.minus2_fulfilled_count - record.minus1_fulfilled_count - record.plus1_unfulfilled_count - record.plus2_unfulfilled_count - record.plus1_fulfilled_count - record.plus2_fulfilled_count}, 10.74 - { color = "#f88", value = record.minus1_unfulfilled_count + record.minus1_fulfilled_count }, 10.75 - { color = "#a00", value = record.minus2_unfulfilled_count + record.minus2_fulfilled_count }, 10.76 - } 10.77 - } 10.78 - end 10.79 - end 10.80 - }, 10.81 - { 10.82 - label = _"My opinion", 10.83 - label_attr = { style = "width: 130px; font-style: italic;" }, 10.84 - content = function(record) 10.85 - local degree 10.86 - local opinion 10.87 - if app.session.member_id then 10.88 - opinion = Opinion:by_pk(app.session.member.id, record.id) 10.89 - end 10.90 - if opinion then 10.91 - degree = opinion.degree 10.92 - end 10.93 - ui.container{ 10.94 - attr = { class = "suggestion_my_opinion" }, 10.95 - content = function() 10.96 - local has_voting_right = app.session.member and app.session.member:has_voting_right_for_unit_id(initiative.issue.area.unit_id) 10.97 - if app.session.member_id and has_voting_right then 10.98 - if initiative.issue.state == "voting" or initiative.issue.state == "closed" then 10.99 - if degree == -2 then 10.100 - ui.tag{ 10.101 - tag = "span", 10.102 - attr = { 10.103 - class = "action" .. (degree == -2 and " active_red2" or "") 10.104 - }, 10.105 - content = _"must not" 10.106 - } 10.107 - end 10.108 - if degree == -1 then 10.109 - ui.tag{ 10.110 - tag = "span", 10.111 - attr = { class = "action" .. (degree == -1 and " active_red1" or "") }, 10.112 - content = _"should not" 10.113 - } 10.114 - end 10.115 - if degree == nil then 10.116 - ui.tag{ 10.117 - tag = "span", 10.118 - attr = { class = "action" .. (degree == nil and " active" or "") }, 10.119 - content = _"neutral" 10.120 - } 10.121 - end 10.122 - if degree == 1 then 10.123 - ui.tag{ 10.124 - tag = "span", 10.125 - attr = { class = "action" .. (degree == 1 and " active_green1" or "") }, 10.126 - content = _"should" 10.127 - } 10.128 - end 10.129 - if degree == 2 then 10.130 - ui.tag{ 10.131 - tag = "span", 10.132 - attr = { class = "action" .. (degree == 2 and " active_green2" or "") }, 10.133 - content = _"must" 10.134 - } 10.135 - end 10.136 - else 10.137 - -- we need to put initiative_id into the parameters to have a redirect target in case the suggestion is gone after the action 10.138 - params = param.get_all_cgi() 10.139 - params['initiative_id'] = initiative.id 10.140 - 10.141 - ui.link{ 10.142 - attr = { class = "action" .. (degree == 2 and " active_green2" or "") }, 10.143 - text = _"must", 10.144 - module = "opinion", 10.145 - action = "update", 10.146 - routing = { default = { mode = "redirect", module = request.get_module(), view = request.get_view(), id = param.get_id_cgi(), params = params } }, 10.147 - params = { 10.148 - suggestion_id = record.id, 10.149 - degree = 2 10.150 - }, 10.151 - partial = partial 10.152 + ui.container{ attr = { class = "title" }, content = _"Suggestions" } 10.153 + ui.container{ attr = { class = "content" }, content = function() 10.154 + ui.paginate{ 10.155 + selector = suggestions_selector, 10.156 + content = function() 10.157 + local suggestions = suggestions_selector:exec() 10.158 + if #suggestions < 1 then 10.159 + if not initiative.issue.fully_frozen and not initiative.issue.closed then 10.160 + ui.tag{ content = _"No suggestions yet" } 10.161 + else 10.162 + ui.tag{ content = _"No suggestions" } 10.163 + end 10.164 + else 10.165 + ui.list{ 10.166 + attr = { style = "table-layout: fixed;" }, 10.167 + records = suggestions, 10.168 + columns = { 10.169 + { 10.170 + label_attr = { style = "width: 101px;" }, 10.171 + content = function(record) 10.172 + if record.minus2_unfulfilled_count then 10.173 + local max_value = record.initiative.supporter_count 10.174 + ui.bargraph{ 10.175 + max_value = max_value, 10.176 + width = 100, 10.177 + bars = { 10.178 + { color = "#0a0", value = record.plus2_unfulfilled_count + record.plus2_fulfilled_count }, 10.179 + { color = "#8f8", value = record.plus1_unfulfilled_count + record.plus1_fulfilled_count }, 10.180 + { color = "#eee", value = max_value - record.minus2_unfulfilled_count - record.minus1_unfulfilled_count - record.minus2_fulfilled_count - record.minus1_fulfilled_count - record.plus1_unfulfilled_count - record.plus2_unfulfilled_count - record.plus1_fulfilled_count - record.plus2_fulfilled_count}, 10.181 + { color = "#f88", value = record.minus1_unfulfilled_count + record.minus1_fulfilled_count }, 10.182 + { color = "#a00", value = record.minus2_unfulfilled_count + record.minus2_fulfilled_count }, 10.183 } 10.184 - slot.put(" ") 10.185 - ui.link{ 10.186 - attr = { class = "action" .. (degree == 1 and " active_green1" or "") }, 10.187 - text = _"should", 10.188 - module = "opinion", 10.189 - action = "update", 10.190 - routing = { default = { mode = "redirect", module = request.get_module(), view = request.get_view(), id = param.get_id_cgi(), params = params} }, 10.191 - params = { 10.192 - suggestion_id = record.id, 10.193 - degree = 1 10.194 - }, 10.195 - partial = partial 10.196 - } 10.197 - slot.put(" ") 10.198 - ui.link{ 10.199 - attr = { class = "action" .. (degree == nil and " active" or "") }, 10.200 - text = _"neutral", 10.201 - module = "opinion", 10.202 - action = "update", 10.203 - routing = { default = { mode = "redirect", module = request.get_module(), view = request.get_view(), id = param.get_id_cgi(), params = params } }, 10.204 - params = { 10.205 - suggestion_id = record.id, 10.206 - delete = true 10.207 - }, 10.208 - partial = partial 10.209 - } 10.210 - slot.put(" ") 10.211 - ui.link{ 10.212 - attr = { class = "action" .. (degree == -1 and " active_red1" or "") }, 10.213 - text = _"should not", 10.214 - module = "opinion", 10.215 - action = "update", 10.216 - routing = { default = { mode = "redirect", module = request.get_module(), view = request.get_view(), id = param.get_id_cgi(), params = params } }, 10.217 - params = { 10.218 - suggestion_id = record.id, 10.219 - degree = -1 10.220 - }, 10.221 - partial = partial 10.222 - } 10.223 - slot.put(" ") 10.224 - ui.link{ 10.225 - attr = { class = "action" .. (degree == -2 and " active_red2" or "") }, 10.226 - text = _"must not", 10.227 - module = "opinion", 10.228 - action = "update", 10.229 - routing = { default = { mode = "redirect", module = request.get_module(), view = request.get_view(), id = param.get_id_cgi(), params = params } }, 10.230 - params = { 10.231 - suggestion_id = record.id, 10.232 - degree = -2 10.233 - }, 10.234 - partial = partial 10.235 - } 10.236 - end 10.237 - elseif app.session.member_id then 10.238 - ui.field.text{ value = _"[No voting privilege]" } 10.239 - else 10.240 - ui.field.text{ value = _"[Registered members only]" } 10.241 + } 10.242 end 10.243 end 10.244 - } 10.245 - end 10.246 - }, 10.247 - { 10.248 - label = _"Suggestion currently not implemented", 10.249 - label_attr = { style = "width: 101px;" }, 10.250 - content = function(record) 10.251 - if record.minus2_unfulfilled_count then 10.252 - local max_value = record.initiative.supporter_count 10.253 - ui.bargraph{ 10.254 - max_value = max_value, 10.255 - width = 100, 10.256 - bars = { 10.257 - { color = "#0a0", value = record.plus2_unfulfilled_count }, 10.258 - { color = "#8f8", value = record.plus1_unfulfilled_count }, 10.259 - { color = "#eee", value = max_value - record.minus2_unfulfilled_count - record.minus1_unfulfilled_count - record.plus1_unfulfilled_count - record.plus2_unfulfilled_count }, 10.260 - { color = "#f88", value = record.minus1_unfulfilled_count }, 10.261 - { color = "#a00", value = record.minus2_unfulfilled_count }, 10.262 - } 10.263 - } 10.264 - end 10.265 - end 10.266 - }, 10.267 - { 10.268 - label = _"Suggestion currently implemented", 10.269 - label_attr = { style = "width: 101px;" }, 10.270 - content = function(record) 10.271 - if record.minus2_fulfilled_count then 10.272 - local max_value = record.initiative.supporter_count 10.273 - ui.bargraph{ 10.274 - max_value = max_value, 10.275 - width = 100, 10.276 - bars = { 10.277 - { color = "#0a0", value = record.plus2_fulfilled_count }, 10.278 - { color = "#8f8", value = record.plus1_fulfilled_count }, 10.279 - { color = "#eee", value = max_value - record.minus2_fulfilled_count - record.minus1_fulfilled_count - record.plus1_fulfilled_count - record.plus2_fulfilled_count}, 10.280 - { color = "#f88", value = record.minus1_fulfilled_count }, 10.281 - { color = "#a00", value = record.minus2_fulfilled_count }, 10.282 + }, 10.283 + { 10.284 + content = function(record) 10.285 + ui.link{ 10.286 + text = record.name, 10.287 + module = "suggestion", 10.288 + view = "show", 10.289 + id = record.id 10.290 } 10.291 - } 10.292 - end 10.293 - end 10.294 - }, 10.295 - { 10.296 - label = app.session.member_id and _"I consider suggestion as" or nil, 10.297 - label_attr = { style = "width: 100px; font-style: italic;" }, 10.298 - content = function(record) 10.299 - local degree 10.300 - local opinion 10.301 - if app.session.member_id then 10.302 - opinion = Opinion:by_pk(app.session.member.id, record.id) 10.303 - end 10.304 - if opinion then 10.305 - degree = opinion.degree 10.306 - end 10.307 - if opinion then 10.308 - 10.309 - ui.link{ 10.310 - attr = { class = opinion.fulfilled and "action active" or "action" }, 10.311 - text = _"implemented", 10.312 - module = "opinion", 10.313 - action = "update", 10.314 - routing = { default = { mode = "redirect", module = request.get_module(), view = request.get_view(), id = param.get_id_cgi(), params = param.get_all_cgi() } }, 10.315 - params = { 10.316 - suggestion_id = record.id, 10.317 - fulfilled = true 10.318 - }, 10.319 - partial = partial 10.320 - } 10.321 - slot.put("<br />") 10.322 - ui.link{ 10.323 - attr = { class = not opinion.fulfilled and "action active" or "action" }, 10.324 - text = _"not implemented", 10.325 - module = "opinion", 10.326 - action = "update", 10.327 - routing = { default = { mode = "redirect", module = request.get_module(), view = request.get_view(), id = param.get_id_cgi(), params = param.get_all_cgi() } }, 10.328 - params = { 10.329 - suggestion_id = record.id, 10.330 - fulfilled = false 10.331 - }, 10.332 - partial = partial 10.333 - } 10.334 - 10.335 - end 10.336 - end 10.337 - }, 10.338 - { 10.339 - label = app.session.member_id and _"So I'm" or nil, 10.340 - content = function(record) 10.341 - local opinion 10.342 - if app.session.member_id then 10.343 - opinion = Opinion:by_pk(app.session.member.id, record.id) 10.344 - end 10.345 - if opinion then 10.346 - if (opinion.fulfilled and opinion.degree > 0) or (not opinion.fulfilled and opinion.degree < 0) then 10.347 - local title = _"satisfied" 10.348 - ui.image{ attr = { alt = title, title = title }, static = "icons/emoticon_happy.png" } 10.349 - elseif opinion.degree == 1 or opinion.degree == -1 then 10.350 - local title = _"a bit unsatisfied" 10.351 - ui.image{ attr = { alt = title, title = title }, static = "icons/emoticon_unhappy.png" } 10.352 - else 10.353 - local title = _"more unsatisfied" 10.354 - ui.image{ attr = { alt = title, title = title }, static = "icons/emoticon_unhappy_red.png" } 10.355 + local degree 10.356 + local opinion 10.357 + if app.session.member_id then 10.358 + opinion = Opinion:by_pk(app.session.member.id, record.id) 10.359 + end 10.360 + if opinion then 10.361 + local degrees = { 10.362 + ["-2"] = _"must not", 10.363 + ["-1"] = _"should not", 10.364 + ["0"] = _"neutral", 10.365 + ["1"] = _"should", 10.366 + ["2"] = _"must" 10.367 + } 10.368 + slot.put(" · ") 10.369 + ui.tag{ content = degrees[tostring(opinion.degree)] } 10.370 + slot.put(" · ") 10.371 + if opinion.fulfilled then 10.372 + ui.tag{ content = _"implemented" } 10.373 + else 10.374 + ui.tag{ content = _"not implemented" } 10.375 + end 10.376 + end 10.377 end 10.378 - end 10.379 - end 10.380 - }, 10.381 - } 10.382 + }, 10.383 + } 10.384 + } 10.385 + end 10.386 + end 10.387 + } 10.388 + end } 10.389 + if not initiative.issue.fully_frozen and not initiative.issue.closed then 10.390 + ui.container{ attr = { class = "content" }, content = function() 10.391 + ui.link{ 10.392 + module = "suggestion", view = "new", params = { initiative_id = initiative.id }, 10.393 + text = _"New suggestion" 10.394 } 10.395 - end 10.396 - } 10.397 + end } 10.398 + end 10.399 end 10.400 }
11.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 11.2 +++ b/app/main/suggestion/_list_element.lua Wed Jun 27 14:37:46 2012 +0200 11.3 @@ -0,0 +1,322 @@ 11.4 + 11.5 +local initiative = param.get("initiative", "table") 11.6 +local suggestions_selector = param.get("suggestions_selector", "table") 11.7 + 11.8 +suggestions_selector:add_order_by("plus2_unfulfilled_count + plus1_unfulfilled_count DESC, id") 11.9 + 11.10 +local tab_id = param.get("tab_id") 11.11 +local show_name = param.get("show_name", atom.boolean) 11.12 +if show_name == nil then 11.13 + show_name = true 11.14 +end 11.15 +local show_filter = param.get("show_filter", atom.boolean) 11.16 +if show_filter == nil then 11.17 + show_filter = true 11.18 +end 11.19 + 11.20 +local partial = { 11.21 + routing = { 11.22 + default = { 11.23 + mode = "redirect", 11.24 + module = "initiative", 11.25 + view = "show_tab", 11.26 + params = { 11.27 + initiative_id = initiative.id, 11.28 + tab = "suggestions", 11.29 + tab_id = tab_id 11.30 + }, 11.31 + } 11.32 + } 11.33 +} 11.34 + 11.35 +local ui_filters = ui.filters 11.36 +if true or not show_filter then 11.37 + ui_filters = function(args) args.content() end 11.38 +end 11.39 + 11.40 +ui.container{ attr = { class = "box" }, 11.41 + content = function() 11.42 + ui.paginate{ 11.43 + selector = suggestions_selector, 11.44 + content = function() 11.45 + ui.list{ 11.46 + attr = { style = "table-layout: fixed;" }, 11.47 + records = suggestions_selector:exec(), 11.48 + columns = { 11.49 + { 11.50 + label = show_name and _"Suggestion" or nil, 11.51 + content = function(record) 11.52 + if show_name then 11.53 + ui.link{ 11.54 + text = record.name, 11.55 + module = "suggestion", 11.56 + view = "show", 11.57 + id = record.id 11.58 + } 11.59 + end 11.60 + end 11.61 + }, 11.62 + { 11.63 + label = _"Collective opinion of supporters", 11.64 + label_attr = { style = "width: 101px;" }, 11.65 + content = function(record) 11.66 + if record.minus2_unfulfilled_count then 11.67 + local max_value = record.initiative.supporter_count 11.68 + ui.bargraph{ 11.69 + max_value = max_value, 11.70 + width = 100, 11.71 + bars = { 11.72 + { color = "#0a0", value = record.plus2_unfulfilled_count + record.plus2_fulfilled_count }, 11.73 + { color = "#8f8", value = record.plus1_unfulfilled_count + record.plus1_fulfilled_count }, 11.74 + { color = "#eee", value = max_value - record.minus2_unfulfilled_count - record.minus1_unfulfilled_count - record.minus2_fulfilled_count - record.minus1_fulfilled_count - record.plus1_unfulfilled_count - record.plus2_unfulfilled_count - record.plus1_fulfilled_count - record.plus2_fulfilled_count}, 11.75 + { color = "#f88", value = record.minus1_unfulfilled_count + record.minus1_fulfilled_count }, 11.76 + { color = "#a00", value = record.minus2_unfulfilled_count + record.minus2_fulfilled_count }, 11.77 + } 11.78 + } 11.79 + end 11.80 + end 11.81 + }, 11.82 + { 11.83 + label = _"My opinion", 11.84 + label_attr = { style = "width: 130px; font-style: italic;" }, 11.85 + content = function(record) 11.86 + local degree 11.87 + local opinion 11.88 + if app.session.member_id then 11.89 + opinion = Opinion:by_pk(app.session.member.id, record.id) 11.90 + end 11.91 + if opinion then 11.92 + degree = opinion.degree 11.93 + end 11.94 + ui.container{ 11.95 + attr = { class = "suggestion_my_opinion" }, 11.96 + content = function() 11.97 + local has_voting_right = app.session.member and app.session.member:has_voting_right_for_unit_id(initiative.issue.area.unit_id) 11.98 + if app.session.member_id and has_voting_right then 11.99 + if initiative.issue.state == "voting" or initiative.issue.state == "closed" then 11.100 + if degree == -2 then 11.101 + ui.tag{ 11.102 + tag = "span", 11.103 + attr = { 11.104 + class = "action" .. (degree == -2 and " active_red2" or "") 11.105 + }, 11.106 + content = _"must not" 11.107 + } 11.108 + end 11.109 + if degree == -1 then 11.110 + ui.tag{ 11.111 + tag = "span", 11.112 + attr = { class = "action" .. (degree == -1 and " active_red1" or "") }, 11.113 + content = _"should not" 11.114 + } 11.115 + end 11.116 + if degree == nil then 11.117 + ui.tag{ 11.118 + tag = "span", 11.119 + attr = { class = "action" .. (degree == nil and " active" or "") }, 11.120 + content = _"neutral" 11.121 + } 11.122 + end 11.123 + if degree == 1 then 11.124 + ui.tag{ 11.125 + tag = "span", 11.126 + attr = { class = "action" .. (degree == 1 and " active_green1" or "") }, 11.127 + content = _"should" 11.128 + } 11.129 + end 11.130 + if degree == 2 then 11.131 + ui.tag{ 11.132 + tag = "span", 11.133 + attr = { class = "action" .. (degree == 2 and " active_green2" or "") }, 11.134 + content = _"must" 11.135 + } 11.136 + end 11.137 + else 11.138 + -- we need to put initiative_id into the parameters to have a redirect target in case the suggestion is gone after the action 11.139 + params = param.get_all_cgi() 11.140 + params['initiative_id'] = initiative.id 11.141 + 11.142 + ui.link{ 11.143 + attr = { class = "action" .. (degree == 2 and " active_green2" or "") }, 11.144 + text = _"must", 11.145 + module = "opinion", 11.146 + action = "update", 11.147 + routing = { default = { mode = "redirect", module = request.get_module(), view = request.get_view(), id = param.get_id_cgi(), params = params } }, 11.148 + params = { 11.149 + suggestion_id = record.id, 11.150 + degree = 2 11.151 + }, 11.152 + partial = partial 11.153 + } 11.154 + slot.put(" ") 11.155 + ui.link{ 11.156 + attr = { class = "action" .. (degree == 1 and " active_green1" or "") }, 11.157 + text = _"should", 11.158 + module = "opinion", 11.159 + action = "update", 11.160 + routing = { default = { mode = "redirect", module = request.get_module(), view = request.get_view(), id = param.get_id_cgi(), params = params} }, 11.161 + params = { 11.162 + suggestion_id = record.id, 11.163 + degree = 1 11.164 + }, 11.165 + partial = partial 11.166 + } 11.167 + slot.put(" ") 11.168 + ui.link{ 11.169 + attr = { class = "action" .. (degree == nil and " active" or "") }, 11.170 + text = _"neutral", 11.171 + module = "opinion", 11.172 + action = "update", 11.173 + routing = { default = { mode = "redirect", module = request.get_module(), view = request.get_view(), id = param.get_id_cgi(), params = params } }, 11.174 + params = { 11.175 + suggestion_id = record.id, 11.176 + delete = true 11.177 + }, 11.178 + partial = partial 11.179 + } 11.180 + slot.put(" ") 11.181 + ui.link{ 11.182 + attr = { class = "action" .. (degree == -1 and " active_red1" or "") }, 11.183 + text = _"should not", 11.184 + module = "opinion", 11.185 + action = "update", 11.186 + routing = { default = { mode = "redirect", module = request.get_module(), view = request.get_view(), id = param.get_id_cgi(), params = params } }, 11.187 + params = { 11.188 + suggestion_id = record.id, 11.189 + degree = -1 11.190 + }, 11.191 + partial = partial 11.192 + } 11.193 + slot.put(" ") 11.194 + ui.link{ 11.195 + attr = { class = "action" .. (degree == -2 and " active_red2" or "") }, 11.196 + text = _"must not", 11.197 + module = "opinion", 11.198 + action = "update", 11.199 + routing = { default = { mode = "redirect", module = request.get_module(), view = request.get_view(), id = param.get_id_cgi(), params = params } }, 11.200 + params = { 11.201 + suggestion_id = record.id, 11.202 + degree = -2 11.203 + }, 11.204 + partial = partial 11.205 + } 11.206 + end 11.207 + elseif app.session.member_id then 11.208 + ui.field.text{ value = _"[No voting privilege]" } 11.209 + else 11.210 + ui.field.text{ value = _"[Registered members only]" } 11.211 + end 11.212 + end 11.213 + } 11.214 + end 11.215 + }, 11.216 + { 11.217 + label = _"Suggestion currently not implemented", 11.218 + label_attr = { style = "width: 101px;" }, 11.219 + content = function(record) 11.220 + if record.minus2_unfulfilled_count then 11.221 + local max_value = record.initiative.supporter_count 11.222 + ui.bargraph{ 11.223 + max_value = max_value, 11.224 + width = 100, 11.225 + bars = { 11.226 + { color = "#0a0", value = record.plus2_unfulfilled_count }, 11.227 + { color = "#8f8", value = record.plus1_unfulfilled_count }, 11.228 + { color = "#eee", value = max_value - record.minus2_unfulfilled_count - record.minus1_unfulfilled_count - record.plus1_unfulfilled_count - record.plus2_unfulfilled_count }, 11.229 + { color = "#f88", value = record.minus1_unfulfilled_count }, 11.230 + { color = "#a00", value = record.minus2_unfulfilled_count }, 11.231 + } 11.232 + } 11.233 + end 11.234 + end 11.235 + }, 11.236 + { 11.237 + label = _"Suggestion currently implemented", 11.238 + label_attr = { style = "width: 101px;" }, 11.239 + content = function(record) 11.240 + if record.minus2_fulfilled_count then 11.241 + local max_value = record.initiative.supporter_count 11.242 + ui.bargraph{ 11.243 + max_value = max_value, 11.244 + width = 100, 11.245 + bars = { 11.246 + { color = "#0a0", value = record.plus2_fulfilled_count }, 11.247 + { color = "#8f8", value = record.plus1_fulfilled_count }, 11.248 + { color = "#eee", value = max_value - record.minus2_fulfilled_count - record.minus1_fulfilled_count - record.plus1_fulfilled_count - record.plus2_fulfilled_count}, 11.249 + { color = "#f88", value = record.minus1_fulfilled_count }, 11.250 + { color = "#a00", value = record.minus2_fulfilled_count }, 11.251 + } 11.252 + } 11.253 + end 11.254 + end 11.255 + }, 11.256 + { 11.257 + label = app.session.member_id and _"I consider suggestion as" or nil, 11.258 + label_attr = { style = "width: 100px; font-style: italic;" }, 11.259 + content = function(record) 11.260 + local degree 11.261 + local opinion 11.262 + if app.session.member_id then 11.263 + opinion = Opinion:by_pk(app.session.member.id, record.id) 11.264 + end 11.265 + if opinion then 11.266 + degree = opinion.degree 11.267 + end 11.268 + if opinion then 11.269 + 11.270 + ui.link{ 11.271 + attr = { class = opinion.fulfilled and "action active" or "action" }, 11.272 + text = _"implemented", 11.273 + module = "opinion", 11.274 + action = "update", 11.275 + routing = { default = { mode = "redirect", module = request.get_module(), view = request.get_view(), id = param.get_id_cgi(), params = param.get_all_cgi() } }, 11.276 + params = { 11.277 + suggestion_id = record.id, 11.278 + fulfilled = true 11.279 + }, 11.280 + partial = partial 11.281 + } 11.282 + slot.put("<br />") 11.283 + ui.link{ 11.284 + attr = { class = not opinion.fulfilled and "action active" or "action" }, 11.285 + text = _"not implemented", 11.286 + module = "opinion", 11.287 + action = "update", 11.288 + routing = { default = { mode = "redirect", module = request.get_module(), view = request.get_view(), id = param.get_id_cgi(), params = param.get_all_cgi() } }, 11.289 + params = { 11.290 + suggestion_id = record.id, 11.291 + fulfilled = false 11.292 + }, 11.293 + partial = partial 11.294 + } 11.295 + 11.296 + end 11.297 + end 11.298 + }, 11.299 + { 11.300 + label = app.session.member_id and _"So I'm" or nil, 11.301 + content = function(record) 11.302 + local opinion 11.303 + if app.session.member_id then 11.304 + opinion = Opinion:by_pk(app.session.member.id, record.id) 11.305 + end 11.306 + if opinion then 11.307 + if (opinion.fulfilled and opinion.degree > 0) or (not opinion.fulfilled and opinion.degree < 0) then 11.308 + local title = _"satisfied" 11.309 + ui.image{ attr = { alt = title, title = title }, static = "icons/emoticon_happy.png" } 11.310 + elseif opinion.degree == 1 or opinion.degree == -1 then 11.311 + local title = _"a bit unsatisfied" 11.312 + ui.image{ attr = { alt = title, title = title }, static = "icons/emoticon_unhappy.png" } 11.313 + else 11.314 + local title = _"more unsatisfied" 11.315 + ui.image{ attr = { alt = title, title = title }, static = "icons/emoticon_unhappy_red.png" } 11.316 + end 11.317 + end 11.318 + end 11.319 + }, 11.320 + } 11.321 + } 11.322 + end 11.323 + } 11.324 + end 11.325 +}
12.1 --- a/app/main/suggestion/_suggestion.lua Wed Jun 27 12:35:34 2012 +0200 12.2 +++ b/app/main/suggestion/_suggestion.lua Wed Jun 27 14:37:46 2012 +0200 12.3 @@ -19,7 +19,7 @@ 12.4 } 12.5 execute.view{ 12.6 module = "suggestion", 12.7 - view = "_list", 12.8 + view = "_list_element", 12.9 params = { 12.10 suggestions_selector = Suggestion:new_selector():add_where{ "id = ?", suggestion.id }, 12.11 initiative = suggestion.initiative,
13.1 --- a/app/main/suggestion/show.lua Wed Jun 27 12:35:34 2012 +0200 13.2 +++ b/app/main/suggestion/show.lua Wed Jun 27 14:37:46 2012 +0200 13.3 @@ -21,9 +21,9 @@ 13.4 app.html_title.title = suggestion.name 13.5 app.html_title.subtitle = _("Suggestion ##{id}", { id = suggestion.id }) 13.6 13.7 -slot.put_into("title", encode.html(_"Suggestion for initiative: '#{name}'":gsub("#{name}", suggestion.initiative.name) )) 13.8 +ui.title(_"Suggestion for initiative: '#{name}'":gsub("#{name}", suggestion.initiative.name)) 13.9 13.10 -slot.select("actions", function() 13.11 +ui.actions(function() 13.12 ui.link{ 13.13 content = function() 13.14 ui.image{ static = "icons/16/resultset_previous.png" }
14.1 --- a/static/style.css Wed Jun 27 12:35:34 2012 +0200 14.2 +++ b/static/style.css Wed Jun 27 14:37:46 2012 +0200 14.3 @@ -68,10 +68,6 @@ 14.4 margin: 0 auto; 14.5 } 14.6 14.7 -table { 14.8 - margin-top: 1ex; 14.9 -} 14.10 - 14.11 body, a { 14.12 color: #000; 14.13 } 14.14 @@ -321,7 +317,6 @@ 14.15 14.16 .member_thumb, 14.17 .member_statement, 14.18 -.draft_content, 14.19 .suggestion_content, 14.20 .slot_head, 14.21 .area_list, 14.22 @@ -329,7 +324,6 @@ 14.23 .issues .issue, 14.24 .ui_tabs_links a, 14.25 .ui_filter a, 14.26 -.draft_content, 14.27 .help, 14.28 .use_terms, 14.29 .motd, 14.30 @@ -375,6 +369,10 @@ 14.31 line-height: 120%; 14.32 } 14.33 14.34 +.initiative_head { 14.35 + margin-bottom: 10px; 14.36 +} 14.37 + 14.38 .page_head .actions:last-child, 14.39 .page_head .unit_head .content:last-child, 14.40 .page_head .area_head .content:last-child, 14.41 @@ -1211,30 +1209,34 @@ 14.42 text-decoration: line-through; 14.43 } 14.44 14.45 +.admitted_info, 14.46 +.not_admitted_info, 14.47 +.revoked_info, 14.48 +.draft_updated_info, 14.49 +.initiator_invite_info { 14.50 + padding: 1ex; 14.51 +} 14.52 + 14.53 .admitted_info { 14.54 background-color: #afc; 14.55 - padding: 1ex; 14.56 - border-radius: 0 0 8px 8px; 14.57 } 14.58 14.59 .not_admitted_info, 14.60 .revoked_info { 14.61 background-color: #fbb; 14.62 - padding: 1ex; 14.63 - border-radius: 8px; 14.64 - border-radius: 0 0 8px 8px; 14.65 } 14.66 14.67 .draft_updated_info, 14.68 -.initiator_invite_info, 14.69 +.initiator_invite_info { 14.70 + background-color: #fda; 14.71 +} 14.72 + 14.73 .motd, 14.74 .public_access_issue_head { 14.75 background-color: #fda; 14.76 padding: 1ex; 14.77 margin-top: 1ex; 14.78 margin-bottom: 2ex; 14.79 - border: 1px solid #fa0; 14.80 - border-radius: 10px; 14.81 } 14.82 14.83 .suggestion_fulfilled { 14.84 @@ -1336,6 +1338,7 @@ 14.85 display: block; 14.86 font-size: 115%; 14.87 font-weight: bold; 14.88 + margin-bottom: 10px; 14.89 } 14.90 14.91 .heading.first { 14.92 @@ -1418,7 +1421,6 @@ 14.93 /* shadows */ 14.94 14.95 .member_statement, 14.96 -.draft_content, 14.97 .suggestion_content, 14.98 .initiative_head, 14.99 .box, 14.100 @@ -1427,7 +1429,6 @@ 14.101 } 14.102 14.103 .member_statement, 14.104 -.draft_content, 14.105 .suggestion_content, 14.106 .initiative_head, 14.107 .box {