@mouseless/baked 1.5.3 → 1.6.1
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/dist/module.json +1 -1
- package/dist/module.mjs +2 -1
- package/dist/runtime/components/AwaitLoading.d.ts +7 -4
- package/dist/runtime/components/AwaitLoading.js +39 -11
- package/dist/runtime/components/Bake.d.vue.ts +1 -3
- package/dist/runtime/components/Bake.vue +101 -60
- package/dist/runtime/components/Bake.vue.d.ts +1 -3
- package/dist/runtime/components/DataContainer.vue +4 -7
- package/dist/runtime/components/DataPanel.vue +5 -7
- package/dist/runtime/components/DataTable.vue +12 -3
- package/dist/runtime/components/ErrorPage.vue +6 -5
- package/dist/runtime/components/ErrorPopover.d.vue.ts +15 -0
- package/dist/runtime/components/ErrorPopover.vue +22 -0
- package/dist/runtime/components/ErrorPopover.vue.d.ts +15 -0
- package/dist/runtime/components/FormPage.vue +36 -8
- package/dist/runtime/components/InlineError.d.vue.ts +15 -0
- package/dist/runtime/components/InlineError.vue +27 -0
- package/dist/runtime/components/InlineError.vue.d.ts +15 -0
- package/dist/runtime/components/Input.d.vue.ts +9 -1
- package/dist/runtime/components/Input.vue +31 -5
- package/dist/runtime/components/Input.vue.d.ts +9 -1
- package/dist/runtime/components/InputNumber.vue +23 -23
- package/dist/runtime/components/InputText.vue +22 -22
- package/dist/runtime/components/Inputs.d.vue.ts +7 -0
- package/dist/runtime/components/Inputs.vue +4 -2
- package/dist/runtime/components/Inputs.vue.d.ts +7 -0
- package/dist/runtime/components/Labeler.d.vue.ts +9 -25
- package/dist/runtime/components/Labeler.vue +32 -25
- package/dist/runtime/components/Labeler.vue.d.ts +9 -25
- package/dist/runtime/components/Layout.vue +5 -1
- package/dist/runtime/components/LoadingError.d.vue.ts +13 -0
- package/dist/runtime/components/LoadingError.vue +27 -0
- package/dist/runtime/components/LoadingError.vue.d.ts +13 -0
- package/dist/runtime/components/Message.d.vue.ts +8 -1
- package/dist/runtime/components/Message.vue +48 -17
- package/dist/runtime/components/Message.vue.d.ts +8 -1
- package/dist/runtime/components/NavLink.vue +6 -3
- package/dist/runtime/components/Paginator.vue +1 -3
- package/dist/runtime/components/Select.vue +39 -39
- package/dist/runtime/components/SelectButton.vue +46 -40
- package/dist/runtime/components/SimpleForm.vue +46 -11
- package/dist/runtime/components/TabbedPage.vue +5 -7
- package/dist/runtime/components/Validation.d.vue.ts +17 -0
- package/dist/runtime/components/Validation.vue +48 -0
- package/dist/runtime/components/Validation.vue.d.ts +17 -0
- package/dist/runtime/composables/useBakeError.d.ts +13 -0
- package/dist/runtime/composables/useBakeError.js +51 -0
- package/dist/runtime/composables/useContext.d.ts +14 -0
- package/dist/runtime/composables/useContext.js +77 -2
- package/dist/runtime/composables/useDataMounter.d.ts +4 -2
- package/dist/runtime/composables/useDataMounter.js +15 -6
- package/dist/runtime/composables/useDefaultValidation.d.ts +4 -0
- package/dist/runtime/composables/useDefaultValidation.js +37 -0
- package/dist/runtime/composables/useRedirect.d.ts +1 -1
- package/dist/runtime/composables/useRedirect.js +7 -1
- package/dist/runtime/composables/useValidation.d.ts +15 -0
- package/dist/runtime/composables/useValidation.js +79 -0
- package/dist/runtime/plugins/errorHandling.js +2 -2
- package/package.json +14 -14
|
@@ -7,14 +7,28 @@ export default function _default(): {
|
|
|
7
7
|
};
|
|
8
8
|
injectDataDescriptor: () => null;
|
|
9
9
|
provideDataDescriptor: (value: any) => void;
|
|
10
|
+
injectError: ({ handle }?: {
|
|
11
|
+
handle?: boolean | undefined;
|
|
12
|
+
}) => import("vue").Ref<{}, {}>;
|
|
13
|
+
provideError: (error: any) => import("vue").Ref<boolean, boolean>;
|
|
10
14
|
injectEvents: () => any;
|
|
11
15
|
provideEvents: (value: any) => void;
|
|
12
16
|
injectExecuting: () => import("vue").Ref<boolean, boolean>;
|
|
13
17
|
provideExecuting: (value: any) => void;
|
|
14
18
|
injectLoading: () => import("vue").Ref<boolean, boolean>;
|
|
15
19
|
provideLoading: (value: any) => void;
|
|
20
|
+
injectMutableValidations: () => {};
|
|
21
|
+
provideMutableValidations: (value: any) => void;
|
|
22
|
+
injectMutableValidation: () => null;
|
|
23
|
+
provideMutableValidation: (value: any) => void;
|
|
16
24
|
injectPageContext: () => any;
|
|
17
25
|
providePageContext: (value: any) => void;
|
|
18
26
|
injectParentContext: () => null;
|
|
19
27
|
provideParentContext: (value: any) => void;
|
|
28
|
+
injectValidations: () => null;
|
|
29
|
+
provideValidations: (value: any) => void;
|
|
30
|
+
injectValidation: () => null;
|
|
31
|
+
provideValidation: (value: any) => void;
|
|
32
|
+
injectValidationHandled: () => boolean;
|
|
33
|
+
provideValidationHandled: (value: any) => void;
|
|
20
34
|
};
|
|
@@ -24,6 +24,29 @@ export default function() {
|
|
|
24
24
|
return provide("__bake_data_descriptor", value);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
function injectError({ handle = false } = {}) {
|
|
28
|
+
const { error, handled } = inject("__bake_error", {
|
|
29
|
+
error: ref({}),
|
|
30
|
+
handled: ref(false)
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
if(handle) {
|
|
34
|
+
handled.value = true;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return error;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function provideError(error) {
|
|
41
|
+
const handled = ref(false);
|
|
42
|
+
provide("__bake_error", {
|
|
43
|
+
error,
|
|
44
|
+
handled
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return handled;
|
|
48
|
+
}
|
|
49
|
+
|
|
27
50
|
function injectEvents() {
|
|
28
51
|
return inject("__bake_events");
|
|
29
52
|
}
|
|
@@ -48,6 +71,22 @@ export default function() {
|
|
|
48
71
|
provide("__bake_loading", value);
|
|
49
72
|
}
|
|
50
73
|
|
|
74
|
+
function injectMutableValidations() {
|
|
75
|
+
return inject("__bake_mutable_validations", {});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function provideMutableValidations(value) {
|
|
79
|
+
provide("__bake_mutable_validations", value);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function injectMutableValidation() {
|
|
83
|
+
return inject("__bake_mutable_validation", null);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function provideMutableValidation(value) {
|
|
87
|
+
provide("__bake_mutable_validation", value);
|
|
88
|
+
}
|
|
89
|
+
|
|
51
90
|
function injectPageContext() {
|
|
52
91
|
return inject("__bake_page_context");
|
|
53
92
|
}
|
|
@@ -64,21 +103,57 @@ export default function() {
|
|
|
64
103
|
provide("__bake_parent_context", value);
|
|
65
104
|
}
|
|
66
105
|
|
|
106
|
+
function injectValidations() {
|
|
107
|
+
return inject("__bake_validations", null);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function provideValidations(value) {
|
|
111
|
+
provide("__bake_validations", value);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function injectValidation() {
|
|
115
|
+
return inject("__bake_validation", null);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function provideValidation(value) {
|
|
119
|
+
provide("__bake_validation", value);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function injectValidationHandled() {
|
|
123
|
+
return inject("__bake_validation_handled", false);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function provideValidationHandled(value) {
|
|
127
|
+
provide("__bake_validation_handled", value);
|
|
128
|
+
}
|
|
129
|
+
|
|
67
130
|
return {
|
|
68
131
|
injectPath,
|
|
69
132
|
providePath,
|
|
70
133
|
injectContextData,
|
|
71
134
|
injectDataDescriptor,
|
|
72
135
|
provideDataDescriptor,
|
|
136
|
+
injectError,
|
|
137
|
+
provideError,
|
|
73
138
|
injectEvents,
|
|
74
139
|
provideEvents,
|
|
75
140
|
injectExecuting,
|
|
76
141
|
provideExecuting,
|
|
77
142
|
injectLoading,
|
|
78
143
|
provideLoading,
|
|
144
|
+
injectMutableValidations,
|
|
145
|
+
provideMutableValidations,
|
|
146
|
+
injectMutableValidation,
|
|
147
|
+
provideMutableValidation,
|
|
79
148
|
injectPageContext,
|
|
80
149
|
providePageContext,
|
|
81
150
|
injectParentContext,
|
|
82
|
-
provideParentContext
|
|
151
|
+
provideParentContext,
|
|
152
|
+
injectValidations,
|
|
153
|
+
provideValidations,
|
|
154
|
+
injectValidation,
|
|
155
|
+
provideValidation,
|
|
156
|
+
injectValidationHandled,
|
|
157
|
+
provideValidationHandled
|
|
83
158
|
};
|
|
84
|
-
}
|
|
159
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
export default function _default(): {
|
|
2
|
-
mount: (schema: any
|
|
1
|
+
export default function _default({ defaultInlineError }?: {}): {
|
|
2
|
+
mount: (schema: any, { inlineError }?: {
|
|
3
|
+
inlineError?: any;
|
|
4
|
+
}) => import("vue").Ref<null, null>;
|
|
3
5
|
onAfterMount: (callback: any) => void;
|
|
4
6
|
onBeforeMount: (callback: any) => void;
|
|
5
7
|
};
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { onMounted, ref } from "vue";
|
|
2
2
|
import { useContext, useDataFetcher } from "#imports";
|
|
3
3
|
|
|
4
|
-
export default function() {
|
|
4
|
+
export default function({ defaultInlineError } = {}) {
|
|
5
5
|
const context = useContext();
|
|
6
6
|
const dataFetcher = useDataFetcher();
|
|
7
|
+
|
|
7
8
|
const contextData = context.injectContextData();
|
|
9
|
+
const error = context.injectError();
|
|
10
|
+
|
|
8
11
|
const mounts = [];
|
|
9
12
|
let onAfterMountCallback = null;
|
|
10
13
|
let onBeforeMountCallback = null;
|
|
@@ -15,8 +18,14 @@ export default function() {
|
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
await Promise.all(
|
|
18
|
-
mounts.map(async({ value, schema }) => {
|
|
19
|
-
|
|
21
|
+
mounts.map(async({ value, schema, inlineError }) => {
|
|
22
|
+
try {
|
|
23
|
+
value.value = await dataFetcher.fetch({ data: schema, contextData });
|
|
24
|
+
} catch (err) {
|
|
25
|
+
if(!inlineError || err.statusCode !== 400) { throw err; }
|
|
26
|
+
|
|
27
|
+
error.value = err;
|
|
28
|
+
}
|
|
20
29
|
})
|
|
21
30
|
);
|
|
22
31
|
|
|
@@ -25,7 +34,7 @@ export default function() {
|
|
|
25
34
|
}
|
|
26
35
|
});
|
|
27
36
|
|
|
28
|
-
function mount(schema) {
|
|
37
|
+
function mount(schema, { inlineError = defaultInlineError } = {}) {
|
|
29
38
|
const value = ref(null);
|
|
30
39
|
|
|
31
40
|
if(!schema) {
|
|
@@ -36,7 +45,7 @@ export default function() {
|
|
|
36
45
|
|
|
37
46
|
const shouldLoad = dataFetcher.shouldLoad(schema);
|
|
38
47
|
if(shouldLoad) {
|
|
39
|
-
mounts.push({ value, schema });
|
|
48
|
+
mounts.push({ value, schema, inlineError });
|
|
40
49
|
}
|
|
41
50
|
|
|
42
51
|
return value;
|
|
@@ -55,4 +64,4 @@ export default function() {
|
|
|
55
64
|
onAfterMount,
|
|
56
65
|
onBeforeMount
|
|
57
66
|
};
|
|
58
|
-
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { useLocalization } from "#imports";
|
|
2
|
+
|
|
3
|
+
const capitalize = str => str.charAt(0).toUpperCase() + str.slice(1);
|
|
4
|
+
|
|
5
|
+
export default function useDefaultValidation({ inputs, model }) {
|
|
6
|
+
const { localize: lc } = useLocalization({ group: "useDefaultValidation" });
|
|
7
|
+
const { localize: l } = useLocalization();
|
|
8
|
+
|
|
9
|
+
const result = {};
|
|
10
|
+
const validation = {
|
|
11
|
+
valid: false,
|
|
12
|
+
persist: false,
|
|
13
|
+
severity: "error",
|
|
14
|
+
message: ""
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
inputs.map(input => {
|
|
18
|
+
const value = model?.[input.name];
|
|
19
|
+
const isEmpty = value === undefined || value === null || String(value).trim() === "";
|
|
20
|
+
const label = input.component?.schema?.label?.text || capitalize(input.name);
|
|
21
|
+
|
|
22
|
+
result[input.name] = {
|
|
23
|
+
...validation,
|
|
24
|
+
required: !!input.required,
|
|
25
|
+
valid: !input.required || !isEmpty,
|
|
26
|
+
persist: !(input.required && isEmpty)
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
if(input.required && isEmpty) {
|
|
30
|
+
result[input.name].message = lc("{label} cannot be empty", { label: l(label) });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return input;
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { useRouter } from "#app";
|
|
2
|
+
import { usePathBuilder } from "#imports";
|
|
2
3
|
|
|
3
4
|
export default function() {
|
|
4
5
|
const router = useRouter();
|
|
6
|
+
const { build } = usePathBuilder();
|
|
5
7
|
|
|
6
|
-
async function run({ route, back, expected, actual } = {}) {
|
|
8
|
+
async function run({ route, back, expected, actual, ...params } = {}) {
|
|
7
9
|
if(expected && actual !== expected) { return; }
|
|
8
10
|
|
|
11
|
+
if(route && Object.keys(params).length > 0) {
|
|
12
|
+
route = build(route, params, { forRoute: true });
|
|
13
|
+
}
|
|
14
|
+
|
|
9
15
|
if(back) {
|
|
10
16
|
router.back();
|
|
11
17
|
} else {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default function _default(): {
|
|
2
|
+
validate: ({ model, inputs, composables }: {
|
|
3
|
+
model: any;
|
|
4
|
+
inputs: any;
|
|
5
|
+
composables?: never[] | undefined;
|
|
6
|
+
}) => {
|
|
7
|
+
isValid: import("vue").ComputedRef<boolean>;
|
|
8
|
+
messages: import("vue").ComputedRef<string>;
|
|
9
|
+
validations: import("vue").ComputedRef<any>;
|
|
10
|
+
};
|
|
11
|
+
injectMutable: () => {
|
|
12
|
+
clear: () => void;
|
|
13
|
+
setError: (message: any) => void;
|
|
14
|
+
} | null;
|
|
15
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { computed } from "vue";
|
|
2
|
+
import { useContext, useComposableResolver } from "#imports";
|
|
3
|
+
|
|
4
|
+
export default function() {
|
|
5
|
+
const context = useContext();
|
|
6
|
+
const composableResolver = useComposableResolver();
|
|
7
|
+
|
|
8
|
+
function validate({ model, inputs, composables = [] }) {
|
|
9
|
+
const validationComposables = ["useDefaultValidation", ...composables.map(c => c.name)].map(vc => {
|
|
10
|
+
try {
|
|
11
|
+
return composableResolver.resolve(vc).default;
|
|
12
|
+
} catch {
|
|
13
|
+
console.error(`${vc} not loaded`);
|
|
14
|
+
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
}).filter(v => v);
|
|
18
|
+
|
|
19
|
+
const validations = computed(() =>
|
|
20
|
+
validationComposables.reduce((acc, useComposable) => ({
|
|
21
|
+
...acc,
|
|
22
|
+
...useComposable({
|
|
23
|
+
inputs,
|
|
24
|
+
model: model.value
|
|
25
|
+
})
|
|
26
|
+
}), {})
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const mutableValidations = {};
|
|
30
|
+
|
|
31
|
+
context.provideValidations(validations);
|
|
32
|
+
context.provideMutableValidations(mutableValidations);
|
|
33
|
+
|
|
34
|
+
const isValid = computed(() =>
|
|
35
|
+
Object.values(validations.value).every(v => v.valid) &&
|
|
36
|
+
Object.values(mutableValidations).every(v => v.value.valid)
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const messages = computed(() =>
|
|
40
|
+
[...Object.values(validations.value), ...Object.values(mutableValidations).map(v => v.value)]
|
|
41
|
+
.filter(v => v.message)
|
|
42
|
+
.map((v, i) => `${i > 0 ? "\n" : ""} - ${v.message}`)
|
|
43
|
+
.join("")
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
return { isValid, messages, validations };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function injectMutable() {
|
|
50
|
+
const ref = context.injectMutableValidation();
|
|
51
|
+
if(!ref) { return null; }
|
|
52
|
+
|
|
53
|
+
return MutableValidation(ref);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
validate,
|
|
58
|
+
injectMutable
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function MutableValidation(ref) {
|
|
63
|
+
function clear() {
|
|
64
|
+
ref.value.valid = true;
|
|
65
|
+
delete ref.value.message;
|
|
66
|
+
delete ref.value.severity;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function setError(message) {
|
|
70
|
+
ref.value.valid = false;
|
|
71
|
+
ref.value.message = message;
|
|
72
|
+
ref.value.severity = "error";
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
clear,
|
|
77
|
+
setError
|
|
78
|
+
};
|
|
79
|
+
}
|
|
@@ -83,7 +83,7 @@ function getMessage(error) {
|
|
|
83
83
|
severity: "error",
|
|
84
84
|
summary: error.data?.title ?? error.statusCode ?? l($defaultAlert.title),
|
|
85
85
|
detail: error.data?.detail ?? error.message ?? error.cause ?? l($defaultAlert.message),
|
|
86
|
-
|
|
86
|
+
wide: true
|
|
87
87
|
};
|
|
88
88
|
}
|
|
89
89
|
|
|
@@ -91,6 +91,6 @@ function getMessage(error) {
|
|
|
91
91
|
severity: "error",
|
|
92
92
|
summary: error.statusCode ?? error.status ?? l($defaultAlert.title),
|
|
93
93
|
detail: error.message ?? error.cause ?? l($defaultAlert.message),
|
|
94
|
-
|
|
94
|
+
wide: true
|
|
95
95
|
};
|
|
96
96
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mouseless/baked",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"description": "Baked is an opinionated framework for .NET and Vue. This is the UI package of Baked project.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"baked",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"lint": "eslint ."
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@nuxtjs/i18n": "10.
|
|
46
|
+
"@nuxtjs/i18n": "10.4.0",
|
|
47
47
|
"@nuxtjs/tailwindcss": "6.14.0",
|
|
48
48
|
"@primeuix/themes": "2.0.3",
|
|
49
49
|
"async-mutex": "0.5.0",
|
|
@@ -54,21 +54,21 @@
|
|
|
54
54
|
"@nuxt/eslint": "1.15.2",
|
|
55
55
|
"@nuxt/module-builder": "1.0.2",
|
|
56
56
|
"@nuxt/test-utils": "4.0.3",
|
|
57
|
-
"@playwright/test": "1.
|
|
58
|
-
"@types/node": "25.
|
|
57
|
+
"@playwright/test": "1.60.0",
|
|
58
|
+
"@types/node": "25.9.1",
|
|
59
59
|
"changelogen": "0.6.2",
|
|
60
|
-
"nuxt": "4.4.
|
|
61
|
-
"sass": "1.
|
|
60
|
+
"nuxt": "4.4.6",
|
|
61
|
+
"sass": "1.100.0",
|
|
62
62
|
"typescript": "6.0.3",
|
|
63
|
-
"vitest": "4.1.
|
|
64
|
-
"vue-tsc": "3.2
|
|
63
|
+
"vitest": "4.1.7",
|
|
64
|
+
"vue-tsc": "3.3.2"
|
|
65
65
|
},
|
|
66
66
|
"optionalDependencies": {
|
|
67
|
-
"@oxc-parser/binding-darwin-arm64": "0.
|
|
68
|
-
"@oxc-parser/binding-linux-arm64-gnu": "0.
|
|
69
|
-
"@oxc-parser/binding-linux-arm64-musl": "0.
|
|
70
|
-
"@oxc-parser/binding-linux-x64-gnu": "0.
|
|
71
|
-
"@oxc-parser/binding-linux-x64-musl": "0.
|
|
72
|
-
"@oxc-parser/binding-win32-x64-msvc": "0.
|
|
67
|
+
"@oxc-parser/binding-darwin-arm64": "0.133.0",
|
|
68
|
+
"@oxc-parser/binding-linux-arm64-gnu": "0.133.0",
|
|
69
|
+
"@oxc-parser/binding-linux-arm64-musl": "0.133.0",
|
|
70
|
+
"@oxc-parser/binding-linux-x64-gnu": "0.133.0",
|
|
71
|
+
"@oxc-parser/binding-linux-x64-musl": "0.133.0",
|
|
72
|
+
"@oxc-parser/binding-win32-x64-msvc": "0.133.0"
|
|
73
73
|
}
|
|
74
74
|
}
|