@eeplatform/nuxt-layer-common 1.2.2 → 1.2.3
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/CHANGELOG.md +6 -0
- package/components/InvitationForm.vue +206 -0
- package/components/InvitationMain.vue +46 -14
- package/components/MemberMain.vue +1 -6
- package/components/RolePermissionMain.vue +0 -1
- package/components/SchoolFormCreate.vue +216 -0
- package/components/SchoolFormEdit.vue +177 -0
- package/components/SchoolMain.vue +429 -0
- package/composables/useSchool.ts +3 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-card width="100%">
|
|
3
|
+
<v-toolbar>
|
|
4
|
+
<v-row no-gutters class="fill-height px-6" align="center">
|
|
5
|
+
<span class="font-weight-bold text-h5"> Invite </span>
|
|
6
|
+
</v-row>
|
|
7
|
+
</v-toolbar>
|
|
8
|
+
<v-card-text style="max-height: 100vh; overflow-y: auto" class="pb-0">
|
|
9
|
+
<v-form v-model="validInvite">
|
|
10
|
+
<v-row no-gutters>
|
|
11
|
+
<v-col cols="12">
|
|
12
|
+
<v-row no-gutters>
|
|
13
|
+
<InputLabel class="text-capitalize" title="E-mail" required />
|
|
14
|
+
<v-col cols="12">
|
|
15
|
+
<v-text-field
|
|
16
|
+
v-model="invite.email"
|
|
17
|
+
density="comfortable"
|
|
18
|
+
:rules="[requiredRule, emailRule]"
|
|
19
|
+
></v-text-field>
|
|
20
|
+
</v-col>
|
|
21
|
+
</v-row>
|
|
22
|
+
</v-col>
|
|
23
|
+
|
|
24
|
+
<v-col cols="12" class="my-2">
|
|
25
|
+
<v-row no-gutters>
|
|
26
|
+
<v-col cols="12">
|
|
27
|
+
<v-input
|
|
28
|
+
:error-messages="invite.role ? [] : ['Role is required']"
|
|
29
|
+
>
|
|
30
|
+
<v-menu z-index="10000">
|
|
31
|
+
<template #activator="{ props }">
|
|
32
|
+
<v-btn
|
|
33
|
+
block
|
|
34
|
+
variant="flat"
|
|
35
|
+
color="black"
|
|
36
|
+
class="text-none"
|
|
37
|
+
height="48"
|
|
38
|
+
v-bind="props"
|
|
39
|
+
>
|
|
40
|
+
{{ invite.role ? roleName : "Select Role" }}
|
|
41
|
+
</v-btn>
|
|
42
|
+
</template>
|
|
43
|
+
|
|
44
|
+
<v-list class="pa-0">
|
|
45
|
+
<v-list-item v-if="!roles.length"> No data </v-list-item>
|
|
46
|
+
|
|
47
|
+
<v-list-item
|
|
48
|
+
v-for="role in roles"
|
|
49
|
+
:key="role._id"
|
|
50
|
+
@click="invite.role = role._id"
|
|
51
|
+
>
|
|
52
|
+
<template #prepend>
|
|
53
|
+
<v-icon v-if="invite.role === role._id">
|
|
54
|
+
mdi-check
|
|
55
|
+
</v-icon>
|
|
56
|
+
</template>
|
|
57
|
+
|
|
58
|
+
<v-list-item-title
|
|
59
|
+
class="text-subtitle-2 font-weight-medium"
|
|
60
|
+
>
|
|
61
|
+
{{ role.name }}
|
|
62
|
+
</v-list-item-title>
|
|
63
|
+
</v-list-item>
|
|
64
|
+
</v-list>
|
|
65
|
+
</v-menu>
|
|
66
|
+
</v-input>
|
|
67
|
+
</v-col>
|
|
68
|
+
</v-row>
|
|
69
|
+
</v-col>
|
|
70
|
+
|
|
71
|
+
<v-col v-if="messageInvite" cols="12" class="text-center mb-4">
|
|
72
|
+
<span
|
|
73
|
+
class="text-none text-subtitle-2 font-weight-medium text-error"
|
|
74
|
+
>
|
|
75
|
+
{{ messageInvite }}
|
|
76
|
+
</span>
|
|
77
|
+
</v-col>
|
|
78
|
+
</v-row>
|
|
79
|
+
</v-form>
|
|
80
|
+
</v-card-text>
|
|
81
|
+
|
|
82
|
+
<v-toolbar class="pa-0" density="compact">
|
|
83
|
+
<v-row no-gutters>
|
|
84
|
+
<v-col cols="6" class="pa-0">
|
|
85
|
+
<v-btn
|
|
86
|
+
block
|
|
87
|
+
variant="text"
|
|
88
|
+
class="text-none"
|
|
89
|
+
size="large"
|
|
90
|
+
@click="cancel()"
|
|
91
|
+
height="48"
|
|
92
|
+
tile
|
|
93
|
+
>
|
|
94
|
+
Cancel
|
|
95
|
+
</v-btn>
|
|
96
|
+
</v-col>
|
|
97
|
+
|
|
98
|
+
<v-col cols="6">
|
|
99
|
+
<v-btn
|
|
100
|
+
block
|
|
101
|
+
variant="flat"
|
|
102
|
+
color="black"
|
|
103
|
+
class="text-none"
|
|
104
|
+
size="large"
|
|
105
|
+
@click="submitInvite()"
|
|
106
|
+
height="48"
|
|
107
|
+
tile
|
|
108
|
+
:disabled="!validInvite"
|
|
109
|
+
>
|
|
110
|
+
Submit
|
|
111
|
+
</v-btn>
|
|
112
|
+
</v-col>
|
|
113
|
+
</v-row>
|
|
114
|
+
</v-toolbar>
|
|
115
|
+
</v-card>
|
|
116
|
+
</template>
|
|
117
|
+
|
|
118
|
+
<script setup lang="ts">
|
|
119
|
+
const prop = defineProps({
|
|
120
|
+
app: {
|
|
121
|
+
type: String,
|
|
122
|
+
required: true,
|
|
123
|
+
default: "admin",
|
|
124
|
+
},
|
|
125
|
+
org: {
|
|
126
|
+
type: String,
|
|
127
|
+
default: "",
|
|
128
|
+
},
|
|
129
|
+
orgName: {
|
|
130
|
+
type: String,
|
|
131
|
+
default: "",
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const { requiredRule, emailRule } = useUtils();
|
|
136
|
+
|
|
137
|
+
const invite = ref({
|
|
138
|
+
email: "",
|
|
139
|
+
app: "",
|
|
140
|
+
role: "",
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const searchRole = ref("");
|
|
144
|
+
const roles = ref<Array<Record<string, any>>>([]);
|
|
145
|
+
|
|
146
|
+
const { getRoles } = useRole();
|
|
147
|
+
|
|
148
|
+
const { data: getRolesData, status: getRolesStatus } = await useLazyAsyncData(
|
|
149
|
+
"get-roles",
|
|
150
|
+
() =>
|
|
151
|
+
getRoles({
|
|
152
|
+
search: searchRole.value,
|
|
153
|
+
type: prop.app,
|
|
154
|
+
id: prop.org,
|
|
155
|
+
limit: 20,
|
|
156
|
+
})
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
const loading = computed(() => {
|
|
160
|
+
return getRolesStatus.value === "pending";
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
watchEffect(() => {
|
|
164
|
+
if (getRolesData.value) {
|
|
165
|
+
roles.value = getRolesData.value.items;
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const validInvite = ref(false);
|
|
170
|
+
|
|
171
|
+
const { inviteUser } = useUser();
|
|
172
|
+
|
|
173
|
+
const messageInvite = ref("");
|
|
174
|
+
|
|
175
|
+
const roleName = computed(() => {
|
|
176
|
+
return roles.value.find((i) => i._id === invite.value.role)?.name || "";
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
const emit = defineEmits(["success", "cancel"]);
|
|
180
|
+
|
|
181
|
+
async function submitInvite() {
|
|
182
|
+
messageInvite.value = "";
|
|
183
|
+
try {
|
|
184
|
+
await inviteUser({
|
|
185
|
+
email: invite.value.email,
|
|
186
|
+
app: prop.app,
|
|
187
|
+
role: invite.value.role,
|
|
188
|
+
roleName: roleName.value,
|
|
189
|
+
org: prop.org,
|
|
190
|
+
orgName: prop.orgName,
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
await emit("success");
|
|
194
|
+
} catch (error: any) {
|
|
195
|
+
messageInvite.value =
|
|
196
|
+
error.response?._data?.message || "Failed to invite user";
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function cancel() {
|
|
201
|
+
invite.value.email = "";
|
|
202
|
+
invite.value.app = prop.app;
|
|
203
|
+
invite.value.role = "";
|
|
204
|
+
emit("cancel");
|
|
205
|
+
}
|
|
206
|
+
</script>
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
class="text-none mr-2"
|
|
7
7
|
rounded="pill"
|
|
8
8
|
variant="tonal"
|
|
9
|
-
|
|
9
|
+
@click="dialogInvite = true"
|
|
10
10
|
size="large"
|
|
11
11
|
v-if="props.inviteMember"
|
|
12
12
|
>
|
|
@@ -46,20 +46,14 @@
|
|
|
46
46
|
<template #extension>
|
|
47
47
|
<v-tabs>
|
|
48
48
|
<v-tab
|
|
49
|
+
v-for="status in statusFilter"
|
|
50
|
+
:key="status.text"
|
|
49
51
|
:to="{
|
|
50
|
-
name:
|
|
51
|
-
params:
|
|
52
|
+
name: props.baseRoute,
|
|
53
|
+
params: status.params,
|
|
52
54
|
}"
|
|
53
55
|
>
|
|
54
|
-
|
|
55
|
-
</v-tab>
|
|
56
|
-
<v-tab
|
|
57
|
-
:to="{
|
|
58
|
-
name: 'org-organization-invitations-status-status',
|
|
59
|
-
params: { status: 'expired', organization },
|
|
60
|
-
}"
|
|
61
|
-
>
|
|
62
|
-
Expired
|
|
56
|
+
{{ status.text }}
|
|
63
57
|
</v-tab>
|
|
64
58
|
</v-tabs>
|
|
65
59
|
</template>
|
|
@@ -86,6 +80,15 @@
|
|
|
86
80
|
</v-data-table>
|
|
87
81
|
</v-card>
|
|
88
82
|
</v-col>
|
|
83
|
+
|
|
84
|
+
<v-dialog v-model="dialogInvite" width="400" persistent>
|
|
85
|
+
<InvitationForm
|
|
86
|
+
:app="props.app"
|
|
87
|
+
:org="props.org"
|
|
88
|
+
@cancel="dialogInvite = false"
|
|
89
|
+
@success="inviteSuccess()"
|
|
90
|
+
/>
|
|
91
|
+
</v-dialog>
|
|
89
92
|
</v-row>
|
|
90
93
|
</template>
|
|
91
94
|
|
|
@@ -95,6 +98,10 @@ const props = defineProps({
|
|
|
95
98
|
type: String,
|
|
96
99
|
default: "active",
|
|
97
100
|
},
|
|
101
|
+
org: {
|
|
102
|
+
type: String,
|
|
103
|
+
default: "",
|
|
104
|
+
},
|
|
98
105
|
app: {
|
|
99
106
|
type: String,
|
|
100
107
|
default: "organization",
|
|
@@ -103,6 +110,10 @@ const props = defineProps({
|
|
|
103
110
|
type: Boolean,
|
|
104
111
|
default: false,
|
|
105
112
|
},
|
|
113
|
+
baseRoute: {
|
|
114
|
+
type: String,
|
|
115
|
+
default: "invitations-status-status",
|
|
116
|
+
},
|
|
106
117
|
inviteRoute: {
|
|
107
118
|
type: Object as PropType<Record<string, any>>,
|
|
108
119
|
default: () => ({
|
|
@@ -112,8 +123,6 @@ const props = defineProps({
|
|
|
112
123
|
},
|
|
113
124
|
});
|
|
114
125
|
|
|
115
|
-
const organization = (useRoute().params.organization as string) ?? "";
|
|
116
|
-
|
|
117
126
|
const headers = [
|
|
118
127
|
{
|
|
119
128
|
title: "Date",
|
|
@@ -129,6 +138,22 @@ const headers = [
|
|
|
129
138
|
},
|
|
130
139
|
];
|
|
131
140
|
|
|
141
|
+
const statusFilter = computed(() => {
|
|
142
|
+
const items = [
|
|
143
|
+
{ text: "Pending", params: { status: "pending" } },
|
|
144
|
+
{ text: "Expired", params: { status: "expired" } },
|
|
145
|
+
];
|
|
146
|
+
|
|
147
|
+
if (props.org) {
|
|
148
|
+
items.map((i) => ({
|
|
149
|
+
...i,
|
|
150
|
+
params: { ...i.params, organization: props.org },
|
|
151
|
+
}));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return items;
|
|
155
|
+
});
|
|
156
|
+
|
|
132
157
|
const { getVerifications } = useVerification();
|
|
133
158
|
|
|
134
159
|
const page = ref(1);
|
|
@@ -186,6 +211,13 @@ watch(selectAll, (curr) => {
|
|
|
186
211
|
selected.value.push(...ids);
|
|
187
212
|
}
|
|
188
213
|
});
|
|
214
|
+
|
|
215
|
+
const dialogInvite = ref(false);
|
|
216
|
+
|
|
217
|
+
function inviteSuccess() {
|
|
218
|
+
dialogInvite.value = false;
|
|
219
|
+
getInvitations();
|
|
220
|
+
}
|
|
189
221
|
</script>
|
|
190
222
|
|
|
191
223
|
<style scoped>
|
|
@@ -293,11 +293,6 @@ const props = defineProps({
|
|
|
293
293
|
|
|
294
294
|
value: "roleName",
|
|
295
295
|
},
|
|
296
|
-
{
|
|
297
|
-
title: "Organization",
|
|
298
|
-
|
|
299
|
-
value: "orgName",
|
|
300
|
-
},
|
|
301
296
|
{
|
|
302
297
|
title: "Action",
|
|
303
298
|
value: "action-table",
|
|
@@ -379,7 +374,7 @@ function setMember({
|
|
|
379
374
|
const roles = ref<Array<Record<string, any>>>([]);
|
|
380
375
|
const { getRoles } = useRole();
|
|
381
376
|
const { data: getAllRoleReq } = useLazyAsyncData("get-roles", () =>
|
|
382
|
-
getRoles({
|
|
377
|
+
getRoles({ id: props.orgId, type: props.type, limit: 20 })
|
|
383
378
|
);
|
|
384
379
|
|
|
385
380
|
watchEffect(() => {
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-card width="100%">
|
|
3
|
+
<v-toolbar>
|
|
4
|
+
<v-row no-gutters class="fill-height px-6" align="center">
|
|
5
|
+
<span class="font-weight-bold text-h5">
|
|
6
|
+
Create School
|
|
7
|
+
</span>
|
|
8
|
+
</v-row>
|
|
9
|
+
</v-toolbar>
|
|
10
|
+
<v-card-text style="max-height: 100vh; overflow-y: auto">
|
|
11
|
+
<v-form v-model="validForm" :disabled="disable">
|
|
12
|
+
<v-row no-gutters>
|
|
13
|
+
<v-col cols="12" class="mt-2">
|
|
14
|
+
<v-row no-gutters>
|
|
15
|
+
<InputLabel class="text-capitalize" title="School Name" required />
|
|
16
|
+
<v-col cols="12">
|
|
17
|
+
<v-text-field
|
|
18
|
+
v-model="name"
|
|
19
|
+
density="comfortable"
|
|
20
|
+
:rules="[requiredRule]"
|
|
21
|
+
placeholder="Enter school name"
|
|
22
|
+
></v-text-field>
|
|
23
|
+
</v-col>
|
|
24
|
+
</v-row>
|
|
25
|
+
</v-col>
|
|
26
|
+
|
|
27
|
+
<v-col cols="12" class="mt-2">
|
|
28
|
+
<v-row no-gutters>
|
|
29
|
+
<InputLabel class="text-capitalize" title="Division" required />
|
|
30
|
+
<v-col cols="12">
|
|
31
|
+
<v-select
|
|
32
|
+
v-model="selectedDivision"
|
|
33
|
+
:items="divisionOptions"
|
|
34
|
+
item-title="name"
|
|
35
|
+
item-value="_id"
|
|
36
|
+
density="comfortable"
|
|
37
|
+
:rules="[requiredRule]"
|
|
38
|
+
placeholder="Select a division"
|
|
39
|
+
:loading="divisionsLoading"
|
|
40
|
+
return-object
|
|
41
|
+
></v-select>
|
|
42
|
+
</v-col>
|
|
43
|
+
</v-row>
|
|
44
|
+
</v-col>
|
|
45
|
+
|
|
46
|
+
<v-col cols="12" class="mt-2">
|
|
47
|
+
<v-row no-gutters>
|
|
48
|
+
<InputLabel class="text-capitalize" title="Principal Name" />
|
|
49
|
+
<v-col cols="12">
|
|
50
|
+
<v-text-field
|
|
51
|
+
v-model="principalName"
|
|
52
|
+
density="comfortable"
|
|
53
|
+
placeholder="Enter principal name (optional)"
|
|
54
|
+
></v-text-field>
|
|
55
|
+
</v-col>
|
|
56
|
+
</v-row>
|
|
57
|
+
</v-col>
|
|
58
|
+
|
|
59
|
+
<v-col cols="12" class="mt-2">
|
|
60
|
+
<v-row no-gutters>
|
|
61
|
+
<InputLabel class="text-capitalize" title="Address" />
|
|
62
|
+
<v-col cols="12">
|
|
63
|
+
<v-textarea
|
|
64
|
+
v-model="address"
|
|
65
|
+
density="comfortable"
|
|
66
|
+
placeholder="Enter school address (optional)"
|
|
67
|
+
rows="3"
|
|
68
|
+
></v-textarea>
|
|
69
|
+
</v-col>
|
|
70
|
+
</v-row>
|
|
71
|
+
</v-col>
|
|
72
|
+
|
|
73
|
+
<v-col cols="12" class="mt-2">
|
|
74
|
+
<v-checkbox v-model="createMore" density="comfortable" hide-details>
|
|
75
|
+
<template #label>
|
|
76
|
+
<span class="text-subtitle-2 font-weight-bold">
|
|
77
|
+
Create more
|
|
78
|
+
</span>
|
|
79
|
+
</template>
|
|
80
|
+
</v-checkbox>
|
|
81
|
+
</v-col>
|
|
82
|
+
|
|
83
|
+
<v-col cols="12" class="my-2">
|
|
84
|
+
<v-row no-gutters>
|
|
85
|
+
<v-col cols="12" class="text-center">
|
|
86
|
+
<span
|
|
87
|
+
class="text-none text-subtitle-2 font-weight-medium text-error"
|
|
88
|
+
>
|
|
89
|
+
{{ message }}
|
|
90
|
+
</span>
|
|
91
|
+
</v-col>
|
|
92
|
+
</v-row>
|
|
93
|
+
</v-col>
|
|
94
|
+
</v-row>
|
|
95
|
+
</v-form>
|
|
96
|
+
</v-card-text>
|
|
97
|
+
|
|
98
|
+
<v-toolbar>
|
|
99
|
+
<v-row class="px-6">
|
|
100
|
+
<v-col cols="6">
|
|
101
|
+
<v-btn
|
|
102
|
+
block
|
|
103
|
+
variant="text"
|
|
104
|
+
class="text-none"
|
|
105
|
+
size="large"
|
|
106
|
+
@click="cancel"
|
|
107
|
+
:disabled="disable"
|
|
108
|
+
>
|
|
109
|
+
Cancel
|
|
110
|
+
</v-btn>
|
|
111
|
+
</v-col>
|
|
112
|
+
|
|
113
|
+
<v-col cols="6">
|
|
114
|
+
<v-btn
|
|
115
|
+
block
|
|
116
|
+
variant="flat"
|
|
117
|
+
color="black"
|
|
118
|
+
class="text-none"
|
|
119
|
+
size="large"
|
|
120
|
+
:disabled="!validForm || disable"
|
|
121
|
+
@click="submit"
|
|
122
|
+
:loading="disable"
|
|
123
|
+
>
|
|
124
|
+
Create School
|
|
125
|
+
</v-btn>
|
|
126
|
+
</v-col>
|
|
127
|
+
</v-row>
|
|
128
|
+
</v-toolbar>
|
|
129
|
+
</v-card>
|
|
130
|
+
</template>
|
|
131
|
+
|
|
132
|
+
<script setup lang="ts">
|
|
133
|
+
const emit = defineEmits(["cancel", "success", "success:create-more"]);
|
|
134
|
+
|
|
135
|
+
const validForm = ref(false);
|
|
136
|
+
|
|
137
|
+
const name = ref("");
|
|
138
|
+
const selectedDivision = ref<Record<string, any> | null>(null);
|
|
139
|
+
const principalName = ref("");
|
|
140
|
+
const address = ref("");
|
|
141
|
+
const createMore = ref(false);
|
|
142
|
+
const disable = ref(false);
|
|
143
|
+
|
|
144
|
+
const { requiredRule } = useUtils();
|
|
145
|
+
|
|
146
|
+
const message = ref("");
|
|
147
|
+
|
|
148
|
+
const { createSchool } = useSchool();
|
|
149
|
+
const { getAll: getDivisions } = useDivision();
|
|
150
|
+
|
|
151
|
+
// Load divisions for selection
|
|
152
|
+
const divisionOptions = ref<Array<Record<string, any>>>([]);
|
|
153
|
+
const divisionsLoading = ref(false);
|
|
154
|
+
|
|
155
|
+
onMounted(async () => {
|
|
156
|
+
divisionsLoading.value = true;
|
|
157
|
+
try {
|
|
158
|
+
const response = await getDivisions({ page: 1, limit: 100 });
|
|
159
|
+
divisionOptions.value = response.items || [];
|
|
160
|
+
} catch (error) {
|
|
161
|
+
console.error('Failed to load divisions:', error);
|
|
162
|
+
} finally {
|
|
163
|
+
divisionsLoading.value = false;
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
async function submit() {
|
|
168
|
+
disable.value = true;
|
|
169
|
+
try {
|
|
170
|
+
const payload: Record<string, any> = {
|
|
171
|
+
name: name.value,
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
if (selectedDivision.value) {
|
|
175
|
+
payload.division = selectedDivision.value._id;
|
|
176
|
+
payload.divisionName = selectedDivision.value.name;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (principalName.value.trim()) {
|
|
180
|
+
payload.principalName = principalName.value.trim();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (address.value.trim()) {
|
|
184
|
+
payload.address = address.value.trim();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
await createSchool(payload);
|
|
188
|
+
|
|
189
|
+
if (createMore.value) {
|
|
190
|
+
name.value = "";
|
|
191
|
+
selectedDivision.value = null;
|
|
192
|
+
principalName.value = "";
|
|
193
|
+
address.value = "";
|
|
194
|
+
message.value = "";
|
|
195
|
+
emit("success:create-more");
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
emit("success");
|
|
200
|
+
} catch (error: any) {
|
|
201
|
+
message.value = error.response?._data?.message || "Failed to create school";
|
|
202
|
+
} finally {
|
|
203
|
+
disable.value = false;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function cancel() {
|
|
208
|
+
name.value = "";
|
|
209
|
+
selectedDivision.value = null;
|
|
210
|
+
principalName.value = "";
|
|
211
|
+
address.value = "";
|
|
212
|
+
createMore.value = false;
|
|
213
|
+
message.value = "";
|
|
214
|
+
emit("cancel");
|
|
215
|
+
}
|
|
216
|
+
</script>
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-card width="100%">
|
|
3
|
+
<v-toolbar>
|
|
4
|
+
<v-row no-gutters class="fill-height px-6" align="center">
|
|
5
|
+
<span class="font-weight-bold text-h5"> Edit School </span>
|
|
6
|
+
</v-row>
|
|
7
|
+
</v-toolbar>
|
|
8
|
+
<v-card-text style="max-height: 100vh; overflow-y: auto">
|
|
9
|
+
<v-form v-model="validForm" :disabled="disable">
|
|
10
|
+
<v-row no-gutters>
|
|
11
|
+
<v-col cols="12" class="mt-2">
|
|
12
|
+
<v-row no-gutters>
|
|
13
|
+
<InputLabel class="text-capitalize" title="School Name" required />
|
|
14
|
+
<v-col cols="12">
|
|
15
|
+
<v-text-field
|
|
16
|
+
v-model="name"
|
|
17
|
+
density="comfortable"
|
|
18
|
+
:rules="[requiredRule]"
|
|
19
|
+
placeholder="Enter school name"
|
|
20
|
+
></v-text-field>
|
|
21
|
+
</v-col>
|
|
22
|
+
</v-row>
|
|
23
|
+
</v-col>
|
|
24
|
+
|
|
25
|
+
<v-col cols="12" class="mt-2">
|
|
26
|
+
<v-row no-gutters>
|
|
27
|
+
<InputLabel class="text-capitalize" title="Principal Name" />
|
|
28
|
+
<v-col cols="12">
|
|
29
|
+
<v-text-field
|
|
30
|
+
v-model="principalName"
|
|
31
|
+
density="comfortable"
|
|
32
|
+
placeholder="Enter principal name (optional)"
|
|
33
|
+
></v-text-field>
|
|
34
|
+
</v-col>
|
|
35
|
+
</v-row>
|
|
36
|
+
</v-col>
|
|
37
|
+
|
|
38
|
+
<v-col cols="12" class="mt-2">
|
|
39
|
+
<v-row no-gutters>
|
|
40
|
+
<InputLabel class="text-capitalize" title="Address" />
|
|
41
|
+
<v-col cols="12">
|
|
42
|
+
<v-textarea
|
|
43
|
+
v-model="address"
|
|
44
|
+
density="comfortable"
|
|
45
|
+
placeholder="Enter school address (optional)"
|
|
46
|
+
rows="3"
|
|
47
|
+
></v-textarea>
|
|
48
|
+
</v-col>
|
|
49
|
+
</v-row>
|
|
50
|
+
</v-col>
|
|
51
|
+
|
|
52
|
+
<v-col cols="12" class="my-2">
|
|
53
|
+
<v-row no-gutters>
|
|
54
|
+
<v-col cols="12" class="text-center">
|
|
55
|
+
<span
|
|
56
|
+
class="text-none text-subtitle-2 font-weight-medium text-error"
|
|
57
|
+
>
|
|
58
|
+
{{ message }}
|
|
59
|
+
</span>
|
|
60
|
+
</v-col>
|
|
61
|
+
</v-row>
|
|
62
|
+
</v-col>
|
|
63
|
+
</v-row>
|
|
64
|
+
</v-form>
|
|
65
|
+
</v-card-text>
|
|
66
|
+
|
|
67
|
+
<v-toolbar>
|
|
68
|
+
<v-row class="px-6">
|
|
69
|
+
<v-col cols="6">
|
|
70
|
+
<v-btn
|
|
71
|
+
block
|
|
72
|
+
variant="text"
|
|
73
|
+
class="text-none"
|
|
74
|
+
size="large"
|
|
75
|
+
@click="cancel"
|
|
76
|
+
:disabled="disable"
|
|
77
|
+
>
|
|
78
|
+
Cancel
|
|
79
|
+
</v-btn>
|
|
80
|
+
</v-col>
|
|
81
|
+
|
|
82
|
+
<v-col cols="6">
|
|
83
|
+
<v-btn
|
|
84
|
+
block
|
|
85
|
+
variant="flat"
|
|
86
|
+
color="black"
|
|
87
|
+
class="text-none"
|
|
88
|
+
size="large"
|
|
89
|
+
:disabled="!validForm || disable || !hasChanges"
|
|
90
|
+
@click="submit"
|
|
91
|
+
:loading="disable"
|
|
92
|
+
>
|
|
93
|
+
Update School
|
|
94
|
+
</v-btn>
|
|
95
|
+
</v-col>
|
|
96
|
+
</v-row>
|
|
97
|
+
</v-toolbar>
|
|
98
|
+
</v-card>
|
|
99
|
+
</template>
|
|
100
|
+
|
|
101
|
+
<script setup lang="ts">
|
|
102
|
+
const props = defineProps({
|
|
103
|
+
school: {
|
|
104
|
+
type: Object as PropType<Record<string, any>>,
|
|
105
|
+
required: true,
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const emit = defineEmits(["cancel", "success"]);
|
|
110
|
+
|
|
111
|
+
const validForm = ref(false);
|
|
112
|
+
|
|
113
|
+
const name = ref("");
|
|
114
|
+
const principalName = ref("");
|
|
115
|
+
const address = ref("");
|
|
116
|
+
const disable = ref(false);
|
|
117
|
+
|
|
118
|
+
const { requiredRule } = useUtils();
|
|
119
|
+
|
|
120
|
+
const message = ref("");
|
|
121
|
+
|
|
122
|
+
const { updateSchoolField } = useSchool();
|
|
123
|
+
|
|
124
|
+
// Initialize form with existing data
|
|
125
|
+
watchEffect(() => {
|
|
126
|
+
if (props.school) {
|
|
127
|
+
name.value = props.school.name || "";
|
|
128
|
+
principalName.value = props.school.principalName || "";
|
|
129
|
+
address.value = props.school.address || "";
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const hasChanges = computed(() => {
|
|
134
|
+
if (!props.school) return false;
|
|
135
|
+
return (
|
|
136
|
+
name.value !== (props.school.name || "") ||
|
|
137
|
+
principalName.value !== (props.school.principalName || "") ||
|
|
138
|
+
address.value !== (props.school.address || "")
|
|
139
|
+
);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
async function submit() {
|
|
143
|
+
if (!props.school?._id) return;
|
|
144
|
+
|
|
145
|
+
disable.value = true;
|
|
146
|
+
try {
|
|
147
|
+
// Update fields that have changed
|
|
148
|
+
if (name.value !== (props.school.name || "")) {
|
|
149
|
+
await updateSchoolField(props.school._id, "name", name.value);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (principalName.value !== (props.school.principalName || "")) {
|
|
153
|
+
await updateSchoolField(props.school._id, "principalName", principalName.value);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (address.value !== (props.school.address || "")) {
|
|
157
|
+
await updateSchoolField(props.school._id, "address", address.value);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
emit("success");
|
|
161
|
+
} catch (error: any) {
|
|
162
|
+
message.value = error.response?._data?.message || "Failed to update school";
|
|
163
|
+
} finally {
|
|
164
|
+
disable.value = false;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function cancel() {
|
|
169
|
+
// Reset to original values
|
|
170
|
+
name.value = props.school?.name || "";
|
|
171
|
+
principalName.value = props.school?.principalName || "";
|
|
172
|
+
address.value = props.school?.address || "";
|
|
173
|
+
message.value = "";
|
|
174
|
+
emit("cancel");
|
|
175
|
+
}
|
|
176
|
+
</script>
|
|
177
|
+
|
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-row no-gutters>
|
|
3
|
+
<v-col cols="12">
|
|
4
|
+
<v-card
|
|
5
|
+
width="100%"
|
|
6
|
+
variant="outlined"
|
|
7
|
+
border="thin"
|
|
8
|
+
rounded="lg"
|
|
9
|
+
:loading="loading"
|
|
10
|
+
>
|
|
11
|
+
<v-toolbar density="compact" color="grey-lighten-4">
|
|
12
|
+
<template #prepend>
|
|
13
|
+
<v-btn fab icon density="comfortable" @click="getSchools()">
|
|
14
|
+
<v-icon>mdi-refresh</v-icon>
|
|
15
|
+
</v-btn>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<template #append>
|
|
19
|
+
<v-row no-gutters justify="end" align="center">
|
|
20
|
+
<span class="mr-2 text-caption text-fontgray">
|
|
21
|
+
{{ pageRange }}
|
|
22
|
+
</span>
|
|
23
|
+
<local-pagination
|
|
24
|
+
v-model="page"
|
|
25
|
+
:length="pages"
|
|
26
|
+
@update:value="getSchools()"
|
|
27
|
+
/>
|
|
28
|
+
</v-row>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<template #extension>
|
|
32
|
+
<v-tabs v-model="theStatus">
|
|
33
|
+
<v-tab
|
|
34
|
+
v-for="status in statusFilter"
|
|
35
|
+
:key="status.text"
|
|
36
|
+
:to="{
|
|
37
|
+
name: prop.baseRoute,
|
|
38
|
+
params: status.params,
|
|
39
|
+
}"
|
|
40
|
+
class="text-capitalize"
|
|
41
|
+
>
|
|
42
|
+
{{ status.text }}
|
|
43
|
+
</v-tab>
|
|
44
|
+
</v-tabs>
|
|
45
|
+
</template>
|
|
46
|
+
</v-toolbar>
|
|
47
|
+
|
|
48
|
+
<v-data-table
|
|
49
|
+
:headers="headers"
|
|
50
|
+
:items="items"
|
|
51
|
+
item-value="_id"
|
|
52
|
+
items-per-page="20"
|
|
53
|
+
fixed-header
|
|
54
|
+
hide-default-footer
|
|
55
|
+
hide-default-header
|
|
56
|
+
@click:row="tableRowClickHandler"
|
|
57
|
+
style="max-height: calc(100vh - (180px))"
|
|
58
|
+
>
|
|
59
|
+
<template #item.createdAt="{ value }">
|
|
60
|
+
{{ new Date(value).toLocaleDateString() }}
|
|
61
|
+
</template>
|
|
62
|
+
</v-data-table>
|
|
63
|
+
</v-card>
|
|
64
|
+
</v-col>
|
|
65
|
+
|
|
66
|
+
<!-- Create Dialog -->
|
|
67
|
+
<v-dialog v-model="createDialog" width="500" persistent>
|
|
68
|
+
<SchoolFormCreate
|
|
69
|
+
@cancel="createDialog = false"
|
|
70
|
+
@success="successCreate()"
|
|
71
|
+
@success:create-more="getSchools()"
|
|
72
|
+
/>
|
|
73
|
+
</v-dialog>
|
|
74
|
+
|
|
75
|
+
<!-- Edit Dialog -->
|
|
76
|
+
<v-dialog v-model="editDialog" width="500" persistent>
|
|
77
|
+
<SchoolFormEdit
|
|
78
|
+
v-if="selectedSchool"
|
|
79
|
+
@cancel="editDialog = false"
|
|
80
|
+
@success="successUpdate()"
|
|
81
|
+
:school="selectedSchool"
|
|
82
|
+
/>
|
|
83
|
+
</v-dialog>
|
|
84
|
+
|
|
85
|
+
<!-- Preview Dialog -->
|
|
86
|
+
<v-dialog v-model="previewDialog" width="500" persistent>
|
|
87
|
+
<v-card width="100%">
|
|
88
|
+
<v-card-text style="max-height: 100vh; overflow-y: auto" class="pb-0">
|
|
89
|
+
<v-row no-gutters v-if="selectedSchool">
|
|
90
|
+
<v-col cols="12" class="mb-3">
|
|
91
|
+
<strong>Name:</strong> {{ selectedSchool.name }}
|
|
92
|
+
</v-col>
|
|
93
|
+
<v-col cols="12" class="mb-3">
|
|
94
|
+
<strong>Address:</strong>
|
|
95
|
+
{{
|
|
96
|
+
`${selectedSchool.address} ${
|
|
97
|
+
selectedSchool.continuedAddress || ""
|
|
98
|
+
}`
|
|
99
|
+
}}
|
|
100
|
+
{{ selectedSchool.city }} {{ selectedSchool.state }}
|
|
101
|
+
{{ selectedSchool.zipCode }}
|
|
102
|
+
</v-col>
|
|
103
|
+
<v-col cols="12" class="mb-3">
|
|
104
|
+
<strong>Region:</strong>
|
|
105
|
+
{{ selectedSchool.regionName || "Not assigned" }}
|
|
106
|
+
</v-col>
|
|
107
|
+
<v-col cols="12" class="mb-3">
|
|
108
|
+
<strong>Division:</strong>
|
|
109
|
+
{{ selectedSchool.divisionName || "Not assigned" }}
|
|
110
|
+
</v-col>
|
|
111
|
+
<v-col cols="12" class="mb-3">
|
|
112
|
+
<strong>Principal:</strong>
|
|
113
|
+
{{ selectedSchool.principalName || "Not assigned" }}
|
|
114
|
+
</v-col>
|
|
115
|
+
<v-col cols="12" class="mb-3">
|
|
116
|
+
<strong>Created:</strong>
|
|
117
|
+
{{
|
|
118
|
+
new Date(selectedSchool.createdAt || "").toLocaleDateString()
|
|
119
|
+
}}
|
|
120
|
+
</v-col>
|
|
121
|
+
<v-col cols="12" class="mb-3" v-if="selectedSchool.updatedAt">
|
|
122
|
+
<strong>Updated:</strong>
|
|
123
|
+
{{ new Date(selectedSchool.updatedAt).toLocaleDateString() }}
|
|
124
|
+
</v-col>
|
|
125
|
+
</v-row>
|
|
126
|
+
</v-card-text>
|
|
127
|
+
|
|
128
|
+
<v-toolbar class="pa-0" density="compact">
|
|
129
|
+
<v-row no-gutter>
|
|
130
|
+
<v-col cols="6" class="pa-0">
|
|
131
|
+
<v-btn
|
|
132
|
+
block
|
|
133
|
+
variant="text"
|
|
134
|
+
class="text-none"
|
|
135
|
+
height="48"
|
|
136
|
+
tile
|
|
137
|
+
@click="previewDialog = false"
|
|
138
|
+
>
|
|
139
|
+
Close
|
|
140
|
+
</v-btn>
|
|
141
|
+
</v-col>
|
|
142
|
+
|
|
143
|
+
<v-col cols="6" class="pa-0" v-if="canUpdate">
|
|
144
|
+
<v-menu>
|
|
145
|
+
<template #activator="{ props }">
|
|
146
|
+
<v-btn
|
|
147
|
+
block
|
|
148
|
+
variant="flat"
|
|
149
|
+
color="black"
|
|
150
|
+
class="text-none"
|
|
151
|
+
height="48"
|
|
152
|
+
v-bind="props"
|
|
153
|
+
tile
|
|
154
|
+
>
|
|
155
|
+
More actions
|
|
156
|
+
</v-btn>
|
|
157
|
+
</template>
|
|
158
|
+
|
|
159
|
+
<v-list class="pa-0">
|
|
160
|
+
<v-list-item
|
|
161
|
+
v-if="selectedSchool?.status === 'pending'"
|
|
162
|
+
@click="submitApproval()"
|
|
163
|
+
>
|
|
164
|
+
<v-list-item-title class="text-subtitle-2">
|
|
165
|
+
Approve School
|
|
166
|
+
</v-list-item-title>
|
|
167
|
+
</v-list-item>
|
|
168
|
+
|
|
169
|
+
<v-list-item @click="editFromPreview()">
|
|
170
|
+
<v-list-item-title class="text-subtitle-2">
|
|
171
|
+
Edit School
|
|
172
|
+
</v-list-item-title>
|
|
173
|
+
</v-list-item>
|
|
174
|
+
|
|
175
|
+
<v-list-item
|
|
176
|
+
@click="openDeleteDialog(selectedSchool?._id)"
|
|
177
|
+
class="text-red"
|
|
178
|
+
>
|
|
179
|
+
<v-list-item-title class="text-subtitle-2">
|
|
180
|
+
Delete School
|
|
181
|
+
</v-list-item-title>
|
|
182
|
+
</v-list-item>
|
|
183
|
+
</v-list>
|
|
184
|
+
</v-menu>
|
|
185
|
+
</v-col>
|
|
186
|
+
</v-row>
|
|
187
|
+
</v-toolbar>
|
|
188
|
+
</v-card>
|
|
189
|
+
</v-dialog>
|
|
190
|
+
|
|
191
|
+
<ConfirmDialog
|
|
192
|
+
v-model="confirmDialog"
|
|
193
|
+
:loading="deleteLoading"
|
|
194
|
+
@submit="handleDeleteSchool"
|
|
195
|
+
>
|
|
196
|
+
<template #title>
|
|
197
|
+
<span class="font-weight-medium text-h5">Delete School</span>
|
|
198
|
+
</template>
|
|
199
|
+
|
|
200
|
+
<template #description>
|
|
201
|
+
<p class="text-subtitle-2">
|
|
202
|
+
Are you sure you want to delete this school? This action cannot be
|
|
203
|
+
undone.
|
|
204
|
+
</p>
|
|
205
|
+
</template>
|
|
206
|
+
|
|
207
|
+
<template #footer>
|
|
208
|
+
<v-btn
|
|
209
|
+
variant="text"
|
|
210
|
+
@click="confirmDialog = false"
|
|
211
|
+
:disabled="deleteLoading"
|
|
212
|
+
class="text-none"
|
|
213
|
+
>
|
|
214
|
+
Close
|
|
215
|
+
</v-btn>
|
|
216
|
+
<v-btn
|
|
217
|
+
color="black"
|
|
218
|
+
variant="flat"
|
|
219
|
+
@click="handleDeleteSchool"
|
|
220
|
+
:loading="deleteLoading"
|
|
221
|
+
class="text-none"
|
|
222
|
+
>
|
|
223
|
+
Delete School
|
|
224
|
+
</v-btn>
|
|
225
|
+
</template>
|
|
226
|
+
</ConfirmDialog>
|
|
227
|
+
|
|
228
|
+
<Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
|
|
229
|
+
</v-row>
|
|
230
|
+
</template>
|
|
231
|
+
|
|
232
|
+
<script setup lang="ts">
|
|
233
|
+
const prop = defineProps({
|
|
234
|
+
status: {
|
|
235
|
+
type: String,
|
|
236
|
+
default: "active",
|
|
237
|
+
},
|
|
238
|
+
org: {
|
|
239
|
+
type: String,
|
|
240
|
+
default: "",
|
|
241
|
+
},
|
|
242
|
+
app: {
|
|
243
|
+
type: String,
|
|
244
|
+
default: "school",
|
|
245
|
+
},
|
|
246
|
+
baseRoute: {
|
|
247
|
+
type: String,
|
|
248
|
+
default: "index",
|
|
249
|
+
},
|
|
250
|
+
headers: {
|
|
251
|
+
type: Array as PropType<Array<Record<string, any>>>,
|
|
252
|
+
default: () => [
|
|
253
|
+
{
|
|
254
|
+
title: "Name",
|
|
255
|
+
value: "name",
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
title: "Region",
|
|
259
|
+
value: "regionName",
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
title: "Division",
|
|
263
|
+
value: "divisionName",
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
title: "Principal",
|
|
267
|
+
value: "principalName",
|
|
268
|
+
},
|
|
269
|
+
],
|
|
270
|
+
},
|
|
271
|
+
canCreate: {
|
|
272
|
+
type: Boolean,
|
|
273
|
+
default: true,
|
|
274
|
+
},
|
|
275
|
+
canUpdate: {
|
|
276
|
+
type: Boolean,
|
|
277
|
+
default: true,
|
|
278
|
+
},
|
|
279
|
+
canDelete: {
|
|
280
|
+
type: Boolean,
|
|
281
|
+
default: true,
|
|
282
|
+
},
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
const statusFilter = computed(() => {
|
|
286
|
+
const items = [
|
|
287
|
+
{ text: "Active", params: { status: "active" } },
|
|
288
|
+
{ text: "Pending", params: { status: "pending" } },
|
|
289
|
+
{ text: "Suspended", params: { status: "suspended" } },
|
|
290
|
+
];
|
|
291
|
+
|
|
292
|
+
if (prop.org) {
|
|
293
|
+
items.map((i) => ({
|
|
294
|
+
...i,
|
|
295
|
+
params: { ...i.params, org: prop.org },
|
|
296
|
+
}));
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return items;
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
const statuses = ["active", "pending", "suspended", "inactive"];
|
|
303
|
+
const theStatus = ref("active");
|
|
304
|
+
|
|
305
|
+
const { headerSearch } = useLocal();
|
|
306
|
+
const { getAll: _getSchools, approvedById } = useSchool();
|
|
307
|
+
|
|
308
|
+
const page = ref(1);
|
|
309
|
+
const pages = ref(0);
|
|
310
|
+
const pageRange = ref("-- - -- of --");
|
|
311
|
+
|
|
312
|
+
const message = ref("");
|
|
313
|
+
const messageSnackbar = ref(false);
|
|
314
|
+
const messageColor = ref("");
|
|
315
|
+
|
|
316
|
+
const items = ref<Array<Record<string, any>>>([]);
|
|
317
|
+
|
|
318
|
+
const propStatus = computed(() => prop.status);
|
|
319
|
+
|
|
320
|
+
const {
|
|
321
|
+
data: getSchoolReq,
|
|
322
|
+
refresh: getSchools,
|
|
323
|
+
status: getSchoolReqStatus,
|
|
324
|
+
} = useLazyAsyncData(
|
|
325
|
+
"schools-get-all-" + prop.status,
|
|
326
|
+
() =>
|
|
327
|
+
_getSchools({
|
|
328
|
+
page: page.value,
|
|
329
|
+
search: headerSearch.value,
|
|
330
|
+
status: prop.status,
|
|
331
|
+
org: prop.org,
|
|
332
|
+
app: prop.app,
|
|
333
|
+
}),
|
|
334
|
+
{
|
|
335
|
+
watch: [page, propStatus],
|
|
336
|
+
}
|
|
337
|
+
);
|
|
338
|
+
|
|
339
|
+
const loading = computed(() => getSchoolReqStatus.value === "pending");
|
|
340
|
+
|
|
341
|
+
watchEffect(() => {
|
|
342
|
+
if (getSchoolReq.value) {
|
|
343
|
+
items.value = getSchoolReq.value.items;
|
|
344
|
+
pages.value = getSchoolReq.value.pages;
|
|
345
|
+
pageRange.value = getSchoolReq.value.pageRange;
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
function tableRowClickHandler(_: any, data: any) {
|
|
350
|
+
selectedSchool.value = data.item;
|
|
351
|
+
previewDialog.value = true;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const createDialog = ref(false);
|
|
355
|
+
const editDialog = ref(false);
|
|
356
|
+
const previewDialog = ref(false);
|
|
357
|
+
const selectedSchool = ref<Record<string, any> | null>(null);
|
|
358
|
+
|
|
359
|
+
function successCreate() {
|
|
360
|
+
createDialog.value = false;
|
|
361
|
+
getSchools();
|
|
362
|
+
showMessage("School created successfully!", "success");
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function successUpdate() {
|
|
366
|
+
editDialog.value = false;
|
|
367
|
+
previewDialog.value = false;
|
|
368
|
+
getSchools();
|
|
369
|
+
showMessage("School updated successfully!", "success");
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function openEditDialog(school: Record<string, any>) {
|
|
373
|
+
selectedSchool.value = school;
|
|
374
|
+
editDialog.value = true;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function editFromPreview() {
|
|
378
|
+
previewDialog.value = false;
|
|
379
|
+
editDialog.value = true;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const confirmDialog = ref(false);
|
|
383
|
+
const selectedSchoolId = ref<string | null>(null);
|
|
384
|
+
const deleteLoading = ref(false);
|
|
385
|
+
|
|
386
|
+
function openDeleteDialog(id: string) {
|
|
387
|
+
selectedSchoolId.value = id;
|
|
388
|
+
confirmDialog.value = true;
|
|
389
|
+
if (previewDialog.value) {
|
|
390
|
+
previewDialog.value = false;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function showMessage(msg: string, color: string) {
|
|
395
|
+
message.value = msg;
|
|
396
|
+
messageColor.value = color;
|
|
397
|
+
messageSnackbar.value = true;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
async function handleDeleteSchool() {
|
|
401
|
+
if (!selectedSchoolId.value) return;
|
|
402
|
+
deleteLoading.value = true;
|
|
403
|
+
try {
|
|
404
|
+
confirmDialog.value = false;
|
|
405
|
+
getSchools();
|
|
406
|
+
} catch (error: any) {
|
|
407
|
+
const errorMessage =
|
|
408
|
+
error?.response?._data?.message || "Failed to delete school";
|
|
409
|
+
showMessage(errorMessage, "error");
|
|
410
|
+
} finally {
|
|
411
|
+
deleteLoading.value = false;
|
|
412
|
+
selectedSchoolId.value = null;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
async function submitApproval() {
|
|
417
|
+
try {
|
|
418
|
+
await approvedById(selectedSchool.value?._id ?? "");
|
|
419
|
+
getSchools();
|
|
420
|
+
} catch (error: any) {
|
|
421
|
+
const errorMessage =
|
|
422
|
+
error?.response?._data?.message || "Failed to delete school";
|
|
423
|
+
showMessage(errorMessage, "error");
|
|
424
|
+
} finally {
|
|
425
|
+
previewDialog.value = false;
|
|
426
|
+
selectedSchoolId.value = null;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
</script>
|
package/composables/useSchool.ts
CHANGED
|
@@ -28,12 +28,12 @@ export default function useSchool() {
|
|
|
28
28
|
page = 1,
|
|
29
29
|
status = "active",
|
|
30
30
|
search = "",
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
org = "",
|
|
32
|
+
app = "admin",
|
|
33
33
|
} = {}) {
|
|
34
34
|
return useNuxtApp().$api<Record<string, any>>("/api/schools", {
|
|
35
35
|
method: "GET",
|
|
36
|
-
query: { page, status, search,
|
|
36
|
+
query: { page, status, search, org, app },
|
|
37
37
|
});
|
|
38
38
|
}
|
|
39
39
|
|