@richpods/coloeus 0.1.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.
- package/LICENSE +55 -0
- package/dist/coloeus-components.css +1 -0
- package/dist/coloeus-components.js +595 -0
- package/dist/coloeus-components.umd.cjs +1 -0
- package/dist/components/ColoeusEditor.vue.d.ts +15 -0
- package/dist/components/ColoeusPolls.vue.d.ts +12 -0
- package/dist/composables/useLocalStorage.d.ts +13 -0
- package/dist/composables/usePoll.d.ts +35 -0
- package/dist/composables/useVote.d.ts +19 -0
- package/dist/index.d.ts +8 -0
- package/dist/services/api.d.ts +9 -0
- package/dist/types/index.d.ts +45 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Blue Oak Model License
|
|
2
|
+
|
|
3
|
+
Version 1.0.0
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
This license gives everyone as much permission to work with
|
|
8
|
+
this software as possible, while protecting contributors
|
|
9
|
+
from liability.
|
|
10
|
+
|
|
11
|
+
## Acceptance
|
|
12
|
+
|
|
13
|
+
In order to receive this license, you must agree to its
|
|
14
|
+
rules. The rules of this license are both obligations
|
|
15
|
+
under that agreement and conditions to your license.
|
|
16
|
+
You must not do anything with this software that triggers
|
|
17
|
+
a rule that you cannot or will not follow.
|
|
18
|
+
|
|
19
|
+
## Copyright
|
|
20
|
+
|
|
21
|
+
Each contributor licenses you to do everything with this
|
|
22
|
+
software that would otherwise infringe that contributor's
|
|
23
|
+
copyright in it.
|
|
24
|
+
|
|
25
|
+
## Notices
|
|
26
|
+
|
|
27
|
+
You must ensure that everyone who gets a copy of
|
|
28
|
+
any part of this software from you, with or without
|
|
29
|
+
changes, also gets the text of this license or a link to
|
|
30
|
+
<https://blueoakcouncil.org/license/1.0.0>.
|
|
31
|
+
|
|
32
|
+
## Excuse
|
|
33
|
+
|
|
34
|
+
If anyone notifies you in writing that you have not
|
|
35
|
+
complied with [Notices](#notices), you can keep your
|
|
36
|
+
license by taking all practical steps to comply within 30
|
|
37
|
+
days after the notice. If you do not do so, your license
|
|
38
|
+
ends immediately.
|
|
39
|
+
|
|
40
|
+
## Patent
|
|
41
|
+
|
|
42
|
+
Each contributor licenses you to do everything with this
|
|
43
|
+
software that would otherwise infringe any patent claims
|
|
44
|
+
they can license or become able to license.
|
|
45
|
+
|
|
46
|
+
## Reliability
|
|
47
|
+
|
|
48
|
+
No contributor can revoke this license.
|
|
49
|
+
|
|
50
|
+
## No Liability
|
|
51
|
+
|
|
52
|
+
***As far as the law allows, this software comes as is,
|
|
53
|
+
without any warranty or condition, and no contributor
|
|
54
|
+
will be liable to anyone for any damages related to this
|
|
55
|
+
software or this license, under any kind of legal claim.***
|
|
@@ -0,0 +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}
|
|
@@ -0,0 +1,595 @@
|
|
|
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 = {
|
|
3
|
+
apiUrl: ""
|
|
4
|
+
};
|
|
5
|
+
function po(t) {
|
|
6
|
+
C = { ...C, ...t };
|
|
7
|
+
}
|
|
8
|
+
function _o() {
|
|
9
|
+
return C;
|
|
10
|
+
}
|
|
11
|
+
async function J() {
|
|
12
|
+
const t = {
|
|
13
|
+
"Content-Type": "application/json"
|
|
14
|
+
};
|
|
15
|
+
if (C.getAuthToken) {
|
|
16
|
+
const l = await C.getAuthToken();
|
|
17
|
+
l && (t.Authorization = `Bearer ${l}`);
|
|
18
|
+
}
|
|
19
|
+
return t;
|
|
20
|
+
}
|
|
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}`);
|
|
25
|
+
}
|
|
26
|
+
return t.json();
|
|
27
|
+
}
|
|
28
|
+
async function de(t) {
|
|
29
|
+
const l = await fetch(`${C.apiUrl}/polls/${t}`, {
|
|
30
|
+
method: "GET",
|
|
31
|
+
headers: {
|
|
32
|
+
"Content-Type": "application/json"
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
return L(l);
|
|
36
|
+
}
|
|
37
|
+
async function pe(t, l) {
|
|
38
|
+
if (l.length === 0)
|
|
39
|
+
throw new Error("At least one option must be selected");
|
|
40
|
+
const c = await fetch(`${C.apiUrl}/polls/${t}/vote`, {
|
|
41
|
+
method: "POST",
|
|
42
|
+
headers: {
|
|
43
|
+
"Content-Type": "application/json"
|
|
44
|
+
},
|
|
45
|
+
body: JSON.stringify({ optionIndices: l })
|
|
46
|
+
});
|
|
47
|
+
return L(c);
|
|
48
|
+
}
|
|
49
|
+
async function _e(t) {
|
|
50
|
+
const l = await J(), c = await fetch(`${C.apiUrl}/admin/polls`, {
|
|
51
|
+
method: "POST",
|
|
52
|
+
headers: l,
|
|
53
|
+
body: JSON.stringify(t)
|
|
54
|
+
});
|
|
55
|
+
return L(c);
|
|
56
|
+
}
|
|
57
|
+
async function ve(t, l) {
|
|
58
|
+
const c = await J(), e = await fetch(`${C.apiUrl}/admin/polls/${t}`, {
|
|
59
|
+
method: "PUT",
|
|
60
|
+
headers: c,
|
|
61
|
+
body: JSON.stringify(l)
|
|
62
|
+
});
|
|
63
|
+
return L(e);
|
|
64
|
+
}
|
|
65
|
+
async function fe(t) {
|
|
66
|
+
const l = await J(), c = await fetch(`${C.apiUrl}/admin/polls/${t}`, {
|
|
67
|
+
method: "GET",
|
|
68
|
+
headers: l
|
|
69
|
+
});
|
|
70
|
+
return L(c);
|
|
71
|
+
}
|
|
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)
|
|
75
|
+
));
|
|
76
|
+
async function o() {
|
|
77
|
+
c.value = !0, e.value = null;
|
|
78
|
+
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;
|
|
82
|
+
} finally {
|
|
83
|
+
c.value = !1;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function a(i) {
|
|
87
|
+
l.value = i;
|
|
88
|
+
}
|
|
89
|
+
return H(() => {
|
|
90
|
+
o();
|
|
91
|
+
}), {
|
|
92
|
+
poll: l,
|
|
93
|
+
loading: c,
|
|
94
|
+
error: e,
|
|
95
|
+
totalVotes: p,
|
|
96
|
+
isActive: y,
|
|
97
|
+
optionPercentages: n,
|
|
98
|
+
loadPoll: o,
|
|
99
|
+
updatePoll: a
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const W = "coloeus-votes";
|
|
103
|
+
function he() {
|
|
104
|
+
try {
|
|
105
|
+
const t = localStorage.getItem(W);
|
|
106
|
+
return t ? JSON.parse(t) : {};
|
|
107
|
+
} catch {
|
|
108
|
+
return {};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function R(t) {
|
|
112
|
+
try {
|
|
113
|
+
localStorage.setItem(W, JSON.stringify(t));
|
|
114
|
+
} catch {
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function ye() {
|
|
118
|
+
const t = g(he());
|
|
119
|
+
function l(n) {
|
|
120
|
+
return n in t.value;
|
|
121
|
+
}
|
|
122
|
+
function c(n) {
|
|
123
|
+
const o = t.value[n];
|
|
124
|
+
if (o == null)
|
|
125
|
+
return [];
|
|
126
|
+
const a = Array.isArray(o) ? o : [o];
|
|
127
|
+
return Array.from(new Set(a)).sort((i, O) => i - O);
|
|
128
|
+
}
|
|
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);
|
|
135
|
+
}
|
|
136
|
+
function p(n) {
|
|
137
|
+
const o = { ...t.value };
|
|
138
|
+
delete o[n], t.value = o, R(t.value);
|
|
139
|
+
}
|
|
140
|
+
function y() {
|
|
141
|
+
t.value = {}, R(t.value);
|
|
142
|
+
}
|
|
143
|
+
return {
|
|
144
|
+
votes: t,
|
|
145
|
+
hasVoted: l,
|
|
146
|
+
getVotedOptions: c,
|
|
147
|
+
recordVote: e,
|
|
148
|
+
clearVote: p,
|
|
149
|
+
clearAllVotes: y
|
|
150
|
+
};
|
|
151
|
+
}
|
|
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));
|
|
158
|
+
},
|
|
159
|
+
{ immediate: !0 }
|
|
160
|
+
);
|
|
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]);
|
|
166
|
+
},
|
|
167
|
+
{ immediate: !0 }
|
|
168
|
+
);
|
|
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);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (o.value.length < a.value) {
|
|
177
|
+
o.value = [...o.value, f];
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
n.value = `You can select up to ${a.value} options.`;
|
|
181
|
+
} else
|
|
182
|
+
o.value = [f];
|
|
183
|
+
}
|
|
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);
|
|
191
|
+
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;
|
|
196
|
+
} finally {
|
|
197
|
+
y.value = !1;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function k() {
|
|
201
|
+
n.value = null;
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
submitting: y,
|
|
205
|
+
error: n,
|
|
206
|
+
selectedOptions: o,
|
|
207
|
+
selectionLimit: a,
|
|
208
|
+
isMultiSelect: T,
|
|
209
|
+
hasSelection: b,
|
|
210
|
+
alreadyVoted: i,
|
|
211
|
+
votedOptionIndices: O,
|
|
212
|
+
selectOption: P,
|
|
213
|
+
submitVote: w,
|
|
214
|
+
clearError: k
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
function be(t) {
|
|
218
|
+
return typeof t != "number" || !Number.isFinite(t) || t <= 0 ? 1 : Math.max(1, Math.floor(t));
|
|
219
|
+
}
|
|
220
|
+
const Se = /* @__PURE__ */ K({
|
|
221
|
+
__name: "ColoeusPolls",
|
|
222
|
+
props: {
|
|
223
|
+
id: { type: String, required: !0 }
|
|
224
|
+
},
|
|
225
|
+
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;
|
|
231
|
+
}), {
|
|
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);
|
|
248
|
+
}
|
|
249
|
+
function ie(m) {
|
|
250
|
+
D.value && M(m);
|
|
251
|
+
}
|
|
252
|
+
function ae(m) {
|
|
253
|
+
return `coloeus-poll-option-${e.id}-stats-${m}`;
|
|
254
|
+
}
|
|
255
|
+
function q(m) {
|
|
256
|
+
return k.value.includes(m);
|
|
257
|
+
}
|
|
258
|
+
function B(m) {
|
|
259
|
+
return F.value.includes(m);
|
|
260
|
+
}
|
|
261
|
+
function re(m) {
|
|
262
|
+
return q(m) || B(m);
|
|
263
|
+
}
|
|
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;
|
|
266
|
+
}
|
|
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 = {
|
|
273
|
+
key: 0,
|
|
274
|
+
class: "coloeus-poll__loading"
|
|
275
|
+
}, Te = ["aria-labelledby", "aria-describedby"], we = ["aria-describedby", "aria-disabled"], ke = ["id"], Ce = ["id"], Ee = {
|
|
276
|
+
key: 0,
|
|
277
|
+
class: "coloeus-poll__instructions-note"
|
|
278
|
+
}, Pe = {
|
|
279
|
+
class: "coloeus-poll__options",
|
|
280
|
+
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,
|
|
283
|
+
class: "coloeus-poll__option-stats",
|
|
284
|
+
"aria-hidden": "true"
|
|
285
|
+
}, Fe = { class: "coloeus-poll__option-percentage" }, De = ["id"], Ne = { key: 0 }, je = { class: "coloeus-poll__footer" }, ze = ["disabled"], Re = {
|
|
286
|
+
class: "coloeus-poll__total",
|
|
287
|
+
"aria-live": "polite"
|
|
288
|
+
}, Ye = ["id"], Je = {
|
|
289
|
+
key: 0,
|
|
290
|
+
class: "coloeus-poll__status coloeus-poll__status--inactive"
|
|
291
|
+
}, qe = {
|
|
292
|
+
key: 1,
|
|
293
|
+
class: "coloeus-poll__status coloeus-poll__status--active"
|
|
294
|
+
}, Be = {
|
|
295
|
+
key: 2,
|
|
296
|
+
class: "coloeus-poll__status coloeus-poll__status--open"
|
|
297
|
+
}, Ge = { class: "coloeus-poll__sr" }, He = {
|
|
298
|
+
key: 0,
|
|
299
|
+
class: "coloeus-poll__error",
|
|
300
|
+
role: "alert",
|
|
301
|
+
"aria-live": "assertive"
|
|
302
|
+
}, Ke = {
|
|
303
|
+
key: 2,
|
|
304
|
+
class: "coloeus-poll__error"
|
|
305
|
+
};
|
|
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", {
|
|
310
|
+
class: "coloeus-poll__form",
|
|
311
|
+
role: "form",
|
|
312
|
+
"aria-labelledby": e.pollTitleId,
|
|
313
|
+
"aria-describedby": `${e.pollInstructionsId} ${e.pollStatusId}`,
|
|
314
|
+
onSubmit: x(e.handleVote, ["prevent"])
|
|
315
|
+
}, [
|
|
316
|
+
s("fieldset", {
|
|
317
|
+
class: "coloeus-poll__fieldset",
|
|
318
|
+
"aria-describedby": `${e.pollInstructionsId} ${e.pollStatusId}`,
|
|
319
|
+
"aria-disabled": !e.canVote
|
|
320
|
+
}, [
|
|
321
|
+
s("legend", {
|
|
322
|
+
id: e.pollTitleId,
|
|
323
|
+
class: "coloeus-poll__title"
|
|
324
|
+
}, h(e.poll.title), 9, ke),
|
|
325
|
+
s("p", {
|
|
326
|
+
id: e.pollInstructionsId,
|
|
327
|
+
class: "coloeus-poll__instructions"
|
|
328
|
+
}, [
|
|
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),
|
|
338
|
+
"coloeus-poll__option--disabled": !e.canVote
|
|
339
|
+
}])
|
|
340
|
+
}, [
|
|
341
|
+
s("label", Ie, [
|
|
342
|
+
s("input", {
|
|
343
|
+
type: e.optionInputType,
|
|
344
|
+
class: "coloeus-poll__option-input",
|
|
345
|
+
name: `coloeus-poll-${e.props.id}`,
|
|
346
|
+
value: o,
|
|
347
|
+
checked: e.isOptionChecked(o),
|
|
348
|
+
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", {
|
|
354
|
+
class: "coloeus-poll__option-progress",
|
|
355
|
+
style: ue({ width: `${e.optionPercentages[o]}%` }),
|
|
356
|
+
"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),
|
|
365
|
+
class: "coloeus-poll__sr"
|
|
366
|
+
}, [
|
|
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. ")
|
|
372
|
+
], 64))
|
|
373
|
+
], 8, De)
|
|
374
|
+
])
|
|
375
|
+
])
|
|
376
|
+
], 2))), 128))
|
|
377
|
+
])
|
|
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", {
|
|
388
|
+
id: e.pollStatusId,
|
|
389
|
+
class: "coloeus-poll__status-group",
|
|
390
|
+
role: "status",
|
|
391
|
+
"aria-live": "polite"
|
|
392
|
+
}, [
|
|
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)
|
|
396
|
+
])
|
|
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"))
|
|
400
|
+
]);
|
|
401
|
+
}
|
|
402
|
+
const vo = /* @__PURE__ */ X(Se, [["render", xe], ["__file", "/Users/philipp/Code/coloeus/frontend/components/src/components/ColoeusPolls.vue"]]), Qe = /* @__PURE__ */ K({
|
|
403
|
+
__name: "ColoeusEditor",
|
|
404
|
+
props: {
|
|
405
|
+
id: { type: String, required: !1 }
|
|
406
|
+
},
|
|
407
|
+
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() {
|
|
412
|
+
if (e.id) {
|
|
413
|
+
O.value = !0, b.value = null;
|
|
414
|
+
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);
|
|
419
|
+
} finally {
|
|
420
|
+
O.value = !1;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
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);
|
|
427
|
+
}
|
|
428
|
+
function f() {
|
|
429
|
+
o.value.length < 20 && (o.value = [...o.value, ""]);
|
|
430
|
+
}
|
|
431
|
+
function S(r) {
|
|
432
|
+
o.value.length > 2 && (o.value = o.value.filter((v, V) => V !== r));
|
|
433
|
+
}
|
|
434
|
+
function A(r, v) {
|
|
435
|
+
const V = [...o.value];
|
|
436
|
+
V[r] = v, o.value = V;
|
|
437
|
+
}
|
|
438
|
+
async function I() {
|
|
439
|
+
if (P.value) {
|
|
440
|
+
T.value = !0, b.value = null;
|
|
441
|
+
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);
|
|
450
|
+
} 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);
|
|
458
|
+
}
|
|
459
|
+
} catch (r) {
|
|
460
|
+
b.value = r instanceof Error ? r.message : "Failed to save poll", p("error", b.value);
|
|
461
|
+
} finally {
|
|
462
|
+
T.value = !1;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
function F() {
|
|
467
|
+
p("cancel");
|
|
468
|
+
}
|
|
469
|
+
H(() => {
|
|
470
|
+
e.id && w();
|
|
471
|
+
}), Y(
|
|
472
|
+
() => e.id,
|
|
473
|
+
(r) => {
|
|
474
|
+
r ? w() : (n.value = "", o.value = ["", ""], a.value = "", i.value = "", b.value = null);
|
|
475
|
+
}
|
|
476
|
+
);
|
|
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;
|
|
479
|
+
}
|
|
480
|
+
}), We = { class: "coloeus-editor" }, Xe = { class: "coloeus-editor__title" }, Ze = {
|
|
481
|
+
key: 0,
|
|
482
|
+
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 = {
|
|
484
|
+
key: 0,
|
|
485
|
+
class: "coloeus-editor__error"
|
|
486
|
+
};
|
|
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", {
|
|
491
|
+
key: 1,
|
|
492
|
+
onSubmit: x(e.handleSubmit, ["prevent"])
|
|
493
|
+
}, [
|
|
494
|
+
s("div", $e, [
|
|
495
|
+
l[3] || (l[3] = s("label", {
|
|
496
|
+
class: "coloeus-editor__label",
|
|
497
|
+
for: "poll-title"
|
|
498
|
+
}, "Title", -1)),
|
|
499
|
+
j(s("input", {
|
|
500
|
+
id: "poll-title",
|
|
501
|
+
"onUpdate:modelValue": l[0] || (l[0] = (n) => e.title = n),
|
|
502
|
+
type: "text",
|
|
503
|
+
class: "coloeus-editor__input",
|
|
504
|
+
placeholder: "Enter poll title",
|
|
505
|
+
maxlength: "500",
|
|
506
|
+
required: ""
|
|
507
|
+
}, null, 512), [
|
|
508
|
+
[z, e.title]
|
|
509
|
+
])
|
|
510
|
+
]),
|
|
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,
|
|
516
|
+
class: "coloeus-editor__option"
|
|
517
|
+
}, [
|
|
518
|
+
s("input", {
|
|
519
|
+
value: n,
|
|
520
|
+
type: "text",
|
|
521
|
+
class: "coloeus-editor__option-input",
|
|
522
|
+
placeholder: `Option ${o + 1}`,
|
|
523
|
+
maxlength: "150",
|
|
524
|
+
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", {
|
|
528
|
+
key: 0,
|
|
529
|
+
type: "button",
|
|
530
|
+
class: "coloeus-editor__remove-btn",
|
|
531
|
+
onClick: (a) => e.removeOption(o)
|
|
532
|
+
}, " Remove ", 8, lo)) : E("", !0)
|
|
533
|
+
]))), 128))
|
|
534
|
+
]),
|
|
535
|
+
e.options.length < 20 && !e.isEditMode ? (d(), u("button", {
|
|
536
|
+
key: 0,
|
|
537
|
+
type: "button",
|
|
538
|
+
class: "coloeus-editor__add-btn",
|
|
539
|
+
onClick: e.addOption
|
|
540
|
+
}, " + Add Option ")) : E("", !0)
|
|
541
|
+
]),
|
|
542
|
+
s("div", no, [
|
|
543
|
+
l[5] || (l[5] = s("label", {
|
|
544
|
+
class: "coloeus-editor__label",
|
|
545
|
+
for: "poll-start"
|
|
546
|
+
}, "Start Time (optional)", -1)),
|
|
547
|
+
j(s("input", {
|
|
548
|
+
id: "poll-start",
|
|
549
|
+
"onUpdate:modelValue": l[1] || (l[1] = (n) => e.startTime = n),
|
|
550
|
+
type: "datetime-local",
|
|
551
|
+
class: "coloeus-editor__input"
|
|
552
|
+
}, null, 512), [
|
|
553
|
+
[z, e.startTime]
|
|
554
|
+
])
|
|
555
|
+
]),
|
|
556
|
+
s("div", so, [
|
|
557
|
+
l[6] || (l[6] = s("label", {
|
|
558
|
+
class: "coloeus-editor__label",
|
|
559
|
+
for: "poll-end"
|
|
560
|
+
}, "End Time (optional)", -1)),
|
|
561
|
+
j(s("input", {
|
|
562
|
+
id: "poll-end",
|
|
563
|
+
"onUpdate:modelValue": l[2] || (l[2] = (n) => e.endTime = n),
|
|
564
|
+
type: "datetime-local",
|
|
565
|
+
class: "coloeus-editor__input"
|
|
566
|
+
}, null, 512), [
|
|
567
|
+
[z, e.endTime]
|
|
568
|
+
])
|
|
569
|
+
]),
|
|
570
|
+
s("div", io, [
|
|
571
|
+
s("button", {
|
|
572
|
+
type: "submit",
|
|
573
|
+
class: "coloeus-editor__submit-btn",
|
|
574
|
+
disabled: !e.canSubmit || e.submitting
|
|
575
|
+
}, h(e.submitting ? "Saving..." : e.isEditMode ? "Update Poll" : "Create Poll"), 9, ao),
|
|
576
|
+
s("button", {
|
|
577
|
+
type: "button",
|
|
578
|
+
class: "coloeus-editor__cancel-btn",
|
|
579
|
+
onClick: e.handleCancel
|
|
580
|
+
}, " Cancel ")
|
|
581
|
+
]),
|
|
582
|
+
e.error ? (d(), u("div", ro, h(e.error), 1)) : E("", !0)
|
|
583
|
+
], 32))
|
|
584
|
+
]);
|
|
585
|
+
}
|
|
586
|
+
const fo = /* @__PURE__ */ X(Qe, [["render", co], ["__file", "/Users/philipp/Code/coloeus/frontend/components/src/components/ColoeusEditor.vue"]]);
|
|
587
|
+
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
|
|
595
|
+
};
|
|
@@ -0,0 +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"})}));
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AdminPoll } from "../types/index.js";
|
|
2
|
+
import "../styles/poll.css";
|
|
3
|
+
type __VLS_Props = {
|
|
4
|
+
id?: string;
|
|
5
|
+
};
|
|
6
|
+
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
7
|
+
error: (error: string) => any;
|
|
8
|
+
saved: (poll: AdminPoll) => any;
|
|
9
|
+
cancel: () => any;
|
|
10
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
11
|
+
onError?: ((error: string) => any) | undefined;
|
|
12
|
+
onSaved?: ((poll: AdminPoll) => any) | undefined;
|
|
13
|
+
onCancel?: (() => any) | undefined;
|
|
14
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
15
|
+
export default _default;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import "../styles/poll.css";
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
id: string;
|
|
4
|
+
};
|
|
5
|
+
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
6
|
+
error: (error: string) => any;
|
|
7
|
+
voted: (pollId: string, optionIndices: number[]) => any;
|
|
8
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
9
|
+
onError?: ((error: string) => any) | undefined;
|
|
10
|
+
onVoted?: ((pollId: string, optionIndices: number[]) => any) | undefined;
|
|
11
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
12
|
+
export default _default;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
type VoteRecord = number | number[];
|
|
2
|
+
interface VoteStorage {
|
|
3
|
+
[pollId: string]: VoteRecord;
|
|
4
|
+
}
|
|
5
|
+
export declare function useLocalStorage(): {
|
|
6
|
+
votes: import("vue").Ref<VoteStorage, VoteStorage>;
|
|
7
|
+
hasVoted: (pollId: string) => boolean;
|
|
8
|
+
getVotedOptions: (pollId: string) => number[];
|
|
9
|
+
recordVote: (pollId: string, optionIndices: number | number[]) => void;
|
|
10
|
+
clearVote: (pollId: string) => void;
|
|
11
|
+
clearAllVotes: () => void;
|
|
12
|
+
};
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { PublicPoll } from "../types/index.js";
|
|
2
|
+
export declare function usePoll(pollId: string): {
|
|
3
|
+
poll: import("vue").Ref<{
|
|
4
|
+
id: string;
|
|
5
|
+
title: string;
|
|
6
|
+
options: {
|
|
7
|
+
text: string;
|
|
8
|
+
voteCount: number;
|
|
9
|
+
}[];
|
|
10
|
+
maxSelections: number;
|
|
11
|
+
startTime: string | null;
|
|
12
|
+
endTime: string | null;
|
|
13
|
+
totalVotes: number;
|
|
14
|
+
isActive: boolean;
|
|
15
|
+
} | null, PublicPoll | {
|
|
16
|
+
id: string;
|
|
17
|
+
title: string;
|
|
18
|
+
options: {
|
|
19
|
+
text: string;
|
|
20
|
+
voteCount: number;
|
|
21
|
+
}[];
|
|
22
|
+
maxSelections: number;
|
|
23
|
+
startTime: string | null;
|
|
24
|
+
endTime: string | null;
|
|
25
|
+
totalVotes: number;
|
|
26
|
+
isActive: boolean;
|
|
27
|
+
} | null>;
|
|
28
|
+
loading: import("vue").Ref<boolean, boolean>;
|
|
29
|
+
error: import("vue").Ref<string | null, string | null>;
|
|
30
|
+
totalVotes: import("vue").ComputedRef<number>;
|
|
31
|
+
isActive: import("vue").ComputedRef<boolean>;
|
|
32
|
+
optionPercentages: import("vue").ComputedRef<number[]>;
|
|
33
|
+
loadPoll: () => Promise<void>;
|
|
34
|
+
updatePoll: (updatedPoll: PublicPoll) => void;
|
|
35
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type ComputedRef, type Ref } from "vue";
|
|
2
|
+
import type { PublicPoll } from "../types/index.js";
|
|
3
|
+
interface UseVoteOptions {
|
|
4
|
+
selectionLimit?: Ref<number> | ComputedRef<number>;
|
|
5
|
+
}
|
|
6
|
+
export declare function useVote(pollId: string, options?: UseVoteOptions): {
|
|
7
|
+
submitting: Ref<boolean, boolean>;
|
|
8
|
+
error: Ref<string | null, string | null>;
|
|
9
|
+
selectedOptions: Ref<number[], number[]>;
|
|
10
|
+
selectionLimit: Ref<number, number>;
|
|
11
|
+
isMultiSelect: ComputedRef<boolean>;
|
|
12
|
+
hasSelection: ComputedRef<boolean>;
|
|
13
|
+
alreadyVoted: ComputedRef<boolean>;
|
|
14
|
+
votedOptionIndices: ComputedRef<number[]>;
|
|
15
|
+
selectOption: (index: number) => void;
|
|
16
|
+
submitVote: () => Promise<PublicPoll | null>;
|
|
17
|
+
clearError: () => void;
|
|
18
|
+
};
|
|
19
|
+
export {};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { default as ColoeusPolls } from "./components/ColoeusPolls.vue";
|
|
2
|
+
export { default as ColoeusEditor } from "./components/ColoeusEditor.vue";
|
|
3
|
+
export { usePoll } from "./composables/usePoll.js";
|
|
4
|
+
export { useVote } from "./composables/useVote.js";
|
|
5
|
+
export { useLocalStorage } from "./composables/useLocalStorage.js";
|
|
6
|
+
export { configureColoeus, getConfig } from "./services/api.js";
|
|
7
|
+
export type { PublicPoll, PollOption, VoteResponse, CreatePollInput, UpdatePollInput, AdminPoll, ColoeusConfig, } from "./types/index.js";
|
|
8
|
+
import "./styles/poll.css";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { PublicPoll, VoteResponse, CreatePollInput, UpdatePollInput, AdminPoll, ColoeusConfig } from "../types/index.js";
|
|
2
|
+
export declare function configureColoeus(config: ColoeusConfig): void;
|
|
3
|
+
export declare function getConfig(): ColoeusConfig;
|
|
4
|
+
export declare function fetchPoll(pollId: string): Promise<PublicPoll>;
|
|
5
|
+
export declare function submitVote(pollId: string, optionIndices: number[]): Promise<VoteResponse>;
|
|
6
|
+
export declare function createPoll(input: CreatePollInput): Promise<AdminPoll>;
|
|
7
|
+
export declare function updatePoll(pollId: string, input: UpdatePollInput): Promise<AdminPoll>;
|
|
8
|
+
export declare function fetchAdminPoll(pollId: string): Promise<AdminPoll>;
|
|
9
|
+
export declare function deletePoll(pollId: string): Promise<void>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export interface PollOption {
|
|
2
|
+
text: string;
|
|
3
|
+
voteCount: number;
|
|
4
|
+
}
|
|
5
|
+
export interface PublicPoll {
|
|
6
|
+
id: string;
|
|
7
|
+
title: string;
|
|
8
|
+
options: PollOption[];
|
|
9
|
+
maxSelections: number;
|
|
10
|
+
startTime: string | null;
|
|
11
|
+
endTime: string | null;
|
|
12
|
+
totalVotes: number;
|
|
13
|
+
isActive: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface VoteResponse {
|
|
16
|
+
success: boolean;
|
|
17
|
+
poll: PublicPoll;
|
|
18
|
+
}
|
|
19
|
+
export interface CreatePollInput {
|
|
20
|
+
title: string;
|
|
21
|
+
options: string[];
|
|
22
|
+
maxSelections?: number;
|
|
23
|
+
startTime?: string;
|
|
24
|
+
endTime?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface UpdatePollInput {
|
|
27
|
+
title?: string;
|
|
28
|
+
startTime?: string | null;
|
|
29
|
+
endTime?: string | null;
|
|
30
|
+
}
|
|
31
|
+
export interface AdminPoll {
|
|
32
|
+
id: string;
|
|
33
|
+
title: string;
|
|
34
|
+
options: PollOption[];
|
|
35
|
+
maxSelections: number;
|
|
36
|
+
startTime: string | null;
|
|
37
|
+
endTime: string | null;
|
|
38
|
+
deleted: boolean;
|
|
39
|
+
createdAt: string;
|
|
40
|
+
totalVotes: number;
|
|
41
|
+
}
|
|
42
|
+
export interface ColoeusConfig {
|
|
43
|
+
apiUrl: string;
|
|
44
|
+
getAuthToken?: () => Promise<string | null>;
|
|
45
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@richpods/coloeus",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Coloeus Poll Components - Embeddable Vue 3 components",
|
|
5
|
+
"license": "BlueOak-1.0.0",
|
|
6
|
+
"author": "Philipp Naderer-Puiu",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/richpods/coloeus.git"
|
|
10
|
+
},
|
|
11
|
+
"keywords": ["vue", "poll", "components", "voting", "embed"],
|
|
12
|
+
"type": "module",
|
|
13
|
+
"main": "./dist/coloeus-components.umd.cjs",
|
|
14
|
+
"module": "./dist/coloeus-components.js",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"import": "./dist/coloeus-components.js",
|
|
20
|
+
"require": "./dist/coloeus-components.umd.cjs"
|
|
21
|
+
},
|
|
22
|
+
"./style.css": "./dist/style.css"
|
|
23
|
+
},
|
|
24
|
+
"files": ["dist", "LICENSE"],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"dev": "vite",
|
|
27
|
+
"build": "vite build && vue-tsc --emitDeclarationOnly",
|
|
28
|
+
"preview": "vite preview",
|
|
29
|
+
"test": "vitest run",
|
|
30
|
+
"test:watch": "vitest"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"vue": "^3.5.0"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"vue": "^3.5.26"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@vitejs/plugin-vue": "^6.0.3",
|
|
40
|
+
"@vue/test-utils": "^2.4.6",
|
|
41
|
+
"typescript": "^5.9.3",
|
|
42
|
+
"vite": "^7.3.0",
|
|
43
|
+
"vite-plugin-dts": "^4.5.4",
|
|
44
|
+
"vitest": "^4.0.16",
|
|
45
|
+
"vue-tsc": "^2.2.12"
|
|
46
|
+
},
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=24.0.0"
|
|
49
|
+
}
|
|
50
|
+
}
|