@richpods/coloeus 0.1.2 → 0.2.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.
@@ -1 +1 @@
1
- .coloeus-poll{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,sans-serif;max-width:600px;margin:0 auto;padding:1.5rem;background:#fff;border-radius:8px;box-shadow:0 2px 8px #0000001a}.coloeus-poll__title{font-size:1.25rem;font-weight:600;margin:0 0 .5rem;color:#1a1a1a}.coloeus-poll__form{margin:0}.coloeus-poll__fieldset{border:none;margin:0;padding:0}.coloeus-poll__instructions{font-size:.875rem;color:#4b5563;margin:0 0 1rem}.coloeus-poll__instructions-note{display:block;margin-top:.25rem;font-size:.8125rem;color:#6b7280}.coloeus-poll__options{list-style:none;padding:0;margin:0}.coloeus-poll__option{position:relative;margin-bottom:.75rem;border-radius:6px;overflow:hidden;transition:transform .15s ease}.coloeus-poll__option:hover:not(.coloeus-poll__option--disabled){transform:translate(4px)}.coloeus-poll__option--selected{outline:none}.coloeus-poll__option--voted .coloeus-poll__option-content{background-color:#f0f9ff}.coloeus-poll__option--disabled{cursor:default}.coloeus-poll__option-label{display:block}.coloeus-poll__option-content{width:100%;display:flex;align-items:center;justify-content:space-between;gap:.75rem;padding:.875rem 1rem;background:#f5f5f5;border:1px solid #e5e5e5;border-radius:6px;font-size:1rem;text-align:left;transition:background-color .15s ease,border-color .15s ease,box-shadow .15s ease;position:relative;z-index:1;cursor:pointer;overflow:hidden}.coloeus-poll__option:not(.coloeus-poll__option--disabled) .coloeus-poll__option-content:hover{background:#ebebeb}.coloeus-poll__option--disabled .coloeus-poll__option-content{cursor:default;opacity:.9}.coloeus-poll__option--selected .coloeus-poll__option-content{border-color:#3b82f6;box-shadow:0 0 0 2px #3b82f640}.coloeus-poll__option-input{position:absolute;width:1px;height:1px;margin:-1px;border:0;padding:0;clip:rect(0 0 0 0);clip-path:inset(50%);overflow:hidden}.coloeus-poll__option-input:focus-visible+.coloeus-poll__option-content{box-shadow:0 0 0 3px #2563eb73}.coloeus-poll__option-progress{position:absolute;top:0;left:0;height:100%;background:#dbeafe;border-radius:6px;transition:width .3s ease;z-index:0}.coloeus-poll__option-text{flex:1;color:#1a1a1a}.coloeus-poll__option-stats{display:flex;align-items:center;gap:.5rem;font-size:.875rem;color:#666}.coloeus-poll__option-percentage{font-weight:600;color:#3b82f6}.coloeus-poll__sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.coloeus-poll__footer{margin-top:1rem;display:flex;align-items:center;justify-content:space-between}.coloeus-poll__vote-btn{padding:.75rem 1.5rem;background:#3b82f6;color:#fff;border:none;border-radius:6px;font-size:1rem;font-weight:500;cursor:pointer;transition:background-color .15s ease}.coloeus-poll__vote-btn:hover:not(:disabled){background:#2563eb}.coloeus-poll__vote-btn:disabled{background:#94a3b8;cursor:not-allowed}.coloeus-poll__total{font-size:.875rem;color:#666}.coloeus-poll__status-group{display:flex;align-items:center;gap:.5rem}.coloeus-poll__error{margin-top:.75rem;padding:.75rem;background:#fef2f2;border:1px solid #fecaca;border-radius:6px;color:#dc2626;font-size:.875rem}.coloeus-poll__loading{text-align:center;padding:2rem;color:#666}.coloeus-poll__status{display:inline-block;padding:.25rem .5rem;border-radius:4px;font-size:.75rem;font-weight:500}.coloeus-poll__status--active{background:#dcfce7;color:#166534}.coloeus-poll__status--open{background:#e0f2fe;color:#075985}.coloeus-poll__status--inactive{background:#fef3c7;color:#92400e}.coloeus-editor{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,sans-serif;max-width:600px;margin:0 auto;padding:1.5rem;background:#fff;border-radius:8px;box-shadow:0 2px 8px #0000001a}.coloeus-editor__title{font-size:1.25rem;font-weight:600;margin:0 0 1.5rem;color:#1a1a1a}.coloeus-editor__field{margin-bottom:1.25rem}.coloeus-editor__label{display:block;font-size:.875rem;font-weight:500;color:#374151;margin-bottom:.5rem}.coloeus-editor__input,.coloeus-editor__textarea{width:100%;padding:.75rem;border:1px solid #d1d5db;border-radius:6px;font-size:1rem;transition:border-color .15s ease;box-sizing:border-box}.coloeus-editor__input:focus,.coloeus-editor__textarea:focus{outline:none;border-color:#3b82f6;box-shadow:0 0 0 3px #3b82f61a}.coloeus-editor__options{list-style:none;padding:0;margin:0}.coloeus-editor__option{display:flex;gap:.5rem;margin-bottom:.5rem}.coloeus-editor__option-input{flex:1;padding:.625rem .75rem;border:1px solid #d1d5db;border-radius:6px;font-size:.9375rem}.coloeus-editor__option-input:focus{outline:none;border-color:#3b82f6}.coloeus-editor__remove-btn{padding:.625rem .875rem;background:#fee2e2;color:#dc2626;border:none;border-radius:6px;cursor:pointer;font-size:.875rem;transition:background-color .15s ease}.coloeus-editor__remove-btn:hover{background:#fecaca}.coloeus-editor__add-btn{margin-top:.5rem;padding:.625rem 1rem;background:#f3f4f6;color:#374151;border:1px dashed #d1d5db;border-radius:6px;cursor:pointer;font-size:.875rem;width:100%;transition:background-color .15s ease}.coloeus-editor__add-btn:hover{background:#e5e7eb}.coloeus-editor__actions{margin-top:1.5rem;display:flex;gap:.75rem}.coloeus-editor__submit-btn{flex:1;padding:.75rem 1.5rem;background:#3b82f6;color:#fff;border:none;border-radius:6px;font-size:1rem;font-weight:500;cursor:pointer;transition:background-color .15s ease}.coloeus-editor__submit-btn:hover:not(:disabled){background:#2563eb}.coloeus-editor__submit-btn:disabled{background:#94a3b8;cursor:not-allowed}.coloeus-editor__cancel-btn{padding:.75rem 1.5rem;background:#f3f4f6;color:#374151;border:1px solid #d1d5db;border-radius:6px;font-size:1rem;cursor:pointer;transition:background-color .15s ease}.coloeus-editor__cancel-btn:hover{background:#e5e7eb}.coloeus-editor__error{margin-top:1rem;padding:.75rem;background:#fef2f2;border:1px solid #fecaca;border-radius:6px;color:#dc2626;font-size:.875rem}
1
+ .coloeus-poll{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,sans-serif;max-width:600px;margin:0 auto;padding:1.5rem;background:#fff;border-radius:8px;box-shadow:0 2px 8px #0000001a}.coloeus-poll__title{font-size:1.25rem;font-weight:600;margin:0 0 .5rem;color:#1a1a1a}.coloeus-poll__form{margin:0}.coloeus-poll__fieldset{border:none;margin:0;padding:0}.coloeus-poll__instructions{font-size:.875rem;color:#4b5563;margin:0 0 1rem}.coloeus-poll__instructions-note{display:block;margin-top:.25rem;font-size:.8125rem;color:#6b7280}.coloeus-poll__options{list-style:none;padding:0;margin:0}.coloeus-poll__option{position:relative;margin-bottom:.75rem;border-radius:6px;padding:2px;transition:transform .15s ease}.coloeus-poll__option:hover:not(.coloeus-poll__option--disabled){transform:translate(4px)}.coloeus-poll__option--selected{outline:none}.coloeus-poll__option--voted .coloeus-poll__option-content{background-color:#f0f9ff}.coloeus-poll__option--disabled{cursor:default}.coloeus-poll__option-label{display:block;position:relative}.coloeus-poll__option-content{width:100%;display:flex;align-items:center;justify-content:space-between;gap:.75rem;padding:.875rem 1rem;background:#f5f5f5;border:1px solid #e5e5e5;border-radius:6px;font-size:1rem;text-align:left;transition:background-color .15s ease,border-color .15s ease,box-shadow .15s ease;position:relative;z-index:1;cursor:pointer;overflow:hidden}.coloeus-poll__option:not(.coloeus-poll__option--disabled) .coloeus-poll__option-content:hover{background:#ebebeb}.coloeus-poll__option--disabled .coloeus-poll__option-content{cursor:default;opacity:.9}.coloeus-poll__option--selected .coloeus-poll__option-content{border-color:#3b82f6;box-shadow:0 0 0 2px #3b82f640}.coloeus-poll__option-input{position:absolute;opacity:0;width:100%;height:100%;top:0;left:0;margin:0;cursor:pointer;z-index:2}.coloeus-poll__option-input:disabled{cursor:default}.coloeus-poll__option-input:focus-visible+.coloeus-poll__option-content{outline:2px solid #2563eb;outline-offset:2px;box-shadow:0 0 0 4px #2563eb40}.coloeus-poll__option-progress{position:absolute;top:0;left:0;height:100%;background:#dbeafe;border-radius:6px;transition:width .3s ease;z-index:0}.coloeus-poll__option-text{flex:1;color:#1a1a1a;position:relative;z-index:1}.coloeus-poll__option-stats{display:flex;align-items:center;gap:.5rem;font-size:.875rem;color:#666;position:relative;z-index:1}.coloeus-poll__option-percentage{font-weight:600;color:#3b82f6}.coloeus-poll__sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.coloeus-poll__footer{margin-top:1rem;display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:.5rem}.coloeus-poll__actions{display:flex;gap:.5rem}.coloeus-poll__vote-btn{padding:.75rem 1.5rem;background:#3b82f6;color:#fff;border:none;border-radius:6px;font-size:1rem;font-weight:500;cursor:pointer;transition:background-color .15s ease}.coloeus-poll__vote-btn:hover:not(:disabled){background:#2563eb}.coloeus-poll__vote-btn:disabled{background:#94a3b8;cursor:not-allowed}.coloeus-poll__results-btn{padding:.75rem 1.5rem;background:transparent;color:#6b7280;border:1px solid #d1d5db;border-radius:6px;font-size:1rem;cursor:pointer;transition:background-color .15s ease,border-color .15s ease}.coloeus-poll__results-btn:hover{background:#f3f4f6;border-color:#9ca3af}.coloeus-poll__total{font-size:.875rem;color:#666}.coloeus-poll__status-group{display:flex;align-items:center;gap:.5rem}.coloeus-poll__error{margin-top:.75rem;padding:.75rem;background:#fef2f2;border:1px solid #fecaca;border-radius:6px;color:#dc2626;font-size:.875rem}.coloeus-poll__loading{text-align:center;padding:2rem;color:#666}.coloeus-poll__status{display:inline-block;padding:.25rem .5rem;border-radius:4px;font-size:.75rem;font-weight:500}.coloeus-poll__status--active{background:#dcfce7;color:#166534}.coloeus-poll__status--open{background:#e0f2fe;color:#075985}.coloeus-poll__status--inactive{background:#fef3c7;color:#92400e}.coloeus-editor{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,sans-serif;max-width:600px;margin:0 auto;padding:1.5rem;background:#fff;border-radius:8px;box-shadow:0 2px 8px #0000001a}.coloeus-editor__title{font-size:1.25rem;font-weight:600;margin:0 0 1.5rem;color:#1a1a1a}.coloeus-editor__field{margin-bottom:1.25rem}.coloeus-editor__label{display:block;font-size:.875rem;font-weight:500;color:#374151;margin-bottom:.5rem}.coloeus-editor__input,.coloeus-editor__textarea{width:100%;padding:.75rem;border:1px solid #d1d5db;border-radius:6px;font-size:1rem;transition:border-color .15s ease;box-sizing:border-box}.coloeus-editor__input:focus,.coloeus-editor__textarea:focus{outline:none;border-color:#3b82f6;box-shadow:0 0 0 3px #3b82f61a}.coloeus-editor__options{list-style:none;padding:0;margin:0}.coloeus-editor__option{display:flex;gap:.5rem;margin-bottom:.5rem}.coloeus-editor__option-input{flex:1;padding:.625rem .75rem;border:1px solid #d1d5db;border-radius:6px;font-size:.9375rem}.coloeus-editor__option-input:focus{outline:none;border-color:#3b82f6}.coloeus-editor__remove-btn{padding:.625rem .875rem;background:#fee2e2;color:#dc2626;border:none;border-radius:6px;cursor:pointer;font-size:.875rem;transition:background-color .15s ease}.coloeus-editor__remove-btn:hover{background:#fecaca}.coloeus-editor__add-btn{margin-top:.5rem;padding:.625rem 1rem;background:#f3f4f6;color:#374151;border:1px dashed #d1d5db;border-radius:6px;cursor:pointer;font-size:.875rem;width:100%;transition:background-color .15s ease}.coloeus-editor__add-btn:hover{background:#e5e7eb}.coloeus-editor__actions{margin-top:1.5rem;display:flex;gap:.75rem}.coloeus-editor__submit-btn{flex:1;padding:.75rem 1.5rem;background:#3b82f6;color:#fff;border:none;border-radius:6px;font-size:1rem;font-weight:500;cursor:pointer;transition:background-color .15s ease}.coloeus-editor__submit-btn:hover:not(:disabled){background:#2563eb}.coloeus-editor__submit-btn:disabled{background:#94a3b8;cursor:not-allowed}.coloeus-editor__cancel-btn{padding:.75rem 1.5rem;background:#f3f4f6;color:#374151;border:1px solid #d1d5db;border-radius:6px;font-size:1rem;cursor:pointer;transition:background-color .15s ease}.coloeus-editor__cancel-btn:hover{background:#e5e7eb}.coloeus-editor__error{margin-top:1rem;padding:.75rem;background:#fef2f2;border:1px solid #fecaca;border-radius:6px;color:#dc2626;font-size:.875rem}.coloeus-editor__max-selections{margin-top:1rem}.coloeus-editor__input--small{width:80px}.coloeus-editor__hint{margin-top:.5rem;font-size:.875rem;color:#666;font-style:italic}.coloeus-editor__max-selections-readonly{margin-top:1rem;display:flex;align-items:center;gap:.75rem}.coloeus-editor__max-selections-readonly .coloeus-editor__label{margin-bottom:0}.coloeus-editor__readonly-value{font-weight:500;color:#1a1a1a}
@@ -1,595 +1,659 @@
1
- import { ref as g, computed as _, onMounted as H, watch as Y, defineComponent as K, createElementBlock as u, openBlock as d, Fragment as U, createElementVNode as s, createCommentVNode as E, withModifiers as x, toDisplayString as h, createTextVNode as N, renderList as Q, normalizeClass as ce, normalizeStyle as ue, withDirectives as j, vModelText as z } from "vue";
2
- let C = {
1
+ import { ref as S, computed as f, onMounted as Q, reactive as pe, toRaw as ve, watch as j, defineComponent as W, createElementBlock as c, openBlock as u, Fragment as x, createElementVNode as n, createCommentVNode as C, withModifiers as X, toDisplayString as h, createTextVNode as Y, renderList as Z, normalizeClass as fe, normalizeStyle as me, withDirectives as N, vModelText as D } from "vue";
2
+ class $ extends Error {
3
+ constructor(o, d, e) {
4
+ super(d), this.status = o, this.data = e, this.name = "ApiError";
5
+ }
6
+ }
7
+ let E = {
3
8
  apiUrl: ""
4
9
  };
5
- function po(t) {
6
- C = { ...C, ...t };
10
+ function ko(l) {
11
+ E = { ...E, ...l };
7
12
  }
8
- function _o() {
9
- return C;
13
+ function To() {
14
+ return E;
10
15
  }
11
- async function J() {
12
- const t = {
16
+ async function q() {
17
+ const l = {
13
18
  "Content-Type": "application/json"
14
19
  };
15
- if (C.getAuthToken) {
16
- const l = await C.getAuthToken();
17
- l && (t.Authorization = `Bearer ${l}`);
20
+ if (E.getAuthToken) {
21
+ const o = await E.getAuthToken();
22
+ o && (l.Authorization = `Bearer ${o}`);
18
23
  }
19
- return t;
24
+ return l;
20
25
  }
21
- async function L(t) {
22
- if (!t.ok) {
23
- const l = await t.json().catch(() => ({ error: "Unknown error" }));
24
- throw new Error(l.error || `HTTP error ${t.status}`);
26
+ async function L(l) {
27
+ if (!l.ok) {
28
+ const o = await l.json().catch(() => ({ error: "Unknown error" }));
29
+ throw new $(
30
+ l.status,
31
+ o.error || `HTTP error ${l.status}`,
32
+ o.data
33
+ );
25
34
  }
26
- return t.json();
35
+ return l.json();
27
36
  }
28
- async function de(t) {
29
- const l = await fetch(`${C.apiUrl}/polls/${t}`, {
37
+ async function he(l) {
38
+ const o = await fetch(`${E.apiUrl}/polls/${l}`, {
30
39
  method: "GET",
31
40
  headers: {
32
41
  "Content-Type": "application/json"
33
42
  }
34
43
  });
35
- return L(l);
44
+ return L(o);
36
45
  }
37
- async function pe(t, l) {
38
- if (l.length === 0)
46
+ async function ye(l, o) {
47
+ if (o.length === 0)
39
48
  throw new Error("At least one option must be selected");
40
- const c = await fetch(`${C.apiUrl}/polls/${t}/vote`, {
49
+ const d = await fetch(`${E.apiUrl}/polls/${l}/vote`, {
41
50
  method: "POST",
42
51
  headers: {
43
52
  "Content-Type": "application/json"
44
53
  },
45
- body: JSON.stringify({ optionIndices: l })
54
+ body: JSON.stringify({ optionIndices: o })
46
55
  });
47
- return L(c);
56
+ return L(d);
48
57
  }
49
- async function _e(t) {
50
- const l = await J(), c = await fetch(`${C.apiUrl}/admin/polls`, {
58
+ async function be(l) {
59
+ const o = await q(), d = await fetch(`${E.apiUrl}/admin/polls`, {
51
60
  method: "POST",
52
- headers: l,
53
- body: JSON.stringify(t)
61
+ headers: o,
62
+ body: JSON.stringify(l)
54
63
  });
55
- return L(c);
64
+ return L(d);
56
65
  }
57
- async function ve(t, l) {
58
- const c = await J(), e = await fetch(`${C.apiUrl}/admin/polls/${t}`, {
66
+ async function ge(l, o) {
67
+ const d = await q(), e = await fetch(`${E.apiUrl}/admin/polls/${l}`, {
59
68
  method: "PUT",
60
- headers: c,
61
- body: JSON.stringify(l)
69
+ headers: d,
70
+ body: JSON.stringify(o)
62
71
  });
63
72
  return L(e);
64
73
  }
65
- async function fe(t) {
66
- const l = await J(), c = await fetch(`${C.apiUrl}/admin/polls/${t}`, {
74
+ async function Se(l) {
75
+ const o = await q(), d = await fetch(`${E.apiUrl}/admin/polls/${l}`, {
67
76
  method: "GET",
68
- headers: l
77
+ headers: o
69
78
  });
70
- return L(c);
79
+ return L(d);
71
80
  }
72
- function me(t) {
73
- const l = g(null), c = g(!0), e = g(null), p = _(() => l.value?.totalVotes ?? 0), y = _(() => l.value?.isActive ?? !1), n = _(() => !l.value || p.value === 0 ? l.value?.options.map(() => 0) ?? [] : l.value.options.map(
74
- (i) => Math.round(i.voteCount / p.value * 100)
81
+ function Oe(l) {
82
+ const o = S(null), d = S(!0), e = S(null), r = f(() => o.value?.totalVotes ?? 0), p = f(() => o.value?.isActive ?? !1), s = f(() => !o.value || r.value === 0 ? o.value?.options.map(() => 0) ?? [] : o.value.options.map(
83
+ (_) => Math.round(_.voteCount / r.value * 100)
75
84
  ));
76
- async function o() {
77
- c.value = !0, e.value = null;
85
+ async function t() {
86
+ d.value = !0, e.value = null;
78
87
  try {
79
- l.value = await de(t);
80
- } catch (i) {
81
- e.value = i instanceof Error ? i.message : "Failed to load poll", l.value = null;
88
+ o.value = await he(l);
89
+ } catch (_) {
90
+ e.value = _ instanceof Error ? _.message : "Failed to load poll", o.value = null;
82
91
  } finally {
83
- c.value = !1;
92
+ d.value = !1;
84
93
  }
85
94
  }
86
- function a(i) {
87
- l.value = i;
95
+ function a(_) {
96
+ o.value = _;
88
97
  }
89
- return H(() => {
90
- o();
98
+ return Q(() => {
99
+ t();
91
100
  }), {
92
- poll: l,
93
- loading: c,
101
+ poll: o,
102
+ loading: d,
94
103
  error: e,
95
- totalVotes: p,
96
- isActive: y,
97
- optionPercentages: n,
98
- loadPoll: o,
104
+ totalVotes: r,
105
+ isActive: p,
106
+ optionPercentages: s,
107
+ loadPoll: t,
99
108
  updatePoll: a
100
109
  };
101
110
  }
102
- const W = "coloeus-votes";
103
- function he() {
111
+ const ee = "coloeus-votes";
112
+ function Ve() {
104
113
  try {
105
- const t = localStorage.getItem(W);
106
- return t ? JSON.parse(t) : {};
114
+ const l = localStorage.getItem(ee);
115
+ return l ? JSON.parse(l) : {};
107
116
  } catch {
108
117
  return {};
109
118
  }
110
119
  }
111
- function R(t) {
120
+ function J(l) {
112
121
  try {
113
- localStorage.setItem(W, JSON.stringify(t));
122
+ localStorage.setItem(ee, JSON.stringify(ve(l)));
114
123
  } catch {
115
124
  }
116
125
  }
117
- function ye() {
118
- const t = g(he());
119
- function l(n) {
120
- return n in t.value;
126
+ const w = pe(Ve());
127
+ function ke() {
128
+ function l(p) {
129
+ return p in w;
121
130
  }
122
- function c(n) {
123
- const o = t.value[n];
124
- if (o == null)
131
+ function o(p) {
132
+ const s = w[p];
133
+ if (s == null)
125
134
  return [];
126
- const a = Array.isArray(o) ? o : [o];
127
- return Array.from(new Set(a)).sort((i, O) => i - O);
135
+ const t = Array.isArray(s) ? s : [s];
136
+ return Array.from(new Set(t)).sort((a, _) => a - _);
128
137
  }
129
- function e(n, o) {
130
- const a = Array.isArray(o) ? o : [o], i = Array.from(new Set(a)).sort((O, T) => O - T);
131
- t.value = {
132
- ...t.value,
133
- [n]: i.length === 1 ? i[0] : i
134
- }, R(t.value);
138
+ function d(p, s) {
139
+ const t = Array.isArray(s) ? s : [s], a = Array.from(new Set(t)).sort((_, y) => _ - y);
140
+ w[p] = a.length === 1 ? a[0] : a, J(w);
135
141
  }
136
- function p(n) {
137
- const o = { ...t.value };
138
- delete o[n], t.value = o, R(t.value);
142
+ function e(p) {
143
+ delete w[p], J(w);
139
144
  }
140
- function y() {
141
- t.value = {}, R(t.value);
145
+ function r() {
146
+ for (const p of Object.keys(w))
147
+ delete w[p];
148
+ J(w);
142
149
  }
143
150
  return {
144
- votes: t,
151
+ votes: w,
145
152
  hasVoted: l,
146
- getVotedOptions: c,
147
- recordVote: e,
148
- clearVote: p,
149
- clearAllVotes: y
153
+ getVotedOptions: o,
154
+ recordVote: d,
155
+ clearVote: e,
156
+ clearAllVotes: r
150
157
  };
151
158
  }
152
- function ge(t, l) {
153
- const { hasVoted: c, getVotedOptions: e, recordVote: p } = ye(), y = g(!1), n = g(null), o = g([]), a = g(1);
154
- l?.selectionLimit && Y(
155
- l.selectionLimit,
156
- (f) => {
157
- a.value = be(f), o.value.length > a.value && (o.value = o.value.slice(0, a.value));
159
+ const Te = 409;
160
+ function we(l, o) {
161
+ const { hasVoted: d, getVotedOptions: e, recordVote: r } = ke(), p = S(!1), s = S(null), t = S([]), a = S(1);
162
+ o?.selectionLimit && j(
163
+ o.selectionLimit,
164
+ (v) => {
165
+ a.value = Ce(v), t.value.length > a.value && (t.value = t.value.slice(0, a.value));
158
166
  },
159
167
  { immediate: !0 }
160
168
  );
161
- const i = _(() => c(t)), O = _(() => e(t)), T = _(() => a.value > 1), b = _(() => o.value.length > 0);
162
- Y(
163
- O,
164
- (f) => {
165
- i.value && f.length && (o.value = [...f]);
169
+ const _ = f(() => d(l)), y = f(() => e(l)), I = f(() => a.value > 1), P = f(() => t.value.length > 0);
170
+ j(
171
+ y,
172
+ (v) => {
173
+ _.value && v.length && (t.value = [...v]);
166
174
  },
167
175
  { immediate: !0 }
168
176
  );
169
- function P(f) {
170
- if (!i.value)
171
- if (k(), T.value) {
172
- if (o.value.includes(f)) {
173
- o.value = o.value.filter((S) => S !== f);
177
+ function O(v) {
178
+ if (!_.value)
179
+ if (k(), I.value) {
180
+ if (t.value.includes(v)) {
181
+ t.value = t.value.filter((m) => m !== v);
174
182
  return;
175
183
  }
176
- if (o.value.length < a.value) {
177
- o.value = [...o.value, f];
184
+ if (t.value.length < a.value) {
185
+ t.value = [...t.value, v];
178
186
  return;
179
187
  }
180
- n.value = `You can select up to ${a.value} options.`;
188
+ s.value = `You can select up to ${a.value} options.`;
181
189
  } else
182
- o.value = [f];
190
+ t.value = [v];
183
191
  }
184
- async function w() {
185
- if (!b.value)
186
- return n.value = "Please select at least one option", null;
187
- if (i.value)
188
- return n.value = "You have already voted on this poll", null;
189
- y.value = !0, n.value = null;
190
- const f = [...o.value].sort((S, A) => S - A);
192
+ async function A() {
193
+ if (!P.value)
194
+ return s.value = "Please select at least one option", null;
195
+ if (_.value)
196
+ return s.value = "You have already voted on this poll", null;
197
+ p.value = !0, s.value = null;
198
+ const v = [...t.value].sort((m, T) => m - T);
191
199
  try {
192
- const S = await pe(t, f);
193
- return p(t, f), S.poll;
194
- } catch (S) {
195
- return n.value = S instanceof Error ? S.message : "Failed to submit vote", null;
200
+ const m = await ye(l, v);
201
+ return r(l, v), m.poll;
202
+ } catch (m) {
203
+ if (s.value = m instanceof Error ? m.message : "Failed to submit vote", m instanceof $ && m.status === Te) {
204
+ const T = m.data?.optionIndices;
205
+ Array.isArray(T) ? r(l, T) : r(l, v);
206
+ }
207
+ return null;
196
208
  } finally {
197
- y.value = !1;
209
+ p.value = !1;
198
210
  }
199
211
  }
200
212
  function k() {
201
- n.value = null;
213
+ s.value = null;
202
214
  }
203
215
  return {
204
- submitting: y,
205
- error: n,
206
- selectedOptions: o,
216
+ submitting: p,
217
+ error: s,
218
+ selectedOptions: t,
207
219
  selectionLimit: a,
208
- isMultiSelect: T,
209
- hasSelection: b,
210
- alreadyVoted: i,
211
- votedOptionIndices: O,
212
- selectOption: P,
213
- submitVote: w,
220
+ isMultiSelect: I,
221
+ hasSelection: P,
222
+ alreadyVoted: _,
223
+ votedOptionIndices: y,
224
+ selectOption: O,
225
+ submitVote: A,
214
226
  clearError: k
215
227
  };
216
228
  }
217
- function be(t) {
218
- return typeof t != "number" || !Number.isFinite(t) || t <= 0 ? 1 : Math.max(1, Math.floor(t));
229
+ function Ce(l) {
230
+ return typeof l != "number" || !Number.isFinite(l) || l <= 0 ? 1 : Math.max(1, Math.floor(l));
219
231
  }
220
- const Se = /* @__PURE__ */ K({
232
+ const Ee = /* @__PURE__ */ W({
221
233
  __name: "ColoeusPolls",
222
234
  props: {
223
235
  id: { type: String, required: !0 }
224
236
  },
225
237
  emits: ["voted", "error"],
226
- setup(t, { expose: l, emit: c }) {
227
- l();
228
- const e = t, p = c, { poll: y, loading: n, error: o, totalVotes: a, isActive: i, optionPercentages: O, updatePoll: T } = me(e.id), b = _(() => {
229
- const m = y.value?.maxSelections;
230
- return typeof m == "number" && m > 0 ? Math.floor(m) : 1;
238
+ setup(l, { expose: o, emit: d }) {
239
+ o();
240
+ const e = S(!1), r = l, p = d, { poll: s, loading: t, error: a, totalVotes: _, isActive: y, optionPercentages: I, updatePoll: P } = Oe(r.id), O = f(() => {
241
+ const g = s.value?.maxSelections;
242
+ return typeof g == "number" && g > 0 ? Math.floor(g) : 1;
231
243
  }), {
232
- submitting: P,
233
- error: w,
234
- selectedOptions: k,
235
- selectionLimit: f,
236
- isMultiSelect: S,
237
- hasSelection: A,
238
- alreadyVoted: I,
239
- votedOptionIndices: F,
240
- selectOption: M,
241
- submitVote: r,
242
- clearError: v
243
- } = ge(e.id, { selectionLimit: b }), V = _(() => o.value || w.value), D = _(() => i.value && !I.value), Z = _(() => I.value || !i.value), $ = _(() => `coloeus-poll-title-${e.id}`), ee = _(() => `coloeus-poll-instructions-${e.id}`), oe = _(() => `coloeus-poll-status-${e.id}`), te = _(() => i.value ? I.value ? "You have already voted on this poll" : "Voting is open" : "Voting is closed"), le = _(() => S.value ? "checkbox" : "radio"), ne = _(() => S.value ? `Select up to ${f.value} options and submit your vote.` : "Select one option and submit your vote.");
244
- async function se() {
245
- v();
246
- const m = await r();
247
- m ? (T(m), p("voted", e.id, [...k.value])) : w.value && p("error", w.value);
244
+ submitting: A,
245
+ error: k,
246
+ selectedOptions: v,
247
+ selectionLimit: m,
248
+ isMultiSelect: T,
249
+ hasSelection: z,
250
+ alreadyVoted: M,
251
+ votedOptionIndices: R,
252
+ selectOption: F,
253
+ submitVote: U,
254
+ clearError: i
255
+ } = we(r.id, { selectionLimit: O }), b = f(() => a.value || k.value), V = f(() => y.value && !M.value), B = f(() => M.value || !y.value || e.value), te = f(() => `coloeus-poll-title-${r.id}`), le = f(() => `coloeus-poll-instructions-${r.id}`), se = f(() => `coloeus-poll-status-${r.id}`), ne = f(() => y.value ? M.value ? "You have already voted on this poll" : "Voting is open" : "Voting is closed"), ie = f(() => T.value ? "checkbox" : "radio"), ae = f(() => T.value ? `Select up to ${m.value} options and submit your vote.` : "Select one option and submit your vote.");
256
+ async function re() {
257
+ i();
258
+ const g = await U();
259
+ g ? (P(g), p("voted", r.id, [...v.value])) : k.value && p("error", k.value);
248
260
  }
249
- function ie(m) {
250
- D.value && M(m);
261
+ function ce(g) {
262
+ V.value && F(g);
251
263
  }
252
- function ae(m) {
253
- return `coloeus-poll-option-${e.id}-stats-${m}`;
264
+ function ue(g) {
265
+ return `coloeus-poll-option-${r.id}-stats-${g}`;
254
266
  }
255
- function q(m) {
256
- return k.value.includes(m);
267
+ function G(g) {
268
+ return v.value.includes(g);
257
269
  }
258
- function B(m) {
259
- return F.value.includes(m);
270
+ function H(g) {
271
+ return R.value.includes(g);
260
272
  }
261
- function re(m) {
262
- return q(m) || B(m);
273
+ function de(g) {
274
+ return G(g) || H(g);
263
275
  }
264
- const G = { props: e, emit: p, poll: y, loading: n, pollError: o, totalVotes: a, isActive: i, optionPercentages: O, updatePoll: T, pollSelectionLimit: b, submitting: P, voteError: w, selectedOptions: k, selectionLimit: f, isMultiSelect: S, hasSelection: A, alreadyVoted: I, votedOptionIndices: F, selectOption: M, submitVote: r, clearError: v, displayError: V, canVote: D, showResults: Z, pollTitleId: $, pollInstructionsId: ee, pollStatusId: oe, pollStatusMessage: te, optionInputType: le, selectionInstruction: ne, handleVote: se, handleOptionSelect: ie, getOptionStatsId: ae, isOptionCurrentlySelected: q, hasVotedForOption: B, isOptionChecked: re };
265
- return Object.defineProperty(G, "__isScriptSetup", { enumerable: !1, value: !0 }), G;
276
+ function _e() {
277
+ e.value = !0;
278
+ }
279
+ const K = { resultsRevealed: e, props: r, emit: p, poll: s, loading: t, pollError: a, totalVotes: _, isActive: y, optionPercentages: I, updatePoll: P, pollSelectionLimit: O, submitting: A, voteError: k, selectedOptions: v, selectionLimit: m, isMultiSelect: T, hasSelection: z, alreadyVoted: M, votedOptionIndices: R, selectOption: F, submitVote: U, clearError: i, displayError: b, canVote: V, showResults: B, pollTitleId: te, pollInstructionsId: le, pollStatusId: se, pollStatusMessage: ne, optionInputType: ie, selectionInstruction: ae, handleVote: re, handleOptionSelect: ce, getOptionStatsId: ue, isOptionCurrentlySelected: G, hasVotedForOption: H, isOptionChecked: de, revealResults: _e };
280
+ return Object.defineProperty(K, "__isScriptSetup", { enumerable: !1, value: !0 }), K;
266
281
  }
267
- }), X = (t, l) => {
268
- const c = t.__vccOpts || t;
269
- for (const [e, p] of l)
270
- c[e] = p;
271
- return c;
272
- }, Oe = { class: "coloeus-poll" }, Ve = {
282
+ }), oe = (l, o) => {
283
+ const d = l.__vccOpts || l;
284
+ for (const [e, r] of o)
285
+ d[e] = r;
286
+ return d;
287
+ }, Pe = { class: "coloeus-poll" }, Ie = {
273
288
  key: 0,
274
289
  class: "coloeus-poll__loading"
275
- }, Te = ["aria-labelledby", "aria-describedby"], we = ["aria-describedby", "aria-disabled"], ke = ["id"], Ce = ["id"], Ee = {
290
+ }, Ae = ["aria-labelledby", "aria-describedby"], Me = ["aria-describedby", "aria-disabled"], Ue = ["id"], xe = ["id"], Le = {
276
291
  key: 0,
277
292
  class: "coloeus-poll__instructions-note"
278
- }, Pe = {
293
+ }, Re = {
279
294
  class: "coloeus-poll__options",
280
295
  role: "list"
281
- }, Ie = { class: "coloeus-poll__option-label" }, Ae = ["type", "name", "value", "checked", "disabled", "aria-describedby", "onChange"], Me = { class: "coloeus-poll__option-content" }, Ue = { class: "coloeus-poll__option-text" }, Le = {
282
- key: 0,
296
+ }, Fe = { class: "coloeus-poll__option-label" }, Ne = ["type", "name", "value", "checked", "disabled", "aria-describedby", "onChange"], De = { class: "coloeus-poll__option-content" }, je = { class: "coloeus-poll__option-text" }, ze = {
297
+ key: 1,
283
298
  class: "coloeus-poll__option-stats",
284
299
  "aria-hidden": "true"
285
- }, Fe = { class: "coloeus-poll__option-percentage" }, De = ["id"], Ne = { key: 0 }, je = { class: "coloeus-poll__footer" }, ze = ["disabled"], Re = {
300
+ }, Ye = { class: "coloeus-poll__option-percentage" }, Je = ["id"], qe = { key: 0 }, Be = { class: "coloeus-poll__footer" }, Ge = { class: "coloeus-poll__actions" }, He = ["disabled"], Ke = {
286
301
  class: "coloeus-poll__total",
287
302
  "aria-live": "polite"
288
- }, Ye = ["id"], Je = {
303
+ }, Qe = ["id"], We = {
289
304
  key: 0,
290
305
  class: "coloeus-poll__status coloeus-poll__status--inactive"
291
- }, qe = {
306
+ }, Xe = {
292
307
  key: 1,
293
308
  class: "coloeus-poll__status coloeus-poll__status--active"
294
- }, Be = {
309
+ }, Ze = {
295
310
  key: 2,
296
311
  class: "coloeus-poll__status coloeus-poll__status--open"
297
- }, Ge = { class: "coloeus-poll__sr" }, He = {
312
+ }, $e = { class: "coloeus-poll__sr" }, eo = {
298
313
  key: 0,
299
314
  class: "coloeus-poll__error",
300
315
  role: "alert",
301
316
  "aria-live": "assertive"
302
- }, Ke = {
317
+ }, oo = {
303
318
  key: 2,
304
319
  class: "coloeus-poll__error"
305
320
  };
306
- function xe(t, l, c, e, p, y) {
307
- return d(), u("div", Oe, [
308
- e.loading ? (d(), u("div", Ve, "Loading poll...")) : e.poll ? (d(), u(U, { key: 1 }, [
309
- s("form", {
321
+ function to(l, o, d, e, r, p) {
322
+ return u(), c("div", Pe, [
323
+ e.loading ? (u(), c("div", Ie, "Loading poll...")) : e.poll ? (u(), c(x, { key: 1 }, [
324
+ n("form", {
310
325
  class: "coloeus-poll__form",
311
326
  role: "form",
312
327
  "aria-labelledby": e.pollTitleId,
313
328
  "aria-describedby": `${e.pollInstructionsId} ${e.pollStatusId}`,
314
- onSubmit: x(e.handleVote, ["prevent"])
329
+ onSubmit: X(e.handleVote, ["prevent"])
315
330
  }, [
316
- s("fieldset", {
331
+ n("fieldset", {
317
332
  class: "coloeus-poll__fieldset",
318
333
  "aria-describedby": `${e.pollInstructionsId} ${e.pollStatusId}`,
319
334
  "aria-disabled": !e.canVote
320
335
  }, [
321
- s("legend", {
336
+ n("legend", {
322
337
  id: e.pollTitleId,
323
338
  class: "coloeus-poll__title"
324
- }, h(e.poll.title), 9, ke),
325
- s("p", {
339
+ }, h(e.poll.title), 9, Ue),
340
+ n("p", {
326
341
  id: e.pollInstructionsId,
327
342
  class: "coloeus-poll__instructions"
328
343
  }, [
329
- N(h(e.selectionInstruction) + " ", 1),
330
- e.canVote ? E("", !0) : (d(), u("span", Ee, h(e.pollStatusMessage), 1))
331
- ], 8, Ce),
332
- s("ul", Pe, [
333
- (d(!0), u(U, null, Q(e.poll.options, (n, o) => (d(), u("li", {
334
- key: o,
335
- class: ce(["coloeus-poll__option", {
336
- "coloeus-poll__option--selected": e.isOptionCurrentlySelected(o),
337
- "coloeus-poll__option--voted": e.hasVotedForOption(o),
344
+ Y(h(e.selectionInstruction) + " ", 1),
345
+ e.canVote ? C("", !0) : (u(), c("span", Le, h(e.pollStatusMessage), 1))
346
+ ], 8, xe),
347
+ n("ul", Re, [
348
+ (u(!0), c(x, null, Z(e.poll.options, (s, t) => (u(), c("li", {
349
+ key: t,
350
+ class: fe(["coloeus-poll__option", {
351
+ "coloeus-poll__option--selected": e.isOptionCurrentlySelected(t),
352
+ "coloeus-poll__option--voted": e.hasVotedForOption(t),
338
353
  "coloeus-poll__option--disabled": !e.canVote
339
354
  }])
340
355
  }, [
341
- s("label", Ie, [
342
- s("input", {
356
+ n("label", Fe, [
357
+ n("input", {
343
358
  type: e.optionInputType,
344
359
  class: "coloeus-poll__option-input",
345
360
  name: `coloeus-poll-${e.props.id}`,
346
- value: o,
347
- checked: e.isOptionChecked(o),
361
+ value: t,
362
+ checked: e.isOptionChecked(t),
348
363
  disabled: !e.canVote,
349
- "aria-describedby": e.getOptionStatsId(o),
350
- onChange: (a) => e.handleOptionSelect(o)
351
- }, null, 40, Ae),
352
- s("span", Me, [
353
- s("span", {
364
+ "aria-describedby": e.getOptionStatsId(t),
365
+ onChange: (a) => e.handleOptionSelect(t)
366
+ }, null, 40, Ne),
367
+ n("span", De, [
368
+ e.showResults ? (u(), c("span", {
369
+ key: 0,
354
370
  class: "coloeus-poll__option-progress",
355
- style: ue({ width: `${e.optionPercentages[o]}%` }),
371
+ style: me({ width: `${e.optionPercentages[t]}%` }),
356
372
  "aria-hidden": "true"
357
- }, null, 4),
358
- s("span", Ue, h(n.text), 1),
359
- e.showResults ? (d(), u("span", Le, [
360
- s("span", Fe, h(e.optionPercentages[o]) + "%", 1),
361
- s("span", null, "(" + h(n.voteCount) + ")", 1)
362
- ])) : E("", !0),
363
- s("span", {
364
- id: e.getOptionStatsId(o),
373
+ }, null, 4)) : C("", !0),
374
+ n("span", je, h(s.text), 1),
375
+ e.showResults ? (u(), c("span", ze, [
376
+ n("span", Ye, h(e.optionPercentages[t]) + "%", 1),
377
+ n("span", null, "(" + h(s.voteCount) + ")", 1)
378
+ ])) : C("", !0),
379
+ n("span", {
380
+ id: e.getOptionStatsId(t),
365
381
  class: "coloeus-poll__sr"
366
382
  }, [
367
- e.showResults ? (d(), u(U, { key: 0 }, [
368
- N(h(n.voteCount) + " vote" + h(n.voteCount === 1 ? "" : "s") + " (" + h(e.optionPercentages[o]) + " percent). ", 1),
369
- e.hasVotedForOption(o) ? (d(), u("span", Ne, "You selected this option.")) : E("", !0)
370
- ], 64)) : (d(), u(U, { key: 1 }, [
371
- N(" Results are hidden until voting closes. ")
383
+ e.showResults ? (u(), c(x, { key: 0 }, [
384
+ Y(h(s.voteCount) + " vote" + h(s.voteCount === 1 ? "" : "s") + " (" + h(e.optionPercentages[t]) + " percent). ", 1),
385
+ e.hasVotedForOption(t) ? (u(), c("span", qe, "You selected this option.")) : C("", !0)
386
+ ], 64)) : (u(), c(x, { key: 1 }, [
387
+ Y(" Results are hidden until voting closes. ")
372
388
  ], 64))
373
- ], 8, De)
389
+ ], 8, Je)
374
390
  ])
375
391
  ])
376
392
  ], 2))), 128))
377
393
  ])
378
- ], 8, we),
379
- s("div", je, [
380
- e.canVote ? (d(), u("button", {
381
- key: 0,
382
- type: "submit",
383
- class: "coloeus-poll__vote-btn",
384
- disabled: !e.hasSelection || e.submitting
385
- }, h(e.submitting ? "Voting..." : "Vote"), 9, ze)) : E("", !0),
386
- s("span", Re, h(e.totalVotes) + " vote" + h(e.totalVotes === 1 ? "" : "s") + " cast ", 1),
387
- s("div", {
394
+ ], 8, Me),
395
+ n("div", Be, [
396
+ n("div", Ge, [
397
+ e.canVote ? (u(), c("button", {
398
+ key: 0,
399
+ type: "submit",
400
+ class: "coloeus-poll__vote-btn",
401
+ disabled: !e.hasSelection || e.submitting
402
+ }, h(e.submitting ? "Voting..." : "Vote"), 9, He)) : C("", !0),
403
+ e.canVote && !e.showResults ? (u(), c("button", {
404
+ key: 1,
405
+ type: "button",
406
+ class: "coloeus-poll__results-btn",
407
+ onClick: e.revealResults
408
+ }, " Show results ")) : C("", !0)
409
+ ]),
410
+ n("span", Ke, h(e.totalVotes) + " vote" + h(e.totalVotes === 1 ? "" : "s") + " cast ", 1),
411
+ n("div", {
388
412
  id: e.pollStatusId,
389
413
  class: "coloeus-poll__status-group",
390
414
  role: "status",
391
415
  "aria-live": "polite"
392
416
  }, [
393
- e.isActive ? e.alreadyVoted ? (d(), u("span", qe, " You voted ")) : (d(), u("span", Be, " Voting open ")) : (d(), u("span", Je, " Voting closed ")),
394
- s("span", Ge, h(e.pollStatusMessage), 1)
395
- ], 8, Ye)
417
+ e.isActive ? e.alreadyVoted ? (u(), c("span", Xe, " You voted ")) : (u(), c("span", Ze, " Voting open ")) : (u(), c("span", We, " Voting closed ")),
418
+ n("span", $e, h(e.pollStatusMessage), 1)
419
+ ], 8, Qe)
396
420
  ])
397
- ], 40, Te),
398
- e.displayError ? (d(), u("div", He, h(e.displayError), 1)) : E("", !0)
399
- ], 64)) : (d(), u("div", Ke, "Failed to load poll"))
421
+ ], 40, Ae),
422
+ e.displayError ? (u(), c("div", eo, h(e.displayError), 1)) : C("", !0)
423
+ ], 64)) : (u(), c("div", oo, "Failed to load poll"))
400
424
  ]);
401
425
  }
402
- const vo = /* @__PURE__ */ X(Se, [["render", xe], ["__file", "/Users/philipp/Code/coloeus/frontend/components/src/components/ColoeusPolls.vue"]]), Qe = /* @__PURE__ */ K({
426
+ const wo = /* @__PURE__ */ oe(Ee, [["render", to], ["__file", "/Users/philipp/Code/coloeus/frontend/components/src/components/ColoeusPolls.vue"]]), lo = /* @__PURE__ */ W({
403
427
  __name: "ColoeusEditor",
404
428
  props: {
405
429
  id: { type: String, required: !1 }
406
430
  },
407
431
  emits: ["saved", "cancel", "error"],
408
- setup(t, { expose: l, emit: c }) {
409
- l();
410
- const e = t, p = c, y = _(() => !!e.id), n = g(""), o = g(["", ""]), a = g(""), i = g(""), O = g(!1), T = g(!1), b = g(null), P = _(() => !(!n.value.trim() || o.value.filter((v) => v.trim()).length < 2));
411
- async function w() {
432
+ setup(l, { expose: o, emit: d }) {
433
+ o();
434
+ const e = l, r = d, p = f(() => !!e.id), s = S(""), t = S(["", ""]), a = S(1), _ = S(""), y = S(""), I = S(!1), P = S(!1), O = S(null), A = f(() => t.value.filter((i) => i.trim()).length || 1);
435
+ j(A, (i) => {
436
+ a.value > i && (a.value = i);
437
+ });
438
+ const k = f(() => {
439
+ if (!s.value.trim()) return !1;
440
+ const i = t.value.filter((b) => b.trim());
441
+ return !(i.length < 2 || a.value < 1 || a.value > i.length);
442
+ });
443
+ async function v() {
412
444
  if (e.id) {
413
- O.value = !0, b.value = null;
445
+ I.value = !0, O.value = null;
414
446
  try {
415
- const r = await fe(e.id);
416
- n.value = r.title, o.value = r.options.map((v) => v.text), a.value = r.startTime ? k(r.startTime) : "", i.value = r.endTime ? k(r.endTime) : "";
417
- } catch (r) {
418
- b.value = r instanceof Error ? r.message : "Failed to load poll", p("error", b.value);
447
+ const i = await Se(e.id);
448
+ s.value = i.title, t.value = i.options.map((b) => b.text), a.value = i.maxSelections ?? 1, _.value = i.startTime ? m(i.startTime) : "", y.value = i.endTime ? m(i.endTime) : "";
449
+ } catch (i) {
450
+ O.value = i instanceof Error ? i.message : "Failed to load poll", r("error", O.value);
419
451
  } finally {
420
- O.value = !1;
452
+ I.value = !1;
421
453
  }
422
454
  }
423
455
  }
424
- function k(r) {
425
- const v = new Date(r), V = v.getTimezoneOffset();
426
- return new Date(v.getTime() - V * 60 * 1e3).toISOString().slice(0, 16);
456
+ function m(i) {
457
+ const b = new Date(i), V = b.getTimezoneOffset();
458
+ return new Date(b.getTime() - V * 60 * 1e3).toISOString().slice(0, 16);
427
459
  }
428
- function f() {
429
- o.value.length < 20 && (o.value = [...o.value, ""]);
460
+ function T() {
461
+ t.value.length < 20 && (t.value = [...t.value, ""]);
430
462
  }
431
- function S(r) {
432
- o.value.length > 2 && (o.value = o.value.filter((v, V) => V !== r));
463
+ function z(i) {
464
+ t.value.length > 2 && (t.value = t.value.filter((b, V) => V !== i));
433
465
  }
434
- function A(r, v) {
435
- const V = [...o.value];
436
- V[r] = v, o.value = V;
466
+ function M(i, b) {
467
+ const V = [...t.value];
468
+ V[i] = b, t.value = V;
437
469
  }
438
- async function I() {
439
- if (P.value) {
440
- T.value = !0, b.value = null;
470
+ async function R() {
471
+ if (k.value) {
472
+ P.value = !0, O.value = null;
441
473
  try {
442
- const r = o.value.filter((v) => v.trim());
443
- if (y.value && e.id) {
444
- const v = {
445
- title: n.value.trim(),
446
- startTime: a.value ? new Date(a.value).toISOString() : null,
447
- endTime: i.value ? new Date(i.value).toISOString() : null
448
- }, V = await ve(e.id, v);
449
- p("saved", V);
474
+ const i = t.value.filter((b) => b.trim());
475
+ if (p.value && e.id) {
476
+ const b = {
477
+ title: s.value.trim(),
478
+ startTime: _.value ? new Date(_.value).toISOString() : null,
479
+ endTime: y.value ? new Date(y.value).toISOString() : null
480
+ }, V = await ge(e.id, b);
481
+ r("saved", V);
450
482
  } else {
451
- const v = {
452
- title: n.value.trim(),
453
- options: r,
454
- startTime: a.value ? new Date(a.value).toISOString() : void 0,
455
- endTime: i.value ? new Date(i.value).toISOString() : void 0
456
- }, V = await _e(v);
457
- p("saved", V);
483
+ const b = {
484
+ title: s.value.trim(),
485
+ options: i,
486
+ maxSelections: a.value,
487
+ startTime: _.value ? new Date(_.value).toISOString() : void 0,
488
+ endTime: y.value ? new Date(y.value).toISOString() : void 0
489
+ }, V = await be(b);
490
+ r("saved", V);
458
491
  }
459
- } catch (r) {
460
- b.value = r instanceof Error ? r.message : "Failed to save poll", p("error", b.value);
492
+ } catch (i) {
493
+ O.value = i instanceof Error ? i.message : "Failed to save poll", r("error", O.value);
461
494
  } finally {
462
- T.value = !1;
495
+ P.value = !1;
463
496
  }
464
497
  }
465
498
  }
466
499
  function F() {
467
- p("cancel");
500
+ r("cancel");
468
501
  }
469
- H(() => {
470
- e.id && w();
471
- }), Y(
502
+ Q(() => {
503
+ e.id && v();
504
+ }), j(
472
505
  () => e.id,
473
- (r) => {
474
- r ? w() : (n.value = "", o.value = ["", ""], a.value = "", i.value = "", b.value = null);
506
+ (i) => {
507
+ i ? v() : (s.value = "", t.value = ["", ""], a.value = 1, _.value = "", y.value = "", O.value = null);
475
508
  }
476
509
  );
477
- const M = { props: e, emit: p, isEditMode: y, title: n, options: o, startTime: a, endTime: i, loading: O, submitting: T, error: b, canSubmit: P, loadPoll: w, formatDateTimeLocal: k, addOption: f, removeOption: S, updateOption: A, handleSubmit: I, handleCancel: F };
478
- return Object.defineProperty(M, "__isScriptSetup", { enumerable: !1, value: !0 }), M;
510
+ const U = { props: e, emit: r, isEditMode: p, title: s, options: t, maxSelections: a, startTime: _, endTime: y, loading: I, submitting: P, error: O, validOptionsCount: A, canSubmit: k, loadPoll: v, formatDateTimeLocal: m, addOption: T, removeOption: z, updateOption: M, handleSubmit: R, handleCancel: F };
511
+ return Object.defineProperty(U, "__isScriptSetup", { enumerable: !1, value: !0 }), U;
479
512
  }
480
- }), We = { class: "coloeus-editor" }, Xe = { class: "coloeus-editor__title" }, Ze = {
513
+ }), so = { class: "coloeus-editor" }, no = { class: "coloeus-editor__title" }, io = {
481
514
  key: 0,
482
515
  class: "coloeus-poll__loading"
483
- }, $e = { class: "coloeus-editor__field" }, eo = { class: "coloeus-editor__field" }, oo = { class: "coloeus-editor__options" }, to = ["value", "placeholder", "disabled", "onInput"], lo = ["onClick"], no = { class: "coloeus-editor__field" }, so = { class: "coloeus-editor__field" }, io = { class: "coloeus-editor__actions" }, ao = ["disabled"], ro = {
516
+ }, ao = { class: "coloeus-editor__field" }, ro = { class: "coloeus-editor__field" }, co = { class: "coloeus-editor__options" }, uo = ["value", "placeholder", "disabled", "onInput"], _o = ["onClick"], po = {
517
+ key: 1,
518
+ class: "coloeus-editor__max-selections"
519
+ }, vo = ["max"], fo = {
520
+ key: 2,
521
+ class: "coloeus-editor__max-selections-readonly"
522
+ }, mo = { class: "coloeus-editor__readonly-value" }, ho = { class: "coloeus-editor__field" }, yo = { class: "coloeus-editor__field" }, bo = { class: "coloeus-editor__actions" }, go = ["disabled"], So = {
484
523
  key: 0,
485
524
  class: "coloeus-editor__error"
486
525
  };
487
- function co(t, l, c, e, p, y) {
488
- return d(), u("div", We, [
489
- s("h3", Xe, h(e.isEditMode ? "Edit Poll" : "Create Poll"), 1),
490
- e.loading ? (d(), u("div", Ze, "Loading...")) : (d(), u("form", {
526
+ function Oo(l, o, d, e, r, p) {
527
+ return u(), c("div", so, [
528
+ n("h3", no, h(e.isEditMode ? "Edit Poll" : "Create Poll"), 1),
529
+ e.loading ? (u(), c("div", io, "Loading...")) : (u(), c("form", {
491
530
  key: 1,
492
- onSubmit: x(e.handleSubmit, ["prevent"])
531
+ onSubmit: X(e.handleSubmit, ["prevent"])
493
532
  }, [
494
- s("div", $e, [
495
- l[3] || (l[3] = s("label", {
533
+ n("div", ao, [
534
+ o[4] || (o[4] = n("label", {
496
535
  class: "coloeus-editor__label",
497
536
  for: "poll-title"
498
537
  }, "Title", -1)),
499
- j(s("input", {
538
+ N(n("input", {
500
539
  id: "poll-title",
501
- "onUpdate:modelValue": l[0] || (l[0] = (n) => e.title = n),
540
+ "onUpdate:modelValue": o[0] || (o[0] = (s) => e.title = s),
502
541
  type: "text",
503
542
  class: "coloeus-editor__input",
504
543
  placeholder: "Enter poll title",
505
544
  maxlength: "500",
506
545
  required: ""
507
546
  }, null, 512), [
508
- [z, e.title]
547
+ [D, e.title]
509
548
  ])
510
549
  ]),
511
- s("div", eo, [
512
- l[4] || (l[4] = s("label", { class: "coloeus-editor__label" }, "Options", -1)),
513
- s("ul", oo, [
514
- (d(!0), u(U, null, Q(e.options, (n, o) => (d(), u("li", {
515
- key: o,
550
+ n("div", ro, [
551
+ o[8] || (o[8] = n("label", { class: "coloeus-editor__label" }, "Options", -1)),
552
+ n("ul", co, [
553
+ (u(!0), c(x, null, Z(e.options, (s, t) => (u(), c("li", {
554
+ key: t,
516
555
  class: "coloeus-editor__option"
517
556
  }, [
518
- s("input", {
519
- value: n,
557
+ n("input", {
558
+ value: s,
520
559
  type: "text",
521
560
  class: "coloeus-editor__option-input",
522
- placeholder: `Option ${o + 1}`,
561
+ placeholder: `Option ${t + 1}`,
523
562
  maxlength: "150",
524
563
  disabled: e.isEditMode,
525
- onInput: (a) => e.updateOption(o, a.target.value)
526
- }, null, 40, to),
527
- e.options.length > 2 && !e.isEditMode ? (d(), u("button", {
564
+ onInput: (a) => e.updateOption(t, a.target.value)
565
+ }, null, 40, uo),
566
+ e.options.length > 2 && !e.isEditMode ? (u(), c("button", {
528
567
  key: 0,
529
568
  type: "button",
530
569
  class: "coloeus-editor__remove-btn",
531
- onClick: (a) => e.removeOption(o)
532
- }, " Remove ", 8, lo)) : E("", !0)
570
+ onClick: (a) => e.removeOption(t)
571
+ }, " Remove ", 8, _o)) : C("", !0)
533
572
  ]))), 128))
534
573
  ]),
535
- e.options.length < 20 && !e.isEditMode ? (d(), u("button", {
574
+ e.options.length < 20 && !e.isEditMode ? (u(), c("button", {
536
575
  key: 0,
537
576
  type: "button",
538
577
  class: "coloeus-editor__add-btn",
539
578
  onClick: e.addOption
540
- }, " + Add Option ")) : E("", !0)
579
+ }, " + Add Option ")) : C("", !0),
580
+ e.isEditMode ? (u(), c("div", fo, [
581
+ o[7] || (o[7] = n("label", { class: "coloeus-editor__label" }, "Max Selections", -1)),
582
+ n("span", mo, h(e.maxSelections), 1)
583
+ ])) : (u(), c("div", po, [
584
+ o[5] || (o[5] = n("label", {
585
+ class: "coloeus-editor__label",
586
+ for: "poll-max-selections"
587
+ }, " Max Selections ", -1)),
588
+ N(n("input", {
589
+ id: "poll-max-selections",
590
+ "onUpdate:modelValue": o[1] || (o[1] = (s) => e.maxSelections = s),
591
+ type: "number",
592
+ class: "coloeus-editor__input coloeus-editor__input--small",
593
+ min: "1",
594
+ max: e.validOptionsCount
595
+ }, null, 8, vo), [
596
+ [
597
+ D,
598
+ e.maxSelections,
599
+ void 0,
600
+ { number: !0 }
601
+ ]
602
+ ]),
603
+ o[6] || (o[6] = n("p", { class: "coloeus-editor__hint" }, " Users can select up to this many options (1 = single choice) ", -1))
604
+ ]))
541
605
  ]),
542
- s("div", no, [
543
- l[5] || (l[5] = s("label", {
606
+ n("div", ho, [
607
+ o[9] || (o[9] = n("label", {
544
608
  class: "coloeus-editor__label",
545
609
  for: "poll-start"
546
610
  }, "Start Time (optional)", -1)),
547
- j(s("input", {
611
+ N(n("input", {
548
612
  id: "poll-start",
549
- "onUpdate:modelValue": l[1] || (l[1] = (n) => e.startTime = n),
613
+ "onUpdate:modelValue": o[2] || (o[2] = (s) => e.startTime = s),
550
614
  type: "datetime-local",
551
615
  class: "coloeus-editor__input"
552
616
  }, null, 512), [
553
- [z, e.startTime]
617
+ [D, e.startTime]
554
618
  ])
555
619
  ]),
556
- s("div", so, [
557
- l[6] || (l[6] = s("label", {
620
+ n("div", yo, [
621
+ o[10] || (o[10] = n("label", {
558
622
  class: "coloeus-editor__label",
559
623
  for: "poll-end"
560
624
  }, "End Time (optional)", -1)),
561
- j(s("input", {
625
+ N(n("input", {
562
626
  id: "poll-end",
563
- "onUpdate:modelValue": l[2] || (l[2] = (n) => e.endTime = n),
627
+ "onUpdate:modelValue": o[3] || (o[3] = (s) => e.endTime = s),
564
628
  type: "datetime-local",
565
629
  class: "coloeus-editor__input"
566
630
  }, null, 512), [
567
- [z, e.endTime]
631
+ [D, e.endTime]
568
632
  ])
569
633
  ]),
570
- s("div", io, [
571
- s("button", {
634
+ n("div", bo, [
635
+ n("button", {
572
636
  type: "submit",
573
637
  class: "coloeus-editor__submit-btn",
574
638
  disabled: !e.canSubmit || e.submitting
575
- }, h(e.submitting ? "Saving..." : e.isEditMode ? "Update Poll" : "Create Poll"), 9, ao),
576
- s("button", {
639
+ }, h(e.submitting ? "Saving..." : e.isEditMode ? "Update Poll" : "Create Poll"), 9, go),
640
+ n("button", {
577
641
  type: "button",
578
642
  class: "coloeus-editor__cancel-btn",
579
643
  onClick: e.handleCancel
580
644
  }, " Cancel ")
581
645
  ]),
582
- e.error ? (d(), u("div", ro, h(e.error), 1)) : E("", !0)
646
+ e.error ? (u(), c("div", So, h(e.error), 1)) : C("", !0)
583
647
  ], 32))
584
648
  ]);
585
649
  }
586
- const fo = /* @__PURE__ */ X(Qe, [["render", co], ["__file", "/Users/philipp/Code/coloeus/frontend/components/src/components/ColoeusEditor.vue"]]);
650
+ const Co = /* @__PURE__ */ oe(lo, [["render", Oo], ["__file", "/Users/philipp/Code/coloeus/frontend/components/src/components/ColoeusEditor.vue"]]);
587
651
  export {
588
- fo as ColoeusEditor,
589
- vo as ColoeusPolls,
590
- po as configureColoeus,
591
- _o as getConfig,
592
- ye as useLocalStorage,
593
- me as usePoll,
594
- ge as useVote
652
+ Co as ColoeusEditor,
653
+ wo as ColoeusPolls,
654
+ ko as configureColoeus,
655
+ To as getConfig,
656
+ ke as useLocalStorage,
657
+ Oe as usePoll,
658
+ we as useVote
595
659
  };
@@ -1 +1 @@
1
- (function(y,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],e):(y=typeof globalThis<"u"?globalThis:y||self,e(y.ColoeusComponents={},y.Vue))})(this,(function(y,e){"use strict";let E={apiUrl:""};function R(l){E={...E,...l}}function Y(){return E}async function v(){const l={"Content-Type":"application/json"};if(E.getAuthToken){const n=await E.getAuthToken();n&&(l.Authorization=`Bearer ${n}`)}return l}async function C(l){if(!l.ok){const n=await l.json().catch(()=>({error:"Unknown error"}));throw new Error(n.error||`HTTP error ${l.status}`)}return l.json()}async function J(l){const n=await fetch(`${E.apiUrl}/polls/${l}`,{method:"GET",headers:{"Content-Type":"application/json"}});return C(n)}async function q(l,n){if(n.length===0)throw new Error("At least one option must be selected");const c=await fetch(`${E.apiUrl}/polls/${l}/vote`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({optionIndices:n})});return C(c)}async function G(l){const n=await v(),c=await fetch(`${E.apiUrl}/admin/polls`,{method:"POST",headers:n,body:JSON.stringify(l)});return C(c)}async function H(l,n){const c=await v(),t=await fetch(`${E.apiUrl}/admin/polls/${l}`,{method:"PUT",headers:c,body:JSON.stringify(n)});return C(t)}async function K(l){const n=await v(),c=await fetch(`${E.apiUrl}/admin/polls/${l}`,{method:"GET",headers:n});return C(c)}function I(l){const n=e.ref(null),c=e.ref(!0),t=e.ref(null),d=e.computed(()=>n.value?.totalVotes??0),_=e.computed(()=>n.value?.isActive??!1),s=e.computed(()=>!n.value||d.value===0?n.value?.options.map(()=>0)??[]:n.value.options.map(a=>Math.round(a.voteCount/d.value*100)));async function o(){c.value=!0,t.value=null;try{n.value=await J(l)}catch(a){t.value=a instanceof Error?a.message:"Failed to load poll",n.value=null}finally{c.value=!1}}function i(a){n.value=a}return e.onMounted(()=>{o()}),{poll:n,loading:c,error:t,totalVotes:d,isActive:_,optionPercentages:s,loadPoll:o,updatePoll:i}}const M="coloeus-votes";function Q(){try{const l=localStorage.getItem(M);return l?JSON.parse(l):{}}catch{return{}}}function P(l){try{localStorage.setItem(M,JSON.stringify(l))}catch{}}function A(){const l=e.ref(Q());function n(s){return s in l.value}function c(s){const o=l.value[s];if(o==null)return[];const i=Array.isArray(o)?o:[o];return Array.from(new Set(i)).sort((a,g)=>a-g)}function t(s,o){const i=Array.isArray(o)?o:[o],a=Array.from(new Set(i)).sort((g,b)=>g-b);l.value={...l.value,[s]:a.length===1?a[0]:a},P(l.value)}function d(s){const o={...l.value};delete o[s],l.value=o,P(l.value)}function _(){l.value={},P(l.value)}return{votes:l,hasVoted:n,getVotedOptions:c,recordVote:t,clearVote:d,clearAllVotes:_}}function L(l,n){const{hasVoted:c,getVotedOptions:t,recordVote:d}=A(),_=e.ref(!1),s=e.ref(null),o=e.ref([]),i=e.ref(1);n?.selectionLimit&&e.watch(n.selectionLimit,u=>{i.value=W(u),o.value.length>i.value&&(o.value=o.value.slice(0,i.value))},{immediate:!0});const a=e.computed(()=>c(l)),g=e.computed(()=>t(l)),b=e.computed(()=>i.value>1),f=e.computed(()=>o.value.length>0);e.watch(g,u=>{a.value&&u.length&&(o.value=[...u])},{immediate:!0});function N(u){if(!a.value)if(S(),b.value){if(o.value.includes(u)){o.value=o.value.filter(h=>h!==u);return}if(o.value.length<i.value){o.value=[...o.value,u];return}s.value=`You can select up to ${i.value} options.`}else o.value=[u]}async function k(){if(!f.value)return s.value="Please select at least one option",null;if(a.value)return s.value="You have already voted on this poll",null;_.value=!0,s.value=null;const u=[...o.value].sort((h,B)=>h-B);try{const h=await q(l,u);return d(l,u),h.poll}catch(h){return s.value=h instanceof Error?h.message:"Failed to submit vote",null}finally{_.value=!1}}function S(){s.value=null}return{submitting:_,error:s,selectedOptions:o,selectionLimit:i,isMultiSelect:b,hasSelection:f,alreadyVoted:a,votedOptionIndices:g,selectOption:N,submitVote:k,clearError:S}}function W(l){return typeof l!="number"||!Number.isFinite(l)||l<=0?1:Math.max(1,Math.floor(l))}const X=e.defineComponent({__name:"ColoeusPolls",props:{id:{type:String,required:!0}},emits:["voted","error"],setup(l,{expose:n,emit:c}){n();const t=l,d=c,{poll:_,loading:s,error:o,totalVotes:i,isActive:a,optionPercentages:g,updatePoll:b}=I(t.id),f=e.computed(()=>{const m=_.value?.maxSelections;return typeof m=="number"&&m>0?Math.floor(m):1}),{submitting:N,error:k,selectedOptions:S,selectionLimit:u,isMultiSelect:h,hasSelection:B,alreadyVoted:O,votedOptionIndices:w,selectOption:T,submitVote:r,clearError:p}=L(t.id,{selectionLimit:f}),V=e.computed(()=>o.value||k.value),D=e.computed(()=>a.value&&!O.value),Re=e.computed(()=>O.value||!a.value),Ye=e.computed(()=>`coloeus-poll-title-${t.id}`),Je=e.computed(()=>`coloeus-poll-instructions-${t.id}`),qe=e.computed(()=>`coloeus-poll-status-${t.id}`),Ge=e.computed(()=>a.value?O.value?"You have already voted on this poll":"Voting is open":"Voting is closed"),He=e.computed(()=>h.value?"checkbox":"radio"),Ke=e.computed(()=>h.value?`Select up to ${u.value} options and submit your vote.`:"Select one option and submit your vote.");async function Qe(){p();const m=await r();m?(b(m),d("voted",t.id,[...S.value])):k.value&&d("error",k.value)}function We(m){D.value&&T(m)}function Xe(m){return`coloeus-poll-option-${t.id}-stats-${m}`}function U(m){return S.value.includes(m)}function j(m){return w.value.includes(m)}function Ze(m){return U(m)||j(m)}const z={props:t,emit:d,poll:_,loading:s,pollError:o,totalVotes:i,isActive:a,optionPercentages:g,updatePoll:b,pollSelectionLimit:f,submitting:N,voteError:k,selectedOptions:S,selectionLimit:u,isMultiSelect:h,hasSelection:B,alreadyVoted:O,votedOptionIndices:w,selectOption:T,submitVote:r,clearError:p,displayError:V,canVote:D,showResults:Re,pollTitleId:Ye,pollInstructionsId:Je,pollStatusId:qe,pollStatusMessage:Ge,optionInputType:He,selectionInstruction:Ke,handleVote:Qe,handleOptionSelect:We,getOptionStatsId:Xe,isOptionCurrentlySelected:U,hasVotedForOption:j,isOptionChecked:Ze};return Object.defineProperty(z,"__isScriptSetup",{enumerable:!1,value:!0}),z}}),F=(l,n)=>{const c=l.__vccOpts||l;for(const[t,d]of n)c[t]=d;return c},Z={class:"coloeus-poll"},x={key:0,class:"coloeus-poll__loading"},$=["aria-labelledby","aria-describedby"],ee=["aria-describedby","aria-disabled"],te=["id"],oe=["id"],le={key:0,class:"coloeus-poll__instructions-note"},ne={class:"coloeus-poll__options",role:"list"},se={class:"coloeus-poll__option-label"},ae=["type","name","value","checked","disabled","aria-describedby","onChange"],ie={class:"coloeus-poll__option-content"},re={class:"coloeus-poll__option-text"},ce={key:0,class:"coloeus-poll__option-stats","aria-hidden":"true"},de={class:"coloeus-poll__option-percentage"},pe=["id"],ue={key:0},me={class:"coloeus-poll__footer"},_e=["disabled"],fe={class:"coloeus-poll__total","aria-live":"polite"},he=["id"],ye={key:0,class:"coloeus-poll__status coloeus-poll__status--inactive"},ge={key:1,class:"coloeus-poll__status coloeus-poll__status--active"},Ve={key:2,class:"coloeus-poll__status coloeus-poll__status--open"},be={class:"coloeus-poll__sr"},Ee={key:0,class:"coloeus-poll__error",role:"alert","aria-live":"assertive"},ke={key:2,class:"coloeus-poll__error"};function Se(l,n,c,t,d,_){return e.openBlock(),e.createElementBlock("div",Z,[t.loading?(e.openBlock(),e.createElementBlock("div",x,"Loading poll...")):t.poll?(e.openBlock(),e.createElementBlock(e.Fragment,{key:1},[e.createElementVNode("form",{class:"coloeus-poll__form",role:"form","aria-labelledby":t.pollTitleId,"aria-describedby":`${t.pollInstructionsId} ${t.pollStatusId}`,onSubmit:e.withModifiers(t.handleVote,["prevent"])},[e.createElementVNode("fieldset",{class:"coloeus-poll__fieldset","aria-describedby":`${t.pollInstructionsId} ${t.pollStatusId}`,"aria-disabled":!t.canVote},[e.createElementVNode("legend",{id:t.pollTitleId,class:"coloeus-poll__title"},e.toDisplayString(t.poll.title),9,te),e.createElementVNode("p",{id:t.pollInstructionsId,class:"coloeus-poll__instructions"},[e.createTextVNode(e.toDisplayString(t.selectionInstruction)+" ",1),t.canVote?e.createCommentVNode("",!0):(e.openBlock(),e.createElementBlock("span",le,e.toDisplayString(t.pollStatusMessage),1))],8,oe),e.createElementVNode("ul",ne,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(t.poll.options,(s,o)=>(e.openBlock(),e.createElementBlock("li",{key:o,class:e.normalizeClass(["coloeus-poll__option",{"coloeus-poll__option--selected":t.isOptionCurrentlySelected(o),"coloeus-poll__option--voted":t.hasVotedForOption(o),"coloeus-poll__option--disabled":!t.canVote}])},[e.createElementVNode("label",se,[e.createElementVNode("input",{type:t.optionInputType,class:"coloeus-poll__option-input",name:`coloeus-poll-${t.props.id}`,value:o,checked:t.isOptionChecked(o),disabled:!t.canVote,"aria-describedby":t.getOptionStatsId(o),onChange:i=>t.handleOptionSelect(o)},null,40,ae),e.createElementVNode("span",ie,[e.createElementVNode("span",{class:"coloeus-poll__option-progress",style:e.normalizeStyle({width:`${t.optionPercentages[o]}%`}),"aria-hidden":"true"},null,4),e.createElementVNode("span",re,e.toDisplayString(s.text),1),t.showResults?(e.openBlock(),e.createElementBlock("span",ce,[e.createElementVNode("span",de,e.toDisplayString(t.optionPercentages[o])+"%",1),e.createElementVNode("span",null,"("+e.toDisplayString(s.voteCount)+")",1)])):e.createCommentVNode("",!0),e.createElementVNode("span",{id:t.getOptionStatsId(o),class:"coloeus-poll__sr"},[t.showResults?(e.openBlock(),e.createElementBlock(e.Fragment,{key:0},[e.createTextVNode(e.toDisplayString(s.voteCount)+" vote"+e.toDisplayString(s.voteCount===1?"":"s")+" ("+e.toDisplayString(t.optionPercentages[o])+" percent). ",1),t.hasVotedForOption(o)?(e.openBlock(),e.createElementBlock("span",ue,"You selected this option.")):e.createCommentVNode("",!0)],64)):(e.openBlock(),e.createElementBlock(e.Fragment,{key:1},[e.createTextVNode(" Results are hidden until voting closes. ")],64))],8,pe)])])],2))),128))])],8,ee),e.createElementVNode("div",me,[t.canVote?(e.openBlock(),e.createElementBlock("button",{key:0,type:"submit",class:"coloeus-poll__vote-btn",disabled:!t.hasSelection||t.submitting},e.toDisplayString(t.submitting?"Voting...":"Vote"),9,_e)):e.createCommentVNode("",!0),e.createElementVNode("span",fe,e.toDisplayString(t.totalVotes)+" vote"+e.toDisplayString(t.totalVotes===1?"":"s")+" cast ",1),e.createElementVNode("div",{id:t.pollStatusId,class:"coloeus-poll__status-group",role:"status","aria-live":"polite"},[t.isActive?t.alreadyVoted?(e.openBlock(),e.createElementBlock("span",ge," You voted ")):(e.openBlock(),e.createElementBlock("span",Ve," Voting open ")):(e.openBlock(),e.createElementBlock("span",ye," Voting closed ")),e.createElementVNode("span",be,e.toDisplayString(t.pollStatusMessage),1)],8,he)])],40,$),t.displayError?(e.openBlock(),e.createElementBlock("div",Ee,e.toDisplayString(t.displayError),1)):e.createCommentVNode("",!0)],64)):(e.openBlock(),e.createElementBlock("div",ke,"Failed to load poll"))])}const Ne=F(X,[["render",Se],["__file","/Users/philipp/Code/coloeus/frontend/components/src/components/ColoeusPolls.vue"]]),Oe=e.defineComponent({__name:"ColoeusEditor",props:{id:{type:String,required:!1}},emits:["saved","cancel","error"],setup(l,{expose:n,emit:c}){n();const t=l,d=c,_=e.computed(()=>!!t.id),s=e.ref(""),o=e.ref(["",""]),i=e.ref(""),a=e.ref(""),g=e.ref(!1),b=e.ref(!1),f=e.ref(null),N=e.computed(()=>!(!s.value.trim()||o.value.filter(p=>p.trim()).length<2));async function k(){if(t.id){g.value=!0,f.value=null;try{const r=await K(t.id);s.value=r.title,o.value=r.options.map(p=>p.text),i.value=r.startTime?S(r.startTime):"",a.value=r.endTime?S(r.endTime):""}catch(r){f.value=r instanceof Error?r.message:"Failed to load poll",d("error",f.value)}finally{g.value=!1}}}function S(r){const p=new Date(r),V=p.getTimezoneOffset();return new Date(p.getTime()-V*60*1e3).toISOString().slice(0,16)}function u(){o.value.length<20&&(o.value=[...o.value,""])}function h(r){o.value.length>2&&(o.value=o.value.filter((p,V)=>V!==r))}function B(r,p){const V=[...o.value];V[r]=p,o.value=V}async function O(){if(N.value){b.value=!0,f.value=null;try{const r=o.value.filter(p=>p.trim());if(_.value&&t.id){const p={title:s.value.trim(),startTime:i.value?new Date(i.value).toISOString():null,endTime:a.value?new Date(a.value).toISOString():null},V=await H(t.id,p);d("saved",V)}else{const p={title:s.value.trim(),options:r,startTime:i.value?new Date(i.value).toISOString():void 0,endTime:a.value?new Date(a.value).toISOString():void 0},V=await G(p);d("saved",V)}}catch(r){f.value=r instanceof Error?r.message:"Failed to save poll",d("error",f.value)}finally{b.value=!1}}}function w(){d("cancel")}e.onMounted(()=>{t.id&&k()}),e.watch(()=>t.id,r=>{r?k():(s.value="",o.value=["",""],i.value="",a.value="",f.value=null)});const T={props:t,emit:d,isEditMode:_,title:s,options:o,startTime:i,endTime:a,loading:g,submitting:b,error:f,canSubmit:N,loadPoll:k,formatDateTimeLocal:S,addOption:u,removeOption:h,updateOption:B,handleSubmit:O,handleCancel:w};return Object.defineProperty(T,"__isScriptSetup",{enumerable:!1,value:!0}),T}}),Ce={class:"coloeus-editor"},Be={class:"coloeus-editor__title"},Te={key:0,class:"coloeus-poll__loading"},we={class:"coloeus-editor__field"},ve={class:"coloeus-editor__field"},Pe={class:"coloeus-editor__options"},De=["value","placeholder","disabled","onInput"],Ie=["onClick"],Me={class:"coloeus-editor__field"},Ae={class:"coloeus-editor__field"},Le={class:"coloeus-editor__actions"},Fe=["disabled"],Ue={key:0,class:"coloeus-editor__error"};function je(l,n,c,t,d,_){return e.openBlock(),e.createElementBlock("div",Ce,[e.createElementVNode("h3",Be,e.toDisplayString(t.isEditMode?"Edit Poll":"Create Poll"),1),t.loading?(e.openBlock(),e.createElementBlock("div",Te,"Loading...")):(e.openBlock(),e.createElementBlock("form",{key:1,onSubmit:e.withModifiers(t.handleSubmit,["prevent"])},[e.createElementVNode("div",we,[n[3]||(n[3]=e.createElementVNode("label",{class:"coloeus-editor__label",for:"poll-title"},"Title",-1)),e.withDirectives(e.createElementVNode("input",{id:"poll-title","onUpdate:modelValue":n[0]||(n[0]=s=>t.title=s),type:"text",class:"coloeus-editor__input",placeholder:"Enter poll title",maxlength:"500",required:""},null,512),[[e.vModelText,t.title]])]),e.createElementVNode("div",ve,[n[4]||(n[4]=e.createElementVNode("label",{class:"coloeus-editor__label"},"Options",-1)),e.createElementVNode("ul",Pe,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(t.options,(s,o)=>(e.openBlock(),e.createElementBlock("li",{key:o,class:"coloeus-editor__option"},[e.createElementVNode("input",{value:s,type:"text",class:"coloeus-editor__option-input",placeholder:`Option ${o+1}`,maxlength:"150",disabled:t.isEditMode,onInput:i=>t.updateOption(o,i.target.value)},null,40,De),t.options.length>2&&!t.isEditMode?(e.openBlock(),e.createElementBlock("button",{key:0,type:"button",class:"coloeus-editor__remove-btn",onClick:i=>t.removeOption(o)}," Remove ",8,Ie)):e.createCommentVNode("",!0)]))),128))]),t.options.length<20&&!t.isEditMode?(e.openBlock(),e.createElementBlock("button",{key:0,type:"button",class:"coloeus-editor__add-btn",onClick:t.addOption}," + Add Option ")):e.createCommentVNode("",!0)]),e.createElementVNode("div",Me,[n[5]||(n[5]=e.createElementVNode("label",{class:"coloeus-editor__label",for:"poll-start"},"Start Time (optional)",-1)),e.withDirectives(e.createElementVNode("input",{id:"poll-start","onUpdate:modelValue":n[1]||(n[1]=s=>t.startTime=s),type:"datetime-local",class:"coloeus-editor__input"},null,512),[[e.vModelText,t.startTime]])]),e.createElementVNode("div",Ae,[n[6]||(n[6]=e.createElementVNode("label",{class:"coloeus-editor__label",for:"poll-end"},"End Time (optional)",-1)),e.withDirectives(e.createElementVNode("input",{id:"poll-end","onUpdate:modelValue":n[2]||(n[2]=s=>t.endTime=s),type:"datetime-local",class:"coloeus-editor__input"},null,512),[[e.vModelText,t.endTime]])]),e.createElementVNode("div",Le,[e.createElementVNode("button",{type:"submit",class:"coloeus-editor__submit-btn",disabled:!t.canSubmit||t.submitting},e.toDisplayString(t.submitting?"Saving...":t.isEditMode?"Update Poll":"Create Poll"),9,Fe),e.createElementVNode("button",{type:"button",class:"coloeus-editor__cancel-btn",onClick:t.handleCancel}," Cancel ")]),t.error?(e.openBlock(),e.createElementBlock("div",Ue,e.toDisplayString(t.error),1)):e.createCommentVNode("",!0)],32))])}const ze=F(Oe,[["render",je],["__file","/Users/philipp/Code/coloeus/frontend/components/src/components/ColoeusEditor.vue"]]);y.ColoeusEditor=ze,y.ColoeusPolls=Ne,y.configureColoeus=R,y.getConfig=Y,y.useLocalStorage=A,y.usePoll=I,y.useVote=L,Object.defineProperty(y,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(y,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],e):(y=typeof globalThis<"u"?globalThis:y||self,e(y.ColoeusComponents={},y.Vue))})(this,(function(y,e){"use strict";class A extends Error{constructor(o,c,t){super(c),this.status=o,this.data=t,this.name="ApiError"}}let V={apiUrl:""};function q(n){V={...V,...n}}function G(){return V}async function v(){const n={"Content-Type":"application/json"};if(V.getAuthToken){const o=await V.getAuthToken();o&&(n.Authorization=`Bearer ${o}`)}return n}async function T(n){if(!n.ok){const o=await n.json().catch(()=>({error:"Unknown error"}));throw new A(n.status,o.error||`HTTP error ${n.status}`,o.data)}return n.json()}async function H(n){const o=await fetch(`${V.apiUrl}/polls/${n}`,{method:"GET",headers:{"Content-Type":"application/json"}});return T(o)}async function K(n,o){if(o.length===0)throw new Error("At least one option must be selected");const c=await fetch(`${V.apiUrl}/polls/${n}/vote`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({optionIndices:o})});return T(c)}async function Q(n){const o=await v(),c=await fetch(`${V.apiUrl}/admin/polls`,{method:"POST",headers:o,body:JSON.stringify(n)});return T(c)}async function W(n,o){const c=await v(),t=await fetch(`${V.apiUrl}/admin/polls/${n}`,{method:"PUT",headers:c,body:JSON.stringify(o)});return T(t)}async function X(n){const o=await v(),c=await fetch(`${V.apiUrl}/admin/polls/${n}`,{method:"GET",headers:o});return T(c)}function L(n){const o=e.ref(null),c=e.ref(!0),t=e.ref(null),r=e.computed(()=>o.value?.totalVotes??0),p=e.computed(()=>o.value?.isActive??!1),s=e.computed(()=>!o.value||r.value===0?o.value?.options.map(()=>0)??[]:o.value.options.map(d=>Math.round(d.voteCount/r.value*100)));async function l(){c.value=!0,t.value=null;try{o.value=await H(n)}catch(d){t.value=d instanceof Error?d.message:"Failed to load poll",o.value=null}finally{c.value=!1}}function i(d){o.value=d}return e.onMounted(()=>{l()}),{poll:o,loading:c,error:t,totalVotes:r,isActive:p,optionPercentages:s,loadPoll:l,updatePoll:i}}const U="coloeus-votes";function Z(){try{const n=localStorage.getItem(U);return n?JSON.parse(n):{}}catch{return{}}}function I(n){try{localStorage.setItem(U,JSON.stringify(e.toRaw(n)))}catch{}}const b=e.reactive(Z());function x(){function n(p){return p in b}function o(p){const s=b[p];if(s==null)return[];const l=Array.isArray(s)?s:[s];return Array.from(new Set(l)).sort((i,d)=>i-d)}function c(p,s){const l=Array.isArray(s)?s:[s],i=Array.from(new Set(l)).sort((d,_)=>d-_);b[p]=i.length===1?i[0]:i,I(b)}function t(p){delete b[p],I(b)}function r(){for(const p of Object.keys(b))delete b[p];I(b)}return{votes:b,hasVoted:n,getVotedOptions:o,recordVote:c,clearVote:t,clearAllVotes:r}}const $=409;function F(n,o){const{hasVoted:c,getVotedOptions:t,recordVote:r}=x(),p=e.ref(!1),s=e.ref(null),l=e.ref([]),i=e.ref(1);o?.selectionLimit&&e.watch(o.selectionLimit,u=>{i.value=ee(u),l.value.length>i.value&&(l.value=l.value.slice(0,i.value))},{immediate:!0});const d=e.computed(()=>c(n)),_=e.computed(()=>t(n)),B=e.computed(()=>i.value>1),N=e.computed(()=>l.value.length>0);e.watch(_,u=>{d.value&&u.length&&(l.value=[...u])},{immediate:!0});function g(u){if(!d.value)if(k(),B.value){if(l.value.includes(u)){l.value=l.value.filter(m=>m!==u);return}if(l.value.length<i.value){l.value=[...l.value,u];return}s.value=`You can select up to ${i.value} options.`}else l.value=[u]}async function C(){if(!N.value)return s.value="Please select at least one option",null;if(d.value)return s.value="You have already voted on this poll",null;p.value=!0,s.value=null;const u=[...l.value].sort((m,S)=>m-S);try{const m=await K(n,u);return r(n,u),m.poll}catch(m){if(s.value=m instanceof Error?m.message:"Failed to submit vote",m instanceof A&&m.status===$){const S=m.data?.optionIndices;Array.isArray(S)?r(n,S):r(n,u)}return null}finally{p.value=!1}}function k(){s.value=null}return{submitting:p,error:s,selectedOptions:l,selectionLimit:i,isMultiSelect:B,hasSelection:N,alreadyVoted:d,votedOptionIndices:_,selectOption:g,submitVote:C,clearError:k}}function ee(n){return typeof n!="number"||!Number.isFinite(n)||n<=0?1:Math.max(1,Math.floor(n))}const te=e.defineComponent({__name:"ColoeusPolls",props:{id:{type:String,required:!0}},emits:["voted","error"],setup(n,{expose:o,emit:c}){o();const t=e.ref(!1),r=n,p=c,{poll:s,loading:l,error:i,totalVotes:d,isActive:_,optionPercentages:B,updatePoll:N}=L(r.id),g=e.computed(()=>{const h=s.value?.maxSelections;return typeof h=="number"&&h>0?Math.floor(h):1}),{submitting:C,error:k,selectedOptions:u,selectionLimit:m,isMultiSelect:S,hasSelection:M,alreadyVoted:O,votedOptionIndices:P,selectOption:D,submitVote:w,clearError:a}=F(r.id,{selectionLimit:g}),f=e.computed(()=>i.value||k.value),E=e.computed(()=>_.value&&!O.value),j=e.computed(()=>O.value||!_.value||t.value),Xe=e.computed(()=>`coloeus-poll-title-${r.id}`),Ze=e.computed(()=>`coloeus-poll-instructions-${r.id}`),$e=e.computed(()=>`coloeus-poll-status-${r.id}`),et=e.computed(()=>_.value?O.value?"You have already voted on this poll":"Voting is open":"Voting is closed"),tt=e.computed(()=>S.value?"checkbox":"radio"),ot=e.computed(()=>S.value?`Select up to ${m.value} options and submit your vote.`:"Select one option and submit your vote.");async function lt(){a();const h=await w();h?(N(h),p("voted",r.id,[...u.value])):k.value&&p("error",k.value)}function nt(h){E.value&&D(h)}function st(h){return`coloeus-poll-option-${r.id}-stats-${h}`}function z(h){return u.value.includes(h)}function Y(h){return P.value.includes(h)}function at(h){return z(h)||Y(h)}function it(){t.value=!0}const J={resultsRevealed:t,props:r,emit:p,poll:s,loading:l,pollError:i,totalVotes:d,isActive:_,optionPercentages:B,updatePoll:N,pollSelectionLimit:g,submitting:C,voteError:k,selectedOptions:u,selectionLimit:m,isMultiSelect:S,hasSelection:M,alreadyVoted:O,votedOptionIndices:P,selectOption:D,submitVote:w,clearError:a,displayError:f,canVote:E,showResults:j,pollTitleId:Xe,pollInstructionsId:Ze,pollStatusId:$e,pollStatusMessage:et,optionInputType:tt,selectionInstruction:ot,handleVote:lt,handleOptionSelect:nt,getOptionStatsId:st,isOptionCurrentlySelected:z,hasVotedForOption:Y,isOptionChecked:at,revealResults:it};return Object.defineProperty(J,"__isScriptSetup",{enumerable:!1,value:!0}),J}}),R=(n,o)=>{const c=n.__vccOpts||n;for(const[t,r]of o)c[t]=r;return c},oe={class:"coloeus-poll"},le={key:0,class:"coloeus-poll__loading"},ne=["aria-labelledby","aria-describedby"],se=["aria-describedby","aria-disabled"],ae=["id"],ie=["id"],re={key:0,class:"coloeus-poll__instructions-note"},ce={class:"coloeus-poll__options",role:"list"},de={class:"coloeus-poll__option-label"},pe=["type","name","value","checked","disabled","aria-describedby","onChange"],ue={class:"coloeus-poll__option-content"},me={class:"coloeus-poll__option-text"},_e={key:1,class:"coloeus-poll__option-stats","aria-hidden":"true"},fe={class:"coloeus-poll__option-percentage"},he=["id"],ye={key:0},ge={class:"coloeus-poll__footer"},Ee={class:"coloeus-poll__actions"},Ve=["disabled"],be={class:"coloeus-poll__total","aria-live":"polite"},ke=["id"],Se={key:0,class:"coloeus-poll__status coloeus-poll__status--inactive"},Ne={key:1,class:"coloeus-poll__status coloeus-poll__status--active"},Be={key:2,class:"coloeus-poll__status coloeus-poll__status--open"},Ce={class:"coloeus-poll__sr"},Oe={key:0,class:"coloeus-poll__error",role:"alert","aria-live":"assertive"},Te={key:2,class:"coloeus-poll__error"};function we(n,o,c,t,r,p){return e.openBlock(),e.createElementBlock("div",oe,[t.loading?(e.openBlock(),e.createElementBlock("div",le,"Loading poll...")):t.poll?(e.openBlock(),e.createElementBlock(e.Fragment,{key:1},[e.createElementVNode("form",{class:"coloeus-poll__form",role:"form","aria-labelledby":t.pollTitleId,"aria-describedby":`${t.pollInstructionsId} ${t.pollStatusId}`,onSubmit:e.withModifiers(t.handleVote,["prevent"])},[e.createElementVNode("fieldset",{class:"coloeus-poll__fieldset","aria-describedby":`${t.pollInstructionsId} ${t.pollStatusId}`,"aria-disabled":!t.canVote},[e.createElementVNode("legend",{id:t.pollTitleId,class:"coloeus-poll__title"},e.toDisplayString(t.poll.title),9,ae),e.createElementVNode("p",{id:t.pollInstructionsId,class:"coloeus-poll__instructions"},[e.createTextVNode(e.toDisplayString(t.selectionInstruction)+" ",1),t.canVote?e.createCommentVNode("",!0):(e.openBlock(),e.createElementBlock("span",re,e.toDisplayString(t.pollStatusMessage),1))],8,ie),e.createElementVNode("ul",ce,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(t.poll.options,(s,l)=>(e.openBlock(),e.createElementBlock("li",{key:l,class:e.normalizeClass(["coloeus-poll__option",{"coloeus-poll__option--selected":t.isOptionCurrentlySelected(l),"coloeus-poll__option--voted":t.hasVotedForOption(l),"coloeus-poll__option--disabled":!t.canVote}])},[e.createElementVNode("label",de,[e.createElementVNode("input",{type:t.optionInputType,class:"coloeus-poll__option-input",name:`coloeus-poll-${t.props.id}`,value:l,checked:t.isOptionChecked(l),disabled:!t.canVote,"aria-describedby":t.getOptionStatsId(l),onChange:i=>t.handleOptionSelect(l)},null,40,pe),e.createElementVNode("span",ue,[t.showResults?(e.openBlock(),e.createElementBlock("span",{key:0,class:"coloeus-poll__option-progress",style:e.normalizeStyle({width:`${t.optionPercentages[l]}%`}),"aria-hidden":"true"},null,4)):e.createCommentVNode("",!0),e.createElementVNode("span",me,e.toDisplayString(s.text),1),t.showResults?(e.openBlock(),e.createElementBlock("span",_e,[e.createElementVNode("span",fe,e.toDisplayString(t.optionPercentages[l])+"%",1),e.createElementVNode("span",null,"("+e.toDisplayString(s.voteCount)+")",1)])):e.createCommentVNode("",!0),e.createElementVNode("span",{id:t.getOptionStatsId(l),class:"coloeus-poll__sr"},[t.showResults?(e.openBlock(),e.createElementBlock(e.Fragment,{key:0},[e.createTextVNode(e.toDisplayString(s.voteCount)+" vote"+e.toDisplayString(s.voteCount===1?"":"s")+" ("+e.toDisplayString(t.optionPercentages[l])+" percent). ",1),t.hasVotedForOption(l)?(e.openBlock(),e.createElementBlock("span",ye,"You selected this option.")):e.createCommentVNode("",!0)],64)):(e.openBlock(),e.createElementBlock(e.Fragment,{key:1},[e.createTextVNode(" Results are hidden until voting closes. ")],64))],8,he)])])],2))),128))])],8,se),e.createElementVNode("div",ge,[e.createElementVNode("div",Ee,[t.canVote?(e.openBlock(),e.createElementBlock("button",{key:0,type:"submit",class:"coloeus-poll__vote-btn",disabled:!t.hasSelection||t.submitting},e.toDisplayString(t.submitting?"Voting...":"Vote"),9,Ve)):e.createCommentVNode("",!0),t.canVote&&!t.showResults?(e.openBlock(),e.createElementBlock("button",{key:1,type:"button",class:"coloeus-poll__results-btn",onClick:t.revealResults}," Show results ")):e.createCommentVNode("",!0)]),e.createElementVNode("span",be,e.toDisplayString(t.totalVotes)+" vote"+e.toDisplayString(t.totalVotes===1?"":"s")+" cast ",1),e.createElementVNode("div",{id:t.pollStatusId,class:"coloeus-poll__status-group",role:"status","aria-live":"polite"},[t.isActive?t.alreadyVoted?(e.openBlock(),e.createElementBlock("span",Ne," You voted ")):(e.openBlock(),e.createElementBlock("span",Be," Voting open ")):(e.openBlock(),e.createElementBlock("span",Se," Voting closed ")),e.createElementVNode("span",Ce,e.toDisplayString(t.pollStatusMessage),1)],8,ke)])],40,ne),t.displayError?(e.openBlock(),e.createElementBlock("div",Oe,e.toDisplayString(t.displayError),1)):e.createCommentVNode("",!0)],64)):(e.openBlock(),e.createElementBlock("div",Te,"Failed to load poll"))])}const Pe=R(te,[["render",we],["__file","/Users/philipp/Code/coloeus/frontend/components/src/components/ColoeusPolls.vue"]]),De=e.defineComponent({__name:"ColoeusEditor",props:{id:{type:String,required:!1}},emits:["saved","cancel","error"],setup(n,{expose:o,emit:c}){o();const t=n,r=c,p=e.computed(()=>!!t.id),s=e.ref(""),l=e.ref(["",""]),i=e.ref(1),d=e.ref(""),_=e.ref(""),B=e.ref(!1),N=e.ref(!1),g=e.ref(null),C=e.computed(()=>l.value.filter(a=>a.trim()).length||1);e.watch(C,a=>{i.value>a&&(i.value=a)});const k=e.computed(()=>{if(!s.value.trim())return!1;const a=l.value.filter(f=>f.trim());return!(a.length<2||i.value<1||i.value>a.length)});async function u(){if(t.id){B.value=!0,g.value=null;try{const a=await X(t.id);s.value=a.title,l.value=a.options.map(f=>f.text),i.value=a.maxSelections??1,d.value=a.startTime?m(a.startTime):"",_.value=a.endTime?m(a.endTime):""}catch(a){g.value=a instanceof Error?a.message:"Failed to load poll",r("error",g.value)}finally{B.value=!1}}}function m(a){const f=new Date(a),E=f.getTimezoneOffset();return new Date(f.getTime()-E*60*1e3).toISOString().slice(0,16)}function S(){l.value.length<20&&(l.value=[...l.value,""])}function M(a){l.value.length>2&&(l.value=l.value.filter((f,E)=>E!==a))}function O(a,f){const E=[...l.value];E[a]=f,l.value=E}async function P(){if(k.value){N.value=!0,g.value=null;try{const a=l.value.filter(f=>f.trim());if(p.value&&t.id){const f={title:s.value.trim(),startTime:d.value?new Date(d.value).toISOString():null,endTime:_.value?new Date(_.value).toISOString():null},E=await W(t.id,f);r("saved",E)}else{const f={title:s.value.trim(),options:a,maxSelections:i.value,startTime:d.value?new Date(d.value).toISOString():void 0,endTime:_.value?new Date(_.value).toISOString():void 0},E=await Q(f);r("saved",E)}}catch(a){g.value=a instanceof Error?a.message:"Failed to save poll",r("error",g.value)}finally{N.value=!1}}}function D(){r("cancel")}e.onMounted(()=>{t.id&&u()}),e.watch(()=>t.id,a=>{a?u():(s.value="",l.value=["",""],i.value=1,d.value="",_.value="",g.value=null)});const w={props:t,emit:r,isEditMode:p,title:s,options:l,maxSelections:i,startTime:d,endTime:_,loading:B,submitting:N,error:g,validOptionsCount:C,canSubmit:k,loadPoll:u,formatDateTimeLocal:m,addOption:S,removeOption:M,updateOption:O,handleSubmit:P,handleCancel:D};return Object.defineProperty(w,"__isScriptSetup",{enumerable:!1,value:!0}),w}}),ve={class:"coloeus-editor"},Ie={class:"coloeus-editor__title"},Me={key:0,class:"coloeus-poll__loading"},Ae={class:"coloeus-editor__field"},Le={class:"coloeus-editor__field"},Ue={class:"coloeus-editor__options"},xe=["value","placeholder","disabled","onInput"],Fe=["onClick"],Re={key:1,class:"coloeus-editor__max-selections"},je=["max"],ze={key:2,class:"coloeus-editor__max-selections-readonly"},Ye={class:"coloeus-editor__readonly-value"},Je={class:"coloeus-editor__field"},qe={class:"coloeus-editor__field"},Ge={class:"coloeus-editor__actions"},He=["disabled"],Ke={key:0,class:"coloeus-editor__error"};function Qe(n,o,c,t,r,p){return e.openBlock(),e.createElementBlock("div",ve,[e.createElementVNode("h3",Ie,e.toDisplayString(t.isEditMode?"Edit Poll":"Create Poll"),1),t.loading?(e.openBlock(),e.createElementBlock("div",Me,"Loading...")):(e.openBlock(),e.createElementBlock("form",{key:1,onSubmit:e.withModifiers(t.handleSubmit,["prevent"])},[e.createElementVNode("div",Ae,[o[4]||(o[4]=e.createElementVNode("label",{class:"coloeus-editor__label",for:"poll-title"},"Title",-1)),e.withDirectives(e.createElementVNode("input",{id:"poll-title","onUpdate:modelValue":o[0]||(o[0]=s=>t.title=s),type:"text",class:"coloeus-editor__input",placeholder:"Enter poll title",maxlength:"500",required:""},null,512),[[e.vModelText,t.title]])]),e.createElementVNode("div",Le,[o[8]||(o[8]=e.createElementVNode("label",{class:"coloeus-editor__label"},"Options",-1)),e.createElementVNode("ul",Ue,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(t.options,(s,l)=>(e.openBlock(),e.createElementBlock("li",{key:l,class:"coloeus-editor__option"},[e.createElementVNode("input",{value:s,type:"text",class:"coloeus-editor__option-input",placeholder:`Option ${l+1}`,maxlength:"150",disabled:t.isEditMode,onInput:i=>t.updateOption(l,i.target.value)},null,40,xe),t.options.length>2&&!t.isEditMode?(e.openBlock(),e.createElementBlock("button",{key:0,type:"button",class:"coloeus-editor__remove-btn",onClick:i=>t.removeOption(l)}," Remove ",8,Fe)):e.createCommentVNode("",!0)]))),128))]),t.options.length<20&&!t.isEditMode?(e.openBlock(),e.createElementBlock("button",{key:0,type:"button",class:"coloeus-editor__add-btn",onClick:t.addOption}," + Add Option ")):e.createCommentVNode("",!0),t.isEditMode?(e.openBlock(),e.createElementBlock("div",ze,[o[7]||(o[7]=e.createElementVNode("label",{class:"coloeus-editor__label"},"Max Selections",-1)),e.createElementVNode("span",Ye,e.toDisplayString(t.maxSelections),1)])):(e.openBlock(),e.createElementBlock("div",Re,[o[5]||(o[5]=e.createElementVNode("label",{class:"coloeus-editor__label",for:"poll-max-selections"}," Max Selections ",-1)),e.withDirectives(e.createElementVNode("input",{id:"poll-max-selections","onUpdate:modelValue":o[1]||(o[1]=s=>t.maxSelections=s),type:"number",class:"coloeus-editor__input coloeus-editor__input--small",min:"1",max:t.validOptionsCount},null,8,je),[[e.vModelText,t.maxSelections,void 0,{number:!0}]]),o[6]||(o[6]=e.createElementVNode("p",{class:"coloeus-editor__hint"}," Users can select up to this many options (1 = single choice) ",-1))]))]),e.createElementVNode("div",Je,[o[9]||(o[9]=e.createElementVNode("label",{class:"coloeus-editor__label",for:"poll-start"},"Start Time (optional)",-1)),e.withDirectives(e.createElementVNode("input",{id:"poll-start","onUpdate:modelValue":o[2]||(o[2]=s=>t.startTime=s),type:"datetime-local",class:"coloeus-editor__input"},null,512),[[e.vModelText,t.startTime]])]),e.createElementVNode("div",qe,[o[10]||(o[10]=e.createElementVNode("label",{class:"coloeus-editor__label",for:"poll-end"},"End Time (optional)",-1)),e.withDirectives(e.createElementVNode("input",{id:"poll-end","onUpdate:modelValue":o[3]||(o[3]=s=>t.endTime=s),type:"datetime-local",class:"coloeus-editor__input"},null,512),[[e.vModelText,t.endTime]])]),e.createElementVNode("div",Ge,[e.createElementVNode("button",{type:"submit",class:"coloeus-editor__submit-btn",disabled:!t.canSubmit||t.submitting},e.toDisplayString(t.submitting?"Saving...":t.isEditMode?"Update Poll":"Create Poll"),9,He),e.createElementVNode("button",{type:"button",class:"coloeus-editor__cancel-btn",onClick:t.handleCancel}," Cancel ")]),t.error?(e.openBlock(),e.createElementBlock("div",Ke,e.toDisplayString(t.error),1)):e.createCommentVNode("",!0)],32))])}const We=R(De,[["render",Qe],["__file","/Users/philipp/Code/coloeus/frontend/components/src/components/ColoeusEditor.vue"]]);y.ColoeusEditor=We,y.ColoeusPolls=Pe,y.configureColoeus=q,y.getConfig=G,y.useLocalStorage=x,y.usePoll=L,y.useVote=F,Object.defineProperty(y,Symbol.toStringTag,{value:"Module"})}));
@@ -3,7 +3,7 @@ interface VoteStorage {
3
3
  [pollId: string]: VoteRecord;
4
4
  }
5
5
  export declare function useLocalStorage(): {
6
- votes: import("vue").Ref<VoteStorage, VoteStorage>;
6
+ votes: VoteStorage;
7
7
  hasVoted: (pollId: string) => boolean;
8
8
  getVotedOptions: (pollId: string) => number[];
9
9
  recordVote: (pollId: string, optionIndices: number | number[]) => void;
@@ -1,4 +1,9 @@
1
1
  import type { PublicPoll, VoteResponse, CreatePollInput, UpdatePollInput, AdminPoll, ColoeusConfig } from "../types/index.js";
2
+ export declare class ApiError extends Error {
3
+ readonly status: number;
4
+ readonly data?: Record<string, unknown> | undefined;
5
+ constructor(status: number, message: string, data?: Record<string, unknown> | undefined);
6
+ }
2
7
  export declare function configureColoeus(config: ColoeusConfig): void;
3
8
  export declare function getConfig(): ColoeusConfig;
4
9
  export declare function fetchPoll(pollId: string): Promise<PublicPoll>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@richpods/coloeus",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "Coloeus Poll Components - Embeddable Vue 3 components",
5
5
  "license": "BlueOak-1.0.0",
6
6
  "author": "Philipp Naderer-Puiu",