Jump to content
Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
If you make an edit, a temporary account will be created for you.

Module:Feedback: Difference between revisions

From The Petit Planet Wiki
ReisuDesign (talk | contribs)
No edit summary
ReisuDesign (talk | contribs)
No edit summary
Line 5: Line 5:
------------------------------------------------------------
------------------------------------------------------------


local function isTruthy(v)
local function isTruthy(value)
if v == nil then
if value == nil then
return false
return false
end
end


v = mw.text.trim(tostring(v)):lower()
value = mw.text.trim(tostring(value)):lower()


return v ~= ''
return value ~= ''
and v ~= '0'
and value ~= '0'
and v ~= 'false'
and value ~= 'false'
and v ~= 'no'
and value ~= 'no'
and v ~= 'off'
and value ~= 'off'
end
 
 
local function trim(value)
return mw.text.trim(
tostring(value or '')
)
end
end




local function formatUser(user)
local function formatUser(user)
user = mw.text.trim(tostring(user or ''))
user = trim(user)


if user == '' or user:lower() == 'anonymous' then
if user == '' or user:lower() == 'anonymous' then
Line 32: Line 39:
user
user
)
)
end
local function currentSubjectPage()
local title =
mw.title.getCurrentTitle()
return title.subjectPageTitle.fullText
end
local function getBucket()
if not (mw.ext and mw.ext.bucket) then
return nil, 'Bucket extension is not available'
end
local bucket =
mw.ext.bucket('feedback')
if not bucket then
return nil, 'Feedback bucket could not be opened'
end
return bucket
end
local function sortNewestFirst(results)
if not results then
return {}
end
table.sort(
results,
function(a, b)
local aDate =
trim(a.createddate)
local bDate =
trim(b.createddate)
-- Entries without a date go last.
if aDate == '' and bDate ~= '' then
return false
end
if aDate ~= '' and bDate == '' then
return true
end
if aDate ~= bDate then
return aDate > bDate
end
-- Stable-ish fallback.
return trim(a.id) > trim(b.id)
end
)
return results
end
local function queryFeedback(page, status)
local bucket, err =
getBucket()
if not bucket then
return nil, err
end
local query =
bucket.select(
'page',
'user',
'status',
'feedback',
'id',
'createddate',
'link',
'image',
'resolvedby',
'resolveddate'
)
if page and page ~= '' then
query.where(
'page',
page
)
end
if status
and status ~= ''
and status ~= 'all'
then
query.where(
'status',
status
)
end
local success, results =
pcall(function()
return query.run()
end)
if not success then
return nil, tostring(results)
end
return sortNewestFirst(
results or {}
)
end
------------------------------------------------------------
-- Save feedback
------------------------------------------------------------
local function saveFeedback(data)
local bucket, err =
getBucket()
if not bucket then
return false, err
end
local success, saveError =
pcall(function()
bucket.put({
page = data.page,
user = data.user,
status = data.status,
feedback = data.feedback,
id = data.id,
createddate = data.createddate,
link = data.link,
image = data.image,
resolvedby = data.resolvedby,
resolveddate = data.resolveddate
})
end)
if not success then
return false, tostring(saveError)
end
return true
end
end




------------------------------------------------------------
------------------------------------------------------------
-- Feedback card
-- Shared metadata
------------------------------------------------------------
------------------------------------------------------------


local function renderCard(entry, showInstruction)
local function renderMetadata(root, entry)


local resolved =
local meta =
(entry.status == 'resolved')
root:tag('div')
:addClass('feedback-meta')
 
if entry.user and entry.user ~= '' then
 
meta:tag('span')
:addClass('feedback-user')
:wikitext(
formatUser(entry.user)
)
 
end
 
if entry.createddate
and entry.createddate ~= ''
then
 
meta:tag('span')
:addClass('feedback-meta-date')
:wikitext(
' · '
.. mw.text.nowiki(
entry.createddate
)
)
 
end
end
 
 
------------------------------------------------------------
-- Attachments
------------------------------------------------------------
 
local function renderAttachments(root, entry)
 
local link =
trim(entry.link)
 
local image =
trim(entry.image)
 
if link == ''
and image == ''
then
return
end
 
local attachments =
root:tag('div')
:addClass('feedback-attachments')
 
if link ~= '' then
 
attachments:tag('span')
:addClass('feedback-attachment')
:wikitext(
'🔗 '
.. '['
.. link
.. ' Link]'
)
 
end
 
if image ~= '' then
 
attachments:tag('span')
:addClass('feedback-attachment')
 
attachments:tag('span')
:addClass('feedback-image-icon')
:wikitext('▣')
 
attachments:tag('span')
:addClass('feedback-image-link')
:wikitext(
'[[Media:'
.. image
.. '|Picture]]'
)


local user =
end
entry.user
end
or 'Anonymous'


local feedback =
entry.feedback
or 'No feedback provided'


local page =
------------------------------------------------------------
entry.page
-- Resolution information
------------------------------------------------------------


local id =
local function renderResolution(root, entry)
mw.text.trim(
tostring(entry.id or '')
)


local resolvedBy =
local resolvedBy =
mw.text.trim(
trim(entry.resolvedby)
tostring(entry.resolvedby or '')
 
local resolvedDate =
trim(entry.resolveddate)
 
if resolvedBy == ''
and resolvedDate == ''
then
return
end
 
local resolution =
root:tag('div')
:addClass('feedback-resolution')
 
resolution:wikitext('↳ Resolved by ')
 
if resolvedBy ~= '' then
resolution:wikitext(
formatUser(resolvedBy)
)
)
else
resolution:wikitext('Unknown')
end


local resolvedDate =
if resolvedDate ~= '' then
mw.text.trim(
resolution:wikitext(
tostring(entry.resolveddate or '')
' on '
.. mw.text.nowiki(resolvedDate)
)
)
end
end
------------------------------------------------------------
-- Resolve control
------------------------------------------------------------
local function renderResolveControl(
parent,
id,
compact
)
if not id or id == '' then
return
end
local control =
parent:tag('span')
:addClass('feedback-resolve-button')
if compact then
control
:addClass('feedback-resolve-button--compact')
:attr('aria-label', 'Resolve feedback')
:attr('title', 'Resolve feedback')
:wikitext('✓')
else
control
:attr('role', 'button')
:attr('tabindex', '0')
:attr('aria-label', 'Resolve feedback')
:wikitext('Resolve')
end
control:attr(
'data-feedback-id',
id
)
end
------------------------------------------------------------
-- Full feedback card
------------------------------------------------------------
local function renderFullCard(entry)
local resolved =
entry.status == 'resolved'
local id =
trim(entry.id)


local root =
local root =
Line 78: Line 395:
or 'feedback-box--unresolved'
or 'feedback-box--unresolved'
)
)
if id ~= '' then
root:attr(
'data-feedback-id',
id
)
end


--------------------------------------------------------
--------------------------------------------------------
-- Meta
-- Main line
--------------------------------------------------------
--------------------------------------------------------


local meta =
local main =
root:tag('div')
root:tag('div')
:addClass('feedback-meta')
:addClass('feedback-main-line')
 
main:tag('span')
:addClass('feedback-status')
:addClass(
resolved
and 'feedback-status--resolved'
or 'feedback-status--unresolved'
)
:wikitext(
resolved
and 'RESOLVED'
or 'UNRESOLVED'
)
 
main:tag('span')
:addClass('feedback-text')
:wikitext(
entry.feedback
or 'No feedback provided'
)
 
--------------------------------------------------------
-- Metadata
--------------------------------------------------------
 
local metadata =
main:tag('span')
:addClass('feedback-inline-meta')
 
if entry.user
and entry.user ~= ''
then
 
metadata:wikitext(
' · '
.. formatUser(entry.user)
)
 
end
 
if entry.createddate
and entry.createddate ~= ''
then
 
metadata:wikitext(
' '
.. mw.text.nowiki(
entry.createddate
)
)
 
end
 
--------------------------------------------------------
-- Resolve button
--------------------------------------------------------
 
if not resolved
and id ~= ''
then
 
renderResolveControl(
main,
id,
false
)
 
end
 
--------------------------------------------------------
-- Attachments
--------------------------------------------------------
 
renderAttachments(
root,
entry
)
 
--------------------------------------------------------
-- Resolution
--------------------------------------------------------
 
if resolved then
renderResolution(
root,
entry
)
end
 
--------------------------------------------------------
-- Missing ID warning
--------------------------------------------------------


if page and page ~= '' then
if not resolved
and id == ''
then


meta:tag('span')
root:tag('div')
:addClass('feedback-page')
:addClass('feedback-missing-id')
:wikitext(
:wikitext(
string.format(
'Add an ID to enable the Resolve button.'
'[[%s]]',
page
)
)
)


meta:tag('span')
end
:addClass('feedback-separator')
 
:wikitext(' • ')
return tostring(root)
end
 
 
------------------------------------------------------------
-- Compact article card
------------------------------------------------------------
 
local function renderCompactCard(entry)
 
local resolved =
entry.status == 'resolved'
 
local id =
trim(entry.id)
 
local root =
mw.html.create('div')
:addClass('feedback-page-card')
:addClass(
resolved
and 'feedback-page-card--resolved'
or 'feedback-page-card--unresolved'
)


if id ~= '' then
root:attr(
'data-feedback-id',
id
)
end
end


meta:tag('span')
local content =
:addClass('feedback-user')
root:tag('div')
:wikitext(
:addClass('feedback-page-card-content')
formatUser(user)
)


meta:tag('span')
local firstLine =
:addClass('feedback-separator')
content:tag('div')
:wikitext(' • ')
:addClass('feedback-page-card-line')


meta:tag('span')
firstLine:tag('span')
:addClass('feedback-status')
:addClass('feedback-status')
:addClass(
:addClass(
Line 123: Line 564:
:wikitext(
:wikitext(
resolved
resolved
and 'Resolved'
and 'RESOLVED'
or 'Unresolved'
or 'UNRESOLVED'
)
)
firstLine:tag('span')
:addClass('feedback-page-card-text')
:wikitext(
entry.feedback
or 'No feedback provided'
)
local meta =
content:tag('div')
:addClass('feedback-page-card-meta')
if entry.user
and entry.user ~= ''
then
meta:wikitext(
formatUser(entry.user)
)
end
if entry.createddate
and entry.createddate ~= ''
then
meta:wikitext(
' · '
.. mw.text.nowiki(
entry.createddate
)
)
end
if resolved then
local resolvedBy =
trim(entry.resolvedby)
local resolvedDate =
trim(entry.resolveddate)
if resolvedBy ~= ''
or resolvedDate ~= ''
then
local resolution =
content:tag('div')
:addClass(
'feedback-page-card-resolution'
)
resolution:wikitext(
'↳ Resolved by '
)
if resolvedBy ~= '' then
resolution:wikitext(
formatUser(resolvedBy)
)
else
resolution:wikitext(
'Unknown'
)
end
if resolvedDate ~= '' then
resolution:wikitext(
' · '
.. mw.text.nowiki(
resolvedDate
)
)
end
end
else
if id ~= '' then
renderResolveControl(
root,
id,
true
)
end
end
renderAttachments(
content,
entry
)
return tostring(root)
end
------------------------------------------------------------
-- Page feedback section
------------------------------------------------------------
function p.page(frame)
local page =
currentSubjectPage()
local allEntries, err =
queryFeedback(page)
if not allEntries then
return ''
end
if #allEntries == 0 then
return ''
end
local unresolved = {}
local resolved = {}
for _, row in ipairs(allEntries) do
if row.status == 'resolved' then
table.insert(
resolved,
row
)
else
table.insert(
unresolved,
row
)
end
end
local root =
mw.html.create('section')
:addClass('feedback-page-section')
:attr('id', 'feedback-section')
:attr(
'data-unresolved-count',
#unresolved
)
:attr(
'data-resolved-count',
#resolved
)


--------------------------------------------------------
--------------------------------------------------------
-- Feedback body
-- Heading
--------------------------------------------------------
--------------------------------------------------------


root:tag('div')
root:tag('h2')
:addClass('feedback-body')
:addClass('feedback-page-heading')
:wikitext(feedback)
:wikitext('Feedbacks')


--------------------------------------------------------
--------------------------------------------------------
-- Resolution information
-- Filter controls
--------------------------------------------------------
--------------------------------------------------------


if resolved and resolvedBy ~= '' then
local filters =
root:tag('div')
:addClass('feedback-page-filters')


local resolution =
local unresolvedFilter =
root:tag('div')
filters:tag('span')
:addClass('feedback-resolution')
:addClass('feedback-page-filter')
:addClass(
'feedback-page-filter--active'
)
:attr(
'role',
'button'
)
:attr(
'tabindex',
'0'
)
:attr(
'data-feedback-filter',
'unresolved'
)
 
unresolvedFilter:wikitext(
'Unresolved '
)


resolution:wikitext(
unresolvedFilter:tag('span')
'Resolved by '
:addClass('feedback-page-filter-count')
.. formatUser(resolvedBy)
:wikitext(
tostring(#unresolved)
)
)


if resolvedDate ~= '' then
if #resolved > 0 then
 
local resolvedFilter =
filters:tag('span')
:addClass('feedback-page-filter')
:attr(
'role',
'button'
)
:attr(
'tabindex',
'0'
)
:attr(
'data-feedback-filter',
'resolved'
)
 
resolvedFilter:wikitext(
'Resolved '
)


resolution:wikitext(
resolvedFilter:tag('span')
' on '
:addClass('feedback-page-filter-count')
.. mw.text.nowiki(resolvedDate)
:wikitext(
tostring(#resolved)
)
)


end
end
end


--------------------------------------------------------
--------------------------------------------------------
-- Resolve instruction
-- Unresolved list
--------------------------------------------------------
--------------------------------------------------------


if showInstruction and not resolved then
local unresolvedList =
root:tag('div')
:addClass(
'feedback-page-list'
)
:addClass(
'feedback-page-list--unresolved'
)
 
if #unresolved > 0 then


local instruction =
for _, row in ipairs(unresolved) do
root:tag('div')
unresolvedList:node(
:addClass('feedback-instruction')
renderCompactCard(row)
)
end


if id ~= '' then
else


instruction:wikitext(
unresolvedList:tag('div')
'If this feedback has been resolved, click the button below. '
:addClass(
'feedback-page-empty'
)
:wikitext(
'No unresolved feedback.'
)
)


instruction:tag('span')
end
:addClass('feedback-resolve-button')
:attr('role', 'button')
:attr('tabindex', '0')
:attr('data-feedback-id', id)
:wikitext('Resolve')


else
--------------------------------------------------------
-- Resolved list
--------------------------------------------------------
 
if #resolved > 0 then
 
local resolvedList =
root:tag('div')
:addClass(
'feedback-page-list'
)
:addClass(
'feedback-page-list--resolved'
)


instruction:wikitext(
for _, row in ipairs(resolved) do
'Add an ID to this feedback to enable the Resolve button.'
resolvedList:node(
renderCompactCard(row)
)
)
end


end
end
end


Line 197: Line 858:


------------------------------------------------------------
------------------------------------------------------------
-- Save feedback
-- Feedback counts
------------------------------------------------------------
------------------------------------------------------------


local function saveFeedback(data)
function p.counts(frame)


if not (mw.ext and mw.ext.bucket) then
local page =
return false, 'Bucket extension is not available'
currentSubjectPage()
end


local bucket =
local entries =
mw.ext.bucket('feedback')
queryFeedback(page)


if not bucket then
if not entries then
return false, 'Feedback bucket could not be opened'
return ''
end
end


local success, err =
local unresolved = 0
pcall(function()
local resolved = 0


bucket.put({
for _, row in ipairs(entries) do
page = data.page,
user = data.user,
status = data.status,
feedback = data.feedback,
id = data.id,
resolvedby = data.resolvedby,
resolveddate = data.resolveddate
})


end)
if row.status == 'resolved' then
resolved = resolved + 1
else
unresolved = unresolved + 1
end


if not success then
return false, tostring(err)
end
end


return true
return tostring(
mw.html.create('span')
:addClass('feedback-count-data')
:attr(
'data-unresolved',
unresolved
)
:attr(
'data-resolved',
resolved
)
)
end
end




------------------------------------------------------------
------------------------------------------------------------
-- Main
-- Main / individual feedback
------------------------------------------------------------
------------------------------------------------------------


Line 248: Line 913:
parent and parent.args
parent and parent.args
or frame.args
or frame.args
local pageTitle = nil
if parent and parent.getTitle then
local title =
parent:getTitle()
if title
and title ~= ''
and not title:find('^Template:')
then
pageTitle = title
end
end
return p._main(
args,
pageTitle
)
end
function p._main(args, pageTitle)


local feedback =
local feedback =
Line 284: Line 923:


local id =
local id =
args.id
trim(args.id)
or ''
 
local createdDate =
trim(args.createddate)
 
local link =
trim(args.link)
 
local image =
trim(args.image)


local resolvedBy =
local resolvedBy =
args.resolvedby
trim(args.resolvedby)
or ''


local resolvedDate =
local resolvedDate =
args.resolveddate
trim(args.resolveddate)
or ''


local resolved =
local resolved =
Line 303: Line 948:
or 'unresolved'
or 'unresolved'


local currentPage =
local page =
pageTitle
currentSubjectPage()
or mw.title.getCurrentTitle().fullText
 
--------------------------------------------------------
-- Save to Bucket
--------------------------------------------------------


local saved, saveError =
local saved, saveError =
saveFeedback({
saveFeedback({
page = currentPage,
page = page,
user = user,
user = user,
status = status,
status = status,
feedback = feedback,
feedback = feedback,
id = id,
id = id,
createddate = createdDate,
link = link,
image = image,
resolvedby = resolvedBy,
resolvedby = resolvedBy,
resolveddate = resolvedDate
resolveddate = resolvedDate
Line 324: Line 967:
if not saved then
if not saved then


return '<strong style="color:red;">Feedback bucket error: '
return
'<strong style="color:red;">'
.. 'Feedback bucket error: '
.. mw.text.nowiki(
.. mw.text.nowiki(
saveError or 'Unknown error'
saveError or 'Unknown error'
Line 331: Line 976:


end
end
--------------------------------------------------------
-- Render card
--------------------------------------------------------


local card =
local card =
renderCard({
renderFullCard({
page = nil,
page = page,
user = user,
user = user,
status = status,
status = status,
feedback = feedback,
feedback = feedback,
id = id,
id = id,
createddate = createdDate,
link = link,
image = image,
resolvedby = resolvedBy,
resolvedby = resolvedBy,
resolveddate = resolvedDate
resolveddate = resolvedDate
}, true)
})
 
--------------------------------------------------------
-- Categories
--------------------------------------------------------


local categories = {}
local categories = {}
Line 369: Line 1,009:
end
end


if mw.text.trim(
if id == '' then
tostring(id)
) == '' then


table.insert(
table.insert(
Line 386: Line 1,024:


------------------------------------------------------------
------------------------------------------------------------
-- Render feedback list
-- Feedback table
------------------------------------------------------------
------------------------------------------------------------


local function renderFeedbackList(results)
local function renderTable(entries, includeResolved)


if not results or #results == 0 then
local tableRoot =
return '<em>No feedback entries found.</em>'
mw.html.create('table')
end
:addClass('feedback-table')


local list = {}
local header =
tableRoot:tag('tr')


for _, row in ipairs(results) do
if includeResolved then


table.insert(
header:tag('th')
list,
:wikitext('Status')
renderCard(
row,
false
)
)


end
end


return table.concat(
header:tag('th')
list,
:wikitext('Page')
'\n'
)
end


header:tag('th')
:wikitext('Username')


------------------------------------------------------------
header:tag('th')
-- View feedback
:wikitext('Date')
------------------------------------------------------------
 
function p.view(frame)


local parent =
header:tag('th')
frame:getParent()
:wikitext('Feedback')


local args
if includeResolved then


if frame
header:tag('th')
and frame.args
:wikitext('Resolved by')
and frame.args.status
then


args =
header:tag('th')
frame.args
:wikitext('Resolved date')
 
elseif parent
and parent.args
and parent.args.status
then
 
args =
parent.args
 
elseif frame
and frame.args
then
 
args =
frame.args
 
elseif parent
and parent.args
then
 
args =
parent.args
 
else
 
args = {}


end
end


return p._view(
if #entries == 0 then
args,
frame
)
end


local row =
tableRoot:tag('tr')


function p._view(args, frame)
local colspan =
includeResolved
and '7'
or '4'


if not (mw.ext and mw.ext.bucket) then
row:tag('td')
return '<em>Bucket extension is not available.</em>'
:attr(
end
'colspan',
 
colspan
frame =
)
frame
:addClass(
or mw.getCurrentFrame()
'feedback-table-empty'
 
)
local rawStatus =
:wikitext(
args.status
'No feedback entries found.'
or args[1]
)
 
local filterStatus =
rawStatus
and mw.text.trim(rawStatus):lower()
or nil
 
--------------------------------------------------------
-- Filtered views
--------------------------------------------------------
 
if filterStatus == 'resolved'
or filterStatus == 'unresolved'
then
 
local query =
mw.ext.bucket('feedback')
 
query.select(
'page',
'user',
'status',
'feedback',
'id',
'resolvedby',
'resolveddate'
)


query.where(
return tostring(tableRoot)
'status',
filterStatus
)
 
return renderFeedbackList(
query.run()
)
end
end


--------------------------------------------------------
for _, entry in ipairs(entries) do
-- All
--------------------------------------------------------


if filterStatus == 'all' then
local row =
tableRoot:tag('tr')
:addClass(
entry.status == 'resolved'
and 'feedback-table-row--resolved'
or 'feedback-table-row--unresolved'
)


local query =
if includeResolved then
mw.ext.bucket('feedback')


query.select(
row:tag('td')
'page',
:addClass('feedback-table-status')
'user',
:wikitext(
'status',
entry.status == 'resolved'
'feedback',
and 'Resolved'
'id',
or 'Unresolved'
'resolvedby',
)
'resolveddate'
)


return renderFeedbackList(
end
query.run()
)
end


--------------------------------------------------------
row:tag('td')
-- Default tabbed view
:addClass('feedback-table-page')
--------------------------------------------------------
:wikitext(
'[['
.. entry.page
.. ']]'
)


local qAll =
row:tag('td')
mw.ext.bucket('feedback')
:addClass('feedback-table-user')
:wikitext(
formatUser(entry.user)
)


qAll.select(
row:tag('td')
'page',
:addClass('feedback-table-date')
'user',
:wikitext(
'status',
mw.text.nowiki(
'feedback',
entry.createddate
'id',
or '—'
'resolvedby',
'resolveddate'
)
 
local allEntries =
qAll.run()
 
local allList = {}
local unresolvedList = {}
local resolvedList = {}
 
if allEntries and #allEntries > 0 then
 
for _, row in ipairs(allEntries) do
 
local card =
renderCard(
row,
false
)
)
)


table.insert(
row:tag('td')
allList,
:addClass('feedback-table-feedback')
card
:wikitext(
entry.feedback
or ''
)
)


if row.status == 'resolved' then
if includeResolved then


table.insert(
row:tag('td')
resolvedList,
:addClass(
card
'feedback-table-resolved-by'
)
:wikitext(
entry.resolvedby
and formatUser(
entry.resolvedby
)
or '—'
)
)


else
row:tag('td')
 
:addClass(
table.insert(
'feedback-table-resolved-date'
unresolvedList,
)
card
:wikitext(
entry.resolveddate
and mw.text.nowiki(
entry.resolveddate
)
or '—'
)
)


end
end
end
end
end


--------------------------------------------------------
return tostring(tableRoot)
-- All content
end
--------------------------------------------------------


local allContent


if #allList > 0 then
------------------------------------------------------------
-- Global feedback page
------------------------------------------------------------


allContent =
function p.view(frame)
table.concat(
allList,
'\n'
)


else
local parent =
frame:getParent()


allContent =
local args =
'<em>No feedback entries found.</em>'
parent and parent.args
or frame.args


end
local status =
trim(
args.status
or ''
):lower()


--------------------------------------------------------
--------------------------------------------------------
-- Unresolved content
-- Specific table
--------------------------------------------------------
--------------------------------------------------------


local unresolvedContent
if status == 'resolved'
or status == 'unresolved'
then


if #unresolvedList > 0 then
local entries =
 
queryFeedback(
unresolvedContent =
nil,
table.concat(
status
unresolvedList,
'\n'
)
)


else
if not entries then
 
return ''
unresolvedContent =
end
'<em>No unresolved feedback entries found.</em>'


return renderTable(
entries,
false
)
end
end


--------------------------------------------------------
--------------------------------------------------------
-- Resolved content
-- Dashboard
--------------------------------------------------------
--------------------------------------------------------


local resolvedContent
local unresolved =
queryFeedback(
nil,
'unresolved'
)
 
local all =
queryFeedback(
nil,
'all'
)


if #resolvedList > 0 then
if not unresolved then
unresolved = {}
end


resolvedContent =
if not all then
table.concat(
all = {}
resolvedList,
end
'\n'
)


else
local root =
mw.html.create('div')
:addClass('feedback-dashboard')


resolvedContent =
root:tag('h2')
'<em>No resolved feedback entries found.</em>'
:addClass('feedback-dashboard-heading')
:wikitext('Unresolved Feedback')


end
root:node(
renderTable(
unresolved,
false
)
)


--------------------------------------------------------
root:tag('h2')
-- Tabber
:addClass('feedback-dashboard-heading')
--------------------------------------------------------
:wikitext('All Feedback')


local tabberBody =
root:node(
string.format(
renderTable(
'All=\n%s\n|-|Unresolved=\n%s\n|-|Resolved=\n%s',
all,
allContent,
true
unresolvedContent,
resolvedContent
)
)
)


return frame:callParserFunction(
return tostring(root)
'#tag',
{
'tabber',
tabberBody
}
)
end
end
------------------------------------------------------------
-- Aliases
------------------------------------------------------------
p.table = p.view


return p
return p