Skip to content

Commit fb53cba

Browse files
committed
almost all set
1 parent 0b60ced commit fb53cba

File tree

1 file changed

+287
-0
lines changed

1 file changed

+287
-0
lines changed

components/SGCUserForm.vue

Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
<template>
2+
<Card>
3+
<template #header>
4+
<div class="p-4">
5+
<h3 class="text-2xl font-semibold text-surface-900 dark:text-surface-0 mb-2">
6+
Create New SGC User
7+
</h3>
8+
<p class="text-surface-600 dark:text-surface-400">
9+
Create a new user account for SGC data registry access
10+
</p>
11+
</div>
12+
</template>
13+
14+
<template #content>
15+
<form @submit.prevent="handleSubmit" class="space-y-4">
16+
<!-- Email -->
17+
<div class="field">
18+
<label for="email" class="block text-surface-900 dark:text-surface-0 font-medium mb-2">
19+
Email *
20+
</label>
21+
<InputText
22+
id="email"
23+
v-model="formData.email"
24+
type="email"
25+
placeholder="Enter email address"
26+
class="w-full"
27+
:class="{ 'p-invalid': errors.email }"
28+
:disabled="loading"
29+
required
30+
/>
31+
<small v-if="errors.email" class="p-error">{{ errors.email }}</small>
32+
</div>
33+
34+
<!-- First Name -->
35+
<div class="field">
36+
<label for="firstName" class="block text-surface-900 dark:text-surface-0 font-medium mb-2">
37+
First Name
38+
</label>
39+
<InputText
40+
id="firstName"
41+
v-model="formData.firstName"
42+
placeholder="Enter first name"
43+
class="w-full"
44+
:disabled="loading"
45+
/>
46+
</div>
47+
48+
<!-- Last Name -->
49+
<div class="field">
50+
<label for="lastName" class="block text-surface-900 dark:text-surface-0 font-medium mb-2">
51+
Last Name
52+
</label>
53+
<InputText
54+
id="lastName"
55+
v-model="formData.lastName"
56+
placeholder="Enter last name"
57+
class="w-full"
58+
:disabled="loading"
59+
/>
60+
</div>
61+
62+
<!-- User Type -->
63+
<div class="field">
64+
<label for="userType" class="block text-surface-900 dark:text-surface-0 font-medium mb-2">
65+
User Type *
66+
</label>
67+
<Dropdown
68+
id="userType"
69+
v-model="formData.userType"
70+
:options="userTypeOptions"
71+
optionLabel="label"
72+
optionValue="value"
73+
placeholder="Select user type"
74+
class="w-full"
75+
:class="{ 'p-invalid': errors.userType }"
76+
:disabled="loading"
77+
required
78+
/>
79+
<small v-if="errors.userType" class="p-error">{{ errors.userType }}</small>
80+
<small class="text-surface-600 dark:text-surface-400 mt-1 block">
81+
<strong>Reviewer:</strong> Can review and approve submissions, create new users<br>
82+
<strong>Uploader:</strong> Can upload cohort data and metadata
83+
</small>
84+
</div>
85+
86+
<!-- Password -->
87+
<div class="field">
88+
<label for="password" class="block text-surface-900 dark:text-surface-0 font-medium mb-2">
89+
Password *
90+
</label>
91+
<Password
92+
id="password"
93+
v-model="formData.password"
94+
placeholder="Enter password"
95+
class="w-full"
96+
inputClass="w-full"
97+
:class="{ 'p-invalid': errors.password }"
98+
:disabled="loading"
99+
:feedback="true"
100+
:toggleMask="true"
101+
required
102+
/>
103+
<small v-if="errors.password" class="p-error">{{ errors.password }}</small>
104+
</div>
105+
106+
<!-- Confirm Password -->
107+
<div class="field">
108+
<label for="confirmPassword" class="block text-surface-900 dark:text-surface-0 font-medium mb-2">
109+
Confirm Password *
110+
</label>
111+
<Password
112+
id="confirmPassword"
113+
v-model="formData.confirmPassword"
114+
placeholder="Confirm password"
115+
class="w-full"
116+
inputClass="w-full"
117+
:class="{ 'p-invalid': errors.confirmPassword }"
118+
:disabled="loading"
119+
:feedback="false"
120+
:toggleMask="true"
121+
required
122+
/>
123+
<small v-if="errors.confirmPassword" class="p-error">{{ errors.confirmPassword }}</small>
124+
</div>
125+
</form>
126+
</template>
127+
128+
<template #footer>
129+
<div class="flex justify-content-end gap-3">
130+
<Button
131+
label="Cancel"
132+
icon="pi pi-times"
133+
severity="secondary"
134+
outlined
135+
@click="handleCancel"
136+
:disabled="loading"
137+
/>
138+
<Button
139+
label="Create User"
140+
icon="pi pi-user-plus"
141+
@click="handleSubmit"
142+
:loading="loading"
143+
:disabled="!isFormValid"
144+
/>
145+
</div>
146+
</template>
147+
</Card>
148+
149+
<Toast position="top-center" />
150+
</template>
151+
152+
<script setup>
153+
import { useUserStore } from "~/stores/UserStore";
154+
import { useToast } from "primevue/usetoast";
155+
156+
const emit = defineEmits(['user-created', 'cancel']);
157+
158+
const userStore = useUserStore();
159+
const toast = useToast();
160+
161+
const loading = ref(false);
162+
const formData = ref({
163+
email: '',
164+
firstName: '',
165+
lastName: '',
166+
password: '',
167+
confirmPassword: '',
168+
userType: ''
169+
});
170+
171+
const errors = ref({});
172+
173+
const userTypeOptions = ref([
174+
{ label: 'Reviewer', value: 'reviewer' },
175+
{ label: 'Uploader', value: 'uploader' }
176+
]);
177+
178+
const isFormValid = computed(() => {
179+
return formData.value.email &&
180+
formData.value.password &&
181+
formData.value.confirmPassword &&
182+
formData.value.userType;
183+
});
184+
185+
const validateForm = () => {
186+
errors.value = {};
187+
188+
if (!formData.value.email) {
189+
errors.value.email = 'Email is required';
190+
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.value.email)) {
191+
errors.value.email = 'Please enter a valid email address';
192+
}
193+
194+
if (!formData.value.userType) {
195+
errors.value.userType = 'User type is required';
196+
}
197+
198+
if (!formData.value.password) {
199+
errors.value.password = 'Password is required';
200+
} else if (formData.value.password.length < 8) {
201+
errors.value.password = 'Password must be at least 8 characters long';
202+
}
203+
204+
if (!formData.value.confirmPassword) {
205+
errors.value.confirmPassword = 'Please confirm the password';
206+
} else if (formData.value.password !== formData.value.confirmPassword) {
207+
errors.value.confirmPassword = 'Passwords do not match';
208+
}
209+
210+
return Object.keys(errors.value).length === 0;
211+
};
212+
213+
const handleSubmit = async () => {
214+
if (!validateForm()) {
215+
return;
216+
}
217+
218+
loading.value = true;
219+
220+
try {
221+
const result = await userStore.createSGCUser(formData.value, formData.value.userType);
222+
223+
toast.add({
224+
severity: 'success',
225+
summary: 'Success',
226+
detail: `User "${formData.value.email}" created successfully`,
227+
life: 5000
228+
});
229+
230+
emit('user-created', result);
231+
232+
// Reset form
233+
formData.value = {
234+
email: '',
235+
firstName: '',
236+
lastName: '',
237+
password: '',
238+
confirmPassword: '',
239+
userType: ''
240+
};
241+
242+
} catch (error) {
243+
console.error('Error creating user:', error);
244+
245+
let errorMessage = 'Failed to create user';
246+
if (error.response?.data?.error) {
247+
errorMessage = error.response.data.error;
248+
} else if (error.message) {
249+
errorMessage = error.message;
250+
}
251+
252+
toast.add({
253+
severity: 'error',
254+
summary: 'Error',
255+
detail: errorMessage,
256+
life: 5000
257+
});
258+
} finally {
259+
loading.value = false;
260+
}
261+
};
262+
263+
const handleCancel = () => {
264+
emit('cancel');
265+
};
266+
267+
// Focus email field when component mounts
268+
onMounted(() => {
269+
nextTick(() => {
270+
const emailInput = document.getElementById('email');
271+
if (emailInput) {
272+
emailInput.focus();
273+
}
274+
});
275+
});
276+
</script>
277+
278+
<style scoped>
279+
.field {
280+
margin-bottom: 1.5rem;
281+
}
282+
283+
.p-error {
284+
color: var(--red-500);
285+
font-size: 0.875rem;
286+
}
287+
</style>

0 commit comments

Comments
 (0)