@goweekdays/layer-common 1.2.4 → 1.2.6

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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # @goweekdays/layer-common
2
2
 
3
+ ## 1.2.6
4
+
5
+ ### Patch Changes
6
+
7
+ - bcdc6ba: Fix setRole() to clear message
8
+
9
+ ## 1.2.5
10
+
11
+ ### Patch Changes
12
+
13
+ - 774d113: Fix role management components and composable
14
+
3
15
  ## 1.2.4
4
16
 
5
17
  ### Patch Changes
@@ -0,0 +1,116 @@
1
+ <template>
2
+ <v-card width="100%" v-bind="attrs">
3
+ <v-list
4
+ v-model:selected="selected"
5
+ lines="two"
6
+ :select-strategy="attrs.readonly ? 'classic' : 'leaf'"
7
+ class="pa-0"
8
+ density="compact"
9
+ read-only
10
+ open-strategy="single"
11
+ >
12
+ <template name="prepend"></template>
13
+
14
+ <template
15
+ v-for="(keyGroup, keyGroupIndex) in props.items"
16
+ :key="keyGroupIndex"
17
+ >
18
+ <v-divider v-if="keyGroupIndex > 0"></v-divider>
19
+ <v-list-group :value="keyGroup.title" fluid>
20
+ <template v-slot:activator="{ props }">
21
+ <v-list-item v-bind="props" density="compact">
22
+ <span class="text-capitalize">
23
+ {{ keyGroup.title }}
24
+ </span>
25
+
26
+ <template #prepend>
27
+ <v-chip class="mr-2" small>
28
+ {{ selectedActionCount(String(keyGroup.key)) }}
29
+ </v-chip>
30
+ </template>
31
+ </v-list-item>
32
+ </template>
33
+
34
+ <template
35
+ v-for="(item, itemIndex) in keyGroup.children"
36
+ :key="itemIndex"
37
+ >
38
+ <v-divider></v-divider>
39
+ <v-list-item v-if="attrs.readonly" density="compact">
40
+ <template #title class="pl-2">
41
+ <span class="text-subtitle-2 text-capitalize">
42
+ {{ item.title }}
43
+ </span>
44
+ </template>
45
+
46
+ <template #subtitle class="pl-2">
47
+ <span class="text-subtitle-2">{{ item.description }}</span>
48
+ </template>
49
+ </v-list-item>
50
+
51
+ <v-list-item v-else :value="item.key" density="compact">
52
+ <template #title class="pl-2">
53
+ <span class="text-subtitle-2 text-capitalize">
54
+ {{ item.title }}
55
+ </span>
56
+ </template>
57
+
58
+ <template #subtitle class="pl-2">
59
+ <span class="text-subtitle-2 text-capitalize">
60
+ {{ item.description }}
61
+ </span>
62
+ </template>
63
+
64
+ <template #prepend="{ isSelected }">
65
+ <v-list-item-action start class="pl-1">
66
+ <v-checkbox-btn :model-value="isSelected"></v-checkbox-btn>
67
+ </v-list-item-action>
68
+ </template>
69
+ </v-list-item>
70
+ </template>
71
+ </v-list-group>
72
+ </template>
73
+
74
+ <slot v-if="noData" name="no-data">
75
+ <v-list-item>
76
+ <v-list-item-title>No data</v-list-item-title>
77
+ </v-list-item>
78
+ </slot>
79
+
80
+ <template name="append"></template>
81
+ </v-list>
82
+ </v-card>
83
+ </template>
84
+
85
+ <script setup lang="ts">
86
+ const selected = defineModel<Array<string>>({ default: [] });
87
+ const attrs = useAttrs();
88
+
89
+ type TListGroup = {
90
+ title: string;
91
+ key: string;
92
+ children: { title: string; key: string; description: string }[];
93
+ };
94
+
95
+ const props = defineProps({
96
+ items: {
97
+ // Array of object
98
+ type: Array as PropType<TListGroup[]>,
99
+ required: true,
100
+ default: () => [],
101
+ },
102
+ message: {
103
+ type: String,
104
+ default: "",
105
+ },
106
+ });
107
+
108
+ const selectedActionCount = (resource: string) => {
109
+ return selected.value.filter((permission) => permission.startsWith(resource))
110
+ .length;
111
+ };
112
+
113
+ const noData = computed(() => {
114
+ return Object.keys(props.items).length === 0;
115
+ });
116
+ </script>
@@ -0,0 +1,326 @@
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 text-capitalize">
6
+ {{ localProps.title }}
7
+ </span>
8
+ </v-row>
9
+ </v-toolbar>
10
+
11
+ <v-card-text style="max-height: 100vh; overflow-y: auto">
12
+ <v-form v-model="valid">
13
+ <v-row no-gutters>
14
+ <v-col cols="12" class="mb-1">
15
+ <v-row no-gutters>
16
+ <InputLabel
17
+ class="text-capitalize"
18
+ title="Name"
19
+ :required="isMutable"
20
+ />
21
+ <v-col cols="12">
22
+ <v-text-field
23
+ v-model="role.name"
24
+ :rules="isMutable ? [requiredRule] : []"
25
+ :readonly="!isMutable"
26
+ ></v-text-field>
27
+ </v-col>
28
+ </v-row>
29
+ </v-col>
30
+
31
+ <v-col cols="12" class="mb-1">
32
+ <v-row no-gutters>
33
+ <InputLabel
34
+ class="text-capitalize"
35
+ title="Permissions"
36
+ :required="isMutable"
37
+ />
38
+ <v-col cols="12">
39
+ <ListGroupSelection
40
+ v-model="role.permissions"
41
+ :items="permissions"
42
+ :readonly="!isMutable"
43
+ variant="outlined"
44
+ border="thin"
45
+ :loading="loadingPermissions"
46
+ />
47
+ <v-input
48
+ :messages="errorPermissionSelection"
49
+ :error="!!errorPermissionSelection"
50
+ ></v-input>
51
+ </v-col>
52
+ </v-row>
53
+ </v-col>
54
+
55
+ <v-col cols="12" class="mb-1">
56
+ <v-row no-gutters>
57
+ <InputLabel class="text-capitalize" title="Description" />
58
+ <v-col cols="12">
59
+ <v-textarea
60
+ v-model="role.description"
61
+ rows="2"
62
+ no-resize
63
+ :readonly="!isMutable"
64
+ ></v-textarea>
65
+ </v-col>
66
+ </v-row>
67
+ </v-col>
68
+
69
+ <v-col v-if="!localProps.app && isMutable" cols="12" class="mb-1">
70
+ <v-row no-gutters>
71
+ <InputLabel
72
+ class="text-capitalize"
73
+ title="App"
74
+ :required="isMutable"
75
+ />
76
+ <v-col cols="12">
77
+ <v-select
78
+ v-model="role.type"
79
+ :items="apps"
80
+ item-title="name"
81
+ item-value="code"
82
+ :readonly="!isMutable"
83
+ :loading="loadingApps"
84
+ ></v-select>
85
+ </v-col>
86
+ </v-row>
87
+ </v-col>
88
+ </v-row>
89
+ </v-form>
90
+
91
+ <v-alert
92
+ v-if="message"
93
+ type="error"
94
+ variant="flat"
95
+ closable
96
+ position="absolute"
97
+ location="bottom"
98
+ style="bottom: 48px"
99
+ @click:close="message = ''"
100
+ width="100%"
101
+ tile
102
+ class="text-caption"
103
+ >
104
+ {{ message }}
105
+ </v-alert>
106
+ </v-card-text>
107
+
108
+ <v-toolbar density="compact">
109
+ <v-row no-gutters>
110
+ <v-col v-if="isMutable" cols="6">
111
+ <v-btn
112
+ tile
113
+ block
114
+ variant="text"
115
+ class="text-none"
116
+ size="48"
117
+ @click="emits('cancel')"
118
+ >
119
+ Cancel
120
+ </v-btn>
121
+ </v-col>
122
+
123
+ <v-col v-if="localProps.mode === 'view'" cols="6">
124
+ <v-btn
125
+ tile
126
+ block
127
+ variant="text"
128
+ class="text-none"
129
+ size="48"
130
+ @click="emits('close')"
131
+ >
132
+ Close
133
+ </v-btn>
134
+ </v-col>
135
+
136
+ <v-col v-if="localProps.mode === 'view'" cols="6">
137
+ <v-menu>
138
+ <template #activator="{ props }">
139
+ <v-btn
140
+ block
141
+ variant="flat"
142
+ color="black"
143
+ class="text-none"
144
+ height="48"
145
+ v-bind="props"
146
+ tile
147
+ >
148
+ More actions
149
+ </v-btn>
150
+ </template>
151
+
152
+ <v-list class="pa-0">
153
+ <v-list-item @click="emits('edit')">
154
+ <v-list-item-title class="text-subtitle-2">
155
+ Edit Role
156
+ </v-list-item-title>
157
+ </v-list-item>
158
+
159
+ <v-list-item @click="emits('delete')" class="text-red">
160
+ <v-list-item-title class="text-subtitle-2">
161
+ Delete Role
162
+ </v-list-item-title>
163
+ </v-list-item>
164
+ </v-list>
165
+ </v-menu>
166
+ </v-col>
167
+
168
+ <v-col v-if="isMutable" cols="6">
169
+ <v-btn
170
+ tile
171
+ block
172
+ variant="flat"
173
+ color="black"
174
+ class="text-none"
175
+ size="48"
176
+ @click="emits('submit')"
177
+ :disabled="!valid"
178
+ >
179
+ {{ localProps.mode === "add" ? "Add Role" : "Save Changes" }}
180
+ </v-btn>
181
+ </v-col>
182
+ </v-row>
183
+ </v-toolbar>
184
+ </v-card>
185
+ </template>
186
+
187
+ <script setup lang="ts">
188
+ const emits = defineEmits(["submit", "cancel", "close", "edit", "delete"]);
189
+
190
+ const isMutable = computed(() => ["add", "edit"].includes(localProps.mode));
191
+
192
+ const localProps = defineProps({
193
+ title: {
194
+ type: String,
195
+ default: "Role Form",
196
+ },
197
+ app: {
198
+ type: String,
199
+ default: "",
200
+ },
201
+ mode: {
202
+ type: String,
203
+ default: "add",
204
+ },
205
+ });
206
+
207
+ const role = defineModel<TRole>({
208
+ default: () => useRole().role.value,
209
+ });
210
+
211
+ const errorPermissionSelection = computed(() =>
212
+ requireListRule(role.value.permissions ?? [])
213
+ );
214
+
215
+ const message = defineModel("message", { default: "" });
216
+
217
+ const valid = ref(false);
218
+
219
+ const { requiredRule, requireListRule } = useUtils();
220
+
221
+ const apps = ref<TApp[]>([]);
222
+
223
+ const { getAll: getAllApps } = useApps();
224
+
225
+ const {
226
+ data: appsData,
227
+ status: appsReqStatus,
228
+ refresh: refreshApps,
229
+ } = await useLazyAsyncData(
230
+ `role-form-get-apps-${localProps.app}`,
231
+ () =>
232
+ getAllApps({
233
+ limit: 20,
234
+ }),
235
+ {
236
+ immediate: false,
237
+ }
238
+ );
239
+
240
+ if (!localProps.app) {
241
+ refreshApps();
242
+ }
243
+
244
+ const loadingApps = computed(() => appsReqStatus.value === "pending");
245
+
246
+ watchEffect(() => {
247
+ if (appsData.value) {
248
+ apps.value = appsData.value.items;
249
+ }
250
+ });
251
+
252
+ type PermChild = {
253
+ title: string;
254
+ key: string;
255
+ description: string;
256
+ };
257
+
258
+ type PermGroup = {
259
+ title: string;
260
+ key: string;
261
+ children: PermChild[];
262
+ };
263
+
264
+ const permissions = ref<PermGroup[]>([]);
265
+
266
+ const { getAllPerm } = usePermission();
267
+
268
+ const {
269
+ data: permissionsData,
270
+ status: permissionsReqStatus,
271
+ refresh: refreshPermissions,
272
+ } = await useLazyAsyncData(
273
+ `role-form-get-all-permission-${localProps.app}`,
274
+ () =>
275
+ getAllPerm({
276
+ app: localProps.app,
277
+ limit: 200,
278
+ })
279
+ );
280
+
281
+ const loadingPermissions = computed(
282
+ () => permissionsReqStatus.value === "pending"
283
+ );
284
+
285
+ function groupPermissions(permissions: TPerm[]): PermGroup[] {
286
+ const groups: Record<string, PermGroup> = {};
287
+
288
+ for (const perm of permissions) {
289
+ if (perm.status !== "active") continue;
290
+
291
+ const groupKey = perm.group;
292
+
293
+ if (!groups[groupKey]) {
294
+ groups[groupKey] = {
295
+ title: groupKey,
296
+ key: groupKey,
297
+ children: [],
298
+ };
299
+ }
300
+
301
+ groups[groupKey].children.push({
302
+ title: perm.name,
303
+ key: perm.key,
304
+ description: perm.description,
305
+ });
306
+ }
307
+
308
+ return Object.values(groups);
309
+ }
310
+
311
+ watchEffect(() => {
312
+ if (
313
+ permissionsData.value &&
314
+ permissionsData.value &&
315
+ permissionsData.value.items
316
+ ) {
317
+ let items = permissionsData.value.items;
318
+ if (localProps.mode === "view") {
319
+ items = permissionsData.value.items.filter((p: TPerm) =>
320
+ role.value.permissions?.includes(p.key)
321
+ );
322
+ }
323
+ permissions.value = groupPermissions(items);
324
+ }
325
+ });
326
+ </script>
@@ -141,12 +141,12 @@ const type = defineModel("type", {
141
141
  default: "",
142
142
  });
143
143
 
144
- const { createRole } = useRole();
144
+ const { add } = useRole();
145
145
 
146
146
  async function submit() {
147
147
  disable.value = true;
148
148
  try {
149
- await createRole({
149
+ await add({
150
150
  name: name.value,
151
151
  permissions: selectedPermissions.value,
152
152
  type: type.value,