@raclettejs/workbench 0.0.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/README.md +1 -0
- package/config/compositions.js +282 -0
- package/config/interactionLinks.js +164 -0
- package/i18n/de-DE.json +245 -0
- package/i18n/en-EU.json +245 -0
- package/i18n/sk-SK.json +245 -0
- package/package.json +63 -0
- package/packages.json +33 -0
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/CompositionConfiguration.vue +140 -0
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/CompositionIntegration.vue +3 -0
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/CompositionWidgetsLayout.vue +129 -0
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetContextMenu.vue +53 -0
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetIcon.vue +99 -0
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetList.vue +122 -0
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetRenderer.vue +65 -0
- package/plugins/pacifico__compositions/frontend/components/compositionConfiguration/widgetsLayout/WidgetSettings.vue +96 -0
- package/plugins/pacifico__compositions/frontend/widgets/CompositionCreateWidget.vue +51 -0
- package/plugins/pacifico__compositions/frontend/widgets/CompositionEditWidget.vue +66 -0
- package/plugins/pacifico__compositions/frontend/widgets/CompositionListWidget.vue +132 -0
- package/plugins/pacifico__compositions/raclette.plugin.ts +8 -0
- package/plugins/pacifico__core/backend/index.ts +25 -0
- package/plugins/pacifico__core/backend/routes.ts +220 -0
- package/plugins/pacifico__core/raclette.plugin.ts +9 -0
- package/plugins/pacifico__interactionLinks/frontend/components/InteractionLinkConfiguration.vue +145 -0
- package/plugins/pacifico__interactionLinks/frontend/widgets/InteractionLinkCreateWidget.vue +48 -0
- package/plugins/pacifico__interactionLinks/frontend/widgets/InteractionLinkEditWidget.vue +66 -0
- package/plugins/pacifico__interactionLinks/frontend/widgets/InteractionLinkListWidget.vue +126 -0
- package/plugins/pacifico__interactionLinks/raclette.plugin.ts +8 -0
- package/plugins/pacifico__plugins/frontend/index.ts +42 -0
- package/plugins/pacifico__plugins/frontend/widgets/PluginDetailWidget.vue +26 -0
- package/plugins/pacifico__plugins/frontend/widgets/PluginListWidget.vue +59 -0
- package/plugins/pacifico__plugins/raclette.plugin.ts +8 -0
- package/plugins/pacifico__tags/frontend/components/TagConfiguration.vue +99 -0
- package/plugins/pacifico__tags/frontend/widgets/TagCreateWidget.vue +43 -0
- package/plugins/pacifico__tags/frontend/widgets/TagEditWidget.vue +64 -0
- package/plugins/pacifico__tags/frontend/widgets/TagListWidget.vue +71 -0
- package/plugins/pacifico__tags/raclette.plugin.ts +8 -0
- package/plugins/pacifico__users/frontend/components/UserConfiguration.vue +110 -0
- package/plugins/pacifico__users/frontend/widgets/UserCreateWidget.vue +48 -0
- package/plugins/pacifico__users/frontend/widgets/UserEditWidget.vue +64 -0
- package/plugins/pacifico__users/frontend/widgets/UserListWidget.vue +86 -0
- package/plugins/pacifico__users/raclette.plugin.ts +8 -0
- package/raclette.config.js +150 -0
- package/services/frontend/.DS_Store +0 -0
- package/services/frontend/public/favicon.png +0 -0
- package/services/frontend/public/home-icon.png +0 -0
- package/services/frontend/public/logo.png +0 -0
- package/services/frontend/src/app/.DS_Store +0 -0
- package/services/frontend/src/app/components/BaseDataTable.vue +284 -0
- package/services/frontend/src/app/components/dynamicForm/DynamicForm.vue +207 -0
- package/services/frontend/src/app/components/dynamicForm/DynamicFormTypes.ts +63 -0
- package/services/frontend/src/app/components/dynamicForm/DynamicInput.vue +344 -0
- package/services/frontend/src/app/components/dynamicForm/TextDivider.vue +13 -0
- package/services/frontend/src/app/components/dynamicForm/testing/DynamicFormInputTesting.vue +477 -0
- package/services/frontend/src/app/components/dynamicForm/typedInputs/CheckboxInput.vue +16 -0
- package/services/frontend/src/app/components/dynamicForm/typedInputs/ColorInput.vue +52 -0
- package/services/frontend/src/app/components/dynamicForm/typedInputs/CompositionInput.vue +32 -0
- package/services/frontend/src/app/components/dynamicForm/typedInputs/IconInput.vue +40 -0
- package/services/frontend/src/app/components/dynamicForm/typedInputs/ListInput.vue +51 -0
- package/services/frontend/src/app/components/dynamicForm/typedInputs/LocalizedInput.vue +51 -0
- package/services/frontend/src/app/components/dynamicForm/typedInputs/PasswordInput.vue +23 -0
- package/services/frontend/src/app/components/dynamicForm/typedInputs/RecordInput.vue +91 -0
- package/services/frontend/src/app/components/dynamicForm/typedInputs/SwitchInput.vue +17 -0
- package/services/frontend/src/app/components/dynamicForm/typedInputs/TagsInput.vue +58 -0
- package/services/frontend/src/app/components/dynamicForm/typedInputs/TriggerInput.vue +159 -0
- package/services/frontend/src/app/components/dynamicForm/typedInputs/UserInput.vue +63 -0
- package/services/frontend/src/app/components/dynamicForm/typedInputs/mdi-icons.ts +7002 -0
- package/services/frontend/src/app/components/dynamicForm/validators.ts +19 -0
- package/services/frontend/src/app/components/stepNavigator/StepNavigator.vue +331 -0
- package/services/frontend/src/app/components/stepNavigator/StepNavigatorTypes.ts +12 -0
- package/services/frontend/src/app/composables/useUserHelper.ts +254 -0
- package/services/frontend/src/app/composables/useVantaFog.ts +37 -0
- package/services/frontend/src/app/composables/useWorkbenchPlugins.ts +44 -0
- package/services/frontend/src/orchestrator/.DS_Store +0 -0
- package/services/frontend/src/orchestrator/LoginApp.vue +62 -0
- package/services/frontend/src/orchestrator/WelcomeScreen.vue +39 -0
- package/services/frontend/src/orchestrator/assets/styles/themes/dark.ts +39 -0
- package/services/frontend/src/orchestrator/assets/styles/themes/highContrast.ts +39 -0
- package/services/frontend/src/orchestrator/assets/styles/themes/light.ts +39 -0
- package/services/frontend/src/orchestrator/components/InitProjectScreen.vue +298 -0
- package/services/frontend/src/orchestrator/components/LoginScreen.vue +73 -0
- package/services/frontend/src/orchestrator/components/RacletteAdCorner.vue +313 -0
- package/services/frontend/src/orchestrator/components/layout/AppButton.vue +109 -0
- package/services/frontend/src/orchestrator/components/layout/AppHeadline.vue +46 -0
- package/services/frontend/src/orchestrator/components/layout/AppLabelledInput.vue +94 -0
- package/services/frontend/src/orchestrator/components/layout/AppModal.vue +28 -0
- package/services/frontend/src/orchestrator/components/layout/SvgButtonArrow.vue +14 -0
- package/services/frontend/src/orchestrator/composables/useWelcomeScreen.ts +19 -0
- package/yarn.lock +3435 -0
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="tw:max-w-4xl tw:mx-auto tw:p-6">
|
|
3
|
+
<div class="tw:mb-6">
|
|
4
|
+
<h1 class="tw:text-2xl tw:font-bold tw:mb-2">
|
|
5
|
+
Dynamic Form Validation Test
|
|
6
|
+
</h1>
|
|
7
|
+
<p class="tw:text-gray-600">
|
|
8
|
+
This form demonstrates all input types with various validation rules.
|
|
9
|
+
</p>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<!-- Validation Status -->
|
|
13
|
+
<div class="tw:mb-6 tw:p-4 tw:rounded-lg" :class="validationStatusClass">
|
|
14
|
+
<div class="tw:flex tw:items-center tw:gap-2">
|
|
15
|
+
<div
|
|
16
|
+
class="tw:w-3 tw:h-3 tw:rounded-full"
|
|
17
|
+
:class="validationIndicatorClass"
|
|
18
|
+
></div>
|
|
19
|
+
<span class="tw:font-medium">{{ validationStatusText }}</span>
|
|
20
|
+
</div>
|
|
21
|
+
<div
|
|
22
|
+
v-if="!isValid && Object.keys(errors).length > 0"
|
|
23
|
+
class="tw:mt-2 tw:text-sm"
|
|
24
|
+
>
|
|
25
|
+
<strong>Errors:</strong>
|
|
26
|
+
<ul class="tw:list-disc tw:list-inside tw:mt-1">
|
|
27
|
+
<li v-for="(error, field) in errors" :key="field">
|
|
28
|
+
{{ field }}: {{ error }}
|
|
29
|
+
</li>
|
|
30
|
+
</ul>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<div>
|
|
35
|
+
<v-number-input
|
|
36
|
+
v-model="inputFilter"
|
|
37
|
+
variant="outlined"
|
|
38
|
+
:min="0"
|
|
39
|
+
:max="inputFields.filter((input) => !('divider' in input)).length"
|
|
40
|
+
/>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<!-- The Dynamic Form -->
|
|
44
|
+
<DynamicForm
|
|
45
|
+
ref="dynamicFormRef"
|
|
46
|
+
v-model="testData"
|
|
47
|
+
:data="filteredInputs"
|
|
48
|
+
:disabled="false"
|
|
49
|
+
@validation-change="onValidationChange"
|
|
50
|
+
/>
|
|
51
|
+
|
|
52
|
+
<!-- Action Buttons -->
|
|
53
|
+
<div class="tw:mt-8 tw:flex tw:gap-4">
|
|
54
|
+
<v-btn @click="validateForm" color="primary" variant="outlined">
|
|
55
|
+
Validate Form
|
|
56
|
+
</v-btn>
|
|
57
|
+
<v-btn @click="resetForm" color="secondary" variant="outlined">
|
|
58
|
+
Reset Form
|
|
59
|
+
</v-btn>
|
|
60
|
+
<v-btn
|
|
61
|
+
@click="showFormData = !showFormData"
|
|
62
|
+
color="info"
|
|
63
|
+
variant="outlined"
|
|
64
|
+
>
|
|
65
|
+
{{ showFormData ? "Hide" : "Show" }} Form Data
|
|
66
|
+
</v-btn>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<!-- Form Data Display -->
|
|
70
|
+
<div v-if="showFormData" class="tw:mt-6 tw:p-4 tw:bg-gray-50 tw:rounded-lg">
|
|
71
|
+
<h3 class="tw:text-lg tw:font-semibold tw:mb-2">Current Form Data:</h3>
|
|
72
|
+
<pre class="tw:text-sm tw:overflow-auto">{{
|
|
73
|
+
JSON.stringify(testData, null, 2)
|
|
74
|
+
}}</pre>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
</template>
|
|
78
|
+
|
|
79
|
+
<script setup lang="ts">
|
|
80
|
+
import { computed, ref, reactive, useTemplateRef } from "vue"
|
|
81
|
+
import DynamicForm from "@app/components/dynamicForm/DynamicForm.vue"
|
|
82
|
+
import type { DynamicFormItem } from "@app/components/dynamicForm/DynamicFormTypes"
|
|
83
|
+
import type { Trigger } from "@raclettejs/core"
|
|
84
|
+
import { emailValidator } from "../validators"
|
|
85
|
+
|
|
86
|
+
// Test data interface
|
|
87
|
+
interface TestFormData {
|
|
88
|
+
// Text inputs
|
|
89
|
+
requiredText: string
|
|
90
|
+
optionalText: string
|
|
91
|
+
textWithLength: string
|
|
92
|
+
textWithPattern: string
|
|
93
|
+
|
|
94
|
+
// Textarea
|
|
95
|
+
description: string
|
|
96
|
+
|
|
97
|
+
// Number
|
|
98
|
+
requiredNumber: number
|
|
99
|
+
numberWithRange: number
|
|
100
|
+
|
|
101
|
+
// Select inputs
|
|
102
|
+
singleSelect: string
|
|
103
|
+
multiSelect: string[]
|
|
104
|
+
|
|
105
|
+
// Boolean inputs
|
|
106
|
+
requiredSwitch: boolean
|
|
107
|
+
optionalCheckbox: boolean
|
|
108
|
+
|
|
109
|
+
// Array inputs
|
|
110
|
+
tags: string[]
|
|
111
|
+
permissions: string[]
|
|
112
|
+
listItems: string[]
|
|
113
|
+
|
|
114
|
+
// Object inputs
|
|
115
|
+
localized: Record<string, string>
|
|
116
|
+
record: Record<string, string>
|
|
117
|
+
|
|
118
|
+
// Special inputs
|
|
119
|
+
password: string
|
|
120
|
+
color: string
|
|
121
|
+
user: string
|
|
122
|
+
composition: string
|
|
123
|
+
triggers: Trigger[]
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const dynamicFormRef = useTemplateRef("dynamicFormRef")
|
|
127
|
+
const isValid = ref(true)
|
|
128
|
+
const errors = ref<Record<string, string>>({})
|
|
129
|
+
const showFormData = ref(false)
|
|
130
|
+
const inputFilter = ref(0)
|
|
131
|
+
|
|
132
|
+
// Test data with initial values
|
|
133
|
+
const testData = reactive<TestFormData>({
|
|
134
|
+
requiredText: "",
|
|
135
|
+
optionalText: "",
|
|
136
|
+
textWithLength: "",
|
|
137
|
+
textWithPattern: "",
|
|
138
|
+
description: "",
|
|
139
|
+
requiredNumber: 0,
|
|
140
|
+
numberWithRange: 0,
|
|
141
|
+
singleSelect: "",
|
|
142
|
+
multiSelect: [],
|
|
143
|
+
requiredSwitch: false,
|
|
144
|
+
optionalCheckbox: false,
|
|
145
|
+
tags: [],
|
|
146
|
+
permissions: [],
|
|
147
|
+
listItems: [],
|
|
148
|
+
localized: { default: "", de: "", en: "" },
|
|
149
|
+
record: {},
|
|
150
|
+
password: "",
|
|
151
|
+
color: "#000000",
|
|
152
|
+
user: "",
|
|
153
|
+
composition: "",
|
|
154
|
+
triggers: [],
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
// Validation status computed properties
|
|
158
|
+
const validationStatusClass = computed(() => ({
|
|
159
|
+
"tw:bg-green-50 tw:border tw:border-green-200": isValid.value,
|
|
160
|
+
"tw:bg-red-50 tw:border tw:border-red-200": !isValid.value,
|
|
161
|
+
}))
|
|
162
|
+
|
|
163
|
+
const validationIndicatorClass = computed(() => ({
|
|
164
|
+
"tw:bg-green-500": isValid.value,
|
|
165
|
+
"tw:bg-red-500": !isValid.value,
|
|
166
|
+
}))
|
|
167
|
+
|
|
168
|
+
const validationStatusText = computed(() =>
|
|
169
|
+
isValid.value ? "Form is valid ✓" : "Form has validation errors ✗",
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
const filteredInputs = computed(() => {
|
|
173
|
+
if (inputFilter.value === 0) {
|
|
174
|
+
return inputFields.value
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const result: typeof inputFields.value = []
|
|
178
|
+
let count = 0
|
|
179
|
+
|
|
180
|
+
for (const input of inputFields.value) {
|
|
181
|
+
result.push(input)
|
|
182
|
+
|
|
183
|
+
if (!("divider" in input)) {
|
|
184
|
+
count++
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (count >= inputFilter.value) {
|
|
188
|
+
break
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return result
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
const onValidationChange = (valid: boolean) => {
|
|
196
|
+
isValid.value = valid
|
|
197
|
+
errors.value = dynamicFormRef.value?.errors ?? {}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const validateForm = () => {
|
|
201
|
+
dynamicFormRef.value?.validateAllFields()
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const resetForm = () => {
|
|
205
|
+
Object.assign(testData, {
|
|
206
|
+
requiredText: "",
|
|
207
|
+
optionalText: "",
|
|
208
|
+
textWithLength: "",
|
|
209
|
+
textWithPattern: "",
|
|
210
|
+
description: "",
|
|
211
|
+
requiredNumber: 0,
|
|
212
|
+
numberWithRange: 0,
|
|
213
|
+
singleSelect: "",
|
|
214
|
+
multiSelect: [],
|
|
215
|
+
requiredSwitch: false,
|
|
216
|
+
optionalCheckbox: false,
|
|
217
|
+
tags: [],
|
|
218
|
+
permissions: [],
|
|
219
|
+
listItems: [],
|
|
220
|
+
localized: {},
|
|
221
|
+
record: {},
|
|
222
|
+
password: "",
|
|
223
|
+
color: "#000000",
|
|
224
|
+
user: "",
|
|
225
|
+
composition: "",
|
|
226
|
+
triggers: [],
|
|
227
|
+
})
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Form field definitions
|
|
231
|
+
const inputFields = computed<DynamicFormItem<TestFormData>[]>(() => [
|
|
232
|
+
{ divider: "Text Inputs" },
|
|
233
|
+
{
|
|
234
|
+
title: "Required Text",
|
|
235
|
+
description: "This field is required and must be at least 3 characters",
|
|
236
|
+
inputType: "text",
|
|
237
|
+
field: "requiredText",
|
|
238
|
+
prependIcon: "text",
|
|
239
|
+
validation: {
|
|
240
|
+
required: true,
|
|
241
|
+
minLength: 3,
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
title: "Optional Text",
|
|
246
|
+
description: "This field is optional but has a max length of 50 characters",
|
|
247
|
+
inputType: "text",
|
|
248
|
+
field: "optionalText",
|
|
249
|
+
prependIcon: "text-short",
|
|
250
|
+
validation: {
|
|
251
|
+
maxLength: 50,
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
title: "Text with Length Validation",
|
|
256
|
+
description: "Must be between 5 and 20 characters",
|
|
257
|
+
inputType: "text",
|
|
258
|
+
field: "textWithLength",
|
|
259
|
+
prependIcon: "ruler",
|
|
260
|
+
validation: {
|
|
261
|
+
minLength: 5,
|
|
262
|
+
maxLength: 20,
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
title: "Email Address",
|
|
267
|
+
description: "Must be a valid email format",
|
|
268
|
+
inputType: "text",
|
|
269
|
+
field: "textWithPattern",
|
|
270
|
+
prependIcon: "email",
|
|
271
|
+
validation: {
|
|
272
|
+
required: true,
|
|
273
|
+
customValidator: emailValidator,
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
|
|
277
|
+
{ divider: "Textarea" },
|
|
278
|
+
{
|
|
279
|
+
title: "Description",
|
|
280
|
+
description: "Optional description with max 200 characters",
|
|
281
|
+
inputType: "textarea",
|
|
282
|
+
field: "description",
|
|
283
|
+
prependIcon: "text",
|
|
284
|
+
validation: {
|
|
285
|
+
maxLength: 200,
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
|
|
289
|
+
{ divider: "Number Inputs" },
|
|
290
|
+
{
|
|
291
|
+
title: "Required Number",
|
|
292
|
+
description: "This number field is required",
|
|
293
|
+
inputType: "number",
|
|
294
|
+
field: "requiredNumber",
|
|
295
|
+
prependIcon: "numeric",
|
|
296
|
+
validation: {
|
|
297
|
+
required: true,
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
title: "Number with Range",
|
|
302
|
+
description: "Must be between 10 and 100",
|
|
303
|
+
inputType: "number",
|
|
304
|
+
field: "numberWithRange",
|
|
305
|
+
prependIcon: "numeric-1",
|
|
306
|
+
validation: {
|
|
307
|
+
customValidator: (value: number) => {
|
|
308
|
+
if (value < 10 || value > 100) {
|
|
309
|
+
return "Number must be between 10 and 100"
|
|
310
|
+
}
|
|
311
|
+
return null
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
},
|
|
315
|
+
|
|
316
|
+
{ divider: "Select Inputs" },
|
|
317
|
+
{
|
|
318
|
+
title: "Single Select",
|
|
319
|
+
description: "Choose one option (required)",
|
|
320
|
+
inputType: "select",
|
|
321
|
+
field: "singleSelect",
|
|
322
|
+
selectItems: ["Option 1", "Option 2", "Option 3", "Option 4"],
|
|
323
|
+
prependIcon: "form-select",
|
|
324
|
+
validation: {
|
|
325
|
+
required: true,
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
title: "Multi Select",
|
|
330
|
+
description: "Choose multiple options (at least 2 required)",
|
|
331
|
+
inputType: "multiselect",
|
|
332
|
+
field: "multiSelect",
|
|
333
|
+
selectItems: ["Tag A", "Tag B", "Tag C", "Tag D", "Tag E"],
|
|
334
|
+
prependIcon: "form-select",
|
|
335
|
+
validation: {
|
|
336
|
+
required: true,
|
|
337
|
+
customValidator: (value: string[]) => {
|
|
338
|
+
if (value.length < 2) {
|
|
339
|
+
return "Please select at least 2 options"
|
|
340
|
+
}
|
|
341
|
+
return null
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
|
|
346
|
+
{ divider: "Boolean Inputs" },
|
|
347
|
+
{
|
|
348
|
+
title: "Required Switch",
|
|
349
|
+
description: "This switch must be enabled",
|
|
350
|
+
inputType: "switch",
|
|
351
|
+
field: "requiredSwitch",
|
|
352
|
+
validation: {
|
|
353
|
+
required: true,
|
|
354
|
+
customValidator: (value: boolean) =>
|
|
355
|
+
value ? null : "This switch must be enabled",
|
|
356
|
+
},
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
title: "Optional Checkbox",
|
|
360
|
+
description: "This checkbox is optional",
|
|
361
|
+
inputType: "checkbox",
|
|
362
|
+
field: "optionalCheckbox",
|
|
363
|
+
},
|
|
364
|
+
|
|
365
|
+
{ divider: "Array Inputs" },
|
|
366
|
+
{
|
|
367
|
+
title: "Tags",
|
|
368
|
+
description: "Add at least 2 tags",
|
|
369
|
+
inputType: "tags",
|
|
370
|
+
field: "tags",
|
|
371
|
+
validation: {
|
|
372
|
+
required: true,
|
|
373
|
+
customValidator: (value: string[]) => {
|
|
374
|
+
if (value.length < 2) {
|
|
375
|
+
return "Please add at least 2 tags"
|
|
376
|
+
}
|
|
377
|
+
return null
|
|
378
|
+
},
|
|
379
|
+
},
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
title: "Permissions",
|
|
383
|
+
description: "Optional permissions list",
|
|
384
|
+
inputType: "permissions",
|
|
385
|
+
field: "permissions",
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
title: "List Items",
|
|
389
|
+
description: "Optional list of items",
|
|
390
|
+
inputType: "list",
|
|
391
|
+
field: "listItems",
|
|
392
|
+
validation: {
|
|
393
|
+
required: true,
|
|
394
|
+
},
|
|
395
|
+
},
|
|
396
|
+
|
|
397
|
+
{ divider: "Object Inputs" },
|
|
398
|
+
{
|
|
399
|
+
title: "Localized Content",
|
|
400
|
+
description: "Add content in at least one language",
|
|
401
|
+
inputType: "localized",
|
|
402
|
+
field: "localized",
|
|
403
|
+
validation: {
|
|
404
|
+
required: true,
|
|
405
|
+
customValidator: (value: Record<string, string>) => {
|
|
406
|
+
if (!value || !value.default?.length) {
|
|
407
|
+
return "A default localization is required"
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
return null
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
title: "Record Data",
|
|
416
|
+
description: "Optional key-value pairs",
|
|
417
|
+
inputType: "record",
|
|
418
|
+
field: "record",
|
|
419
|
+
},
|
|
420
|
+
|
|
421
|
+
{ divider: "Special Inputs" },
|
|
422
|
+
{
|
|
423
|
+
title: "Password",
|
|
424
|
+
description: "Must be at least 8 characters with special characters",
|
|
425
|
+
inputType: "password",
|
|
426
|
+
field: "password",
|
|
427
|
+
prependIcon: "lock",
|
|
428
|
+
validation: {
|
|
429
|
+
required: true,
|
|
430
|
+
minLength: 8,
|
|
431
|
+
customValidator: (value: string) => {
|
|
432
|
+
if (!/(?=.*[!@#$%^&*])/.test(value)) {
|
|
433
|
+
return "Password must contain at least one special character (!@#$%^&*)"
|
|
434
|
+
}
|
|
435
|
+
return null
|
|
436
|
+
},
|
|
437
|
+
},
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
title: "Color",
|
|
441
|
+
description: "Choose a color (required)",
|
|
442
|
+
inputType: "color",
|
|
443
|
+
field: "color",
|
|
444
|
+
prependIcon: "palette",
|
|
445
|
+
validation: {
|
|
446
|
+
required: true,
|
|
447
|
+
},
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
title: "User",
|
|
451
|
+
description: "Select a user (required)",
|
|
452
|
+
inputType: "user",
|
|
453
|
+
field: "user",
|
|
454
|
+
prependIcon: "account",
|
|
455
|
+
validation: {
|
|
456
|
+
required: true,
|
|
457
|
+
},
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
title: "Composition",
|
|
461
|
+
description: "Optional composition field",
|
|
462
|
+
inputType: "composition",
|
|
463
|
+
field: "composition",
|
|
464
|
+
prependIcon: "file-document",
|
|
465
|
+
validation: {
|
|
466
|
+
required: true,
|
|
467
|
+
},
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
title: "Triggers",
|
|
471
|
+
description: "Optional triggers configuration",
|
|
472
|
+
inputType: "triggers",
|
|
473
|
+
field: "triggers",
|
|
474
|
+
prependIcon: "gesture-tap-button",
|
|
475
|
+
},
|
|
476
|
+
])
|
|
477
|
+
</script>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-checkbox
|
|
3
|
+
v-model="modelValue"
|
|
4
|
+
:id="inputId"
|
|
5
|
+
variant="outlined"
|
|
6
|
+
hide-details
|
|
7
|
+
/>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<script setup lang="ts">
|
|
11
|
+
defineProps<{
|
|
12
|
+
inputId: string
|
|
13
|
+
}>()
|
|
14
|
+
|
|
15
|
+
const modelValue = defineModel<boolean>({ default: false })
|
|
16
|
+
</script>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-menu v-model="showPicker" :close-on-content-click="false">
|
|
3
|
+
<template #activator="{ props }">
|
|
4
|
+
<v-text-field
|
|
5
|
+
v-model="color"
|
|
6
|
+
v-bind="{ ...props, ...$attrs }"
|
|
7
|
+
:label="$t('color')"
|
|
8
|
+
:color="color"
|
|
9
|
+
variant="outlined"
|
|
10
|
+
hide-details
|
|
11
|
+
:append-inner-icon="
|
|
12
|
+
!showPicker ? 'mdi-chevron-left' : 'mdi-chevron-down'
|
|
13
|
+
"
|
|
14
|
+
readonly
|
|
15
|
+
>
|
|
16
|
+
<template #prepend-inner>
|
|
17
|
+
<v-chip
|
|
18
|
+
:style="{
|
|
19
|
+
'background-color': color,
|
|
20
|
+
}"
|
|
21
|
+
size="x-small"
|
|
22
|
+
:color="color"
|
|
23
|
+
label
|
|
24
|
+
class="tw:m-2 tw:w-[45px]"
|
|
25
|
+
/>
|
|
26
|
+
</template>
|
|
27
|
+
</v-text-field>
|
|
28
|
+
</template>
|
|
29
|
+
<v-color-picker v-model="color" show-swatches />
|
|
30
|
+
<template #header></template>
|
|
31
|
+
</v-menu>
|
|
32
|
+
</template>
|
|
33
|
+
|
|
34
|
+
<script setup lang="ts">
|
|
35
|
+
import { ref } from "vue"
|
|
36
|
+
|
|
37
|
+
defineOptions({
|
|
38
|
+
inheritAttrs: false,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
defineProps({
|
|
42
|
+
readOnly: Boolean,
|
|
43
|
+
inputId: {
|
|
44
|
+
type: String,
|
|
45
|
+
default: "",
|
|
46
|
+
},
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
const color = defineModel<string>({ required: true })
|
|
50
|
+
|
|
51
|
+
const showPicker = ref(false)
|
|
52
|
+
</script>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-autocomplete
|
|
3
|
+
v-model="modelValue"
|
|
4
|
+
:id="inputId"
|
|
5
|
+
:items="compositions"
|
|
6
|
+
:loading="query.loading"
|
|
7
|
+
:disabled="query.loading"
|
|
8
|
+
item-value="_id"
|
|
9
|
+
item-title="title"
|
|
10
|
+
variant="outlined"
|
|
11
|
+
hide-details
|
|
12
|
+
/>
|
|
13
|
+
</template>
|
|
14
|
+
|
|
15
|
+
<script setup lang="ts">
|
|
16
|
+
import { usePluginApi } from "@raclettejs/core/orchestrator/composables"
|
|
17
|
+
import { computed } from "vue"
|
|
18
|
+
|
|
19
|
+
defineProps<{
|
|
20
|
+
inputId: string
|
|
21
|
+
}>()
|
|
22
|
+
|
|
23
|
+
const modelValue = defineModel<string | null>({ required: true })
|
|
24
|
+
|
|
25
|
+
const { $data } = usePluginApi("raclette__core")
|
|
26
|
+
|
|
27
|
+
const { data, query } = $data.composition.getAll({
|
|
28
|
+
options: { immediate: true },
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const compositions = computed(() => Object.values(data.value || {}))
|
|
32
|
+
</script>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-autocomplete
|
|
3
|
+
v-model="modelProxy"
|
|
4
|
+
:items="icons"
|
|
5
|
+
label="Icon"
|
|
6
|
+
item-title="name"
|
|
7
|
+
item-value="name"
|
|
8
|
+
variant="outlined"
|
|
9
|
+
hide-details
|
|
10
|
+
:menu-props="{ maxHeight: 400 }"
|
|
11
|
+
:virtual-scroll="true"
|
|
12
|
+
>
|
|
13
|
+
<template #item="{ props, item }">
|
|
14
|
+
<v-list-item v-bind="props" :prepend-icon="`mdi-${item.raw}`" />
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
<template #selection="{ item }">
|
|
18
|
+
<v-icon :icon="`mdi-${item.value}`" class="me-2" />
|
|
19
|
+
{{ item.value }}
|
|
20
|
+
</template>
|
|
21
|
+
</v-autocomplete>
|
|
22
|
+
</template>
|
|
23
|
+
|
|
24
|
+
<script setup lang="ts">
|
|
25
|
+
import icons from "./mdi-icons"
|
|
26
|
+
import { computed } from "vue"
|
|
27
|
+
|
|
28
|
+
const modelValue = defineModel<string | null>()
|
|
29
|
+
|
|
30
|
+
const modelProxy = computed({
|
|
31
|
+
get() {
|
|
32
|
+
return modelValue.value?.startsWith("mdi-")
|
|
33
|
+
? modelValue.value.slice(4)
|
|
34
|
+
: modelValue.value
|
|
35
|
+
},
|
|
36
|
+
set(newValue: string | null) {
|
|
37
|
+
modelValue.value = newValue ? `mdi-${newValue}` : null
|
|
38
|
+
},
|
|
39
|
+
})
|
|
40
|
+
</script>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="tw:grid tw:gap-2">
|
|
3
|
+
<!-- existing tags -->
|
|
4
|
+
<v-text-field
|
|
5
|
+
v-for="(_, index) in modelValue"
|
|
6
|
+
v-bind="$attrs"
|
|
7
|
+
:key="index"
|
|
8
|
+
:id="index === 0 ? inputId : undefined"
|
|
9
|
+
v-model="modelValue[index]"
|
|
10
|
+
variant="outlined"
|
|
11
|
+
append-icon="mdi-minus"
|
|
12
|
+
@click:append="() => modelValue.length > 1 && modelValue.splice(index, 1)"
|
|
13
|
+
/>
|
|
14
|
+
|
|
15
|
+
<!-- add new entry -->
|
|
16
|
+
<v-text-field
|
|
17
|
+
:id="modelValue?.length === 0 ? inputId : undefined"
|
|
18
|
+
v-bind="$attrs"
|
|
19
|
+
v-model="virtualInput"
|
|
20
|
+
variant="outlined"
|
|
21
|
+
hide-details
|
|
22
|
+
append-icon="mdi-plus"
|
|
23
|
+
@click:append="addVirtualInput"
|
|
24
|
+
@blur="addVirtualInput"
|
|
25
|
+
/>
|
|
26
|
+
</div>
|
|
27
|
+
</template>
|
|
28
|
+
|
|
29
|
+
<script setup lang="ts">
|
|
30
|
+
import { ref } from "vue"
|
|
31
|
+
|
|
32
|
+
defineOptions({
|
|
33
|
+
inheritAttrs: false,
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
defineProps<{
|
|
37
|
+
inputId?: string
|
|
38
|
+
}>()
|
|
39
|
+
|
|
40
|
+
const modelValue = defineModel<string[]>({ required: true })
|
|
41
|
+
|
|
42
|
+
const virtualInput = ref("")
|
|
43
|
+
|
|
44
|
+
const addVirtualInput = () => {
|
|
45
|
+
const val = virtualInput.value.trim()
|
|
46
|
+
if (val !== "") {
|
|
47
|
+
modelValue.value.push(val)
|
|
48
|
+
virtualInput.value = ""
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
</script>
|