more form changes
This commit is contained in:
parent
c76db68e5b
commit
b1f58276d0
60
package-lock.json
generated
60
package-lock.json
generated
@ -12,8 +12,10 @@
|
|||||||
"@heroicons/vue": "^2.0.13",
|
"@heroicons/vue": "^2.0.13",
|
||||||
"@vueuse/core": "^9.10.0",
|
"@vueuse/core": "^9.10.0",
|
||||||
"jwt-decode": "^3.1.2",
|
"jwt-decode": "^3.1.2",
|
||||||
|
"lodash.get": "^4.4.2",
|
||||||
"lodash.omit": "^4.5.0",
|
"lodash.omit": "^4.5.0",
|
||||||
"lodash.pick": "^4.4.0",
|
"lodash.pick": "^4.4.0",
|
||||||
|
"lodash.set": "^4.3.2",
|
||||||
"pinia": "^2.0.28",
|
"pinia": "^2.0.28",
|
||||||
"sass": "^1.57.1",
|
"sass": "^1.57.1",
|
||||||
"vue": "^3.2.45",
|
"vue": "^3.2.45",
|
||||||
@ -22,8 +24,10 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/forms": "^0.5.3",
|
"@tailwindcss/forms": "^0.5.3",
|
||||||
"@tailwindcss/line-clamp": "^0.4.2",
|
"@tailwindcss/line-clamp": "^0.4.2",
|
||||||
|
"@types/lodash.get": "^4.4.7",
|
||||||
"@types/lodash.omit": "^4.5.7",
|
"@types/lodash.omit": "^4.5.7",
|
||||||
"@types/lodash.pick": "^4.4.7",
|
"@types/lodash.pick": "^4.4.7",
|
||||||
|
"@types/lodash.set": "^4.3.7",
|
||||||
"@vitejs/plugin-vue": "^4.0.0",
|
"@vitejs/plugin-vue": "^4.0.0",
|
||||||
"autoprefixer": "^10.4.13",
|
"autoprefixer": "^10.4.13",
|
||||||
"postcss": "^8.4.21",
|
"postcss": "^8.4.21",
|
||||||
@ -479,6 +483,15 @@
|
|||||||
"integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==",
|
"integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/lodash.get": {
|
||||||
|
"version": "4.4.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lodash.get/-/lodash.get-4.4.7.tgz",
|
||||||
|
"integrity": "sha512-af34Mj+KdDeuzsJBxc/XeTtOx0SZHZNLd+hdrn+PcKGQs0EG2TJTzQAOTCZTgDJCArahlCzLWSy8c2w59JRz7Q==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/lodash": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/lodash.omit": {
|
"node_modules/@types/lodash.omit": {
|
||||||
"version": "4.5.7",
|
"version": "4.5.7",
|
||||||
"resolved": "https://registry.npmjs.org/@types/lodash.omit/-/lodash.omit-4.5.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/lodash.omit/-/lodash.omit-4.5.7.tgz",
|
||||||
@ -497,6 +510,15 @@
|
|||||||
"@types/lodash": "*"
|
"@types/lodash": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/lodash.set": {
|
||||||
|
"version": "4.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lodash.set/-/lodash.set-4.3.7.tgz",
|
||||||
|
"integrity": "sha512-bS5Wkg/nrT82YUfkNYPSccFrNZRL+irl7Yt4iM6OTSQ0VZJED2oUIVm15NkNtUAQ8SRhCe+axqERUV6MJgkeEg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/lodash": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/web-bluetooth": {
|
"node_modules/@types/web-bluetooth": {
|
||||||
"version": "0.0.16",
|
"version": "0.0.16",
|
||||||
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz",
|
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz",
|
||||||
@ -1248,6 +1270,11 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/lodash.get": {
|
||||||
|
"version": "4.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
|
||||||
|
"integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ=="
|
||||||
|
},
|
||||||
"node_modules/lodash.omit": {
|
"node_modules/lodash.omit": {
|
||||||
"version": "4.5.0",
|
"version": "4.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz",
|
||||||
@ -1258,6 +1285,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz",
|
||||||
"integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q=="
|
"integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q=="
|
||||||
},
|
},
|
||||||
|
"node_modules/lodash.set": {
|
||||||
|
"version": "4.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz",
|
||||||
|
"integrity": "sha512-4hNPN5jlm/N/HLMCO43v8BXKq9Z7QdAGc/VGrRD61w8gN9g/6jF9A4L1pbUgBLCffi0w9VsXfTOij5x8iTyFvg=="
|
||||||
|
},
|
||||||
"node_modules/magic-string": {
|
"node_modules/magic-string": {
|
||||||
"version": "0.25.9",
|
"version": "0.25.9",
|
||||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
|
||||||
@ -2218,6 +2250,15 @@
|
|||||||
"integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==",
|
"integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/lodash.get": {
|
||||||
|
"version": "4.4.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lodash.get/-/lodash.get-4.4.7.tgz",
|
||||||
|
"integrity": "sha512-af34Mj+KdDeuzsJBxc/XeTtOx0SZHZNLd+hdrn+PcKGQs0EG2TJTzQAOTCZTgDJCArahlCzLWSy8c2w59JRz7Q==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/lodash": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/lodash.omit": {
|
"@types/lodash.omit": {
|
||||||
"version": "4.5.7",
|
"version": "4.5.7",
|
||||||
"resolved": "https://registry.npmjs.org/@types/lodash.omit/-/lodash.omit-4.5.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/lodash.omit/-/lodash.omit-4.5.7.tgz",
|
||||||
@ -2236,6 +2277,15 @@
|
|||||||
"@types/lodash": "*"
|
"@types/lodash": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/lodash.set": {
|
||||||
|
"version": "4.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lodash.set/-/lodash.set-4.3.7.tgz",
|
||||||
|
"integrity": "sha512-bS5Wkg/nrT82YUfkNYPSccFrNZRL+irl7Yt4iM6OTSQ0VZJED2oUIVm15NkNtUAQ8SRhCe+axqERUV6MJgkeEg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/lodash": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/web-bluetooth": {
|
"@types/web-bluetooth": {
|
||||||
"version": "0.0.16",
|
"version": "0.0.16",
|
||||||
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz",
|
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz",
|
||||||
@ -2786,6 +2836,11 @@
|
|||||||
"integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==",
|
"integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"lodash.get": {
|
||||||
|
"version": "4.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
|
||||||
|
"integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ=="
|
||||||
|
},
|
||||||
"lodash.omit": {
|
"lodash.omit": {
|
||||||
"version": "4.5.0",
|
"version": "4.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz",
|
||||||
@ -2796,6 +2851,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz",
|
||||||
"integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q=="
|
"integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q=="
|
||||||
},
|
},
|
||||||
|
"lodash.set": {
|
||||||
|
"version": "4.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz",
|
||||||
|
"integrity": "sha512-4hNPN5jlm/N/HLMCO43v8BXKq9Z7QdAGc/VGrRD61w8gN9g/6jF9A4L1pbUgBLCffi0w9VsXfTOij5x8iTyFvg=="
|
||||||
|
},
|
||||||
"magic-string": {
|
"magic-string": {
|
||||||
"version": "0.25.9",
|
"version": "0.25.9",
|
||||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
|
||||||
|
@ -13,8 +13,10 @@
|
|||||||
"@heroicons/vue": "^2.0.13",
|
"@heroicons/vue": "^2.0.13",
|
||||||
"@vueuse/core": "^9.10.0",
|
"@vueuse/core": "^9.10.0",
|
||||||
"jwt-decode": "^3.1.2",
|
"jwt-decode": "^3.1.2",
|
||||||
|
"lodash.get": "^4.4.2",
|
||||||
"lodash.omit": "^4.5.0",
|
"lodash.omit": "^4.5.0",
|
||||||
"lodash.pick": "^4.4.0",
|
"lodash.pick": "^4.4.0",
|
||||||
|
"lodash.set": "^4.3.2",
|
||||||
"pinia": "^2.0.28",
|
"pinia": "^2.0.28",
|
||||||
"sass": "^1.57.1",
|
"sass": "^1.57.1",
|
||||||
"vue": "^3.2.45",
|
"vue": "^3.2.45",
|
||||||
@ -23,8 +25,10 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/forms": "^0.5.3",
|
"@tailwindcss/forms": "^0.5.3",
|
||||||
"@tailwindcss/line-clamp": "^0.4.2",
|
"@tailwindcss/line-clamp": "^0.4.2",
|
||||||
|
"@types/lodash.get": "^4.4.7",
|
||||||
"@types/lodash.omit": "^4.5.7",
|
"@types/lodash.omit": "^4.5.7",
|
||||||
"@types/lodash.pick": "^4.4.7",
|
"@types/lodash.pick": "^4.4.7",
|
||||||
|
"@types/lodash.set": "^4.3.7",
|
||||||
"@vitejs/plugin-vue": "^4.0.0",
|
"@vitejs/plugin-vue": "^4.0.0",
|
||||||
"autoprefixer": "^10.4.13",
|
"autoprefixer": "^10.4.13",
|
||||||
"postcss": "^8.4.21",
|
"postcss": "^8.4.21",
|
||||||
|
52
src/components/Accordion.vue
Normal file
52
src/components/Accordion.vue
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<button class="px-4 py-4" @click="open = !open" :aria-expanded="open">
|
||||||
|
<slot name="title" :open="open" :close="close">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span>{{ title }}</span>
|
||||||
|
|
||||||
|
<ChevronDownIcon class="h-6 w-6" v-if="!open" aria-hidden="true" />
|
||||||
|
<ChevronUpIcon class="h-6 w-6" v-else aria-hidden="true" />
|
||||||
|
</div>
|
||||||
|
</slot>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<Transition
|
||||||
|
enter-active-class="transition-max-height ease-out duration-500"
|
||||||
|
enter-from-class="max-h-0"
|
||||||
|
enter-to-class="max-h-screen"
|
||||||
|
leave-active-class="transition-max-height ease-in duration-250"
|
||||||
|
leave-from-class="max-h-screen"
|
||||||
|
leave-to-class="max-h-0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="open"
|
||||||
|
:class="[
|
||||||
|
open ? 'border-t-2 border-gray-100' : '',
|
||||||
|
'overflow-hidden px-4 py-4',
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<slot :close="close" />
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/vue/24/outline';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
title?: string;
|
||||||
|
open?: boolean;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
open: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const open = ref(props.open);
|
||||||
|
|
||||||
|
const close = () => (open.value = false);
|
||||||
|
</script>
|
@ -23,19 +23,18 @@
|
|||||||
:onBlur="onBlur"
|
:onBlur="onBlur"
|
||||||
:setValue="setValue"
|
:setValue="setValue"
|
||||||
>
|
>
|
||||||
<FormInput
|
<input
|
||||||
:for-id="forId"
|
|
||||||
:type="type"
|
:type="type"
|
||||||
:id="forId"
|
:id="forId"
|
||||||
:name="name"
|
:name="name"
|
||||||
:value="value"
|
:value="value"
|
||||||
|
:class="inputClass"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
:invalid="!!errors?.length"
|
:disabled="disabled"
|
||||||
@input="onInput"
|
@input="onInput"
|
||||||
@change="onChange"
|
@change="onChange"
|
||||||
@focus="onFocus"
|
@focus="onFocus"
|
||||||
@blur="onBlur"
|
@blur="onBlur"
|
||||||
@set-value="setValue"
|
|
||||||
/>
|
/>
|
||||||
</slot>
|
</slot>
|
||||||
<slot />
|
<slot />
|
||||||
@ -54,7 +53,6 @@ import get from 'lodash.get';
|
|||||||
import { computed, ref, Ref } from 'vue';
|
import { computed, ref, Ref } from 'vue';
|
||||||
import { inject } from 'vue';
|
import { inject } from 'vue';
|
||||||
import { FormData, FormErrors } from './form.types';
|
import { FormData, FormErrors } from './form.types';
|
||||||
import FormInput from './FormInput.vue';
|
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
@ -68,10 +66,12 @@ const props = withDefaults(
|
|||||||
| 'radio';
|
| 'radio';
|
||||||
label: string;
|
label: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
disabled?: boolean;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
type: 'text',
|
type: 'text',
|
||||||
|
disabled: false,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ const fieldName = computed(() =>
|
|||||||
formGroup?.value ? `${formGroup.value}.${props.name}` : props.name
|
formGroup?.value ? `${formGroup.value}.${props.name}` : props.name
|
||||||
);
|
);
|
||||||
|
|
||||||
const forId = computed(() => `form-${fieldName}`);
|
const forId = computed(() => `form-${fieldName.value}`);
|
||||||
const value = computed(() => get(formData?.value, fieldName.value));
|
const value = computed(() => get(formData?.value, fieldName.value));
|
||||||
const errors = computed(() => formErrors?.value[fieldName.value] || []);
|
const errors = computed(() => formErrors?.value[fieldName.value] || []);
|
||||||
|
|
||||||
@ -122,4 +122,13 @@ const onBlur = () => {
|
|||||||
emit('blur');
|
emit('blur');
|
||||||
fieldChange?.(fieldName.value);
|
fieldChange?.(fieldName.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const inputClass = computed(() => {
|
||||||
|
return [
|
||||||
|
errors.value.length
|
||||||
|
? 'border-red-300 focus:border-red-500 focus:ring-red-500'
|
||||||
|
: 'border-gray-300 focus:border-blue-500 focus:ring-blue-500',
|
||||||
|
`mt-1 block w-full rounded-md shadow-sm sm:text-sm transition-colors duration-200`,
|
||||||
|
];
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,65 +0,0 @@
|
|||||||
<template>
|
|
||||||
<ColorInput
|
|
||||||
v-if="type === 'color'"
|
|
||||||
:for-id="forId"
|
|
||||||
:model-value="(value as string)"
|
|
||||||
@update:model-value="(newValue: string) => emit('setValue', newValue)"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
v-else
|
|
||||||
:type="type"
|
|
||||||
:id="forId"
|
|
||||||
:name="name"
|
|
||||||
:value="value"
|
|
||||||
:class="inputClass"
|
|
||||||
:placeholder="placeholder"
|
|
||||||
@input="emit('input', $event)"
|
|
||||||
@change="emit('change', $event)"
|
|
||||||
@focus="emit('focus', $event)"
|
|
||||||
@blur="emit('blur', $event)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { computed } from 'vue';
|
|
||||||
import ColorInput from '../ColorInput.vue';
|
|
||||||
|
|
||||||
const props = withDefaults(
|
|
||||||
defineProps<{
|
|
||||||
type?:
|
|
||||||
| 'text'
|
|
||||||
| 'number'
|
|
||||||
| 'password'
|
|
||||||
| 'color'
|
|
||||||
| 'email'
|
|
||||||
| 'checkbox'
|
|
||||||
| 'radio';
|
|
||||||
forId: string;
|
|
||||||
name: string;
|
|
||||||
value: unknown;
|
|
||||||
invalid?: boolean;
|
|
||||||
placeholder?: string;
|
|
||||||
}>(),
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
invalid: false,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
|
||||||
(e: 'setValue', value: unknown): void;
|
|
||||||
(e: 'input', ev: Event): void;
|
|
||||||
(e: 'change', ev: Event): void;
|
|
||||||
(e: 'focus', ev: Event): void;
|
|
||||||
(e: 'blur', ev: Event): void;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const inputClass = computed(() => {
|
|
||||||
return [
|
|
||||||
props.invalid
|
|
||||||
? 'border-red-300 focus:border-red-500 focus:ring-red-500'
|
|
||||||
: 'border-gray-300 focus:border-blue-500 focus:ring-blue-500',
|
|
||||||
`mt-1 block w-full rounded-md shadow-sm sm:text-sm transition-colors duration-200`,
|
|
||||||
];
|
|
||||||
});
|
|
||||||
</script>
|
|
130
src/components/form/base/Select.vue
Normal file
130
src/components/form/base/Select.vue
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
<template>
|
||||||
|
<Listbox
|
||||||
|
:modelValue="selected"
|
||||||
|
@update:modelValue="valueChanged"
|
||||||
|
:disabled="disabled"
|
||||||
|
by="value"
|
||||||
|
>
|
||||||
|
<div class="relative mt-1">
|
||||||
|
<ListboxButton :class="inputClass" :id="forId">
|
||||||
|
<span class="block truncate">{{ selected?.name || '' }}</span>
|
||||||
|
<span
|
||||||
|
class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2"
|
||||||
|
>
|
||||||
|
<ChevronUpDownIcon class="h-5 w-5 text-gray-400" aria-hidden="true" />
|
||||||
|
</span>
|
||||||
|
</ListboxButton>
|
||||||
|
|
||||||
|
<Transition
|
||||||
|
leave-active-class="transition duration-100 ease-in"
|
||||||
|
leave-from-class="opacity-100"
|
||||||
|
leave-to-class="opacity-0"
|
||||||
|
>
|
||||||
|
<ListboxOptions
|
||||||
|
class="absolute z-20 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"
|
||||||
|
>
|
||||||
|
<ListboxOption
|
||||||
|
v-slot="{ active, selected }"
|
||||||
|
v-for="option in options"
|
||||||
|
:key="option.name"
|
||||||
|
:value="option"
|
||||||
|
as="template"
|
||||||
|
>
|
||||||
|
<li :class="itemClass(active, selected)">
|
||||||
|
<span
|
||||||
|
:class="[
|
||||||
|
selected ? 'font-medium' : 'font-normal',
|
||||||
|
'block truncate',
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
{{ option.name }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-if="selected"
|
||||||
|
class="absolute inset-y-0 left-0 flex items-center pl-2 text-blue-600"
|
||||||
|
>
|
||||||
|
<CheckIcon class="h-5 w-5" aria-hidden="true" />
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ListboxOption>
|
||||||
|
</ListboxOptions>
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
</Listbox>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
import {
|
||||||
|
Listbox,
|
||||||
|
ListboxButton,
|
||||||
|
ListboxOptions,
|
||||||
|
ListboxOption,
|
||||||
|
} from '@headlessui/vue';
|
||||||
|
import { CheckIcon, ChevronUpDownIcon } from '@heroicons/vue/24/outline';
|
||||||
|
|
||||||
|
export interface SelectOption {
|
||||||
|
value: string | number;
|
||||||
|
name: string;
|
||||||
|
description?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
forId?: string;
|
||||||
|
modelValue: string | number | null;
|
||||||
|
options: SelectOption[];
|
||||||
|
disabled?: boolean;
|
||||||
|
invalid?: boolean;
|
||||||
|
placeholder?: string;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
modelValue: null,
|
||||||
|
disabled: false,
|
||||||
|
invalid: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const localValue = ref(props.modelValue);
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'update:modelValue', value: string | number | null): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const valueChanged = (option: SelectOption) => {
|
||||||
|
localValue.value = option.value;
|
||||||
|
emit('update:modelValue', localValue.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const selected = computed(() =>
|
||||||
|
props.options.find((item) => localValue.value === item.value)
|
||||||
|
);
|
||||||
|
|
||||||
|
const inputClass = computed(() => {
|
||||||
|
return [
|
||||||
|
props.invalid
|
||||||
|
? 'border-red-300 focus:border-red-500 focus:ring-red-500'
|
||||||
|
: 'border-gray-300 focus:border-blue-500 focus:ring-blue-500',
|
||||||
|
`block w-full rounded-md shadow-sm sm:text-sm transition-colors duration-200`,
|
||||||
|
'py-2 pl-3 pr-10 text-left cursor-default h-[38px] border-[1px]',
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
const itemClass = (active: boolean, selected: boolean) => {
|
||||||
|
const classList = ['relative cursor-default select-none py-2 pl-8 pr-4'];
|
||||||
|
if (selected) {
|
||||||
|
classList.push('text-blue-900');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (active && selected) {
|
||||||
|
classList.push('bg-blue-200');
|
||||||
|
} else if (active || selected) {
|
||||||
|
classList.push('bg-blue-100');
|
||||||
|
} else {
|
||||||
|
classList.push('text-gray-900');
|
||||||
|
}
|
||||||
|
|
||||||
|
return classList;
|
||||||
|
};
|
||||||
|
</script>
|
35
src/components/form/fields/FormColorField.vue
Normal file
35
src/components/form/fields/FormColorField.vue
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<template>
|
||||||
|
<FormField
|
||||||
|
:name="name"
|
||||||
|
:label="label"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:disabled="disabled"
|
||||||
|
>
|
||||||
|
<template #input="{ forId, value, setValue }">
|
||||||
|
<ColorInput
|
||||||
|
:for-id="forId"
|
||||||
|
:model-value="(value as string)"
|
||||||
|
@update:model-value="setValue"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #default><slot /></template>
|
||||||
|
</FormField>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import FormField from '../FormField.vue';
|
||||||
|
import ColorInput from '../base/ColorInput.vue';
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
forId?: string;
|
||||||
|
label: string;
|
||||||
|
name: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
placeholder?: string;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
disabled: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
</script>
|
40
src/components/form/fields/FormSelectField.vue
Normal file
40
src/components/form/fields/FormSelectField.vue
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<template>
|
||||||
|
<FormField
|
||||||
|
:name="name"
|
||||||
|
:label="label"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:disabled="disabled"
|
||||||
|
>
|
||||||
|
<template #input="{ invalid, value, setValue }">
|
||||||
|
<SelectVue
|
||||||
|
:for-id="forId"
|
||||||
|
:invalid="invalid"
|
||||||
|
:disabled="disabled"
|
||||||
|
:options="options || []"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:model-value="(value as string)"
|
||||||
|
@update:model-value="(newValue) => setValue(newValue)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #default><slot /></template>
|
||||||
|
</FormField>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import FormField from '../FormField.vue';
|
||||||
|
import SelectVue, { SelectOption } from '../base/Select.vue';
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
forId?: string;
|
||||||
|
label: string;
|
||||||
|
name: string;
|
||||||
|
options: SelectOption[];
|
||||||
|
disabled?: boolean;
|
||||||
|
placeholder?: string;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
disabled: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
</script>
|
@ -1,41 +0,0 @@
|
|||||||
<template>
|
|
||||||
<Dropdown :title="title">
|
|
||||||
<template #trigger="{ title, open, toggle }">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
@click="() => toggle()"
|
|
||||||
:aria-expanded="open"
|
|
||||||
:class="[
|
|
||||||
open ? 'text-gray-900' : 'text-gray-500',
|
|
||||||
'group inline-flex items-center rounded-md bg-white text-base font-medium hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2',
|
|
||||||
]"
|
|
||||||
>
|
|
||||||
<span>{{ title }}</span>
|
|
||||||
<ChevronDownIcon class="ml-2 h-5 w-5" />
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #default="{ title, open, toggle }">
|
|
||||||
<div
|
|
||||||
class="absolute z-10 -ml-4 mt-3 w-screen max-w-xs transform px-2 sm:px-0 lg:left-1/2 lg:ml-0 lg:-translate-x-1/2"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="overflow-hidden rounded-lg shadow-lg ring-1 ring-black ring-opacity-5"
|
|
||||||
>
|
|
||||||
<slot></slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</Dropdown>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ChevronDownIcon } from '@heroicons/vue/24/outline';
|
|
||||||
import { onMounted, ref } from 'vue';
|
|
||||||
import { onBeforeRouteLeave } from 'vue-router';
|
|
||||||
import Dropdown from '../Dropdown.vue';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
title: string;
|
|
||||||
}>();
|
|
||||||
</script>
|
|
@ -1,65 +1,143 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="relative border-b-2 border-gray-100 bg-white">
|
<Popover class="relative border-b-2 border-gray-100 bg-white">
|
||||||
<div class="mx-auto max-w-7xl px-5">
|
<div class="mx-auto max-w-7xl px-6">
|
||||||
<div
|
<div class="flex items-center py-6 md:justify-start md:space-x-8">
|
||||||
class="flex items-center justify-between py-5 md:justify-start md:space-x-10"
|
<div class="mr-auto flex md:mr-0">
|
||||||
>
|
<div class="mr-auto flex justify-start md:mr-0">
|
||||||
<div class="flex w-0 justify-start md:mr-4">
|
<router-link to="/"><HomeIcon class="h-8 w-8" /></router-link>
|
||||||
<router-link to="/"><HomeIcon class="h-8 w-8" /></router-link>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="-my-2 -mr-2 md:hidden">
|
<div class="-my-2 -mr-2 md:hidden">
|
||||||
<button
|
<PopoverButton
|
||||||
type="button"
|
|
||||||
class="inline-flex items-center justify-center rounded-md bg-white p-2 text-gray-400 hover:bg-gray-100 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500"
|
class="inline-flex items-center justify-center rounded-md bg-white p-2 text-gray-400 hover:bg-gray-100 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500"
|
||||||
aria-expanded="false"
|
|
||||||
>
|
>
|
||||||
<span class="sr-only">Open menu</span>
|
<span class="sr-only">Open menu</span>
|
||||||
<Bars3Icon class="h-6 w-6" />
|
<Bars3Icon class="h-6 w-6" aria-hidden="true" />
|
||||||
</button>
|
</PopoverButton>
|
||||||
</div>
|
</div>
|
||||||
|
<PopoverGroup as="nav" class="hidden space-x-6 md:flex">
|
||||||
|
<Popover class="relative" v-slot="{ open }">
|
||||||
|
<PopoverButton
|
||||||
|
:class="[
|
||||||
|
open ? 'text-gray-900' : 'text-gray-500',
|
||||||
|
'group inline-flex items-center rounded-md bg-white text-base font-medium hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2',
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<span>Buildings</span>
|
||||||
|
<ChevronDownIcon
|
||||||
|
:class="[
|
||||||
|
open ? 'text-gray-600' : 'text-gray-400',
|
||||||
|
'ml-2 h-5 w-5 group-hover:text-gray-500',
|
||||||
|
]"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
</PopoverButton>
|
||||||
|
|
||||||
<nav class="hidden space-x-10 md:flex">
|
<transition
|
||||||
<Dropdown title="My Buildings">
|
enter-active-class="transition ease-out duration-200"
|
||||||
<div class="relative bg-white pt-4 pb-4 sm:gap-5">
|
enter-from-class="opacity-0 translate-y-1"
|
||||||
<template v-for="building of buildings">
|
enter-to-class="opacity-100 translate-y-0"
|
||||||
<Building :building="building" />
|
leave-active-class="transition ease-in duration-150"
|
||||||
</template>
|
leave-from-class="opacity-100 translate-y-0"
|
||||||
</div>
|
leave-to-class="opacity-0 translate-y-1"
|
||||||
</Dropdown>
|
>
|
||||||
<button
|
<PopoverPanel
|
||||||
type="button"
|
class="absolute z-10 -ml-4 mt-3 w-screen max-w-md transform px-2 sm:px-0"
|
||||||
:class="[
|
>
|
||||||
'text-gray-500',
|
<div
|
||||||
'inline-flex items-center rounded-md bg-white text-base font-medium hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2',
|
class="overflow-hidden rounded-lg shadow-lg ring-1 ring-black ring-opacity-5"
|
||||||
]"
|
>
|
||||||
|
<div class="relative bg-white pt-4 pb-4 sm:gap-5">
|
||||||
|
<template v-for="building of buildings">
|
||||||
|
<Building :building="building" />
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</PopoverPanel>
|
||||||
|
</transition>
|
||||||
|
</Popover>
|
||||||
|
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="text-base font-medium text-gray-500 hover:text-gray-900"
|
||||||
|
>Groups</a
|
||||||
>
|
>
|
||||||
<span>Groups</span>
|
</PopoverGroup>
|
||||||
</button>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div class="hidden items-center justify-end md:flex md:flex-1 lg:w-0">
|
<div class="hidden items-center justify-end md:flex md:flex-1 lg:w-0">
|
||||||
<UserPill :user="user" />
|
<UserPill :user="user" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
<transition
|
||||||
|
enter-active-class="duration-200 ease-out"
|
||||||
|
enter-from-class="opacity-0 scale-95"
|
||||||
|
enter-to-class="opacity-100 scale-100"
|
||||||
|
leave-active-class="duration-100 ease-in"
|
||||||
|
leave-from-class="opacity-100 scale-100"
|
||||||
|
leave-to-class="opacity-0 scale-95"
|
||||||
|
>
|
||||||
|
<PopoverPanel
|
||||||
|
focus
|
||||||
|
class="absolute inset-x-0 top-0 z-50 origin-top-right transform p-2 transition md:hidden"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="divide-y-2 divide-gray-50 rounded-lg bg-white shadow-lg ring-1 ring-black ring-opacity-5"
|
||||||
|
>
|
||||||
|
<div class="px-5 pt-5 pb-6">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<router-link to="/"><HomeIcon class="h-8 w-8" /></router-link>
|
||||||
|
</div>
|
||||||
|
<div class="-mr-2">
|
||||||
|
<PopoverButton
|
||||||
|
class="inline-flex items-center justify-center rounded-md bg-white p-2 text-gray-400 hover:bg-gray-100 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500"
|
||||||
|
>
|
||||||
|
<span class="sr-only">Close menu</span>
|
||||||
|
<XMarkIcon class="h-6 w-6" aria-hidden="true" />
|
||||||
|
</PopoverButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-6">
|
||||||
|
<span class="text-lg font-bold">Buildings</span>
|
||||||
|
<div class="-mx-5">
|
||||||
|
<template v-for="building of buildings">
|
||||||
|
<Building :building="building" />
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-6 py-6 px-5">
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<UserPill :user="user" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</PopoverPanel>
|
||||||
|
</transition>
|
||||||
|
</Popover>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverButton,
|
||||||
|
PopoverGroup,
|
||||||
|
PopoverPanel,
|
||||||
|
} from '@headlessui/vue';
|
||||||
import {
|
import {
|
||||||
HomeIcon,
|
HomeIcon,
|
||||||
Bars3Icon,
|
Bars3Icon,
|
||||||
ChevronDownIcon,
|
ChevronDownIcon,
|
||||||
UserIcon,
|
XMarkIcon,
|
||||||
} from '@heroicons/vue/24/outline';
|
} from '@heroicons/vue/24/outline';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { useUserStore } from '../../store/user.store';
|
import { useUserStore } from '../../store/user.store';
|
||||||
import UserPill from './UserPill.vue';
|
import UserPill from './UserPill.vue';
|
||||||
import Dropdown from './Dropdown.vue';
|
|
||||||
import Building from './Building.vue';
|
import Building from './Building.vue';
|
||||||
import { useBuildingStore } from '../../store/building.store';
|
import { useBuildingStore } from '../../store/building.store';
|
||||||
import { onMounted } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const buildingsStore = useBuildingStore();
|
const buildingsStore = useBuildingStore();
|
||||||
const { user } = storeToRefs(userStore);
|
const { user } = storeToRefs(userStore);
|
||||||
|
@ -50,7 +50,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from '@vue/reactivity';
|
import { computed } from '@vue/reactivity';
|
||||||
import ColorInput from '../ColorInput.vue';
|
import ColorInput from '../form/base/ColorInput.vue';
|
||||||
import { ObjectProperty } from './interfaces/properties.interfaces';
|
import { ObjectProperty } from './interfaces/properties.interfaces';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
@ -8,11 +8,11 @@
|
|||||||
<FormAlert :message="error" />
|
<FormAlert :message="error" />
|
||||||
<Form @submit="onSubmit" v-model="data" :validators="validators">
|
<Form @submit="onSubmit" v-model="data" :validators="validators">
|
||||||
<FormField name="displayName" label="Display Name" />
|
<FormField name="displayName" label="Display Name" />
|
||||||
<FormField name="type" label="Type" />
|
<FormSelectField :options="selectOptions" name="type" label="Type" />
|
||||||
<FormField name="locationDescription" label="Location description">
|
<FormField name="locationDescription" label="Location description">
|
||||||
<span class="text-sm">Describe the location of this storage</span>
|
<span class="text-sm">Describe the location of this storage</span>
|
||||||
</FormField>
|
</FormField>
|
||||||
<FormField type="color" name="color" label="Color" />
|
<FormColorField name="color" label="Color" />
|
||||||
<button type="submit">Submit</button>
|
<button type="submit">Submit</button>
|
||||||
</Form>
|
</Form>
|
||||||
</template>
|
</template>
|
||||||
@ -20,9 +20,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import { useAccessToken } from '../../composables/useAccessToken';
|
import { useAccessToken } from '../../composables/useAccessToken';
|
||||||
import { BACKEND_URL } from '../../constants';
|
import { BACKEND_URL } from '../../constants';
|
||||||
|
import { StorageSetTypeName } from '../../enums/storage-set-type.enum';
|
||||||
|
import { StorageTypeName } from '../../enums/storage-type.enum';
|
||||||
import { BuildingListItem } from '../../interfaces/building.interfaces';
|
import { BuildingListItem } from '../../interfaces/building.interfaces';
|
||||||
import { RoomListItem } from '../../interfaces/room.interfaces';
|
import { RoomListItem } from '../../interfaces/room.interfaces';
|
||||||
import {
|
import {
|
||||||
@ -35,8 +37,11 @@ import { FormSubmit } from '../form/form.types';
|
|||||||
import Form from '../form/Form.vue';
|
import Form from '../form/Form.vue';
|
||||||
import FormAlert from '../form/FormAlert.vue';
|
import FormAlert from '../form/FormAlert.vue';
|
||||||
import FormField from '../form/FormField.vue';
|
import FormField from '../form/FormField.vue';
|
||||||
|
import FormSelectField from '../form/fields/FormSelectField.vue';
|
||||||
|
import { SelectOption } from '../form/base/Select.vue';
|
||||||
import { IsRequired, MinLength } from '../form/validators';
|
import { IsRequired, MinLength } from '../form/validators';
|
||||||
import Modal from '../Modal.vue';
|
import Modal from '../Modal.vue';
|
||||||
|
import FormColorField from '../form/fields/FormColorField.vue';
|
||||||
|
|
||||||
const { authHeader } = useAccessToken();
|
const { authHeader } = useAccessToken();
|
||||||
const defaults = {
|
const defaults = {
|
||||||
@ -52,6 +57,16 @@ const building = ref<BuildingListItem>();
|
|||||||
const set = ref<StorageSetListItem | boolean>();
|
const set = ref<StorageSetListItem | boolean>();
|
||||||
const data = ref({ ...defaults });
|
const data = ref({ ...defaults });
|
||||||
const error = ref('');
|
const error = ref('');
|
||||||
|
const selectOptions = computed(() => {
|
||||||
|
const source = set.value === true ? StorageSetTypeName : StorageTypeName;
|
||||||
|
return Object.keys(source).reduce<SelectOption[]>(
|
||||||
|
(list, key) => [
|
||||||
|
...list,
|
||||||
|
{ value: key.toString(), name: source[key as keyof typeof source] },
|
||||||
|
],
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
const validators = ref([
|
const validators = ref([
|
||||||
{
|
{
|
||||||
|
@ -12,3 +12,34 @@ export enum ItemType {
|
|||||||
SERVICE = 'SERVICE',
|
SERVICE = 'SERVICE',
|
||||||
OTHER = 'OTHER',
|
OTHER = 'OTHER',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const ItemTypeName: Record<ItemType, string> = {
|
||||||
|
[ItemType.ITEM]: 'Generic item',
|
||||||
|
[ItemType.OBJECT]: 'Generic object',
|
||||||
|
[ItemType.TECHNOLOGY]: 'Technology',
|
||||||
|
[ItemType.TOOL]: 'Tool',
|
||||||
|
[ItemType.FOOD]: 'Food',
|
||||||
|
[ItemType.MEDICINE]: 'Medicine',
|
||||||
|
[ItemType.ART]: 'Arts',
|
||||||
|
[ItemType.CRAFT]: 'Crafts',
|
||||||
|
[ItemType.COMPOSITION]: 'Composition/Creation',
|
||||||
|
[ItemType.CLOTHING]: 'Clothing',
|
||||||
|
[ItemType.SERVICE]: '(Virtual) Service',
|
||||||
|
[ItemType.OTHER]: 'Other',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ItemTypeDescription: Record<ItemType, string> = {
|
||||||
|
[ItemType.ITEM]: 'An item, the classification is up to you',
|
||||||
|
[ItemType.OBJECT]: 'An object, the classification is up to you',
|
||||||
|
[ItemType.TECHNOLOGY]:
|
||||||
|
'Such as computer hardware, electronic components, etc',
|
||||||
|
[ItemType.TOOL]: 'Tools of any kind',
|
||||||
|
[ItemType.FOOD]: 'Edible, drinkable or otherwise consumable',
|
||||||
|
[ItemType.MEDICINE]: 'Medicine',
|
||||||
|
[ItemType.ART]: 'Artworks, prints',
|
||||||
|
[ItemType.CRAFT]: 'Crafted items',
|
||||||
|
[ItemType.COMPOSITION]: 'A combination of items that created a new item',
|
||||||
|
[ItemType.CLOTHING]: 'Clothing, wearables',
|
||||||
|
[ItemType.SERVICE]: 'Online service, computer software, etc',
|
||||||
|
[ItemType.OTHER]: 'Other',
|
||||||
|
};
|
||||||
|
@ -9,3 +9,15 @@ export enum StorageSetType {
|
|||||||
BOXES = 'BOXES',
|
BOXES = 'BOXES',
|
||||||
OTHER = 'OTHER',
|
OTHER = 'OTHER',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const StorageSetTypeName: Record<StorageSetType, string> = {
|
||||||
|
[StorageSetType.DRAWERS]: 'Set of drawers',
|
||||||
|
[StorageSetType.SHELVES]: 'Shelves',
|
||||||
|
[StorageSetType.CUPBOARD]: 'Cupboard',
|
||||||
|
[StorageSetType.CLOSET]: 'Closet',
|
||||||
|
[StorageSetType.FRIDGE]: 'Fridge',
|
||||||
|
[StorageSetType.FREEZER]: 'Freezer',
|
||||||
|
[StorageSetType.BOX]: 'Box',
|
||||||
|
[StorageSetType.BOXES]: 'Boxes',
|
||||||
|
[StorageSetType.OTHER]: 'Other',
|
||||||
|
};
|
||||||
|
@ -12,3 +12,18 @@ export enum StorageType {
|
|||||||
PERSON = 'PERSON',
|
PERSON = 'PERSON',
|
||||||
OTHER = 'OTHER',
|
OTHER = 'OTHER',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const StorageTypeName: Record<StorageType, string> = {
|
||||||
|
[StorageType.DRAWER]: 'Drawer',
|
||||||
|
[StorageType.SHELF]: 'Shelf',
|
||||||
|
[StorageType.ASSEMBLY]: 'Assembly',
|
||||||
|
[StorageType.CUPBOARD]: 'Cupboard',
|
||||||
|
[StorageType.CLOSET]: 'Closet',
|
||||||
|
[StorageType.FRIDGE]: 'Fridge',
|
||||||
|
[StorageType.FREEZER]: 'Freezer',
|
||||||
|
[StorageType.TABLE]: 'Table',
|
||||||
|
[StorageType.BOX]: 'Box',
|
||||||
|
[StorageType.VIRTUAL]: 'Virtual',
|
||||||
|
[StorageType.PERSON]: 'Person',
|
||||||
|
[StorageType.OTHER]: 'Other',
|
||||||
|
};
|
||||||
|
@ -6,3 +6,24 @@ export enum TransactionType {
|
|||||||
MOVED = 'MOVED',
|
MOVED = 'MOVED',
|
||||||
TRANSFERRED = 'TRANSFERRED',
|
TRANSFERRED = 'TRANSFERRED',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const TransactionTypeName: Record<TransactionType, string> = {
|
||||||
|
[TransactionType.ACQUIRED]: 'Acquired / Purchased',
|
||||||
|
[TransactionType.SOLD]: 'Sold',
|
||||||
|
[TransactionType.DESTROYED]: 'Destroyed (got rid of)',
|
||||||
|
[TransactionType.BINNED]: 'Binned (threw away)',
|
||||||
|
[TransactionType.MOVED]: 'Moved',
|
||||||
|
[TransactionType.TRANSFERRED]: 'Transferred',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TransactionTypeDescription: Record<TransactionType, string> = {
|
||||||
|
[TransactionType.ACQUIRED]:
|
||||||
|
'Items were received into your possession by any means',
|
||||||
|
[TransactionType.SOLD]:
|
||||||
|
'Item was sold, optionally mark proceeds with negative price',
|
||||||
|
[TransactionType.DESTROYED]:
|
||||||
|
'Burned, broken beyond recognition, unusable etc',
|
||||||
|
[TransactionType.BINNED]: 'Thrown away for recycle or otherwise',
|
||||||
|
[TransactionType.MOVED]: 'Moved to another place / storage',
|
||||||
|
[TransactionType.TRANSFERRED]: 'Transferred to another person / location',
|
||||||
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user