Skip to content

Commit 4065011

Browse files
committed
Merge branch 'organization-feature' of https://github.com/ruxailab/RUXAILAB into organization-feature
2 parents a24c29a + ed66511 commit 4065011

File tree

10 files changed

+2323
-23
lines changed

10 files changed

+2323
-23
lines changed

functions/src/https/email.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import * as path from "path";
66
export const sendEmail = functions.onCall({
77
handler: async (data) => {
88
const content = data.data;
9-
console.log(content);
109

1110
const transporter = nodemailer.createTransport({
1211
host: process.env.SMTP_HOST,
@@ -26,7 +25,9 @@ export const sendEmail = functions.onCall({
2625
.replace("{{site}}", process.env.SITE_URL)
2726
.replace("{{message}}", content.data.message)
2827
.replace(/{{testTitle}}/g, content.data.testTitle)
29-
.replace(/{{adminEmail}}/g, content.data.adminEmail);
28+
.replace(/{{testDescription}}/g, content.data.testDescription)
29+
.replace(/{{adminEmail}}/g, content.data.adminEmail)
30+
.replace(/{{adminName}}/g, content.data.adminName);
3031
}
3132
else if (content.template === 'passwordReset') {
3233
const actionCodeSettings = {

functions/src/templates/mails/invitations.html

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,15 @@
2222
<p>{{message}}</p>
2323
<p>
2424
You have been invited to participate in the study:
25+
<br>
2526
<strong>{{testTitle}}</strong>
2627
</p>
28+
<p style="margin:0 0 15px 0; font-size:14px; color:#555;">
29+
Organized by <strong>{{adminName}}</strong>
30+
</p>
31+
<p>
32+
{{testDescription}}
33+
</p>
2734
<p>
2835
Click the button below to access the platform and begin your participation:
2936
</p>
@@ -43,10 +50,7 @@
4350
<!-- Footer -->
4451
<tr>
4552
<td style="background:#f0f0f0; padding:20px; text-align:center; font-size:13px; color:#555; line-height:1.5;">
46-
<p>Need help? Contact:</p>
47-
<p>
48-
<a href="mailto:{{adminEmail}}" style="color:#4CAF50; text-decoration:none;">{{adminEmail}}</a>
49-
</p>
53+
<p>Need help? Contact: <a href="mailto:{{adminEmail}}" style="color:#4CAF50; text-decoration:none;">{{adminEmail}}</a></p>
5054
<p style="margin-top:15px; font-size:12px; color:#999;">
5155
© 2025 RUXAILAB - All rights reserved.
5256
</p>

src/shared/views/CooperatorsView.vue

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ const showIntroView = computed(() => {
204204
// Computeds
205205
const dialog = computed(() => store.getters.getDialogLeaveStatus);
206206
const test = computed(() => store.getters.test);
207+
const userAuth = computed(() => store.getters.user);
207208
const users = computed(() => store.state.Users?.users || []);
208209
const cooperatorsEdit = computed(() => test.value?.cooperators ? [...test.value.cooperators] : []);
209210
const loading = computed(() => store.getters.loading);
@@ -239,7 +240,9 @@ const handleSendEmail = async (guest) => {
239240
data: {
240241
message: inviteMessages.value || '',
241242
testTitle: test.value.testTitle,
243+
testDescription: test.value.testDescription,
242244
adminEmail: test.value.testAdmin.email,
245+
adminName: userAuth.value.name || userAuth.value.email,
243246
}
244247
})
245248
}
@@ -307,19 +310,19 @@ const submit = async () => {
307310
const coops = cooperatorsEdit.value.map((coop) => new Cooperators({...coop, userDocId: coop.userDocId || coop.id}))
308311
test.value.cooperators = [...coops]
309312
310-
try {
311-
await store.dispatch('updateStudy', test.value);
312-
await store.dispatch('getStudy', { id: test.value.id });
313-
} catch (error) {
314-
console.error('Error updating study:', error);
315-
}
316-
317313
const newCooperators = cooperatorsEdit.value.filter(
318314
(guest) => !cooperatorsUpdate.value.some((c) => c.email === guest.email)
319315
);
320316
321-
for (const guest of newCooperators) {
322-
await sendMenssages(guest);
317+
try {
318+
await store.dispatch('updateStudy', test.value);
319+
320+
await Promise.all([
321+
store.dispatch('getStudy', { id: test.value.id }),
322+
...newCooperators.map(guest => sendMenssages(guest))
323+
]);
324+
} catch (error) {
325+
console.error('Error updating study:', error);
323326
}
324327
};
325328
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
<template>
2+
<v-card class="h-100">
3+
<v-card-title class="d-flex align-center pb-2">
4+
<v-icon class="mr-2" color="primary">mdi-account-supervisor</v-icon>
5+
Moderator Actions
6+
</v-card-title>
7+
8+
<v-card-text class="pa-4">
9+
<div class="d-flex flex-column gap-3">
10+
<v-btn
11+
color="primary"
12+
variant="outlined"
13+
block
14+
@click="startSession"
15+
>
16+
<v-icon start>mdi-play</v-icon>
17+
Start Session
18+
</v-btn>
19+
20+
<v-btn
21+
color="success"
22+
variant="outlined"
23+
block
24+
@click="scheduleSession"
25+
>
26+
<v-icon start>mdi-calendar-plus</v-icon>
27+
Schedule Session
28+
</v-btn>
29+
30+
<v-btn
31+
color="info"
32+
variant="outlined"
33+
block
34+
@click="viewSessions"
35+
>
36+
<v-icon start>mdi-calendar-check</v-icon>
37+
View Sessions
38+
</v-btn>
39+
40+
<v-btn
41+
color="warning"
42+
variant="outlined"
43+
block
44+
@click="editTest"
45+
>
46+
<v-icon start>mdi-pencil</v-icon>
47+
Edit Test
48+
</v-btn>
49+
50+
<v-btn
51+
color="orange"
52+
variant="outlined"
53+
block
54+
@click="moderatorGuide"
55+
>
56+
<v-icon start>mdi-book-open-variant</v-icon>
57+
Moderator Guide
58+
</v-btn>
59+
60+
<v-btn
61+
v-if="test.status === 'active'"
62+
color="error"
63+
variant="outlined"
64+
block
65+
@click="pauseTest"
66+
>
67+
<v-icon start>mdi-pause</v-icon>
68+
Pause Test
69+
</v-btn>
70+
71+
<v-btn
72+
v-else-if="test.status === 'paused'"
73+
color="success"
74+
variant="outlined"
75+
block
76+
@click="resumeTest"
77+
>
78+
<v-icon start>mdi-play</v-icon>
79+
Resume Test
80+
</v-btn>
81+
</div>
82+
</v-card-text>
83+
</v-card>
84+
</template>
85+
86+
<script setup>
87+
import { useRouter } from 'vue-router'
88+
import { useStore } from 'vuex'
89+
90+
const props = defineProps({
91+
test: {
92+
type: Object,
93+
default: () => ({})
94+
}
95+
})
96+
97+
const router = useRouter()
98+
const store = useStore()
99+
100+
const startSession = () => {
101+
router.push(`/userTest/moderated/session/${props.test.id}`)
102+
}
103+
104+
const scheduleSession = () => {
105+
router.push(`/userTest/moderated/schedule/${props.test.id}`)
106+
}
107+
108+
const viewSessions = () => {
109+
router.push(`/userTest/moderated/sessions/${props.test.id}`)
110+
}
111+
112+
const editTest = () => {
113+
router.push(`/userTest/moderated/edit/${props.test.id}`)
114+
}
115+
116+
const moderatorGuide = () => {
117+
// Open moderator guide or help documentation
118+
store.commit('SET_TOAST', {
119+
type: 'info',
120+
message: 'Moderator guide will be available soon!'
121+
})
122+
}
123+
124+
const pauseTest = async () => {
125+
try {
126+
await store.dispatch('updateTestStatus', {
127+
testId: props.test.id,
128+
status: 'paused'
129+
})
130+
store.commit('SET_TOAST', {
131+
type: 'success',
132+
message: 'Test paused successfully'
133+
})
134+
} catch (error) {
135+
store.commit('SET_TOAST', {
136+
type: 'error',
137+
message: 'Failed to pause test'
138+
})
139+
}
140+
}
141+
142+
const resumeTest = async () => {
143+
try {
144+
await store.dispatch('updateTestStatus', {
145+
testId: props.test.id,
146+
status: 'active'
147+
})
148+
store.commit('SET_TOAST', {
149+
type: 'success',
150+
message: 'Test resumed successfully'
151+
})
152+
} catch (error) {
153+
store.commit('SET_TOAST', {
154+
type: 'error',
155+
message: 'Failed to resume test'
156+
})
157+
}
158+
}
159+
</script>

0 commit comments

Comments
 (0)