nodebb-plugin-poll 3.2.9 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,13 +2,11 @@
2
2
 
3
3
  (function (Poll) {
4
4
  function vote(view, options) {
5
- var form = view.dom.votingPanel.find('form');
6
- var votes = form.serializeArray().map(function (option) {
7
- return parseInt(option.value, 10);
8
- });
5
+ const form = view.dom.votingPanel.find('form');
6
+ const votes = form.serializeArray().map(option => parseInt(option.value, 10));
9
7
 
10
8
  if (votes.length > 0) {
11
- var voteData = {
9
+ const voteData = {
12
10
  pollId: view.pollData.info.pollId,
13
11
  options: votes,
14
12
  voteAnon: options.voteAnon,
@@ -28,11 +26,11 @@
28
26
  }
29
27
  }
30
28
 
31
- var Actions = [
29
+ const Actions = [
32
30
  {
33
31
  // Voting
34
32
  register: function (view) {
35
- var self = this;
33
+ const self = this;
36
34
  view.dom.voteButton.off('click').on('click', function () {
37
35
  self.handle(view);
38
36
  });
@@ -46,7 +44,7 @@
46
44
  {
47
45
  // vote anon
48
46
  register: function (view) {
49
- var self = this;
47
+ const self = this;
50
48
  view.dom.voteAnonButton.off('click').on('click', function () {
51
49
  self.handle(view);
52
50
  });
@@ -60,19 +58,19 @@
60
58
  {
61
59
  // update voting
62
60
  register: function (view) {
63
- var self = this;
61
+ const self = this;
64
62
  view.dom.updateVoteButton.off('click').on('click', function () {
65
63
  self.handle(view);
66
64
  });
67
65
  },
68
66
  handle: function (view) {
69
- var form = view.dom.votingPanel.find('form');
70
- var votes = form.serializeArray().map(function (option) {
67
+ const form = view.dom.votingPanel.find('form');
68
+ const votes = form.serializeArray().map(function (option) {
71
69
  return parseInt(option.value, 10);
72
70
  });
73
71
 
74
72
  if (votes.length > 0) {
75
- var voteData = {
73
+ const voteData = {
76
74
  pollId: view.pollData.info.pollId,
77
75
  options: votes,
78
76
  };
@@ -88,13 +86,13 @@
88
86
  {
89
87
  // Remove vote
90
88
  register: function (view) {
91
- var self = this;
89
+ const self = this;
92
90
  view.dom.removeVoteButton.off('click').on('click', function () {
93
91
  self.handle(view);
94
92
  });
95
93
  },
96
94
  handle: function (view) {
97
- var voteData = { pollId: view.pollData.info.pollId };
95
+ const voteData = { pollId: view.pollData.info.pollId };
98
96
 
99
97
  socket.emit('plugins.poll.removeVote', voteData, function (err) {
100
98
  if (err) {
@@ -107,7 +105,7 @@
107
105
  {
108
106
  // Results button
109
107
  register: function (view) {
110
- var self = this;
108
+ const self = this;
111
109
  view.dom.resultsPanelButton.off('click').on('click', function () {
112
110
  self.handle(view);
113
111
  });
@@ -119,7 +117,7 @@
119
117
  {
120
118
  // To Voting button
121
119
  register: function (view) {
122
- var self = this;
120
+ const self = this;
123
121
  view.dom.votingPanelButton.off('click').on('click', function () {
124
122
  self.handle(view);
125
123
  });
@@ -131,13 +129,13 @@
131
129
  {
132
130
  // Option details
133
131
  register: function (view) {
134
- var self = this;
132
+ const self = this;
135
133
  view.dom.resultsPanel.off('click').on('click', '.poll-result-votecount', function (e) {
136
134
  self.handle(view, e);
137
135
  });
138
136
  },
139
137
  handle: function (view, e) {
140
- var optionId = $(e.currentTarget).parents('[data-poll-option-id]').data('poll-option-id');
138
+ const optionId = $(e.currentTarget).parents('[data-poll-option-id]').data('poll-option-id');
141
139
 
142
140
  socket.emit('plugins.poll.getOptionDetails', {
143
141
  pollId: view.pollData.info.pollId,
@@ -151,66 +149,50 @@
151
149
  });
152
150
  },
153
151
  },
154
- {
155
- // Editing
156
- register: function (view) {
157
- var self = this;
158
- view.dom.editButton.off('click').on('click', function () {
159
- self.handle(view);
160
- });
161
- },
162
- handle: function (view) {
163
- socket.emit('plugins.poll.getConfig', null, function (err, config) {
164
- if (err) {
165
- console.error(err);
166
- }
167
- Poll.creator.show(view.pollData, config, function () {});
168
- });
169
- },
170
- },
171
152
  ];
172
153
 
173
- var View = function (pollData) {
154
+ const View = function (pollData) {
174
155
  this.pollData = pollData;
175
156
  };
176
157
 
177
- View.prototype.load = function () {
178
- var self = this;
158
+ View.prototype.load = async function () {
159
+ const self = this;
179
160
  if (!self.pollData.container.length) {
180
161
  return;
181
162
  }
182
163
 
183
- app.parseAndTranslate('poll/view', { poll: self.pollData }, function (panel) {
184
- self.pollData.container.prepend(panel);
185
- self.dom = {
186
- panel: panel,
187
- votingForm: panel.find('.poll-voting-form'),
188
- messages: panel.find('.poll-view-messages'),
189
- votingPanel: panel.find('.poll-view-voting'),
190
- resultsPanel: panel.find('.poll-view-results'),
191
- voteButton: panel.find('.poll-button-vote'),
192
- voteAnonButton: panel.find('.poll-button-vote-anon'),
193
- updateVoteButton: panel.find('.poll-button-update-vote'),
194
- removeVoteButton: panel.find('.poll-button-remove-vote'),
195
- votingPanelButton: panel.find('.poll-button-voting'),
196
- resultsPanelButton: panel.find('.poll-button-results'),
197
- editButton: panel.find('.poll-button-edit'),
198
- };
199
-
200
- self.hideMessage();
201
-
202
- self.pollEndedOrDeleted();
203
- self.hasVotedAndVotingUpdateDisallowed();
204
-
205
- if (!app.user.uid || self.pollData.hasVoted) {
206
- self.showResultsPanel();
207
- } else {
208
- self.showVotingPanel();
209
- }
164
+ const panel = await app.parseAndTranslate('poll/view', {
165
+ poll: self.pollData,
166
+ isAdmin: app.user.isAdmin,
167
+ });
168
+ self.pollData.container.prepend(panel);
169
+ self.dom = {
170
+ panel: panel,
171
+ votingForm: panel.find('.poll-voting-form'),
172
+ messages: panel.find('.poll-view-messages'),
173
+ votingPanel: panel.find('.poll-view-voting'),
174
+ resultsPanel: panel.find('.poll-view-results'),
175
+ voteButton: panel.find('.poll-button-vote'),
176
+ voteAnonButton: panel.find('.poll-button-vote-anon'),
177
+ updateVoteButton: panel.find('.poll-button-update-vote'),
178
+ removeVoteButton: panel.find('.poll-button-remove-vote'),
179
+ votingPanelButton: panel.find('.poll-button-voting'),
180
+ resultsPanelButton: panel.find('.poll-button-results'),
181
+ };
182
+
183
+ self.hideMessage();
184
+
185
+ self.pollEndedOrDeleted();
186
+ self.hasVotedAndVotingUpdateDisallowed();
187
+
188
+ if (!app.user.uid || self.pollData.hasVoted) {
189
+ self.showResultsPanel();
190
+ } else {
191
+ self.showVotingPanel();
192
+ }
210
193
 
211
- Actions.forEach(function (action) {
212
- action.register(self);
213
- });
194
+ Actions.forEach(function (action) {
195
+ action.register(self);
214
196
  });
215
197
  };
216
198
 
@@ -219,7 +201,7 @@
219
201
  };
220
202
 
221
203
  View.prototype.voteUpdateAllowed = function () {
222
- return parseInt(this.pollData.settings.disallowVoteUpdate, 10) !== 1;
204
+ return parseInt(this.pollData.disallowVoteUpdate, 10) !== 1;
223
205
  };
224
206
 
225
207
  View.prototype.pollEndedOrDeleted = function () {
@@ -252,7 +234,7 @@
252
234
  this.pollEndedOrDeleted();
253
235
 
254
236
  this.pollData.options.forEach(function (option) {
255
- var el = this.dom.resultsPanel.find('[data-poll-option-id=' + option.id + ']');
237
+ const el = this.dom.resultsPanel.find('[data-poll-option-id=' + option.id + ']');
256
238
  el.find('.poll-result-votecount span').translateText(`[[poll:x-votes, ${option.voteCount}]]`);
257
239
  el.find('.poll-result-progressbar').css('width', option.percentage + '%')
258
240
  .find('span.percent').text(option.percentage);
@@ -264,7 +246,7 @@
264
246
  };
265
247
 
266
248
  View.prototype.showMessage = function (title, content) {
267
- var self = this;
249
+ const self = this;
268
250
 
269
251
  app.parseAndTranslate('poll/view/messages', { title: title, content: content }, function (html) {
270
252
  self.dom.messages.html(html).removeClass('hidden');
@@ -279,6 +261,7 @@
279
261
  require(['bootbox'], function (bootbox) {
280
262
  app.parseAndTranslate('poll/view/details', details, function (html) {
281
263
  bootbox.dialog({
264
+ title: `[[poll:x-users-voted-for-this-option, ${details.voteCount}]]`,
282
265
  message: html,
283
266
  backdrop: true,
284
267
  buttons: {
@@ -292,7 +275,7 @@
292
275
  };
293
276
 
294
277
  View.prototype.fillVotingForm = function () {
295
- var self = this;
278
+ const self = this;
296
279
  this.resetVotingForm();
297
280
  if (this.pollData.vote && this.pollData.vote.options) {
298
281
  this.pollData.vote.options.forEach(function (id) {
@@ -318,7 +301,7 @@
318
301
  }
319
302
  } else {
320
303
  this.showVoteButton();
321
- if (this.pollData.settings.allowAnonVoting) {
304
+ if (this.pollData.allowAnonVoting) {
322
305
  this.showVoteAnonButton();
323
306
  }
324
307
  }
@@ -399,16 +382,19 @@
399
382
  };
400
383
 
401
384
  Poll.view = {
402
- polls: {},
403
- load: function (pollData) {
404
- var view = new View(pollData);
405
- this.polls[pollData.info.pollId] = view;
406
-
407
- view.load();
385
+ polls: Object.create(null),
386
+ load: async function (pollData) {
387
+ const pollId = pollData.info.pollId;
388
+ if (Object.hasOwn(this.polls, pollId)) {
389
+ pollData.container.find(`[data-poll-id="${pollId}"]`).remove();
390
+ }
391
+ const view = new View(pollData);
392
+ this.polls[pollId] = view;
393
+ await view.load();
408
394
  },
409
395
  update: function (pollData, uid) {
410
- var pollId = pollData.info.pollId;
411
- if (this.polls.hasOwnProperty(pollId)) {
396
+ const pollId = pollData.info.pollId;
397
+ if (Object.hasOwn(this.polls, pollId)) {
412
398
  this.polls[pollId].update(pollData, uid);
413
399
  }
414
400
  },
@@ -1,4 +1 @@
1
- // Hide poll button in composer on reply
2
- .composer.reply .poll, .composer.reply .ql-poll {
3
- display: none;
4
- }
1
+
package/schema.md ADDED
@@ -0,0 +1,24 @@
1
+
2
+ post:100 {
3
+ ...
4
+ pollIds: ["200", "201", ...]
5
+ }
6
+
7
+ poll:200 {
8
+ "_key" : "poll:200",
9
+ "deleted" : 0,
10
+ "end" : 0, // or unix timestamp when voting will close
11
+ "pid" : 100,
12
+ "pollId" : 200,
13
+ "timestamp" : 1772893625284,
14
+ "uid" : 1,
15
+ "options": [
16
+ { id: "300", title: "option 1" }, { id: "301", title: "option 2" }, { id: "302", title: "option 3" }
17
+ ]
18
+ }
19
+
20
+ poll:123:voters // uids of voters zset
21
+
22
+ poll:123:options:300:votes { //zset of uids who voted for this option
23
+ score: "<timestamp>", value: "1"
24
+ }
@@ -1,5 +1,5 @@
1
1
  <div class="mb-3">
2
- <label class="form-label">Topic ID:</label>
3
- <input type="text" class="form-control" name="tid" />
2
+ <label class="form-label">Poll ID:</label>
3
+ <input type="text" class="form-control" name="pollId" />
4
4
  </div>
5
5
 
@@ -1,57 +1,72 @@
1
1
  <div class="acp-page-container">
2
2
  <div component="settings/main/header" class="row border-bottom py-2 m-0 sticky-top acp-page-main-header align-items-center">
3
- <div class="col-12 col-md-8 px-0 mb-1 mb-md-0">
4
- <h4 class="fw-bold tracking-tight mb-0">{title}</h4>
5
- </div>
6
- <div class="col-12 col-md-4 px-0 px-md-3 text-end">
7
- <button id="reset" class="btn btn-warning btn-sm fw-semibold ff-secondary text-center text-nowrap">[[poll:reset]]</button>
8
- <button id="save" class="btn btn-primary btn-sm fw-semibold ff-secondary text-center text-nowrap">[[admin/admin:save-changes]]</button>
9
- </div>
10
- </div>
3
+ <div class="col-12 col-md-8 px-0 mb-1 mb-md-0">
4
+ <h4 class="fw-bold tracking-tight mb-0">{title}</h4>
5
+ </div>
6
+ <div class="col-12 col-md-4 px-0 px-md-3 text-end">
7
+ <button id="save" class="btn btn-primary btn-sm fw-semibold ff-secondary text-center text-nowrap">[[admin/admin:save-changes]]</button>
8
+ </div>
9
+ </div>
11
10
 
12
11
  <div class="row m-0">
13
- <div id="spy-container" class="col-12 px-0 mb-4" tabindex="0">
14
- <form class="form poll-settings">
15
- <div class="card">
16
- <div class="card-header">[[poll:poll]]</div>
17
- <div class="card-body">
18
- <div class="mb-3">
19
- <h6>[[poll:toggles]]</h6>
20
- <div class="mb-3">
21
- <div class="form-check">
22
- <label class="form-check-label">[[poll:allow_guests]]</label>
23
- <input class="form-check-input" type="checkbox" data-key="toggles.allowAnon" data-trim="false">
24
- </div>
25
- </div>
26
- </div>
27
- <div class="mb-3">
28
- <h6>[[poll:limits]]</h6>
29
- <div>
30
- <label class="form-label" for="maxPollOptions">[[poll:max_options]]</label>
31
- <input type="number" class="form-control" id="maxPollOptions" placeholder="10" min="1" max="100" data-key="limits.maxOptions">
32
- </div>
33
- </div>
34
- </div>
35
- </div>
36
-
37
- <div class="card">
38
- <div class="card-header">[[poll:defaults]]</div>
39
- <div class="card-body">
40
- <div class="mb-3">
41
- <label class="form-label" for="defaultsTitle">[[poll:default_title]]</label>
42
- <input type="text" class="form-control" id="defaultsTitle" placeholder="Poll" data-key="defaults.title">
43
- </div>
44
- <div>
45
- <label class="form-label" for="defaultsMaxVotes">[[poll:max_votes]]</label>
46
- <input type="number" class="form-control" id="defaultsMaxVotes" placeholder="1" min="1" max="100" data-key="defaults.maxvotes">
47
- <p class="form-text">[[poll:info_choices]]</p>
48
- </div>
49
- </div>
50
- </div>
51
- </form>
12
+ <div class="col-12 col-lg-9">
13
+ <div class="table-responsive">
14
+ <table id="poll-table" class="table">
15
+ <thead>
16
+ <tr class="text-sm">
17
+ <th>ID</th>
18
+ <th class="text-nowrap">[[poll:poll_title]]</th>
19
+ <th class="text-nowrap">[[poll:created-time]]</th>
20
+ <th class="text-nowrap">[[poll:end-time]]</th>
21
+ <th class="text-end text-nowrap text-capitalize">[[poll:votes]]</th>
22
+ </tr>
23
+ </thead>
24
+ <tbody class="text-xs text-tabular">
25
+ {{{ each polls }}}
26
+ <tr class="align-middle">
27
+ <td>{./pollId}</td>
28
+ <td><a href="{config.relative_path}/post/{./pid}">{./title}</a></td>
29
+ <td class="text-nowrap"><span class="timeago" title="{./timestampISO}"></span></td>
30
+ <td class="text-nowrap">
31
+ {{{ if ./end }}}
32
+ <span class="timeago" title="{./endISO}"></span>
33
+ {{{ else }}}
34
+ -
35
+ {{{ end }}}
36
+ </td>
37
+ <td class="text-end">
38
+ {./voteCount}
39
+ </td>
40
+ </tr>
41
+ {{{ end }}}
42
+ </tbody>
43
+ </table>
44
+ </div>
45
+ <!-- IMPORT admin/partials/paginator.tpl -->
52
46
  </div>
47
+ <div id="spy-container" class="col-12 col-lg-3" tabindex="0">
48
+ <form class="form poll-settings">
49
+ <div class="form-check mb-3">
50
+ <label class="form-check-label" for="allowGuestsToViewResults">[[poll:allow_guests]]</label>
51
+ <input class="form-check-input" type="checkbox" id="allowGuestsToViewResults" name="allowGuestsToViewResults">
52
+ </div>
53
+
54
+ <div class="mb-3">
55
+ <label class="form-label" for="maxOptions">[[poll:max_options]]</label>
56
+ <input type="number" class="form-control" id="maxOptions" placeholder="10" min="1" max="100" name="maxOptions">
57
+ </div>
53
58
 
54
- <!-- IMPORT admin/partials/settings/toc.tpl -->
59
+ <div class="mb-3">
60
+ <label class="form-label" for="defaultTitle">[[poll:default_title]]</label>
61
+ <input type="text" class="form-control" id="defaultTitle" placeholder="Poll" name="defaultTitle">
62
+ </div>
63
+ <div>
64
+ <label class="form-label" for="maximumVotesPerUser">[[poll:max_votes]]</label>
65
+ <input type="number" class="form-control" id="maximumVotesPerUser" placeholder="1" min="1" max="100" name="maximumVotesPerUser">
66
+ <p class="form-text">[[poll:info_choices]]</p>
67
+ </div>
68
+ </form>
69
+ </div>
55
70
  </div>
56
71
  </div>
57
72
 
@@ -1,52 +1,48 @@
1
- <form class="form" id="pollCreator">
1
+ <form class="form" id="pollCreator" onsubmit="return false;">
2
2
  <div id="pollErrorBox" class="alert alert-danger hidden"></div>
3
3
 
4
+ <input type="hidden" name="pollId" value="{poll.pollId}">
5
+
4
6
  <div class="mb-3">
5
7
  <label class="form-label" for="pollInputTitle">[[poll:poll_title]]</label>
6
- <input type="text" name="settings.title" id="pollInputTitle" value="{poll.settings.title}" placeholder="[[poll:poll_title_placeholder]]" class="form-control">
8
+ <input type="text" name="title" id="pollInputTitle" value="{poll.title}" placeholder="[[poll:poll_title_placeholder]]" class="form-control">
7
9
  </div>
8
10
 
9
- <div class="mb-3">
11
+ <div class="mb-3 d-flex flex-column gap-1">
10
12
  <label class="form-label" for="pollInputOptions">[[poll:options_title]]</label>
11
- <!-- IF poll.options.length -->
12
- <!-- BEGIN poll.options -->
13
- <input type="text" name="options" id="pollInputOptions" value="{@value}" class="form-control mb-2"/>
14
- <!-- END poll.options -->
15
- <!-- ELSE -->
16
- <input type="text" name="options" id="pollInputOptions" class="form-control mb-2"/>
17
- <!-- ENDIF poll.options.length -->
18
- <button type="button" id="pollAddOption" class="btn btn-primary btn-sm btn-block">[[poll:options_add]]</button>
13
+ <div id="poll-options-container" class="d-flex flex-column gap-1">
14
+ {{{ if poll.options.length }}}
15
+ {{{ each poll.options }}}
16
+ <!-- IMPORT poll/option-input.tpl -->
17
+ {{{ end }}}
18
+ {{{ end }}}
19
+ </div>
20
+ <button type="button" id="pollAddOption" class="btn btn-primary btn-sm btn-block mt-2">[[poll:options_add]]</button>
19
21
  </div>
20
22
 
21
23
  <hr>
22
24
 
23
25
  <div class="mb-3">
24
26
  <label class="form-label" for="pollInputAmount">[[poll:max_votes]]</label>
25
- <input type="number" name="settings.maxvotes" id="pollInputAmount" value="{poll.settings.maxvotes}"
26
- min="1" max="10" step="1" placeholder="[[poll:max_votes_placeholder]]" class="form-control">
27
+ <input type="number" name="maximumVotesPerUser" id="pollInputAmount" value="{poll.maximumVotesPerUser}" min="1" max="10" step="1" placeholder="[[poll:max_votes_placeholder]]" class="form-control">
28
+ <p class="form-text">[[poll:info_choices]]</p>
27
29
  </div>
28
30
 
29
31
  <div class="form-check mb-3">
30
32
  <label class="form-check-label" for="pollDisallowVoteUpdate">[[poll:disallow_vote_update]]</label>
31
- <input class="form-check-input" type="checkbox" name="settings.disallowVoteUpdate" id="pollDisallowVoteUpdate" {{{if poll.settings.disallowVoteUpdate}}}checked{{{end}}}>
33
+ <input class="form-check-input" type="checkbox" name="disallowVoteUpdate" id="pollDisallowVoteUpdate" {{{if poll.disallowVoteUpdate}}}checked{{{end}}}>
32
34
  </div>
33
35
 
34
36
  <div class="form-check mb-3">
35
37
  <label class="form-check-label" for="allowAnonVoting">[[poll:allow_anon_voting]]</label>
36
- <input class="form-check-input" type="checkbox" name="settings.allowAnonVoting" id="allowAnonVoting" {{{if poll.settings.allowAnonVoting}}}checked{{{end}}}>
38
+ <input class="form-check-input" type="checkbox" name="allowAnonVoting" id="allowAnonVoting" {{{if poll.allowAnonVoting}}}checked{{{end}}}>
37
39
  </div>
38
40
 
39
41
  <div class="mb-3">
40
42
  <label class="form-label" for="pollInputEnd">[[poll:auto_end_title]]</label>
41
43
  <div class="input-group date">
42
- <input id="pollInputEnd" placeholder="[[poll:auto_end_placeholder]]" name="settings.end" class="form-control" value="{poll.settings.end}" type="datetime-local">
44
+ <input id="pollInputEnd" placeholder="[[poll:auto_end_placeholder]]" name="end" class="form-control" value="{poll.end}" type="datetime-local">
43
45
  </div>
44
46
  <p class="form-text">[[poll:auto_end_help]]</p>
45
47
  </div>
46
-
47
- {{{ if isRedactor }}}
48
- <div class="alert alert-warning" role="alert">
49
- [[poll:warning.redactor]]
50
- </div>
51
- {{{ end }}}
52
48
  </form>
@@ -0,0 +1,18 @@
1
+ <div class="topic-poll-modal">
2
+ {{{ if !post.polls.length }}}
3
+ <div class="alert alert-info">[[poll:no-polls]]</div>
4
+ {{{ end }}}
5
+ {{{ each post.polls }}}
6
+ <div component="post/poll/item" data-poll-id="{./pollId}" class="bg-body d-flex align-items-center justify-content-between mb-3 p-1 border rounded-1">
7
+ <div class="d-flex align-items-center gap-1">
8
+ <a href="#" component="sort/handle" class="btn btn-ghost btn-sm align-self-start" style="cursor:grab;"><i class="fa fa-arrows-up-down text-muted"></i></a>
9
+ <div class="text-nowrap flex-grow-1">{./title}</div>
10
+ </div>
11
+
12
+ <div class="d-flex align-items-center gap-1">
13
+ <button class="btn btn-ghost btn-sm text-nowrap" data-action="edit" data-poll-id="{./pollId}"><i class="fa fa-pencil text-primary"></i></button>
14
+ <button class="btn btn-ghost btn-sm text-nowrap" data-action="remove" data-poll-id="{./pollId}"><i class="fa fa-times text-danger"></i></button>
15
+ </div>
16
+ </div>
17
+ {{{ end }}}
18
+ </div>
@@ -0,0 +1,6 @@
1
+ <div component="post/poll/option/item" data-option-id="{./id}" class="bg-body d-flex align-items-center justify-content-between gap-2 p-1 rounded-1">
2
+ <a href="#" component="sort/handle" class="btn btn-ghost btn-sm" style="cursor:grab;"><i class="fa fa-arrows-up-down text-muted"></i></a>
3
+ <input type="text" name="options" id="pollInputOptions" value="{./title}" class="form-control"/>
4
+ <input type="hidden" name="id" value="{./id}"/>
5
+ <button class="btn btn-ghost btn-sm" data-action="remove-option" data-option-id="{./id}"><i class="fa fa-trash text-danger"></i></button>
6
+ </div>
@@ -1,6 +1,7 @@
1
- <h4>{voteCount} [[poll:vote_count]]</h4>
2
- <!-- BEGIN votes -->
3
- <a href="{config.relative_path}/user/{votes.userslug}" class="poll-result-details">
4
- {buildAvatar(votes, "24px", true)}
5
- </a>
6
- <!-- END votes -->
1
+ <div class="d-flex flex-wrap gap-2">
2
+ {{{ each votes }}}
3
+ <a href="{config.relative_path}/user/{votes.userslug}" class="poll-result-details">
4
+ {buildAvatar(votes, "24px", true)}
5
+ </a>
6
+ {{{ end }}}
7
+ </div>
@@ -1,18 +1,23 @@
1
1
  <div class="poll-view mb-3" data-poll-id="{poll.info.pollId}">
2
- <div class="card">
3
- <div class="card-header d-flex justify-content-between align-items-center">
4
- <h5 class="card-title mb-0">{poll.settings.title}</h5>
5
-
6
- <a href="#" class="btn btn-sm btn-light border poll-button-edit hidden">
7
- <span class="fa fa-pencil"></span>
8
- </a>
2
+ <div class="card shadow-sm border-0">
3
+ <div class="card-header border-0 d-flex justify-content-between align-items-center">
4
+ <h5 class="card-title mb-0">
5
+ {{{ if poll.isWidget }}}
6
+ <a class="text-reset" href="{config.relative_path}/post/{poll.info.pid}">{poll.info.title}</a>
7
+ {{{ else }}}
8
+ {poll.info.title}
9
+ {{{ end }}}
10
+ </h5>
11
+ {{{ if isAdmin }}}
12
+ <span class="text-sm text-secondary">[[poll:poll-id-x, {poll.info.pollId}]]</span>
13
+ {{{ end }}}
9
14
  </div>
10
15
  <div class="card-body">
11
16
  <div class="poll-view-messages hidden"></div>
12
- <div class="poll-view-voting <!-- IF poll.hasVoted -->hidden<!-- ENDIF poll.hasVoted -->">
17
+ <div class="poll-view-voting {{{ if poll.hasVoted }}}hidden{{{ end }}}">
13
18
  <!-- IMPORT poll/view/voting.tpl -->
14
19
  </div>
15
- <div class="poll-view-results d-flex flex-column gap-3 mb-3 <!-- IF !poll.hasVoted -->hidden<!-- ENDIF !poll.hasVoted -->">
20
+ <div class="poll-view-results d-flex flex-column gap-3 mb-3 {{{ if !poll.hasVoted }}}hidden{{{ end }}}">
16
21
  <!-- IMPORT poll/view/results.tpl -->
17
22
  <div class="text-end text-sm text-secondary poll-result-total-votecount">[[poll:total-votes-x, {poll.info.voteCount}]]</div>
18
23
  </div>