Skip to content

Commit d18bed4

Browse files
committed
add error form in vue
1 parent 27e2214 commit d18bed4

File tree

5 files changed

+99
-21
lines changed

5 files changed

+99
-21
lines changed

react/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,12 @@
1717
"preview": "vite preview"
1818
},
1919
"dependencies": {
20+
"@vercel/analytics": "^1.5.0",
2021
"@emotion/cache": "^11.14.0",
2122
"@emotion/react": "^11.14.0",
2223
"@emotion/styled": "^11.14.1",
2324
"@mui/icons-material": "^7.3.1",
2425
"@mui/material": "^7.3.1",
25-
"@reduxjs/toolkit": "^2.8.2",
26-
"@vercel/analytics": "^1.5.0",
2726
"ag-grid-community": "^34.1.1",
2827
"ag-grid-react": "^34.1.1",
2928
"axios": "^1.11.0",
@@ -32,8 +31,9 @@
3231
"react-dom": "^19.1.1",
3332
"react-intl": "^7.1.11",
3433
"react-perfect-scrollbar": "^1.5.8",
35-
"react-redux": "^9.2.0",
3634
"react-router-dom": "^7.8.0",
35+
"@reduxjs/toolkit": "^2.8.2",
36+
"react-redux": "^9.2.0",
3737
"redux": "^5.0.1",
3838
"redux-persist": "^6.0.0",
3939
"sonner": "^2.0.7",

vue3/src/assets/locales/en.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,8 @@
3939
"renamed, or might never exist!": "renamed, or might never exist!",
4040

4141
"success.delete.student": "Student deleted successfully\nAlso all of their assignments were deleted.",
42-
"success.delete.assignment": "Assignment deleted successfully"
42+
"success.delete.assignment": "Assignment deleted successfully",
43+
"form.error": "Save failed",
44+
"form.error.unique": "The field {field} must be unique",
45+
"form.error.required.dynamic": "The field {field} is required"
4346
}

vue3/src/common/stores/general.ts

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -63,19 +63,14 @@ export const useGeneralStore = defineStore('general', {
6363
data.student_id = auth_store.student_id;
6464
}
6565

66-
try {
67-
await axios.post(`${model}/${id}`, data);
68-
toast.success(message);
69-
if (model === ModelType.teacher) {
70-
await router.push('/login');
71-
} else if (model === ModelType.student && is_add) {
72-
await router.push(`/${ModelType.student}`);
73-
} else {
74-
await router.push(`/${ModelType.student}/view/${auth_store.student_id}`);
75-
}
76-
} catch (error) {
77-
console.error(error);
78-
throw error;
66+
await axios.post(`${model}/${id}`, data);
67+
toast.success(message);
68+
if (model === ModelType.teacher) {
69+
await router.push('/login');
70+
} else if (model === ModelType.student && is_add) {
71+
await router.push(`/${ModelType.student}`);
72+
} else {
73+
await router.push(`/${ModelType.student}/view/${auth_store.student_id}`);
7974
}
8075
}
8176
}

vue3/src/views/components/form/Form.vue

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
<script setup lang="ts">
2-
import { computed } from 'vue';
3-
2+
import { computed, ref } from 'vue';
3+
import { useI18n } from 'vue-i18n';
4+
import { isAxiosError } from 'axios';
45
import { useRoute } from 'vue-router';
6+
57
import { ModelType, create_form_fields, get_form_by_model, type inputFilters, RowWithId } from '@/common/types';
68
import DynamicForm from '@/views/components/form/DynamicForm.vue';
79
810
import { useGeneralStore } from '@/common/stores/general';
911
1012
import { array_obj_to_obj_with_key } from '@/common/helper';
11-
import { useI18n } from 'vue-i18n';
13+
import FormError from '@/views/components/form/FormError.vue';
1214
1315
const { t } = useI18n();
1416
@@ -25,11 +27,31 @@ const obj = isAdd
2527
: (array_obj_to_obj_with_key<RowWithId, 'id'>(general_store.list[model.value] as RowWithId[], Number(url.value), 'id') ?? {});
2628
2729
const controls = computed<inputFilters>(() => create_form_fields(get_form_by_model(model.value), obj));
30+
const fieldError = ref<string>('');
31+
2832
const create_update = async (data: inputFilters) => {
33+
fieldError.value = '';
34+
2935
const message = t(isAdd ? 'toast.create_success' : 'toast.edit_success', {
3036
model: t(`models.${model.value}`)
3137
});
32-
await general_store.create_update_row_input_form(data, model.value, url.value, isAdd, message);
38+
39+
try {
40+
await general_store.create_update_row_input_form(data, model.value, url.value, isAdd, message);
41+
} catch (err: any) {
42+
if (typeof err === 'string') {
43+
fieldError.value = 'form.error';
44+
return;
45+
}
46+
47+
let intlId = err?.message ?? 'form.error';
48+
const errors = err?.errors;
49+
if (Array.isArray(errors) && errors.length > 0 && errors[0]?.field) {
50+
intlId = `form.label.${errors[0].field}`;
51+
}
52+
53+
fieldError.value = intlId;
54+
}
3355
};
3456
</script>
3557
<template>
@@ -38,4 +60,5 @@ const create_update = async (data: inputFilters) => {
3860
<DynamicForm :is-add="isAdd" :model="model" :controls="controls" @send-form="create_update" />
3961
</v-col>
4062
</v-row>
63+
<FormError :fieldError="fieldError" />
4164
</template>
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<script setup lang="ts">
2+
import { computed, watch } from 'vue';
3+
import { useI18n } from 'vue-i18n';
4+
import { useToast } from 'vue-toastification';
5+
6+
const props = defineProps<{
7+
fieldError: string;
8+
}>();
9+
10+
const { t } = useI18n();
11+
const toast = useToast();
12+
13+
const isFieldSpecific = computed(() => props.fieldError?.startsWith('form.label.'));
14+
const isUnique = computed(() => props.fieldError?.includes('unique:'));
15+
const baseFieldId = computed(() => {
16+
const fe = props.fieldError || '';
17+
if (isUnique.value) return fe.slice(fe.indexOf('unique:') + 'unique:'.length);
18+
if (isFieldSpecific.value) return fe.slice('form.label.'.length);
19+
return '';
20+
});
21+
22+
watch(
23+
() => props.fieldError,
24+
(val) => {
25+
if (!val) return;
26+
27+
const error_msg = isUnique.value
28+
? t('form.error.unique', { field: t(baseFieldId.value) })
29+
: isFieldSpecific.value
30+
? t('form.error.required.dynamic', { field: t(baseFieldId.value) })
31+
: t('form.error');
32+
33+
toast.error(error_msg);
34+
},
35+
{ immediate: true }
36+
);
37+
</script>
38+
39+
<template>
40+
<h4 v-if="fieldError" class="text-error text-h4 mt-3 text-center">
41+
<template v-if="isUnique">
42+
{{ t('form.error.unique', { field: t(baseFieldId) }) }}
43+
</template>
44+
45+
<template v-else-if="isFieldSpecific">
46+
{{ t('form.error.required.dynamic', { field: t(baseFieldId) }) }}
47+
</template>
48+
49+
<template v-else-if="fieldError === 'form.error'">
50+
{{ t(fieldError) }}
51+
</template>
52+
53+
<template v-else>
54+
{{ fieldError }}
55+
</template>
56+
</h4>
57+
</template>

0 commit comments

Comments
 (0)