Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(dashboard/user-overview): refactored create user form #324

Merged
merged 3 commits into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apps/dashboard/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,8 @@
"User Type": "User Type",
"firstName": "First Name",
"lastName": "Last Name",
"ofAge": "18+"
"ofAge": "18+",
"createUser": "Create user"
},
"errorMessages": {
"posNotFound": "The requested POS was not found."
Expand Down
3 changes: 2 additions & 1 deletion apps/dashboard/src/locales/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@
"User Type": "Gebruikerstype",
"firstName": "Voornaam",
"lastName": "Achternaam",
"ofAge": "18+"
"ofAge": "18+",
"createUser": "Maak gebruiker"
},
"contact": {
"Contact": "Contact",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<template>
<div class="flex flex-column gap-2">
<InputSpan :label="t('userDetails.First name')"
:value="form.model.firstName.value.value"
:attributes="form.model.firstName.attr.value"
@update:value="form.context.setFieldValue('firstName', $event)"
:errors="form.context.errors.value.firstName"
:disabled="!edit"
id="name" :placeholder="t('userDetails.First name')" type="text" />
<InputSpan :label="t('userDetails.Last name')"
:value="form.model.lastName?.value.value"
:attributes="form.model.lastName?.attr.value"
@update:value="form.context.setFieldValue('lastName', $event)"
:errors="form.context.errors.value.lastName"
:disabled="!edit"
id="name" placeholder="Doe" type="text" />
<InputSpan :label="t('userDetails.Nickname')"
:value="form.model.nickname?.value.value || undefined"
:attributes="form.model.nickname?.attr.value"
@update:value="form.context.setFieldValue('nickname', $event)"
:errors="form.context.errors.value.nickname"
:disabled="!edit"
id="name" :placeholder="t('userDetails.Nickname')" type="text" />
<InputSpan
:label="t('userDetails.Email address')"
:value="form.model.email?.value.value"
:attributes="form.model.email?.attr.value"
@update:value="form.context.setFieldValue('email', $event)"
:errors="form.context.errors.value.email"
:disabled="!edit"
id="name" placeholder="[email protected]" type="text" />
<InputSpan :label="t('userDetails.Usertype')"
:value="form.model.userType?.value.value"
:attributes="form.model.userType?.attr.value"
@update:value="form.context.setFieldValue('userType', $event)"
:errors="form.context.errors.value.userType"
id="name" :placeholder="t('userDetails.Usertype')" type="usertype"/>
<InputSpan :label="t('profile.ofAge')"
:value="form.model.ofAge?.value.value"
:attributes="form.model.ofAge?.attr.value"
@update:value="form.context.setFieldValue('ofAge', $event)"
:errors="form.context.errors.value.ofAge"
:disabled="!edit"
id="name" :placeholder="t('profile.ofAge')" type="boolean"/>
<InputSpan :label="t('profile.canGoIntoDebt')"
:value="form.model.canGoIntoDebt?.value.value"
:attributes="form.model.canGoIntoDebt?.attr.value"
@update:value="form.context.setFieldValue('canGoIntoDebt', $event)"
:errors="form.context.errors.value.canGoIntoDebt"
:disabled="!edit"
id="name" :placeholder="t('profile.canGoIntoDebt')" type="boolean"/>
</div>
</template>

<script setup lang="ts">
import InputSpan from "@/components/InputSpan.vue";
import { useI18n } from "vue-i18n";
import { useToast } from "primevue/usetoast";
import { type Form, setSubmit } from "@/utils/formUtils";
import type { CreateUserRequest } from "@sudosos/sudosos-client";
import { type PropType } from "vue";
import { createUserSchema } from "@/utils/validation-schema";
import * as yup from "yup";
import apiService from "@/services/ApiService";
import { handleError } from "@/utils/errorUtils";

const { t } = useI18n();
const toast = useToast();

const emit = defineEmits(['update:edit']);

const props = defineProps({
form: {
type: Object as PropType<Form<yup.InferType<typeof createUserSchema>>>,
required: true,
},
edit: {
type: Boolean,
required: false,
default: true,
},
});

setSubmit(props.form, props.form.context.handleSubmit(async (values) => {
const createUserRequest: CreateUserRequest = {
...values,
email: values.email || '',
nickname: values.nickname || '',
type: values.userType,
ofAge: values.ofAge,
canGoIntoDebt: values.canGoIntoDebt,
};

await apiService.user.createUser(createUserRequest).then(() => {
toast.add({
severity: 'success',
summary: t('successMessages.success'),
detail: t('successMessages.userUpdated'),
life: 3000,
});
emit('update:edit', false);
}).catch((error) => {
handleError(error, toast);
});
}));

</script>

<style scoped>

</style>
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
</CardComponent>
</div>
</div>
</template>s
</template>

<script setup lang="ts">
import { onBeforeMount, ref } from "vue";
Expand Down
132 changes: 31 additions & 101 deletions apps/dashboard/src/modules/admin/views/AdminUserOverView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<InputIcon class="pi pi-search"> </InputIcon>
<InputText v-model="searchQuery" :placeholder="t('app.Search')" />
</IconField>
<Button :label="t('app.Create')" icon="pi pi-plus" @click="visible = true" />
<Button :label="t('app.Create')" icon="pi pi-plus" @click="showDialog = true" />
</div>
</template>
<Column field="gewisId" header="GEWIS ID">
Expand Down Expand Up @@ -105,101 +105,60 @@

</Column>
</DataTable>
<Dialog v-model:visible="visible" modal header="Create User" :style="{ width: '50vw' }" @hide="resetForm">
<form @submit.prevent="handleCreateUser" class="p-fluid">
<div class="field">
<label for="firstName">{{ t('c_userTable.firstName')}}</label>
<InputText id="firstName" v-model="firstName" v-bind="firstNameAttrs" />
<small class="p-error">{{ errors.firstName }}</small>
</div>
<div class="field">
<label for="lastName">{{ t('c_userTable.lastName')}}</label>
<InputText id="lastName" v-model="lastName" v-bind="lastNameAttrs" />
<small class="p-error">{{ errors.lastName }}</small>
</div>
<div class="field">
<label for="userType">{{ t('c_userTable.User Type')}}</label>
<Dropdown
id="userType"
v-model="userType"
v-bind="userTypeAttrs"
:options="userTypes"
optionLabel="name"
:placeholder="t('c_userTable.Select Type')"
optionValue="name"
/>
<small class="p-error">{{ errors.userType }}</small>
</div>
<div class="field">
<label for="email">{{ t('userDetails.Email address')}}</label>
<InputText id="email" v-model="email" v-bind="emailAttrs" />
<small class="p-error">{{ errors.email }}</small>
</div>
<div class="field">
<label for="ofAge">{{ t('c_userTable.ofAge')}}</label>
<Checkbox id="ofAge" v-model="ofAge" v-bind="ofAgeAttrs" binary />
<small class="p-error">{{ errors.ofAge }}</small>
</div>
<div class="field">
<label for="canGoIntoDebt">{{ t('profile.canGoIntoDebt') }}</label>
<Checkbox id="canGoIntoDebt" v-model="canGoIntoDebt" v-bind="canGoIntoDebtAttrs" binary />
<small class="p-error">{{ errors.canGoIntoDebt }}</small>
</div>
<div class="flex justify-content-end">
<Button :label="t('c_containerEditModal.cancel')" class="p-button-text" @click="visible = false" />
<Button :label="t('c_confirmationModal.Save')" class="p-button-outlined" type="submit" />
</div>
</form>
</Dialog>
</CardComponent>
<FormDialog :header="t('c_userTable.createUser')" v-model:modelValue="showDialog"
:form="form" >
<template #form="slotProps">
<UserCreateForm
:form="slotProps.form"
v-model:isVisible="showDialog"
:edit="true"
@submit:success="showDialog = false"
/>
</template>
</FormDialog>
</div>
</template>

<script setup lang="ts">
import { useUserStore } from "@sudosos/sudosos-frontend-common";
import { computed, onMounted, ref, type Ref, watch } from "vue";
import apiService from '@/services/ApiService';
import type { CreateUserRequest, GewisUserResponse, UserResponse } from "@sudosos/sudosos-client";
import type { GewisUserResponse, UserResponse } from "@sudosos/sudosos-client";
import DataTable from 'primevue/datatable';
import Column from 'primevue/column';
import { FilterMatchMode } from 'primevue/api';
import Checkbox from "primevue/checkbox";
import Dropdown from 'primevue/dropdown';
import InputText from 'primevue/inputtext';
import router from '@/router';
import { userDetailsSchema, userTypes } from "@/utils/validation-schema";
import { useForm } from 'vee-validate';
import { createUserSchema, userTypes } from "@/utils/validation-schema";
import Fuse from 'fuse.js';
import CardComponent from '@/components/CardComponent.vue';
import Skeleton from "primevue/skeleton";
import IconField from "primevue/iconfield";
import InputIcon from "primevue/inputicon";
import { useI18n } from "vue-i18n";
import FormDialog from "@/components/FormDialog.vue";
import UserCreateForm from "@/modules/admin/components/users/forms/UserCreateForm.vue";
import { schemaToForm } from "@/utils/formUtils";
import { useUserStore } from "@sudosos/sudosos-frontend-common";
import router from "@/router";

const { t } = useI18n();

const userStore = useUserStore();

const searchQuery: Ref<string> = ref('');
const { defineField, handleSubmit, errors, resetForm } = useForm({
validationSchema: userDetailsSchema,
});

const showDialog: Ref<boolean> = ref(false);
const form = schemaToForm(createUserSchema);


const filters = ref({
global: { value: null, matchMode: FilterMatchMode.CONTAINS },
type: { value: null, matchMode: FilterMatchMode.EQUALS },
});

const [firstName, firstNameAttrs] = defineField('firstName', {});
const [lastName, lastNameAttrs] = defineField('lastName', {});
const [userType, userTypeAttrs] = defineField('userType', {});
const [email, emailAttrs] = defineField('email', {});
const [ofAge, ofAgeAttrs] = defineField('ofAge', {});
const [canGoIntoDebt, canGoIntoDebtAttrs] = defineField('canGoIntoDebt', {});


const isActiveFilter: Ref<boolean> = ref(true);
const ofAgeFilter: Ref<boolean> = ref(true);
const visible: Ref<boolean> = ref(false);
const isLoading = ref(true);
const totalRecords = ref(0);
const allUsers: Ref<GewisUserResponse[]> = ref(new Array(10));
Expand Down Expand Up @@ -270,42 +229,6 @@ watch(searchQuery, () => {
delayedAPICall(0);
});

const handleCreateUser = handleSubmit(async (values) => {
// Ensure userType is a number. You might need a more complex check depending on your data.
const userTypeAsNumber = Number(values.userType);

// Check if conversion was successful or if the value was already a number
if (isNaN(userTypeAsNumber)) {
console.error('User type is not a valid number:', values.userType);
// Handle error appropriately, possibly aborting the submit or setting a default
return;
}

const createUserRequest: CreateUserRequest = {
firstName: values.firstName,
lastName: values.lastName,
type: userTypeAsNumber, // Use the converted/validated number here
email: values.email || '',
ofAge: values.ofAge,
canGoIntoDebt: values.canGoIntoDebt,
};
const response = await apiService.user.createUser(createUserRequest);
if (response.status === 200) {
await router.push({ name: 'user-overview' });
} else {
console.error(response.status + ': ' + response.statusText);
}
visible.value = false;
});

async function handleInfoPush(userId: number) {
const clickedUser: UserResponse | undefined = allUsers.value.find(
(record) => record.id == userId
);
if (clickedUser) userStore.addUser(clickedUser);
router.push({ name: 'user', params: { userId } });
}

const sortedUsers = computed(() => {
const fuzzed: UserResponse[] = new Fuse(allUsersWithFullName.value, {
keys: ['fullName'],
Expand All @@ -318,6 +241,13 @@ const sortedUsers = computed(() => {
return fuzzed;
});

async function handleInfoPush(userId: number) {
const clickedUser: UserResponse | undefined = allUsers.value.find(
(record) => record.id == userId
);
if (clickedUser) userStore.addUser(clickedUser);
router.push({ name: 'user', params: { userId } });
}
</script>

<style scoped>
Expand Down
13 changes: 5 additions & 8 deletions apps/dashboard/src/utils/validation-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,23 @@ import type { ContainerInStore } from "@/stores/container.store";

const t = i18n.global.t;

export const userDetailsSchema = toTypedSchema(
export const createUserSchema =
yup.object({
firstName: yup.string().required(),
lastName: yup.string().required(),
email: yup.string().email(),
nickname: yup.string(),
userType: yup.mixed().required(),
isActive: yup.boolean().required().default(true),
userType: yup.number().required().default(4),
ofAge: yup.boolean().required().default(false),
canGoIntoDebt: yup.boolean().required().default(false),
})
);
});

export const simpleUserDetailsSchema = toTypedSchema(
export const simpleUserDetailsSchema =
yup.object({
firstName: yup.string().required(),
lastName: yup.string().required(),
email: yup.string().email(),
})
);
});

export const editPasswordSchema = toTypedSchema(
yup.object({
Expand Down
Loading