create-nuxt-base 0.1.22 → 0.2.0
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/.eslintignore +14 -0
- package/.eslintrc +3 -0
- package/.github/workflows/release.yml +1 -1
- package/.prettierignore +5 -0
- package/.prettierrc +6 -0
- package/.vscode/settings.json +10 -10
- package/CHANGELOG.md +22 -33
- package/README.md +1 -0
- package/index.js +30 -29
- package/nuxt-base-template/.env.example +8 -0
- package/nuxt-base-template/.eslintrc +1 -1
- package/nuxt-base-template/.vscode/settings.json +6 -16
- package/nuxt-base-template/nuxt.config.ts +27 -12
- package/nuxt-base-template/package-lock.json +5772 -4389
- package/nuxt-base-template/package.json +45 -29
- package/nuxt-base-template/src/app.vue +4 -4
- package/nuxt-base-template/src/assets/css/tailwind.css +42 -33
- package/nuxt-base-template/src/components/SocialMediaBubble.vue +1 -1
- package/nuxt-base-template/src/components/base/BaseAccordion.vue +16 -13
- package/nuxt-base-template/src/components/base/BaseButton.vue +10 -13
- package/nuxt-base-template/src/components/base/BaseContainer.vue +1 -1
- package/nuxt-base-template/src/components/base/BaseContextMenuContainer.vue +61 -0
- package/nuxt-base-template/src/components/base/BaseInfinityList.vue +1 -1
- package/nuxt-base-template/src/components/base/BaseProgressbar.vue +28 -30
- package/nuxt-base-template/src/components/base/BaseToggle.vue +17 -10
- package/nuxt-base-template/src/components/form/FormInput.vue +34 -0
- package/nuxt-base-template/src/components/form/FormPassword.vue +47 -0
- package/nuxt-base-template/src/components/form/FormSelect.vue +40 -0
- package/nuxt-base-template/src/components/form/FormSubmit.vue +18 -0
- package/nuxt-base-template/src/components/form/FormTextarea.vue +36 -0
- package/nuxt-base-template/src/components/form/FormToggle.vue +27 -0
- package/nuxt-base-template/src/components/modal/Modal.vue +48 -0
- package/nuxt-base-template/src/components/modal/ModalConfirm.vue +38 -0
- package/nuxt-base-template/src/components/{base/BaseModalContainer.vue → modal/ModalContainer.vue} +1 -1
- package/nuxt-base-template/src/components/modal/ModalInfo.vue +22 -0
- package/nuxt-base-template/src/components/{ModalShare.vue → modal/ModalShare.vue} +7 -11
- package/nuxt-base-template/src/components/{base/BaseNotification.vue → notification/Notification.vue} +8 -7
- package/nuxt-base-template/src/components/{base/BaseNotificationContainer.vue → notification/NotificationContainer.vue} +11 -5
- package/nuxt-base-template/src/components/pwa/pwa-install-banner.vue +224 -0
- package/nuxt-base-template/src/components/transition/TransitionFade.vue +10 -7
- package/nuxt-base-template/src/components/transition/TransitionFadeScale.vue +10 -7
- package/nuxt-base-template/src/composables/use-auth-fetch.ts +22 -8
- package/nuxt-base-template/src/composables/use-context-menu.ts +19 -0
- package/nuxt-base-template/src/composables/use-file.ts +1 -2
- package/nuxt-base-template/src/composables/use-modal.ts +1 -1
- package/nuxt-base-template/src/composables/use-notification.ts +2 -2
- package/nuxt-base-template/src/composables/use-share.ts +3 -3
- package/nuxt-base-template/src/error.vue +10 -14
- package/nuxt-base-template/src/layouts/default.vue +13 -0
- package/nuxt-base-template/src/middleware/auth.global.ts +2 -2
- package/nuxt-base-template/src/pages/index.vue +31 -6
- package/nuxt-base-template/src/plugins/auth.server.ts +72 -0
- package/nuxt-base-template/src/plugins/form.plugin.ts +21 -0
- package/nuxt-base-template/src/plugins/pwa.plugin.ts +110 -0
- package/nuxt-base-template/src/tests/init.test.ts +1 -1
- package/nuxt-base-template/tailwind.config.js +33 -23
- package/nuxt-base-template/vitest.config.js +3 -3
- package/package.json +3 -1
- package/nuxt-base-template/formkit-theme.js +0 -137
- package/nuxt-base-template/formkit.config.js +0 -35
- package/nuxt-base-template/src/components/hello-world.vue +0 -10
- package/nuxt-base-template/src/composables/use-form-helper.ts +0 -100
- package/nuxt-base-template/src/composables/use-helper.ts +0 -52
- package/nuxt-base-template/src/forms/inputs/InputCheckbox.vue +0 -29
- package/nuxt-base-template/src/forms/inputs/InputFreeTags.vue +0 -98
- package/nuxt-base-template/src/forms/inputs/InputImage.vue +0 -65
- package/nuxt-base-template/src/forms/inputs/InputTags.vue +0 -112
- package/nuxt-base-template/src/forms/inputs/InputToggle.vue +0 -18
- package/nuxt-base-template/src/forms/plugins/asterisk-plugin.ts +0 -29
- package/nuxt-base-template/src/forms/plugins/scroll-error-plugin.ts +0 -36
- package/nuxt-base-template/src/forms/plugins/value-changes-plugin.ts +0 -13
- package/nuxt-base-template/src/plugins/4.auth.server.ts +0 -70
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import type { FormKitEventListener, FormKitNode } from '@formkit/core';
|
|
2
|
-
import { getNode, submitForm as submitFormkit } from '@formkit/core';
|
|
3
|
-
|
|
4
|
-
export function useFormHelper() {
|
|
5
|
-
function prepareNode(node: FormKitNode | undefined, listener: FormKitEventListener, eventName: string = 'input') {
|
|
6
|
-
if (node?.on) {
|
|
7
|
-
node.on(eventName, listener);
|
|
8
|
-
}
|
|
9
|
-
node?.children?.forEach((childNode) => {
|
|
10
|
-
prepareNode(childNode as FormKitNode, listener, eventName);
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
async function valueChanges(nodeId: string, listener: FormKitEventListener, eventName: string = 'input') {
|
|
15
|
-
// Puffer to load form
|
|
16
|
-
await new Promise<void>((resolve) => setTimeout(() => {
|
|
17
|
-
resolve();
|
|
18
|
-
}, 1));
|
|
19
|
-
prepareNode(getNode(nodeId), listener, eventName);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function submitForm(formId: string) {
|
|
23
|
-
submitFormkit(formId);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function isDirty(formId: string): boolean {
|
|
27
|
-
const form = getNode(formId);
|
|
28
|
-
|
|
29
|
-
return !!form?.context?.state?.dirty;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async function getChangedValue(formId: string): Promise<null | object> {
|
|
33
|
-
await new Promise(f => setTimeout(f, 100));
|
|
34
|
-
const form = getNode(formId);
|
|
35
|
-
|
|
36
|
-
if (!form) {
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
return getDifferences(form?.props?.initial, form?.value);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function getDifferences(obj1: any, obj2: any): any {
|
|
43
|
-
if (obj1 === obj2) {
|
|
44
|
-
return undefined;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (typeof obj1 !== typeof obj2) {
|
|
48
|
-
return obj2;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (typeof obj1 !== 'object' || obj1 === null || obj2 === null) {
|
|
52
|
-
return obj2;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (Array.isArray(obj1) !== Array.isArray(obj2)) {
|
|
56
|
-
return obj2;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (Array.isArray(obj1)) {
|
|
60
|
-
if (obj1.length !== obj2.length) {
|
|
61
|
-
return obj2;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const diffArray: any[] = [];
|
|
65
|
-
|
|
66
|
-
for (let i = 0; i < obj1.length; i++) {
|
|
67
|
-
const itemDiff = getDifferences(obj1[i], obj2[i]);
|
|
68
|
-
if (itemDiff !== undefined) {
|
|
69
|
-
diffArray[i] = itemDiff;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (diffArray.length === 0) {
|
|
74
|
-
return undefined;
|
|
75
|
-
} else {
|
|
76
|
-
return diffArray;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const keys1 = Object.keys(obj1);
|
|
81
|
-
const keys2 = Object.keys(obj2);
|
|
82
|
-
|
|
83
|
-
const allKeys = new Set([...keys1, ...keys2]);
|
|
84
|
-
const diffObj: any = {};
|
|
85
|
-
for (const key of allKeys) {
|
|
86
|
-
const keyDiff = getDifferences(obj1[key], obj2[key]);
|
|
87
|
-
if (keyDiff !== undefined) {
|
|
88
|
-
diffObj[key] = keyDiff;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (Object.keys(diffObj).length === 0) {
|
|
93
|
-
return undefined;
|
|
94
|
-
} else {
|
|
95
|
-
return diffObj;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return { getChangedValue, getDifferences, isDirty, submitForm, prepareNode, valueChanges };
|
|
100
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
export function useHelper() {
|
|
2
|
-
function removeFields(obj: any): any {
|
|
3
|
-
const ignoreFields = ['id', '__typename'];
|
|
4
|
-
if (Array.isArray(obj)) {
|
|
5
|
-
return obj.map(item => removeFields(item));
|
|
6
|
-
} else if (typeof obj === 'object' && obj !== null) {
|
|
7
|
-
const newObj: any = {};
|
|
8
|
-
for (const key in obj) {
|
|
9
|
-
if (!ignoreFields.includes(key)) {
|
|
10
|
-
newObj[key] = removeFields(obj[key]);
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
return newObj;
|
|
14
|
-
} else {
|
|
15
|
-
return obj;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function removeNullOrUndefined(obj: any): any {
|
|
20
|
-
if (Array.isArray(obj)) {
|
|
21
|
-
return obj.map(item => removeNullOrUndefined(item)).filter(item => item !== null && item !== undefined);
|
|
22
|
-
} else if (typeof obj === 'object' && obj !== null) {
|
|
23
|
-
const newObj: any = {};
|
|
24
|
-
for (const key in obj) {
|
|
25
|
-
const value = removeNullOrUndefined(obj[key]);
|
|
26
|
-
if (value !== null && value !== undefined) {
|
|
27
|
-
newObj[key] = value;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return newObj;
|
|
31
|
-
} else {
|
|
32
|
-
return obj;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function isValidMongoID(id: string): boolean {
|
|
37
|
-
const validHexChars = /^[0-9a-fA-F]{24}$/;
|
|
38
|
-
|
|
39
|
-
return validHexChars.test(id);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function groupBy<T>(arr: T[], fn: (item: T) => any) {
|
|
43
|
-
return arr.reduce<Record<string, T[]>>((prev, curr) => {
|
|
44
|
-
const groupKey = fn(curr);
|
|
45
|
-
const group = prev[groupKey] || [];
|
|
46
|
-
group.push(curr);
|
|
47
|
-
return { ...prev, [groupKey]: group };
|
|
48
|
-
}, {});
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return { removeFields, removeNullOrUndefined, isValidMongoID, groupBy };
|
|
52
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
const props = defineProps({
|
|
3
|
-
context: Object,
|
|
4
|
-
});
|
|
5
|
-
const name = props.context.name;
|
|
6
|
-
const label = props.context.attrs.text;
|
|
7
|
-
|
|
8
|
-
function setInput(e) {
|
|
9
|
-
props.context.node.input(e.target.checked);
|
|
10
|
-
}
|
|
11
|
-
</script>
|
|
12
|
-
|
|
13
|
-
<template>
|
|
14
|
-
<div :class="context.classes.container">
|
|
15
|
-
<label :id="props.context?.id" :class="context.classes.wrapper">
|
|
16
|
-
<div :class="context.classes.inner">
|
|
17
|
-
<input :class="context.classes.input" type="checkbox" :name="name" @input="setInput" />
|
|
18
|
-
<span :class="context.classes.decorator" aria-hidden="true">
|
|
19
|
-
<span :class="[context.classes.decoratorIcon, context.classes.icon]">
|
|
20
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 27">
|
|
21
|
-
<polygon fill="currentColor" points="26.99 0 10.13 17.17 4.69 11.63 0 16.41 10.4 27 15.05 22.27 15.09 22.31 32 5.1 26.99 0" />
|
|
22
|
-
</svg>
|
|
23
|
-
</span>
|
|
24
|
-
</span>
|
|
25
|
-
</div>
|
|
26
|
-
</label>
|
|
27
|
-
<span :class="context.classes.label" v-html="label"></span>
|
|
28
|
-
</div>
|
|
29
|
-
</template>
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
const props = defineProps({
|
|
3
|
-
context: Object,
|
|
4
|
-
});
|
|
5
|
-
|
|
6
|
-
const inputRef = ref();
|
|
7
|
-
const inputValue = ref<string>();
|
|
8
|
-
const name = props.context?.name;
|
|
9
|
-
const placeholder = props.context?.attrs?.placeholder;
|
|
10
|
-
const defaultSuggestions = [...props.context?.attrs?.suggestions] || [];
|
|
11
|
-
const suggestions = ref<string[]>([...props.context?.attrs?.suggestions]);
|
|
12
|
-
const tags = ref<string[]>([]);
|
|
13
|
-
patch();
|
|
14
|
-
|
|
15
|
-
function patch() {
|
|
16
|
-
const values = props.context?._value;
|
|
17
|
-
if (values && Array.isArray(values)) {
|
|
18
|
-
for (let item of values) {
|
|
19
|
-
addTag(item);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
function addTag(tag?: string) {
|
|
26
|
-
if (!inputValue.value && !tag) {
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (tag) {
|
|
31
|
-
tags.value.push(tag);
|
|
32
|
-
// remove tag from suggestions
|
|
33
|
-
if (suggestions.value.findIndex((e) => e === tag) >= 0) {
|
|
34
|
-
suggestions.value.splice(suggestions.value.findIndex((e) => e === tag), 1);
|
|
35
|
-
}
|
|
36
|
-
} else {
|
|
37
|
-
if (!tags.value.includes(inputValue.value as string)) {
|
|
38
|
-
tags.value.push(inputValue.value as string);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (suggestions.value.includes(inputValue.value as string)) {
|
|
42
|
-
if (suggestions.value.findIndex((e) => e === tag) >= 0) {
|
|
43
|
-
suggestions.value.splice(suggestions.value.findIndex((e) => e === inputValue.value), 1);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
inputValue.value = '';
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
props.context?.node.input(tags.value);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function removeTag(tag: string) {
|
|
54
|
-
tags.value.splice(tags.value.findIndex((e) => e === tag), 1);
|
|
55
|
-
|
|
56
|
-
if (defaultSuggestions.includes(tag) && !suggestions.value.includes(tag)) {
|
|
57
|
-
suggestions.value.push(tag);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
props.context?.node.input(tags.value);
|
|
61
|
-
}
|
|
62
|
-
</script>
|
|
63
|
-
|
|
64
|
-
<template>
|
|
65
|
-
<div class="flex flex-col">
|
|
66
|
-
<div class="flex flex-row flex-wrap gap-2">
|
|
67
|
-
<span v-for="(tag, index) of tags" :key="index" :class="context?.classes.tag">{{ tag }} <span class="i-bi-x" :class="context?.classes.tagIcon" @click="removeTag(tag)"></span> </span>
|
|
68
|
-
</div>
|
|
69
|
-
<div :class="context?.classes.inputWrapper">
|
|
70
|
-
<input
|
|
71
|
-
:id="props.context?.id"
|
|
72
|
-
ref="inputRef"
|
|
73
|
-
v-model="inputValue"
|
|
74
|
-
:name="name"
|
|
75
|
-
type="text"
|
|
76
|
-
:placeholder="placeholder"
|
|
77
|
-
:class="context?.classes.input"
|
|
78
|
-
@keydown.enter.prevent="addTag()"
|
|
79
|
-
/>
|
|
80
|
-
<span :class="context?.classes.inputIcon"></span>
|
|
81
|
-
</div>
|
|
82
|
-
<div>
|
|
83
|
-
<button type="button" :disabled="!inputValue" :class="context?.classes.button" @click="addTag()">
|
|
84
|
-
{{ context?.attrs.buttonText ?? context?.attrs['button-text'] }}
|
|
85
|
-
</button>
|
|
86
|
-
</div>
|
|
87
|
-
<div v-if="suggestions?.length" :class="context?.classes.suggestionsWrapper">
|
|
88
|
-
<p :class="context?.classes.suggestionsHeadline">
|
|
89
|
-
Beliebte Skills
|
|
90
|
-
</p>
|
|
91
|
-
<div class="flex flex-row flex-wrap gap-2">
|
|
92
|
-
<div v-for="(tag, index) of suggestions" :key="index" :class="context?.classes.suggestionsTag">
|
|
93
|
-
{{ tag }} <span :class="context?.classes.suggestionsIcon" @click="addTag(tag)"></span>
|
|
94
|
-
</div>
|
|
95
|
-
</div>
|
|
96
|
-
</div>
|
|
97
|
-
</div>
|
|
98
|
-
</template>
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
const props = defineProps({
|
|
3
|
-
context: Object,
|
|
4
|
-
});
|
|
5
|
-
|
|
6
|
-
const value = ref('');
|
|
7
|
-
const label = props.context?.label;
|
|
8
|
-
const text = props.context?.attrs.text;
|
|
9
|
-
const name = props.context?.name;
|
|
10
|
-
const config = useRuntimeConfig();
|
|
11
|
-
await getPreviewUrl(props.context?._value);
|
|
12
|
-
|
|
13
|
-
watch(() => props.context?._value, async () => {
|
|
14
|
-
await getPreviewUrl(props.context?._value);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
function handleInput(e: any) {
|
|
18
|
-
if (!e) {
|
|
19
|
-
props.context?.node.input(null);
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
props.context?.node.input(e.target.files[0]);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
async function getPreviewUrl(src: string | File) {
|
|
27
|
-
if (!src) {
|
|
28
|
-
value.value = '';
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const validHexChars = /^[0-9a-fA-F]{24}$/;
|
|
32
|
-
if (typeof src === 'object' && validHexChars.test(src?.id)) {
|
|
33
|
-
// return config.public.apiUrl + '/files/' + src.id;
|
|
34
|
-
const response = await useAuthFetch(config.public.apiUrl + '/files/' + src?.id, { method: 'GET' });
|
|
35
|
-
value.value = URL.createObjectURL(response as any);
|
|
36
|
-
}
|
|
37
|
-
else if (typeof src === 'string') {
|
|
38
|
-
value.value = src;
|
|
39
|
-
} else if (src instanceof File) {
|
|
40
|
-
value.value = URL.createObjectURL(src);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
</script>
|
|
44
|
-
|
|
45
|
-
<template>
|
|
46
|
-
<div class="image-container" :class="props?.context?.classes.container">
|
|
47
|
-
<div v-if="!props.context?._value" :class="props?.context?.classes.uploader">
|
|
48
|
-
<label :class="context?.classes.placeholder">{{ text }}</label>
|
|
49
|
-
<input
|
|
50
|
-
:id="props.context?.id"
|
|
51
|
-
:name="name"
|
|
52
|
-
type="file"
|
|
53
|
-
:class="props?.context?.classes.input"
|
|
54
|
-
@input="handleInput"
|
|
55
|
-
/>
|
|
56
|
-
</div>
|
|
57
|
-
<div v-else :class="props?.context.classes.fileItem">
|
|
58
|
-
<img :src="value" :class="props?.context?.classes.fileItemImage" />
|
|
59
|
-
<span :class="props?.context.classes.fileItemImageName">{{ props.context?._value?.name ?? props.context?._value?.filename }}</span>
|
|
60
|
-
<button type="button" :class="props?.context.classes.fileItemRemove" @click="handleInput(null)">
|
|
61
|
-
Entfernen
|
|
62
|
-
</button>
|
|
63
|
-
</div>
|
|
64
|
-
</div>
|
|
65
|
-
</template>
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
interface TagOption {
|
|
3
|
-
label: string;
|
|
4
|
-
value: string;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
const props = defineProps({
|
|
8
|
-
context: Object,
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
const inputRef = ref();
|
|
12
|
-
const inputValue = ref<string>();
|
|
13
|
-
const inputFocus = ref();
|
|
14
|
-
const name = props.context?.name;
|
|
15
|
-
const placeholder = props.context?.attrs?.placeholder;
|
|
16
|
-
const tags = ref<TagOption[]>([]);
|
|
17
|
-
const defaultOptions: TagOption[] = [...props?.context?.attrs?.options] || [];
|
|
18
|
-
const selectOptions = ref<TagOption[]>([...props?.context?.attrs?.options]);
|
|
19
|
-
patch();
|
|
20
|
-
|
|
21
|
-
function patch() {
|
|
22
|
-
const values = props.context?._value;
|
|
23
|
-
if (!values) {
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (Array.isArray(values)) {
|
|
28
|
-
for (let item of values) {
|
|
29
|
-
handleSelect(item);
|
|
30
|
-
}
|
|
31
|
-
} else {
|
|
32
|
-
handleSelect(values);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function handleSelect(value: string) {
|
|
37
|
-
const tag = selectOptions.value.find((e) => e.value === value);
|
|
38
|
-
const included = tags.value?.find((e) => e.value === tag?.value) ?? false;
|
|
39
|
-
|
|
40
|
-
if (!tag) {
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (!included) {
|
|
45
|
-
tags.value.push(tag);
|
|
46
|
-
selectOptions.value = defaultOptions.filter((e) => e.value !== tag.value);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
props.context?.node.input(tags.value.map((e) => e.value));
|
|
50
|
-
inputValue.value = '';
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function handleInput(event: any) {
|
|
54
|
-
const value = event.target.value.toLowerCase();
|
|
55
|
-
|
|
56
|
-
if (!value) {
|
|
57
|
-
selectOptions.value = defaultOptions.filter((e: TagOption) => !props.context?._value?.includes(e.value));
|
|
58
|
-
} else {
|
|
59
|
-
selectOptions.value = selectOptions.value.filter((e: TagOption) => e.label.toLowerCase().includes(value));
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function removeTag(tag: TagOption) {
|
|
64
|
-
tags.value.splice(tags.value.findIndex((e) => e.value === tag.value), 1);
|
|
65
|
-
props.context?.node.input(tags.value.map((e) => e.value));
|
|
66
|
-
selectOptions.value = defaultOptions.filter((e: TagOption) => !props.context?._value?.includes(e.value));
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function setFocus() {
|
|
70
|
-
inputFocus.value = true;
|
|
71
|
-
inputValue.value = '';
|
|
72
|
-
selectOptions.value = defaultOptions.filter((e: TagOption) => !props.context?._value?.includes(e.value));
|
|
73
|
-
}
|
|
74
|
-
</script>
|
|
75
|
-
|
|
76
|
-
<template>
|
|
77
|
-
<div class="flex flex-col">
|
|
78
|
-
<div class="flex flex-row flex-wrap gap-2">
|
|
79
|
-
<span v-for="tag of tags" :key="tag.value" :class="context?.classes.tag">{{ tag.label }} <span class="i-bi-x" :class="context?.classes.tagIcon" @click="removeTag(tag)"></span> </span>
|
|
80
|
-
</div>
|
|
81
|
-
<div v-if="selectOptions.length" class="relative" :class="context?.classes.inputWrapper">
|
|
82
|
-
<div class="relative w-full flex items-center">
|
|
83
|
-
<input
|
|
84
|
-
:id="props.context?.id"
|
|
85
|
-
ref="inputRef"
|
|
86
|
-
v-model="inputValue"
|
|
87
|
-
autocomplete="off"
|
|
88
|
-
:name="name" type="text" :placeholder="placeholder" :class="context?.classes.input" @focus="setFocus"
|
|
89
|
-
@blur="inputFocus = false" @input="handleInput"
|
|
90
|
-
/>
|
|
91
|
-
<span :class="context?.classes.selectIcon" class="formkit-select-icon flex p-[3px] shrink-0 w-5 mr-2 -ml-[1.5em] h-full pointer-events-none formkit-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 7"><path d="M8,6.5c-.13,0-.26-.05-.35-.15L3.15,1.85c-.2-.2-.2-.51,0-.71,.2-.2,.51-.2,.71,0l4.15,4.15L12.15,1.15c.2-.2,.51-.2,.71,0,.2,.2,.2,.51,0,.71l-4.5,4.5c-.1,.1-.23,.15-.35,.15Z" fill="currentColor" /></svg></span>
|
|
92
|
-
</div>
|
|
93
|
-
<TransitionFade :start-duration="200" :leave-duration="200">
|
|
94
|
-
<div
|
|
95
|
-
v-show="inputFocus && defaultOptions.length"
|
|
96
|
-
class="absolute right-0 left-0 z-50 h-fit max-h-12 flex flex-col justify-start items-start"
|
|
97
|
-
:style="`top: ${inputRef?.clientHeight + 1}px`"
|
|
98
|
-
:class="context?.classes.dropdown"
|
|
99
|
-
>
|
|
100
|
-
<div v-if="selectOptions.length" class="w-full">
|
|
101
|
-
<button v-for="option of selectOptions" :key="option.value" :class="context?.classes.dropdownItem" @click="handleSelect(option.value)">
|
|
102
|
-
{{ option.label }}
|
|
103
|
-
</button>
|
|
104
|
-
</div>
|
|
105
|
-
<div v-else :class="context?.classes.noItemsFound">
|
|
106
|
-
Keine Einträge gefunden
|
|
107
|
-
</div>
|
|
108
|
-
</div>
|
|
109
|
-
</TransitionFade>
|
|
110
|
-
</div>
|
|
111
|
-
</div>
|
|
112
|
-
</template>
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
const props = defineProps({
|
|
3
|
-
context: Object,
|
|
4
|
-
});
|
|
5
|
-
const inputValue = ref(props?.context?._value ?? false);
|
|
6
|
-
|
|
7
|
-
watch(() => inputValue.value, () => {
|
|
8
|
-
props.context?.node.input(inputValue.value);
|
|
9
|
-
});
|
|
10
|
-
</script>
|
|
11
|
-
|
|
12
|
-
<template>
|
|
13
|
-
<div class="flex justify-between items-center" @click="inputValue = !inputValue">
|
|
14
|
-
<div class="w-8 h-5 flex items-center bg-gray-300 rounded-full p-1 duration-300 ease-in-out" :class="{ 'bg-green-400': inputValue }">
|
|
15
|
-
<div class="bg-white w-4 h-4 rounded-full shadow-md transform duration-300 ease-in-out" :class="{ 'translate-x-2': inputValue }"></div>
|
|
16
|
-
</div>
|
|
17
|
-
</div>
|
|
18
|
-
</template>
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
const isCheckboxAndRadioMultiple = (node: any) => (node.props.type === 'checkbox' || node.props.type === 'radio') && node.props.options;
|
|
2
|
-
|
|
3
|
-
export function addAsteriskPlugin(node: any) {
|
|
4
|
-
node.on('created', () => {
|
|
5
|
-
const isRequired = node.props.parsedRules.some((rule: any) => rule.name === 'required');
|
|
6
|
-
if (!isRequired) {
|
|
7
|
-
return;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const isMultiOption = isCheckboxAndRadioMultiple(node);
|
|
11
|
-
|
|
12
|
-
// if we're going to modify the schema then we need
|
|
13
|
-
// to update the schemaMemoKey so we don't get an
|
|
14
|
-
// invalid cached schema.
|
|
15
|
-
node.props.definition.schemaMemoKey = `required_${isMultiOption ? 'multi_' : ''}${node.props.definition.schemaMemoKey}`;
|
|
16
|
-
|
|
17
|
-
const schemaFn = node.props.definition.schema;
|
|
18
|
-
node.props.definition.schema = (sectionsSchema: any = {}) => {
|
|
19
|
-
if (isRequired) {
|
|
20
|
-
if (!isMultiOption) {
|
|
21
|
-
sectionsSchema.label = {
|
|
22
|
-
children: ['$label', ' *'],
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
return schemaFn(sectionsSchema);
|
|
27
|
-
};
|
|
28
|
-
});
|
|
29
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import type { FormKitNode } from '@formkit/core';
|
|
2
|
-
|
|
3
|
-
export function scrollToErrors(node: any) {
|
|
4
|
-
if (node.props.type === 'form') {
|
|
5
|
-
function scrollTo(node: FormKitNode) {
|
|
6
|
-
const el = document.getElementById(node.props.id);
|
|
7
|
-
if (el) {
|
|
8
|
-
el.scrollIntoView({ block: 'center', behavior: 'smooth' });
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function scrollToErrors() {
|
|
13
|
-
node.walk((child: any) => {
|
|
14
|
-
// Check if this child has errors
|
|
15
|
-
if (child.ledger.value('blocking') || child.ledger.value('errors')) {
|
|
16
|
-
// We found an input with validation errors
|
|
17
|
-
scrollTo(child);
|
|
18
|
-
// Stop searching
|
|
19
|
-
return false;
|
|
20
|
-
}
|
|
21
|
-
}, true);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function defaultInvalid(node: FormKitNode) {
|
|
25
|
-
console.error('FormKit::invalidForm', node);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const onSubmitInvalid = node.props.onSubmitInvalid ?? defaultInvalid;
|
|
29
|
-
node.props.onSubmitInvalid = () => {
|
|
30
|
-
onSubmitInvalid(node);
|
|
31
|
-
scrollToErrors();
|
|
32
|
-
};
|
|
33
|
-
node.on('unsettled:errors', scrollToErrors);
|
|
34
|
-
}
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { FormKitEvent, FormKitNode } from '@formkit/core';
|
|
2
|
-
|
|
3
|
-
export function valueChangesPlugin(node: FormKitNode) {
|
|
4
|
-
const { getDifferences } = useFormHelper();
|
|
5
|
-
if (node.props.type === 'form') {
|
|
6
|
-
setTimeout(() => {
|
|
7
|
-
node.on('commit', (event: FormKitEvent) => {
|
|
8
|
-
const diff = getDifferences(event.origin.props._init, event.payload);
|
|
9
|
-
node.emit('valueChanges', { init: event.origin.props._init, current: event.payload, diff: diff });
|
|
10
|
-
});
|
|
11
|
-
}, 300);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { callWithNuxt, defineNuxtPlugin, useNuxtApp, useRuntimeConfig } from 'nuxt/app';
|
|
2
|
-
import { ofetch } from 'ofetch';
|
|
3
|
-
|
|
4
|
-
export default defineNuxtPlugin({
|
|
5
|
-
name: 'auth-server',
|
|
6
|
-
enforce: 'pre',
|
|
7
|
-
async setup() {
|
|
8
|
-
console.debug('4.auth.server.ts::init');
|
|
9
|
-
const _nuxt = useNuxtApp();
|
|
10
|
-
const config = await callWithNuxt(_nuxt, useRuntimeConfig);
|
|
11
|
-
const { refreshToken, accessToken } = await callWithNuxt(_nuxt, useAuthState);
|
|
12
|
-
const { setCurrentUser, getDecodedAccessToken, setTokens } = await callWithNuxt(_nuxt, useAuth);
|
|
13
|
-
const payload = accessToken.value ? getDecodedAccessToken(accessToken.value) : null;
|
|
14
|
-
|
|
15
|
-
if (!refreshToken.value) {
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const refreshTokenResult = await ofetch(config.public.host, {
|
|
20
|
-
method: 'POST',
|
|
21
|
-
headers: {
|
|
22
|
-
Authorization: `Bearer ${refreshToken.value}`,
|
|
23
|
-
},
|
|
24
|
-
body: JSON.stringify({
|
|
25
|
-
query: 'mutation refreshToken {refreshToken {token, refreshToken}}',
|
|
26
|
-
variables: {},
|
|
27
|
-
}),
|
|
28
|
-
}).catch((err) => {
|
|
29
|
-
console.error('4.auth.server.ts::refreshToken::catch', err.data);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
console.debug('4.auth.server.ts::refreshTokenResult', refreshTokenResult);
|
|
33
|
-
|
|
34
|
-
if (refreshTokenResult?.data) {
|
|
35
|
-
const data = refreshTokenResult.data.refreshToken;
|
|
36
|
-
console.debug('4.auth.server.ts::token', data?.token);
|
|
37
|
-
console.debug('4.auth.server.ts::refreshToken', data?.refreshToken);
|
|
38
|
-
setTokens(data.token, data.refreshToken);
|
|
39
|
-
|
|
40
|
-
if (payload?.id) {
|
|
41
|
-
const userResult = await ofetch(config.public.host, {
|
|
42
|
-
method: 'POST',
|
|
43
|
-
headers: {
|
|
44
|
-
Authorization: `Bearer ${refreshTokenResult.data.refreshToken?.token}`,
|
|
45
|
-
},
|
|
46
|
-
body: JSON.stringify({
|
|
47
|
-
query: 'query getUser($id: String!){' +
|
|
48
|
-
'getUser(id: $id){' +
|
|
49
|
-
'id ' +
|
|
50
|
-
'avatar ' +
|
|
51
|
-
'firstName ' +
|
|
52
|
-
'lastName ' +
|
|
53
|
-
'email ' +
|
|
54
|
-
'roles ' +
|
|
55
|
-
'}}',
|
|
56
|
-
variables: {
|
|
57
|
-
id: payload.id,
|
|
58
|
-
},
|
|
59
|
-
}),
|
|
60
|
-
}).catch((err) => {
|
|
61
|
-
console.error('4.auth.server.ts::getUser::catch', err.data);
|
|
62
|
-
});
|
|
63
|
-
console.debug('4.auth.server.ts::getUser::userResult', userResult);
|
|
64
|
-
if (userResult?.data) {
|
|
65
|
-
setCurrentUser(userResult?.data?.getUser);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
|
-
});
|