liquid_feedback_frontend
view app/main/issue/_list.lua @ 1645:afff8198bd98
Show info when no applications are connected
| author | bsw | 
|---|---|
| date | Tue Feb 09 23:10:56 2021 +0100 (2021-02-09) | 
| parents | d14462e2b38b | 
| children | 12a73990db76 | 
 line source
     1 local for_member = param.get ( "for_member", "table" )
     2 local for_unit =   param.get ( "for_unit",   "table" )
     3 local for_area =   param.get ( "for_area",   "table" )
     4 local for_issue =  param.get ( "for_issue",  "table" )
     5 local for_initiative =  param.get ( "for_initiative",  "table" )
     6 local for_sidebar = param.get("for_sidebar", atom.boolean)
     7 local no_filter =  param.get ( "no_filter",  atom.boolean )
     8 local search =     param.get ( "search" )
    10 local limit = 25
    12 local mode = request.get_param{ name = "mode" } or "issue"
    14 if for_initiative or for_issue or for_member then
    15   mode = "timeline"
    16 end
    18 if config.single_unit_id then
    19   for_unit = Unit:by_id(config.single_unit_id)
    20 end
    22 local selector
    24 if search then
    26   selector = Issue:get_search_selector(search)
    29 elseif mode == "timeline" then
    31   local event_max_id = request.get_param_strings()["event_max_id"]
    33   selector = Event:new_selector()
    34     :add_order_by("event.id DESC")
    35     :join("issue", nil, "issue.id = event.issue_id")
    36     :add_field("now() - event.occurrence", "time_ago")
    37     :limit(limit + 1)
    39   if event_max_id then
    40     selector:add_where{ "event.id < ?", event_max_id }
    41   end
    43   if for_member then
    44     selector:add_where{ "event.member_id = ?", for_member.id }
    45   end
    47   if for_initiative then
    48     selector:add_where{ "event.initiative_id = ?", for_initiative.id }
    49   end
    52 elseif mode == "issue" then
    54   selector = Issue:new_selector()
    56 end
    58 if for_unit then
    59   selector:join("area", nil, "area.id = issue.area_id")
    60   selector:add_where{ "area.unit_id = ?", for_unit.id }
    61 elseif for_area then
    62   selector:add_where{ "issue.area_id = ?", for_area.id }
    63 elseif for_issue then
    64   selector:add_where{ "issue.id = ?", for_issue.id }
    65 end
    67 if not search and app.session.member_id then
    68   selector
    69     :left_join("interest", "_interest", { 
    70       "_interest.issue_id = issue.id AND _interest.member_id = ?", app.session.member.id 
    71     } )
    72     :add_field("(_interest.member_id NOTNULL)", "is_interested")
    73     :left_join("delegating_interest_snapshot", "_delegating_interest", { [[
    74       _delegating_interest.issue_id = issue.id AND 
    75       _delegating_interest.member_id = ? AND
    76       _delegating_interest.snapshot_id = issue.latest_snapshot_id
    77     ]], app.session.member.id } )
    78     :add_field("_delegating_interest.delegate_member_ids[1]", "is_interested_by_delegation_to_member_id")
    79     :add_field("_delegating_interest.delegate_member_ids[array_upper(_delegating_interest.delegate_member_ids, 1)]", "is_interested_via_member_id")    
    80     :add_field("array_length(_delegating_interest.delegate_member_ids, 1)", "delegation_chain_length")
    81 end
    83 local function doit()
    85   local last_event_id
    87   local items = selector:exec()
    89   local row_class = "sectionRow"
    90   if for_sidebar then
    91     row_class = "sidebarRow"
    92   end
    94   if mode == "timeline" then
    95     local issues = items:load ( "issue" )
    96     local initiative = items:load ( "initiative" )
    97     items:load ( "suggestion" )
    98     items:load ( "member" )
    99     issues:load_everything_for_member_id ( app.session.member_id )
   100     initiative:load_everything_for_member_id ( app.session.member_id )
   101   elseif mode == "issue" then
   102     items:load_everything_for_member_id ( app.session.member_id )
   103   end
   105   local last_event_date
   106   for i, item in ipairs(items) do
   107     local event
   108     local issue
   109     if mode == "timeline" then
   110       event = item
   111       issue = item.issue
   112     elseif mode == "issue" then
   113       event = {}
   114       issue = item
   115     end
   117     last_event_id = event.id
   119     local class = "mdl-card mdl-shadow--2dp mdl-card__fullwidth event " .. row_class
   120     if event.suggestion_id then
   121       class = class .. " suggestion"
   122     end
   124     ui.container{ attr = { class = class, id = "issue_" .. issue.id }, content = function()
   125       local event_name
   126       local event_icon
   127       local negative_event = false
   129       local days_ago_text
   131       if mode == "timeline" then
   132         event_name = event.event_name
   133           if event.event == "issue_state_changed" then
   134             if event.state == "discussion" then
   135               event_name = _"Discussion started"
   136             elseif event.state == "verification" then
   137               event_name = _"Verification started"
   138             elseif event.state == "voting" then
   139               event_name = _"Voting started"
   140             elseif event.state == "finished_with_winner" then
   141               event_name = event.state_name
   142             elseif event.state == "finished_without_winner" then
   143               event_name = event.state_name
   144               negative_event = true
   145             else
   146               event_name = event.state_name
   147               negative_event = true
   148             end
   149           elseif event.event == "initiative_revoked" then
   150             negative_event = true
   151           end
   153         if event.time_ago == 0 then
   154           days_ago_text = _("today at #{time}", { time = format.time(event.occurrence) })
   155         elseif event.time_ago == 1 then
   156           days_ago_text = _("yesterday at #{time}", { time = format.time(event.occurrence) })
   157         else
   158           days_ago_text = _("#{interval} ago", { interval = format.interval_text ( event.time_ago ) } )
   159         end
   161       elseif mode == "issue" then
   162         local event_icons_map = {
   163           admission = "bubble_chart",
   164           discussion = "question_answer",
   165           verification = "find_in_page",
   166           voting = "mail",
   167           finished_with_winner = "gavel",
   168           finished_without_winner = "gavel",
   169           canceled = "clear"
   170         }
   171         event_icon = event_icons_map[issue.state] or event_icons_map["canceled"]
   172         event_name = issue.state_name
   173         if issue.state_time_left:sub(1,1) ~= "-" then
   174           days_ago_text = _( "#{interval_text} left", {
   175             interval_text = format.interval_text ( issue.state_time_left )
   176           })
   177         elseif issue.closed then
   178           days_ago_text = _( "#{interval_text} ago", {
   179             interval_text = format.interval_text ( issue.closed_ago )
   180           })
   181         else
   182           days_ago_text = _"phase ends soon" 
   183         end
   184         if issue.closed and not issue.fully_frozen then
   185           negative_event = true
   186         end
   187         if issue.state == "finished_without_winner" 
   188             or issue.state == "canceled_no_initiative_admitted"
   189             or issue.state == "canceled_by_admin"
   190         then
   191           negative_event = true
   192         end
   193       end
   195       local class= "event_info"
   197       if negative_event then
   198         class = class .. " negative"
   199       end
   201       if not for_issue and not for_initiative then
   202         ui.container{ attr = { class = "mdl-card__title mdl-card--has-fab mdl-card--border card-issue" }, content = function()
   203           ui.container{ attr = { class = "contextlinks" }, content = function()
   204             if not (config.single_unit_id and config.single_area_id) then
   205               if not config.single_unit_id then
   206                 slot.put ( " " )
   207                 ui.link{
   208                   module = "index", view = "index", params = { unit = issue.area.unit_id },
   209                   attr = { class = "unit" }, content = issue.area.unit.name
   210                 }
   211               end
   212               if not config.single_area_id then
   213                 if not config.single_unit_id then
   214                   slot.put(" » ")
   215                 end
   216                 ui.link{
   217                   module = "index", view = "index", params = { unit = issue.area.unit_id, area = issue.area_id },
   218                   attr = { class = "area" }, content = issue.area.name
   219                 }
   220               end
   221             end
   222             slot.put(" » ")
   223             ui.link{
   224               module = "issue", view = "show", id = issue.id,
   225               attr = { class = "issue" }, content = issue.name
   226             }
   227 --          end }
   228 --          ui.container{ attr = { class = "mdl-card__subtitle-text .mdl-cell--hide-phone" }, content = function()
   229             ui.container{ attr = { class = class, style = "float: right; color: #fff;" }, content = function ()
   230               if event_icon then
   231                 ui.tag{ tag = "i", attr = { class = "material-icons", ["aria-hidden"] = "true" }, content = event_icon }
   232               end
   233               slot.put(" ")
   234               ui.tag { content = event_name }
   235               slot.put(" ")
   236               ui.tag{ content = "(" .. days_ago_text .. ")" }
   237             end }
   238           end }
   239           if app.session.member and issue.fully_frozen and not issue.closed and not issue.member_info.direct_voted and app.session.member:has_voting_right_for_unit_id(issue.area.unit_id) then
   240             ui.link {
   241               attr = { class = "mdl-button mdl-js-button mdl-button--fab mdl-button--colored" ,
   242                 style = "position: absolute; right: 20px; bottom: -27px;"
   243               },
   244               module = "vote", view = "list", 
   245               params = { issue_id = issue.id },
   246               content = function()
   247                 ui.tag{ tag = "i", attr = { class = "material-icons" }, content = config.voting_icon or "mail_outline" }
   248               end 
   249             }
   250           end
   251         end }
   252       end
   254       if event.suggestion_id then
   255         ui.container{ attr = { class = "suggestion" }, content = function()
   256           ui.link{
   257             text = format.string(event.suggestion.name, {
   258               truncate_at = 160, truncate_suffix = true
   259             }),
   260             module = "initiative", view = "show", id = event.initiative.id,
   261             params = { suggestion_id = event.suggestion_id },
   262             anchor = "s" .. event.suggestion_id
   263           }
   264         end }
   265       end
   267       if not for_initiative and (not for_issue or event.initiative_id) then
   269         ui.container{ attr = { class = "initiative_list" }, content = function()
   270           if event.initiative_id then
   271             local initiative = event.initiative
   273             execute.view{ module = "initiative", view = "_list", params = { 
   274               issue = issue,
   275               initiative = initiative,
   276               for_event = mode == "timeline" and not (event.state == issue.state)
   278             } }
   279           else
   280             local initiatives = issue.initiatives
   281             execute.view{ module = "initiative", view = "_list", params = { 
   282               issue = issue,
   283               initiatives = initiatives,
   284               for_event = mode == "timeline" and not (event.state == issue.state)
   285             } }
   286           end
   287         end }
   288       end
   289       if 
   290         app.session.member_id and (
   291           (not issue.fully_frozen and app.session.member:has_initiative_right_for_unit_id(issue.area.unit_id))
   292           or (issue.fully_frozen and app.session.member:has_voting_right_for_unit_id(issue.area.unit_id))
   293         )
   294       then
   295         ui.container{ attr = { class = "mdl-card__actions mdl-card--border" }, content = function()
   296           execute.view{ 
   297             module = "delegation", view = "_info", params = { 
   298               issue = issue, member = for_member 
   299             }
   300           }
   301         end }
   302       end
   303     end }
   305   end
   307   if mode == "timeline" then
   308     if for_sidebar then
   309       ui.container { attr = { class = row_class }, content = function ()
   310         ui.link{
   311           attr = { class = "moreLink" },
   312           text = _"Show full history",
   313           module = "initiative", view = "history", id = for_initiative.id
   314         }
   315       end }
   316     elseif #items > limit then
   317       ui.container { attr = { class = row_class }, content = function ()
   318         local params = request.get_param_strings()
   319         ui.link{
   320           attr = { class = "moreLink" },
   321           text = _"Show older events",
   322           module = request.get_module(),
   323           view = request.get_view(),
   324           id = for_unit and for_unit.id or for_area and for_area.id or for_issue and for_issue.id or for_member and for_member.id,
   325           params = {
   326             mode = "timeline",
   327             event_max_id = last_event_id,
   328             tab = params["tab"],
   329             phase = params["phase"],
   330             closed = params["closed"]
   331           }
   332         }
   333       end }
   334     elseif #items < 1 then
   335       ui.container { attr = { class = row_class }, content = _"No more events available" }
   336     end
   337   end
   339   if #items < 1 then
   340     ui.section( function()
   341       ui.sectionRow( function()
   342         ui.container{ content = _"No results for this selection" }
   343       end )
   344     end )
   345   end
   348 end
   351 local filters = {}
   353 if not for_initiative and not for_issue and not no_filter then
   355   filters = execute.chunk{
   356     module = "issue", chunk = "_filters", params = {
   357     for_events = mode == "timeline" and true or false,
   358     member = app.session.member, 
   359     for_member = for_member, 
   360     state = for_state, 
   361     for_unit = for_unit and true or false, 
   362     for_area = for_area and true or false
   363   }}
   364 end
   366 filters.opened = true
   367 filters.selector = selector
   370 local function dotabs()
   371   slot.select("filter", function()
   372     ui.container{ attr = { class = "mdl-tabs mdl-js-tabs mdl-js-ripple-effect float-left" }, content = function()
   373       ui.container{ attr = { class = "mdl-tabs__tab-bar" }, content = function()
   374         local mode = request.get_param{ name = "mode" }
   375         local css_active = (not mode or mode == "issue") and " is-active" or ""
   376         ui.link{ module = request.get_module(), view = request.get_view(), id = request.get_id_string(), content = "Issues", attr = { class = "mdl-tabs__tab" .. css_active } }
   377         local css_active = mode and " is-active" or " "
   378         ui.link{ module = request.get_module(), view = request.get_view(), id = request.get_id_string(), params = { mode = "timeline" }, content = "Timeline", attr = { class = "mdl-tabs__tab" .. css_active } }
   379         ui.link{ module = "member", view = "list", content = "Member", attr = { class = "mdl-tabs__tab" } }
   380       end }
   381     end }
   382   end)
   383 end
   386 if mode == "timeline" then
   387   --dotabs()
   388   filters.content = function()
   389     doit()
   390   end
   391 else
   392   -- dotabs()
   393   filters.content = function()
   394     if config.voting_only then
   395       local admission_order_field = "filter_issue_order.order_in_unit"
   396       if for_area then
   397         admission_order_field = "filter_issue_order.order_in_area"
   398       end
   399       selector:left_join ( "issue_order_in_admission_state", "filter_issue_order",    "filter_issue_order.id = issue.id" )
   400       selector:add_order_by ( "issue.closed DESC NULLS FIRST" )
   401       selector:add_order_by ( "issue.accepted ISNULL" )
   402       selector:add_order_by ( "CASE WHEN issue.accepted ISNULL THEN NULL ELSE justify_interval(coalesce(issue.fully_frozen + issue.voting_time, issue.half_frozen + issue.verification_time, issue.accepted + issue.discussion_time, issue.created + issue.max_admission_time) - now()) END" )
   403       selector:add_order_by ( "CASE WHEN issue.accepted ISNULL THEN " .. admission_order_field .. " ELSE NULL END" )
   404       selector:add_order_by ( "id" )
   405     end
   406     if not search then
   407 --      execute.view{ module = "index", view = "_head" }
   408     end
   409     ui.paginate{
   410       selector = selector,
   411       per_page = 25,
   412       content = doit
   413     }
   414   end
   415 end
   417 filters.class = "mdl-special-card mdl-card__fullwidth mdl-shadow--2dp"
   419 filters.legend = _"Filter issues:"
   421 ui.filters(filters)
