0351d front end linting (#377)

* feat: disable custom script for  trial users

* after lint fix

* frontend linting

---------

Co-authored-by: Julien Nahum <julien@nahum.net>
This commit is contained in:
Favour Olayinka
2024-04-15 18:39:03 +01:00
committed by GitHub
parent 8d35fc8b1a
commit bcd45ce8a6
228 changed files with 17036 additions and 8744 deletions

View File

@@ -1,47 +1,64 @@
<template>
<div v-if="content" class="flex flex-wrap">
<div
v-if="content"
class="flex flex-wrap"
>
<div class="w-full font-semibold text-gray-700 dark:text-gray-300 mb-2">
{{ property.name }}
</div>
<SelectInput v-model="content.operator" class="w-full" :options="operators" :name="'operator_' + property.id"
placeholder="Comparison operator" @update:model-value="operatorChanged()" />
<SelectInput
v-model="content.operator"
class="w-full"
:options="operators"
:name="'operator_' + property.id"
placeholder="Comparison operator"
@update:model-value="operatorChanged()"
/>
<template v-if="needsInput">
<component v-bind="inputComponentData" :is="inputComponentData.component" v-model="content.value" class="w-full"
:name="'value_' + property.id" placeholder="Filter Value" @update:model-value="emitInput()" />
<component
v-bind="inputComponentData"
:is="inputComponentData.component"
v-model="content.value"
class="w-full"
:name="'value_' + property.id"
placeholder="Filter Value"
@update:model-value="emitInput()"
/>
</template>
</div>
</template>
<script>
import OpenFilters from '../../../../../data/open_filters.json'
import OpenFilters from "../../../../../data/open_filters.json"
export default {
components: {},
props: {
modelValue: { required: true }
modelValue: { type: Object, required: true },
},
emits: ['update:modelValue'],
data() {
return {
content: { ...this.modelValue },
available_filters: OpenFilters,
hasInput: false,
inputComponent: {
text: 'TextInput',
number: 'TextInput',
rating: 'TextInput',
scale: 'TextInput',
slider: 'TextInput',
select: 'SelectInput',
multi_select: 'SelectInput',
date: 'DateInput',
files: 'FileInput',
checkbox: 'CheckboxInput',
url: 'TextInput',
email: 'TextInput',
phone_number: 'TextInput'
}
text: "TextInput",
number: "TextInput",
rating: "TextInput",
scale: "TextInput",
slider: "TextInput",
select: "SelectInput",
multi_select: "SelectInput",
date: "DateInput",
files: "FileInput",
checkbox: "CheckboxInput",
url: "TextInput",
email: "TextInput",
phone_number: "TextInput",
},
}
},
@@ -51,34 +68,41 @@ export default {
const componentData = {
component: this.inputComponent[this.property.type],
name: this.property.id,
required: true
required: true,
}
if (this.property.type === 'phone_number' && !this.property.use_simple_text_input) {
componentData.component = 'PhoneInput'
if (
this.property.type === "phone_number" &&
!this.property.use_simple_text_input
) {
componentData.component = "PhoneInput"
}
if (['select', 'multi_select'].includes(this.property.type)) {
if (["select", "multi_select"].includes(this.property.type)) {
componentData.multiple = false
componentData.options = this.property[this.property.type].options.map(option => {
return {
name: option.name,
value: option.name
}
})
} else if (this.property.type === 'date') {
componentData.options = this.property[this.property.type].options.map(
(option) => {
return {
name: option.name,
value: option.name,
}
},
)
} else if (this.property.type === "date") {
// componentData.withTime = true
} else if (this.property.type === 'checkbox') {
} else if (this.property.type === "checkbox") {
componentData.label = this.property.name
}
return componentData
},
operators() {
return Object.keys(this.available_filters[this.property.type].comparators).map(key => {
return Object.keys(
this.available_filters[this.property.type].comparators,
).map((key) => {
return {
value: key,
name: this.optionFilterNames(key, this.property.type)
name: this.optionFilterNames(key, this.property.type),
}
})
},
@@ -90,9 +114,17 @@ export default {
const operatorFormat = operator.format
if (!operatorFormat) return true
if (operator.expected_type === 'boolean' && operatorFormat.type === 'enum' && operatorFormat.values.length === 1) {
if (
operator.expected_type === "boolean" &&
operatorFormat.type === "enum" &&
operatorFormat.values.length === 1
) {
return false
} else if (operator.expected_type === 'object' && operatorFormat.type === 'empty' && operatorFormat.values === '{}') {
} else if (
operator.expected_type === "object" &&
operatorFormat.type === "empty" &&
operatorFormat.values === "{}"
) {
return false
}
@@ -104,21 +136,28 @@ export default {
modelValue() {
this.refreshContent()
},
'content.operator': function (val) {
"content.operator": function (val) {
if (val) {
this.operatorChanged()
}
}
},
},
mounted() {
this.refreshContent()
},
methods: {
castContent(content) {
if (['number', 'rating', 'scale', 'slider'].includes(this.property.type) && content.value) {
if (
["number", "rating", "scale", "slider"].includes(this.property.type) &&
content.value
) {
content.value = Number(content.value)
}
const operator = this.selectedOperator()
if (operator.expected_type === 'boolean') {
if (operator.expected_type === "boolean") {
content.value = Boolean(content.value)
}
@@ -132,11 +171,22 @@ export default {
const operator = this.selectedOperator()
const operatorFormat = operator.format
if (operator.expected_type === 'boolean' && operatorFormat.type === 'enum' && operatorFormat.values.length === 1) {
if (
operator.expected_type === "boolean" &&
operatorFormat.type === "enum" &&
operatorFormat.values.length === 1
) {
this.content.value = operator.format.values[0]
} else if (operator.expected_type === 'object' && operatorFormat.type === 'empty' && operatorFormat.values === '{}') {
} else if (
operator.expected_type === "object" &&
operatorFormat.type === "empty" &&
operatorFormat.values === "{}"
) {
this.content.value = {}
} else if (typeof this.content.value === 'boolean' || typeof this.content.value === 'object') {
} else if (
typeof this.content.value === "boolean" ||
typeof this.content.value === "object"
) {
this.content.value = null
}
this.emitInput()
@@ -145,21 +195,26 @@ export default {
if (!this.content.operator) {
return null
}
return this.available_filters[this.property.type].comparators[this.content.operator]
return this.available_filters[this.property.type].comparators[
this.content.operator
]
},
optionFilterNames(key, propertyType) {
if (propertyType === 'checkbox') {
if (propertyType === "checkbox") {
return {
equals: 'Is checked',
does_not_equal: 'Is not checked'
equals: "Is checked",
does_not_equal: "Is not checked",
}[key]
}
return key.split('_').map(function (item) {
return item.charAt(0).toUpperCase() + item.substring(1)
}).join(' ')
return key
.split("_")
.map(function (item) {
return item.charAt(0).toUpperCase() + item.substring(1)
})
.join(" ")
},
emitInput() {
this.$emit('update:modelValue', this.castContent(this.content))
this.$emit("update:modelValue", this.castContent(this.content))
},
refreshContent() {
this.content = {
@@ -167,14 +222,10 @@ export default {
...this.modelValue,
property_meta: {
id: this.property.id,
type: this.property.type
}
type: this.property.type,
},
}
}
},
},
mounted() {
this.refreshContent()
}
}
</script>

View File

@@ -1,7 +1,14 @@
<template>
<query-builder v-model="query" :rules="rules" :config="config" @update:model-value="onChange">
<query-builder
v-model="query"
:rules="rules"
:config="config"
@update:model-value="onChange"
>
<template #groupOperator="props">
<div class="query-builder-group-slot__group-selection flex items-center px-5 border-b py-1 mb-1 flex">
<div
class="query-builder-group-slot__group-selection flex items-center px-5 border-b py-1 mb-1 flex"
>
<p class="mr-2 font-semibold">
Operator
</p>
@@ -30,91 +37,100 @@
</query-builder>
</template>
<style src="query-builder-vue-3/dist/style.css" />
<script>
import { defineComponent } from 'vue'
import QueryBuilder from 'query-builder-vue-3'
import ColumnCondition from './ColumnCondition.vue'
import GroupControlSlot from './GroupControlSlot.vue'
/* eslint-disable vue/one-component-per-file */
import { defineComponent } from "vue"
import QueryBuilder from "query-builder-vue-3"
import ColumnCondition from "./ColumnCondition.vue"
import GroupControlSlot from "./GroupControlSlot.vue"
export default {
components: {
GroupControlSlot,
QueryBuilder,
ColumnCondition
ColumnCondition,
},
props: {
form: { type: Object, required: true },
modelValue: { required: false }
modelValue: { type: Object, required: false },
},
emits: ['update:modelValue'],
data () {
data() {
return {
query: this.modelValue
query: this.modelValue,
}
},
computed: {
rules () {
return this.form.properties.filter((property) => {
return !property.type.startsWith('nf-')
}).map((property) => {
const workspaceId = this.form.workspace_id
const formSlug = this.form.slug
return {
identifier: property.id,
name: property.name,
component: (function () {
return defineComponent({
extends: ColumnCondition,
computed: {
property () {
return property
rules() {
return this.form.properties
.filter((property) => {
return !property.type.startsWith("nf-")
})
.map((property) => {
const workspaceId = this.form.workspace_id
const formSlug = this.form.slug
return {
identifier: property.id,
name: property.name,
component: (function () {
return defineComponent({
extends: ColumnCondition,
computed: {
property() {
return property
},
viewContext() {
return {
form_slug: formSlug,
workspace_id: workspaceId,
}
},
},
viewContext () {
return {
form_slug: formSlug,
workspace_id: workspaceId
}
}
}
})
})()
}
})
})
})(),
}
})
},
config () {
config() {
return {
operators: [
{
name: 'And',
identifier: 'and'
name: "And",
identifier: "and",
},
{
name: 'Or',
identifier: 'or'
}
name: "Or",
identifier: "or",
},
],
rules: this.rules,
colors: ['#ef4444', '#22c55e', '#f97316', '#0ea5e9', '#8b5cf6', '#ec4899']
colors: [
"#ef4444",
"#22c55e",
"#f97316",
"#0ea5e9",
"#8b5cf6",
"#ec4899",
],
}
}
},
},
watch: {
modelValue () {
modelValue() {
this.query = this.modelValue
}
},
},
methods: {
onChange () {
this.$emit('update:modelValue', this.query)
}
}
onChange() {
this.$emit("update:modelValue", this.query)
},
},
}
</script>
<style src="query-builder-vue-3/dist/style.css" />

View File

@@ -1,24 +1,60 @@
<template>
<div v-if="logic" :key="resetKey">
<div
v-if="logic"
:key="resetKey"
>
<h3 class="font-semibold block text-lg">
Logic
</h3>
<p class="text-gray-400 text-xs mb-3">
Add some logic to this block. Start by adding some conditions, and then add some actions.
Add some logic to this block. Start by adding some conditions, and then
add some actions.
</p>
<div class="relative flex">
<div>
<v-button color="light-gray" size="small" @click="showCopyFormModal=true">
<svg class="h-4 w-4 text-blue-600 inline mr-1 -mt-1" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5 15H4C3.46957 15 2.96086 14.7893 2.58579 14.4142C2.21071 14.0391 2 13.5304 2 13V4C2 3.46957 2.21071 2.96086 2.58579 2.58579C2.96086 2.21071 3.46957 2 4 2H13C13.5304 2 14.0391 2.21071 14.4142 2.58579C14.7893 2.96086 15 3.46957 15 4V5M11 9H20C21.1046 9 22 9.89543 22 11V20C22 21.1046 21.1046 22 20 22H11C9.89543 22 9 21.1046 9 20V11C9 9.89543 9.89543 9 11 9Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
<v-button
color="light-gray"
size="small"
@click="showCopyFormModal = true"
>
<svg
class="h-4 w-4 text-blue-600 inline mr-1 -mt-1"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5 15H4C3.46957 15 2.96086 14.7893 2.58579 14.4142C2.21071 14.0391 2 13.5304 2 13V4C2 3.46957 2.21071 2.96086 2.58579 2.58579C2.96086 2.21071 3.46957 2 4 2H13C13.5304 2 14.0391 2.21071 14.4142 2.58579C14.7893 2.96086 15 3.46957 15 4V5M11 9H20C21.1046 9 22 9.89543 22 11V20C22 21.1046 21.1046 22 20 22H11C9.89543 22 9 21.1046 9 20V11C9 9.89543 9.89543 9 11 9Z"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
Copy from
</v-button>
</div>
<div>
<v-button color="light-gray" shade="light" size="small" class="ml-1" @click="clearAll">
<svg class="text-red-600 h-4 w-4 inline -mt-1 mr-1" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18 9L12 15M12 9L18 15M21 4H8L1 12L8 20H21C21.5304 20 22.0391 19.7893 22.4142 19.4142C22.7893 19.0391 23 18.5304 23 18V6C23 5.46957 22.7893 4.96086 22.4142 4.58579C22.0391 4.21071 21.5304 4 21 4Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
<v-button
color="light-gray"
shade="light"
size="small"
class="ml-1"
@click="clearAll"
>
<svg
class="text-red-600 h-4 w-4 inline -mt-1 mr-1"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M18 9L12 15M12 9L18 15M21 4H8L1 12L8 20H21C21.5304 20 22.0391 19.7893 22.4142 19.4142C22.7893 19.0391 23 18.5304 23 18V6C23 5.46957 22.7893 4.96086 22.4142 4.58579C22.0391 4.21071 21.5304 4 21 4Z"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
Clear All
@@ -29,35 +65,64 @@
<h5 class="font-semibold mt-3">
1. Conditions
</h5>
<condition-editor ref="filter-editor" v-model="logic.conditions" class="mt-1 border-t border rounded-md" :form="form" />
<condition-editor
ref="filter-editor"
v-model="logic.conditions"
class="mt-1 border-t border rounded-md"
:form="form"
/>
<h5 class="font-semibold mt-3">
2. Actions
</h5>
<select-input :key="resetKey" v-model="logic.actions" name="actions"
:multiple="true" class="mt-1" placeholder="Actions..."
help="Action(s) triggered when above conditions are true"
:options="actionOptions"
@update:model-value="onActionInput"
<select-input
:key="resetKey"
v-model="logic.actions"
name="actions"
:multiple="true"
class="mt-1"
placeholder="Actions..."
help="Action(s) triggered when above conditions are true"
:options="actionOptions"
@update:model-value="onActionInput"
/>
<modal :show="showCopyFormModal" @close="showCopyFormModal = false">
<modal
:show="showCopyFormModal"
@close="showCopyFormModal = false"
>
<div class="min-h-[450px]">
<h3 class="font-semibold block text-lg">
Copy logic from another field
</h3>
<p class="text-gray-400 text-xs mb-5">
Select another field/block to copy its logic and apply it to "{{ field.name }}".
Select another field/block to copy its logic and apply it to "{{
field.name
}}".
</p>
<select-input v-model="copyFrom" name="copy_from" emit-key="value"
label="Copy logic from" placeholder="Choose a field/block..."
:options="copyFromOptions" :searchable="copyFromOptions && copyFromOptions.options > 5"
<select-input
v-model="copyFrom"
name="copy_from"
emit-key="value"
label="Copy logic from"
placeholder="Choose a field/block..."
:options="copyFromOptions"
:searchable="copyFromOptions && copyFromOptions.options > 5"
/>
<div class="flex justify-between mb-6">
<v-button color="blue" shade="light" @click="copyLogic">
<v-button
color="blue"
shade="light"
@click="copyLogic"
>
Confirm & Copy
</v-button>
<v-button color="gray" shade="light" class="ml-1" @click="showCopyFormModal=false">
<v-button
color="gray"
shade="light"
class="ml-1"
@click="showCopyFormModal = false"
>
Close
</v-button>
</div>
@@ -67,136 +132,161 @@
</template>
<script>
import ConditionEditor from './ConditionEditor.client.vue'
import Modal from '../../../../global/Modal.vue'
import clonedeep from 'clone-deep'
import { default as _has } from 'lodash/has'
import ConditionEditor from "./ConditionEditor.client.vue"
import Modal from "../../../../global/Modal.vue"
import clonedeep from "clone-deep"
import { default as _has } from "lodash/has"
export default {
name: 'FormBlockLogicEditor',
name: "FormBlockLogicEditor",
components: { Modal, ConditionEditor },
props: {
field: {
type: Object,
required: false
required: false,
},
form: {
type: Object,
required: false
}
required: false,
},
},
data () {
data() {
return {
resetKey: 0,
logic: this.field.logic || {
conditions: null,
actions: []
actions: [],
},
showCopyFormModal: false,
copyFrom: null
copyFrom: null,
}
},
computed: {
copyFromOptions () {
return this.form.properties.filter((field) => {
return field.id !== this.field.id && _has(field, 'logic') && field.logic !== null && field.logic !== {}
}).map((field) => {
return { name: field.name, value: field.id }
})
copyFromOptions() {
return this.form.properties
.filter((field) => {
return (
field.id !== this.field.id &&
_has(field, "logic") &&
field.logic !== null &&
field.logic !== {}
)
})
.map((field) => {
return { name: field.name, value: field.id }
})
},
actionOptions () {
if (['nf-text', 'nf-code', 'nf-page-break', 'nf-divider', 'nf-image'].includes(this.field.type)) {
actionOptions() {
if (
[
"nf-text",
"nf-code",
"nf-page-break",
"nf-divider",
"nf-image",
].includes(this.field.type)
) {
if (this.field.hidden) {
return [{ name: 'Show Block', value: 'show-block' }]
return [{ name: "Show Block", value: "show-block" }]
} else {
return [{ name: 'Hide Block', value: 'hide-block' }]
return [{ name: "Hide Block", value: "hide-block" }]
}
}
if (this.field.hidden) {
return [
{ name: 'Show Block', value: 'show-block' },
{ name: 'Require answer', value: 'require-answer' }
{ name: "Show Block", value: "show-block" },
{ name: "Require answer", value: "require-answer" },
]
} else if (this.field.disabled) {
return [
{ name: 'Enable Block', value: 'enable-block' },
(this.field.required
? { name: 'Make it optional', value: 'make-it-optional' }
{ name: "Enable Block", value: "enable-block" },
this.field.required
? { name: "Make it optional", value: "make-it-optional" }
: {
name: 'Require answer',
value: 'require-answer'
})
name: "Require answer",
value: "require-answer",
},
]
} else {
return [
{ name: 'Hide Block', value: 'hide-block' },
{ name: 'Disable Block', value: 'disable-block' },
(this.field.required
? { name: 'Make it optional', value: 'make-it-optional' }
{ name: "Hide Block", value: "hide-block" },
{ name: "Disable Block", value: "disable-block" },
this.field.required
? { name: "Make it optional", value: "make-it-optional" }
: {
name: 'Require answer',
value: 'require-answer'
})
name: "Require answer",
value: "require-answer",
},
]
}
}
},
},
watch: {
logic: {
handler () {
handler() {
this.field.logic = this.logic
},
deep: true
deep: true,
},
'field.id': {
handler (field, oldField) {
"field.id": {
handler() {
// On field change, reset logic
this.logic = this.field.logic || {
conditions: null,
actions: []
actions: [],
}
}
},
},
'field.required': 'cleanConditions',
'field.disabled': 'cleanConditions',
'field.hidden': 'cleanConditions'
"field.required": "cleanConditions",
"field.disabled": "cleanConditions",
"field.hidden": "cleanConditions",
},
mounted () {
if (!_has(this.field, 'logic')) {
mounted() {
if (!_has(this.field, "logic")) {
this.field.logic = this.logic
}
},
methods: {
clearAll () {
clearAll() {
this.logic.conditions = null
this.logic.actions = []
this.refreshActions()
},
onActionInput () {
onActionInput() {
if (this.logic.actions.length >= 2) {
if (this.logic.actions[1] === 'require-answer' && this.logic.actions[0] === 'hide-block') {
this.logic.actions = ['require-answer']
} else if (this.logic.actions[1] === 'hide-block' && this.logic.actions[0] === 'require-answer') {
this.logic.actions = ['hide-block']
if (
this.logic.actions[1] === "require-answer" &&
this.logic.actions[0] === "hide-block"
) {
this.logic.actions = ["require-answer"]
} else if (
this.logic.actions[1] === "hide-block" &&
this.logic.actions[0] === "require-answer"
) {
this.logic.actions = ["hide-block"]
}
this.refreshActions()
}
},
cleanConditions () {
const availableActions = this.actionOptions.map(function (op) { return op.value })
this.logic.actions = availableActions.filter(value => this.logic.actions.includes(value))
cleanConditions() {
const availableActions = this.actionOptions.map(function (op) {
return op.value
})
this.logic.actions = availableActions.filter((value) =>
this.logic.actions.includes(value),
)
this.refreshActions()
},
refreshActions () {
refreshActions() {
this.resetKey++
},
copyLogic () {
copyLogic() {
if (this.copyFrom) {
const property = this.form.properties.find((property) => {
return property.id === this.copyFrom
@@ -207,7 +297,7 @@ export default {
}
}
this.showCopyFormModal = false
}
}
},
},
}
</script>

View File

@@ -1,16 +1,33 @@
<template>
<div class="flex flex-wrap px-4 py-1 -ml-1 -mt-1">
<select-input ref="ruleSelect" v-model="selectedRule" class="flex-grow ml-1 mr-1 mt-1"
wrapper-class="relative" placeholder="Add condition on input field"
:options="groupCtrl.rules" margin-bottom="" :searchable="groupCtrl.rules.length > 5"
emit-key="identifier"
option-key="identifier"
name="group-control-slot-rule"
<select-input
ref="ruleSelect"
v-model="selectedRule"
class="flex-grow ml-1 mr-1 mt-1"
wrapper-class="relative"
placeholder="Add condition on input field"
:options="groupCtrl.rules"
margin-bottom=""
:searchable="groupCtrl.rules.length > 5"
emit-key="identifier"
option-key="identifier"
name="group-control-slot-rule"
/>
<v-button class="ml-1 mt-1" color="blue" size="small" :disabled="(selectedRule === '')?true:null" @click="addRule">
<v-button
class="ml-1 mt-1"
color="blue"
size="small"
:disabled="selectedRule === '' ? true : null"
@click="addRule"
>
Add Condition
</v-button>
<v-button class="ml-1 mt-1" color="outline-blue" size="small" @click="groupCtrl.newGroup">
<v-button
class="ml-1 mt-1"
color="outline-blue"
size="small"
@click="groupCtrl.newGroup"
>
Add Group
</v-button>
</div>
@@ -20,20 +37,19 @@
export default {
components: {},
props: { groupCtrl: { type: Object, required: true } },
data () {
data() {
return {
selectedRule: null
selectedRule: null,
}
},
methods: {
addRule () {
addRule() {
if (this.selectedRule) {
this.groupCtrl.addRule(this.selectedRule)
this.$refs.ruleSelect.content = null
this.selectedRule = null
}
}
}
},
},
}
</script>