@wix/ditto-codegen-public 1.0.19 → 1.0.21
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/dist/examples-apps/survey-manager/README.md +21 -0
- package/dist/examples-apps/survey-manager/package-lock.json +14252 -0
- package/dist/examples-apps/survey-manager/package.json +36 -0
- package/dist/examples-apps/survey-manager/src/dashboard/pages/page.tsx +551 -0
- package/dist/examples-apps/survey-manager/src/dashboard/pages/wix_logo.svg +18 -0
- package/dist/examples-apps/survey-manager/src/extensions.ts +12 -0
- package/dist/examples-apps/survey-manager/tsconfig.json +8 -0
- package/dist/examples-apps/survey-manager/wix.config.json +4 -0
- package/dist/out.js +39 -211
- package/package.json +2 -2
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "wix-astro-blank",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"astro": "astro",
|
|
7
|
+
"dev": "wix dev",
|
|
8
|
+
"build": "wix build",
|
|
9
|
+
"wix": "wix",
|
|
10
|
+
"preview": "wix preview",
|
|
11
|
+
"release": "wix release",
|
|
12
|
+
"generate": "wix generate",
|
|
13
|
+
"env": "wix env"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@wix/astro": "^2.2.0",
|
|
17
|
+
"@wix/ecom": "^1.0.1392",
|
|
18
|
+
"@wix/data": "^1.0.291",
|
|
19
|
+
"@wix/design-system": "^1.219.0",
|
|
20
|
+
"@wix/wix-ui-icons-common": "^3.92.0",
|
|
21
|
+
"@wix/dashboard": "^1.3.36",
|
|
22
|
+
"@wix/essentials": "^0.1.23",
|
|
23
|
+
"@wix/sdk": "^1.15.23",
|
|
24
|
+
"astro": "^5.8.0",
|
|
25
|
+
"typescript": "^5.8.3"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@astrojs/cloudflare": "^12.5.3",
|
|
29
|
+
"@astrojs/react": "^4.3.0",
|
|
30
|
+
"@types/react": "^18.3.1",
|
|
31
|
+
"@types/react-dom": "^18.3.1",
|
|
32
|
+
"@wix/cli": "^1.1.92",
|
|
33
|
+
"react": "18.3.1",
|
|
34
|
+
"react-dom": "18.3.1"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,551 @@
|
|
|
1
|
+
import React, { useState, useEffect, type FC } from 'react';
|
|
2
|
+
import { dashboard } from '@wix/dashboard';
|
|
3
|
+
import { items } from '@wix/data';
|
|
4
|
+
import {
|
|
5
|
+
Button,
|
|
6
|
+
Page,
|
|
7
|
+
WixDesignSystemProvider,
|
|
8
|
+
Table,
|
|
9
|
+
TableActionCell,
|
|
10
|
+
TableToolbar,
|
|
11
|
+
Modal,
|
|
12
|
+
CustomModalLayout,
|
|
13
|
+
FormField,
|
|
14
|
+
Input,
|
|
15
|
+
ToggleSwitch,
|
|
16
|
+
Layout,
|
|
17
|
+
Cell,
|
|
18
|
+
Badge,
|
|
19
|
+
Box,
|
|
20
|
+
Text,
|
|
21
|
+
EmptyState,
|
|
22
|
+
Loader,
|
|
23
|
+
Card,
|
|
24
|
+
Heading,
|
|
25
|
+
} from '@wix/design-system';
|
|
26
|
+
import '@wix/design-system/styles.global.css';
|
|
27
|
+
import * as Icons from '@wix/wix-ui-icons-common';
|
|
28
|
+
|
|
29
|
+
export type WixDataItem = items.WixDataItem;
|
|
30
|
+
|
|
31
|
+
// Type definitions based on the CMS collections
|
|
32
|
+
interface SurveyQuestion extends WixDataItem {
|
|
33
|
+
questionText: string;
|
|
34
|
+
maxRating: number;
|
|
35
|
+
isActive: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface SurveyResponse extends WixDataItem {
|
|
39
|
+
questionId: string;
|
|
40
|
+
rating: number;
|
|
41
|
+
submittedAt: Date;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface QuestionStats {
|
|
45
|
+
totalResponses: number;
|
|
46
|
+
averageRating: number;
|
|
47
|
+
ratingDistribution: { [rating: number]: number };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Creates a new survey question
|
|
52
|
+
*/
|
|
53
|
+
async function createQuestion(questionData: Omit<SurveyQuestion, '_id' | '_createdDate' | '_updatedDate' | '_owner'>): Promise<SurveyQuestion> {
|
|
54
|
+
try {
|
|
55
|
+
const result = await items.insert('survey-questions', questionData);
|
|
56
|
+
return result as SurveyQuestion;
|
|
57
|
+
} catch (error) {
|
|
58
|
+
console.error('Error creating survey question:', error);
|
|
59
|
+
throw new Error(
|
|
60
|
+
error instanceof Error ? error.message : 'Failed to create survey question'
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Retrieves all survey questions
|
|
67
|
+
*/
|
|
68
|
+
async function getAllQuestions(): Promise<items.WixDataResult<SurveyQuestion>> {
|
|
69
|
+
try {
|
|
70
|
+
const result = await items.query('survey-questions')
|
|
71
|
+
.descending('_createdDate')
|
|
72
|
+
.find();
|
|
73
|
+
return result as items.WixDataResult<SurveyQuestion>;
|
|
74
|
+
} catch (error) {
|
|
75
|
+
console.error('Error fetching survey questions:', error);
|
|
76
|
+
throw new Error(
|
|
77
|
+
error instanceof Error ? error.message : 'Failed to fetch survey questions'
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Updates an existing survey question
|
|
84
|
+
*/
|
|
85
|
+
async function updateQuestion(questionData: SurveyQuestion): Promise<SurveyQuestion> {
|
|
86
|
+
try {
|
|
87
|
+
if (!questionData._id) {
|
|
88
|
+
throw new Error('Question ID is required for update');
|
|
89
|
+
}
|
|
90
|
+
const result = await items.update('survey-questions', questionData);
|
|
91
|
+
return result as SurveyQuestion;
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error('Error updating survey question:', error);
|
|
94
|
+
throw new Error(
|
|
95
|
+
error instanceof Error ? error.message : 'Failed to update survey question'
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Deletes a survey question by ID
|
|
102
|
+
*/
|
|
103
|
+
async function deleteQuestion(questionId: string): Promise<SurveyQuestion> {
|
|
104
|
+
try {
|
|
105
|
+
if (!questionId) {
|
|
106
|
+
throw new Error('Question ID is required for deletion');
|
|
107
|
+
}
|
|
108
|
+
const result = await items.remove('survey-questions', questionId);
|
|
109
|
+
return result as SurveyQuestion;
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error('Error deleting survey question:', error);
|
|
112
|
+
throw new Error(
|
|
113
|
+
error instanceof Error ? error.message : 'Failed to delete survey question'
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Gets statistics for a specific question
|
|
120
|
+
*/
|
|
121
|
+
async function getQuestionStats(questionId: string): Promise<QuestionStats> {
|
|
122
|
+
try {
|
|
123
|
+
const responses = await items.query('survey-responses')
|
|
124
|
+
.eq('questionId', questionId)
|
|
125
|
+
.find();
|
|
126
|
+
|
|
127
|
+
const responseItems = responses.items as SurveyResponse[];
|
|
128
|
+
const totalResponses = responseItems.length;
|
|
129
|
+
|
|
130
|
+
if (totalResponses === 0) {
|
|
131
|
+
return {
|
|
132
|
+
totalResponses: 0,
|
|
133
|
+
averageRating: 0,
|
|
134
|
+
ratingDistribution: {}
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const ratings = responseItems.map(r => r.rating);
|
|
139
|
+
const averageRating = ratings.reduce((sum, rating) => sum + rating, 0) / totalResponses;
|
|
140
|
+
|
|
141
|
+
const ratingDistribution: { [rating: number]: number } = {};
|
|
142
|
+
ratings.forEach(rating => {
|
|
143
|
+
ratingDistribution[rating] = (ratingDistribution[rating] || 0) + 1;
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
totalResponses,
|
|
148
|
+
averageRating: Math.round(averageRating * 100) / 100,
|
|
149
|
+
ratingDistribution
|
|
150
|
+
};
|
|
151
|
+
} catch (error) {
|
|
152
|
+
console.error('Error fetching question stats:', error);
|
|
153
|
+
return {
|
|
154
|
+
totalResponses: 0,
|
|
155
|
+
averageRating: 0,
|
|
156
|
+
ratingDistribution: {}
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const SurveyManager: FC = () => {
|
|
162
|
+
const [questions, setQuestions] = useState<SurveyQuestion[]>([]);
|
|
163
|
+
const [questionStats, setQuestionStats] = useState<{ [questionId: string]: QuestionStats }>({});
|
|
164
|
+
const [loading, setLoading] = useState(true);
|
|
165
|
+
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
166
|
+
const [editingQuestion, setEditingQuestion] = useState<SurveyQuestion | null>(null);
|
|
167
|
+
const [formData, setFormData] = useState({
|
|
168
|
+
questionText: '',
|
|
169
|
+
maxRating: 5,
|
|
170
|
+
isActive: true
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Load questions and their stats
|
|
174
|
+
const loadQuestions = async () => {
|
|
175
|
+
try {
|
|
176
|
+
setLoading(true);
|
|
177
|
+
const result = await getAllQuestions();
|
|
178
|
+
setQuestions(result.items);
|
|
179
|
+
|
|
180
|
+
// Load stats for each question
|
|
181
|
+
const stats: { [questionId: string]: QuestionStats } = {};
|
|
182
|
+
for (const question of result.items) {
|
|
183
|
+
if (question._id) {
|
|
184
|
+
stats[question._id] = await getQuestionStats(question._id);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
setQuestionStats(stats);
|
|
188
|
+
} catch (error) {
|
|
189
|
+
dashboard.showToast({
|
|
190
|
+
message: 'Failed to load survey questions',
|
|
191
|
+
type: 'error'
|
|
192
|
+
});
|
|
193
|
+
} finally {
|
|
194
|
+
setLoading(false);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
useEffect(() => {
|
|
199
|
+
loadQuestions();
|
|
200
|
+
}, []);
|
|
201
|
+
|
|
202
|
+
const openModal = (question?: SurveyQuestion) => {
|
|
203
|
+
setEditingQuestion(question || null);
|
|
204
|
+
|
|
205
|
+
if (question) {
|
|
206
|
+
setFormData({
|
|
207
|
+
questionText: question.questionText,
|
|
208
|
+
maxRating: question.maxRating,
|
|
209
|
+
isActive: question.isActive
|
|
210
|
+
});
|
|
211
|
+
} else {
|
|
212
|
+
setFormData({
|
|
213
|
+
questionText: '',
|
|
214
|
+
maxRating: 5,
|
|
215
|
+
isActive: true
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
setIsModalOpen(true);
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const closeModal = () => {
|
|
223
|
+
setIsModalOpen(false);
|
|
224
|
+
setEditingQuestion(null);
|
|
225
|
+
setFormData({
|
|
226
|
+
questionText: '',
|
|
227
|
+
maxRating: 5,
|
|
228
|
+
isActive: true
|
|
229
|
+
});
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const handleSave = async () => {
|
|
233
|
+
try {
|
|
234
|
+
if (!formData.questionText.trim()) {
|
|
235
|
+
dashboard.showToast({
|
|
236
|
+
message: 'Question text is required',
|
|
237
|
+
type: 'error'
|
|
238
|
+
});
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (editingQuestion) {
|
|
243
|
+
// Update existing question
|
|
244
|
+
const updatedQuestion: SurveyQuestion = {
|
|
245
|
+
...editingQuestion,
|
|
246
|
+
questionText: formData.questionText,
|
|
247
|
+
maxRating: formData.maxRating,
|
|
248
|
+
isActive: formData.isActive
|
|
249
|
+
};
|
|
250
|
+
await updateQuestion(updatedQuestion);
|
|
251
|
+
dashboard.showToast({
|
|
252
|
+
message: 'Question updated successfully',
|
|
253
|
+
type: 'success'
|
|
254
|
+
});
|
|
255
|
+
} else {
|
|
256
|
+
// Create new question
|
|
257
|
+
await createQuestion({
|
|
258
|
+
questionText: formData.questionText,
|
|
259
|
+
maxRating: formData.maxRating,
|
|
260
|
+
isActive: formData.isActive
|
|
261
|
+
});
|
|
262
|
+
dashboard.showToast({
|
|
263
|
+
message: 'Question created successfully',
|
|
264
|
+
type: 'success'
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
closeModal();
|
|
269
|
+
loadQuestions();
|
|
270
|
+
} catch (error) {
|
|
271
|
+
dashboard.showToast({
|
|
272
|
+
message: editingQuestion ? 'Failed to update question' : 'Failed to create question',
|
|
273
|
+
type: 'error'
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
const handleDelete = async (questionId: string, questionText: string) => {
|
|
279
|
+
try {
|
|
280
|
+
await deleteQuestion(questionId);
|
|
281
|
+
dashboard.showToast({
|
|
282
|
+
message: `"${questionText}" deleted successfully`,
|
|
283
|
+
type: 'success'
|
|
284
|
+
});
|
|
285
|
+
loadQuestions();
|
|
286
|
+
} catch (error) {
|
|
287
|
+
dashboard.showToast({
|
|
288
|
+
message: 'Failed to delete question',
|
|
289
|
+
type: 'error'
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
const toggleQuestionStatus = async (question: SurveyQuestion) => {
|
|
295
|
+
try {
|
|
296
|
+
const updatedQuestion: SurveyQuestion = {
|
|
297
|
+
...question,
|
|
298
|
+
isActive: !question.isActive
|
|
299
|
+
};
|
|
300
|
+
await updateQuestion(updatedQuestion);
|
|
301
|
+
dashboard.showToast({
|
|
302
|
+
message: `Question ${updatedQuestion.isActive ? 'activated' : 'deactivated'}`,
|
|
303
|
+
type: 'success'
|
|
304
|
+
});
|
|
305
|
+
loadQuestions();
|
|
306
|
+
} catch (error) {
|
|
307
|
+
dashboard.showToast({
|
|
308
|
+
message: 'Failed to update question status',
|
|
309
|
+
type: 'error'
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
const columns = [
|
|
315
|
+
{
|
|
316
|
+
title: 'Question',
|
|
317
|
+
render: (question: SurveyQuestion) => (
|
|
318
|
+
<Box direction="vertical" gap="3px">
|
|
319
|
+
<Text size="medium" weight="normal">
|
|
320
|
+
{question.questionText}
|
|
321
|
+
</Text>
|
|
322
|
+
<Box gap="6px">
|
|
323
|
+
<Badge
|
|
324
|
+
skin={question.isActive ? 'success' : 'neutral'}
|
|
325
|
+
size="small"
|
|
326
|
+
>
|
|
327
|
+
{question.isActive ? 'Active' : 'Inactive'}
|
|
328
|
+
</Badge>
|
|
329
|
+
<Text size="tiny" secondary>
|
|
330
|
+
Max Rating: {question.maxRating}
|
|
331
|
+
</Text>
|
|
332
|
+
</Box>
|
|
333
|
+
</Box>
|
|
334
|
+
),
|
|
335
|
+
width: 'auto'
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
title: 'Responses',
|
|
339
|
+
render: (question: SurveyQuestion) => {
|
|
340
|
+
const stats = question._id ? questionStats[question._id] : null;
|
|
341
|
+
return (
|
|
342
|
+
<Box direction="vertical" gap="3px">
|
|
343
|
+
<Text size="medium" weight="bold">
|
|
344
|
+
{stats?.totalResponses || 0}
|
|
345
|
+
</Text>
|
|
346
|
+
<Text size="tiny" secondary>
|
|
347
|
+
Total responses
|
|
348
|
+
</Text>
|
|
349
|
+
</Box>
|
|
350
|
+
);
|
|
351
|
+
},
|
|
352
|
+
width: '120px'
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
title: 'Average Rating',
|
|
356
|
+
render: (question: SurveyQuestion) => {
|
|
357
|
+
const stats = question._id ? questionStats[question._id] : null;
|
|
358
|
+
return (
|
|
359
|
+
<Box direction="vertical" gap="3px">
|
|
360
|
+
<Text size="medium" weight="bold">
|
|
361
|
+
{stats?.averageRating || 0}
|
|
362
|
+
</Text>
|
|
363
|
+
<Text size="tiny" secondary>
|
|
364
|
+
out of {question.maxRating}
|
|
365
|
+
</Text>
|
|
366
|
+
</Box>
|
|
367
|
+
);
|
|
368
|
+
},
|
|
369
|
+
width: '120px'
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
title: 'Created',
|
|
373
|
+
render: (question: SurveyQuestion) => (
|
|
374
|
+
<Text size="small">
|
|
375
|
+
{question._createdDate ? new Date(question._createdDate).toLocaleDateString() : ''}
|
|
376
|
+
</Text>
|
|
377
|
+
),
|
|
378
|
+
width: '100px'
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
title: 'Actions',
|
|
382
|
+
render: (question: SurveyQuestion) => (
|
|
383
|
+
<TableActionCell
|
|
384
|
+
primaryAction={{
|
|
385
|
+
text: 'Edit',
|
|
386
|
+
onClick: () => openModal(question)
|
|
387
|
+
}}
|
|
388
|
+
secondaryActions={[
|
|
389
|
+
{
|
|
390
|
+
text: question.isActive ? 'Deactivate' : 'Activate',
|
|
391
|
+
icon: question.isActive ? <Icons.Hidden /> : <Icons.Visible />,
|
|
392
|
+
onClick: () => toggleQuestionStatus(question)
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
text: 'Delete',
|
|
396
|
+
icon: <Icons.Delete />,
|
|
397
|
+
onClick: () => {
|
|
398
|
+
if (question._id) {
|
|
399
|
+
handleDelete(question._id, question.questionText);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
]}
|
|
404
|
+
/>
|
|
405
|
+
),
|
|
406
|
+
width: '120px'
|
|
407
|
+
}
|
|
408
|
+
];
|
|
409
|
+
|
|
410
|
+
if (loading) {
|
|
411
|
+
return (
|
|
412
|
+
<WixDesignSystemProvider features={{ newColorsBranding: true }}>
|
|
413
|
+
<Page height="100vh">
|
|
414
|
+
<Page.Header title="Survey Manager" />
|
|
415
|
+
<Page.Content>
|
|
416
|
+
<Box align="center" verticalAlign="middle" height="50vh">
|
|
417
|
+
<Loader text="Loading survey questions..." />
|
|
418
|
+
</Box>
|
|
419
|
+
</Page.Content>
|
|
420
|
+
</Page>
|
|
421
|
+
</WixDesignSystemProvider>
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
return (
|
|
426
|
+
<WixDesignSystemProvider features={{ newColorsBranding: true }}>
|
|
427
|
+
<Page height="100vh">
|
|
428
|
+
<Page.Header
|
|
429
|
+
title="Survey Manager"
|
|
430
|
+
subtitle="Create and manage rating survey questions, view aggregated results"
|
|
431
|
+
actionsBar={
|
|
432
|
+
<Button
|
|
433
|
+
onClick={() => openModal()}
|
|
434
|
+
prefixIcon={<Icons.Add />}
|
|
435
|
+
>
|
|
436
|
+
Add Question
|
|
437
|
+
</Button>
|
|
438
|
+
}
|
|
439
|
+
/>
|
|
440
|
+
<Page.Content>
|
|
441
|
+
{questions.length === 0 ? (
|
|
442
|
+
<EmptyState
|
|
443
|
+
title="No survey questions yet"
|
|
444
|
+
subtitle="Create your first rating question to start collecting feedback from visitors"
|
|
445
|
+
skin="page"
|
|
446
|
+
>
|
|
447
|
+
<Button
|
|
448
|
+
onClick={() => openModal()}
|
|
449
|
+
prefixIcon={<Icons.Add />}
|
|
450
|
+
>
|
|
451
|
+
Create First Question
|
|
452
|
+
</Button>
|
|
453
|
+
</EmptyState>
|
|
454
|
+
) : (
|
|
455
|
+
<>
|
|
456
|
+
{/* Summary Cards */}
|
|
457
|
+
<Box gap="24px" marginBottom="24px">
|
|
458
|
+
<Card>
|
|
459
|
+
<Card.Content>
|
|
460
|
+
<Box direction="vertical" gap="6px">
|
|
461
|
+
<Heading size="small">Total Questions</Heading>
|
|
462
|
+
<Text size="medium" weight="bold">{questions.length}</Text>
|
|
463
|
+
</Box>
|
|
464
|
+
</Card.Content>
|
|
465
|
+
</Card>
|
|
466
|
+
<Card>
|
|
467
|
+
<Card.Content>
|
|
468
|
+
<Box direction="vertical" gap="6px">
|
|
469
|
+
<Heading size="small">Active Questions</Heading>
|
|
470
|
+
<Text size="medium" weight="bold">
|
|
471
|
+
{questions.filter(q => q.isActive).length}
|
|
472
|
+
</Text>
|
|
473
|
+
</Box>
|
|
474
|
+
</Card.Content>
|
|
475
|
+
</Card>
|
|
476
|
+
<Card>
|
|
477
|
+
<Card.Content>
|
|
478
|
+
<Box direction="vertical" gap="6px">
|
|
479
|
+
<Heading size="small">Total Responses</Heading>
|
|
480
|
+
<Text size="medium" weight="bold">
|
|
481
|
+
{Object.values(questionStats).reduce((sum, stats) => sum + stats.totalResponses, 0)}
|
|
482
|
+
</Text>
|
|
483
|
+
</Box>
|
|
484
|
+
</Card.Content>
|
|
485
|
+
</Card>
|
|
486
|
+
</Box>
|
|
487
|
+
|
|
488
|
+
{/* Questions Table */}
|
|
489
|
+
<Table data={questions} columns={columns}>
|
|
490
|
+
<TableToolbar>
|
|
491
|
+
<TableToolbar.ItemGroup position="start">
|
|
492
|
+
<TableToolbar.Item>
|
|
493
|
+
<TableToolbar.Title>Survey Questions</TableToolbar.Title>
|
|
494
|
+
</TableToolbar.Item>
|
|
495
|
+
</TableToolbar.ItemGroup>
|
|
496
|
+
</TableToolbar>
|
|
497
|
+
<Table.Content />
|
|
498
|
+
</Table>
|
|
499
|
+
</>
|
|
500
|
+
)}
|
|
501
|
+
</Page.Content>
|
|
502
|
+
</Page>
|
|
503
|
+
|
|
504
|
+
{/* Add/Edit Question Modal */}
|
|
505
|
+
<Modal isOpen={isModalOpen} onRequestClose={closeModal}>
|
|
506
|
+
<CustomModalLayout
|
|
507
|
+
primaryButtonText="Save"
|
|
508
|
+
secondaryButtonText="Cancel"
|
|
509
|
+
onCloseButtonClick={closeModal}
|
|
510
|
+
primaryButtonOnClick={handleSave}
|
|
511
|
+
secondaryButtonOnClick={closeModal}
|
|
512
|
+
title={`${editingQuestion ? 'Edit' : 'Add'} Survey Question`}
|
|
513
|
+
content={
|
|
514
|
+
<Layout gap="24px">
|
|
515
|
+
<Cell span={12}>
|
|
516
|
+
<FormField label="Question Text" required>
|
|
517
|
+
<Input
|
|
518
|
+
value={formData.questionText}
|
|
519
|
+
onChange={(e) => setFormData({ ...formData, questionText: e.target.value })}
|
|
520
|
+
placeholder="How would you rate our service?"
|
|
521
|
+
/>
|
|
522
|
+
</FormField>
|
|
523
|
+
</Cell>
|
|
524
|
+
<Cell span={6}>
|
|
525
|
+
<FormField label="Maximum Rating">
|
|
526
|
+
<Input
|
|
527
|
+
type="number"
|
|
528
|
+
value={formData.maxRating.toString()}
|
|
529
|
+
onChange={(e) => setFormData({ ...formData, maxRating: Math.max(1, Math.min(10, Number(e.target.value))) })}
|
|
530
|
+
min={1}
|
|
531
|
+
max={10}
|
|
532
|
+
/>
|
|
533
|
+
</FormField>
|
|
534
|
+
</Cell>
|
|
535
|
+
<Cell span={6}>
|
|
536
|
+
<FormField label="Active" labelPlacement="right" stretchContent={false}>
|
|
537
|
+
<ToggleSwitch
|
|
538
|
+
checked={formData.isActive}
|
|
539
|
+
onChange={() => setFormData({ ...formData, isActive: !formData.isActive })}
|
|
540
|
+
/>
|
|
541
|
+
</FormField>
|
|
542
|
+
</Cell>
|
|
543
|
+
</Layout>
|
|
544
|
+
}
|
|
545
|
+
/>
|
|
546
|
+
</Modal>
|
|
547
|
+
</WixDesignSystemProvider>
|
|
548
|
+
);
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
export default SurveyManager;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<svg width="166" height="64" viewBox="0 0 166 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<g clip-path="url(#clip0_14809_9463)">
|
|
3
|
+
<path
|
|
4
|
+
d="M165.304 0H156.173C153.64 0 151.273 1.25433 149.85 3.35199L137.639 21.3662C137.313 21.844 136.608 21.844 136.283 21.3662L124.071 3.35199C122.651 1.25433 120.281 0 117.748 0H108.617L130.371 32.0894L108.737 64H117.868C120.401 64 122.769 62.7457 124.192 60.648L136.283 42.8126C136.608 42.3349 137.313 42.3349 137.639 42.8126L149.73 60.648C151.15 62.7457 153.52 64 156.053 64H165.184L143.551 32.0894L165.304 0Z"
|
|
5
|
+
fill="black" />
|
|
6
|
+
<path
|
|
7
|
+
d="M89.8281 6.54652V64H94.1922C97.8088 64 100.74 61.0697 100.74 57.4535V0H96.3755C92.7588 0 89.8281 2.93032 89.8281 6.54652Z"
|
|
8
|
+
fill="black" />
|
|
9
|
+
<path
|
|
10
|
+
d="M81.8276 0H77.944C73.6681 0 69.9633 2.95701 69.0158 7.12564L60.3278 45.3185L52.7234 9.66632C51.3168 3.0771 44.5559 -1.36641 37.6375 0.544431C33.2307 1.76139 29.9637 5.48434 29.0108 9.95455L21.4839 45.2705L12.8118 7.12831C11.8616 2.95968 8.15687 0 3.88092 0H0L14.5548 63.9973H20.0692C25.0738 63.9973 29.3978 60.4986 30.4415 55.604L39.7461 11.9401C39.8608 11.3984 40.3466 11.006 40.8991 11.006C41.4516 11.006 41.9374 11.3984 42.0522 11.9401L51.3648 55.6067C52.4084 60.5012 56.7324 63.9973 61.7371 63.9973H67.2702L81.8276 0Z"
|
|
11
|
+
fill="black" />
|
|
12
|
+
</g>
|
|
13
|
+
<defs>
|
|
14
|
+
<clipPath id="clip0_14809_9463">
|
|
15
|
+
<rect width="165.305" height="64" fill="white" />
|
|
16
|
+
</clipPath>
|
|
17
|
+
</defs>
|
|
18
|
+
</svg>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { app } from '@wix/astro/builders';
|
|
2
|
+
import * as extensions from '@wix/astro/builders';
|
|
3
|
+
|
|
4
|
+
export default app()
|
|
5
|
+
.use(
|
|
6
|
+
extensions.backofficePage({
|
|
7
|
+
component: './dashboard/pages/page.tsx',
|
|
8
|
+
id: '4344c4a2-1864-4587-87e9-5e4109b16999',
|
|
9
|
+
routePath: 'survey-manager',
|
|
10
|
+
title: 'Survey Manager',
|
|
11
|
+
})
|
|
12
|
+
);
|