mirror of
https://github.com/directus/directus.git
synced 2026-01-27 15:17:58 -05:00
Interface many to one (#524)
* Start on m2o * Render preview in m2o * Add icons * Style inline icons * Add editing modal * Disable any linter * Add add-new button * Pass existing selection on to layout * Update v-table to allow for keys-only-selection * Fix batch on tabular
This commit is contained in:
@@ -16,6 +16,7 @@ module.exports = {
|
||||
'@typescript-eslint/camelcase': 0,
|
||||
'@typescript-eslint/no-use-before-define': 0,
|
||||
'@typescript-eslint/ban-ts-ignore': 0,
|
||||
'@typescript-eslint/no-explicit-any': 0,
|
||||
'comma-dangle': [
|
||||
'error',
|
||||
{
|
||||
@@ -37,7 +38,6 @@ module.exports = {
|
||||
jest: true,
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/no-explicit-any': 0,
|
||||
'@typescript-eslint/no-empty-function': 0,
|
||||
'@typescript-eslint/no-non-null-assertion': 0,
|
||||
},
|
||||
|
||||
@@ -90,6 +90,8 @@
|
||||
"
|
||||
:width="field.width"
|
||||
:type="field.type"
|
||||
:collection="field.collection"
|
||||
:field="field.field"
|
||||
@input="setValue(field, $event)"
|
||||
/>
|
||||
</div>
|
||||
@@ -112,7 +114,6 @@ import marked from 'marked';
|
||||
import getDefaultInterfaceForType from '@/utils/get-default-interface-for-type';
|
||||
|
||||
type FieldValues = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[field: string]: any;
|
||||
};
|
||||
|
||||
@@ -273,7 +274,6 @@ export default defineComponent({
|
||||
return { formFields, gridClass };
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function setValue(field: Field, value: any) {
|
||||
const edits = props.edits ? clone(props.edits) : {};
|
||||
edits[field.field] = value;
|
||||
|
||||
@@ -12,18 +12,20 @@
|
||||
<slot name="prepend" :value="value" :disabled="disabled" />
|
||||
</div>
|
||||
<span v-if="prefix" class="prefix">{{ prefix }}</span>
|
||||
<input
|
||||
v-bind="$attrs"
|
||||
v-focus="autofocus"
|
||||
v-on="_listeners"
|
||||
:type="type"
|
||||
:min="min"
|
||||
:max="max"
|
||||
:step="step"
|
||||
:disabled="disabled"
|
||||
:value="value"
|
||||
ref="input"
|
||||
/>
|
||||
<slot name="input">
|
||||
<input
|
||||
v-bind="$attrs"
|
||||
v-focus="autofocus"
|
||||
v-on="_listeners"
|
||||
:type="type"
|
||||
:min="min"
|
||||
:max="max"
|
||||
:step="step"
|
||||
:disabled="disabled"
|
||||
:value="value"
|
||||
ref="input"
|
||||
/>
|
||||
</slot>
|
||||
<span v-if="suffix" class="suffix">{{ suffix }}</span>
|
||||
<span v-if="(type === 'number')">
|
||||
<v-icon
|
||||
|
||||
@@ -50,7 +50,6 @@ export function usePopper(
|
||||
}
|
||||
|
||||
function getModifiers(callback: () => void = () => undefined) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const modifiers: Partial<Modifier<string, any>>[] = [
|
||||
popperOffsets,
|
||||
{
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, PropType, computed } from '@vue/composition-api';
|
||||
import { defineComponent, ref, PropType, computed, watch } from '@vue/composition-api';
|
||||
import { usePopper } from './use-popper';
|
||||
import { Placement } from '@popperjs/core';
|
||||
|
||||
@@ -118,6 +118,7 @@ export default defineComponent({
|
||||
|
||||
function useActiveState() {
|
||||
const localIsActive = ref(false);
|
||||
|
||||
const isActive = computed<boolean>({
|
||||
get() {
|
||||
if (props.value !== undefined) {
|
||||
@@ -127,17 +128,19 @@ export default defineComponent({
|
||||
return localIsActive.value;
|
||||
},
|
||||
async set(newActive) {
|
||||
if (newActive === true) {
|
||||
await start();
|
||||
} else {
|
||||
stop();
|
||||
}
|
||||
|
||||
localIsActive.value = newActive;
|
||||
emit('input', newActive);
|
||||
},
|
||||
});
|
||||
|
||||
watch(isActive, async (newActive) => {
|
||||
if (newActive !== null && newActive === true) {
|
||||
await start();
|
||||
} else {
|
||||
stop();
|
||||
}
|
||||
});
|
||||
|
||||
return { isActive, activate, deactivate, toggle };
|
||||
|
||||
function activate() {
|
||||
|
||||
@@ -70,7 +70,6 @@ export default defineComponent({
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const sidebarActive = ref(false);
|
||||
|
||||
const localActive = ref(false);
|
||||
|
||||
const _active = computed({
|
||||
|
||||
@@ -119,7 +119,6 @@ type Item = {
|
||||
value: string;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type ItemsRaw = (string | any)[];
|
||||
type InputValue = string[] | string;
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ export type HeaderRaw = {
|
||||
export type Header = Required<HeaderRaw>;
|
||||
|
||||
export type Item = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ export default defineComponent({
|
||||
default: null,
|
||||
},
|
||||
selection: {
|
||||
type: Array as PropType<Item[]>,
|
||||
type: Array as PropType<any>,
|
||||
default: () => [],
|
||||
},
|
||||
fixedHeader: {
|
||||
@@ -159,6 +159,10 @@ export default defineComponent({
|
||||
type: Number,
|
||||
default: 48,
|
||||
},
|
||||
selectionUseKeys: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
setup(props, { emit, listeners, slots }) {
|
||||
const _headers = computed({
|
||||
@@ -287,23 +291,31 @@ export default defineComponent({
|
||||
function onItemSelected(event: ItemSelectEvent) {
|
||||
emit('item-selected', event);
|
||||
|
||||
const selection: Item[] = clone(props.selection);
|
||||
let selection = clone(props.selection) as any[];
|
||||
|
||||
if (event.value === true) {
|
||||
selection.push(event.item);
|
||||
if (props.selectionUseKeys) {
|
||||
selection.push(event.item[props.itemKey]);
|
||||
} else {
|
||||
selection.push(event.item);
|
||||
}
|
||||
} else {
|
||||
const itemIndex: number = selection.findIndex(
|
||||
(item: Item) => item[props.itemKey] === event.item[props.itemKey]
|
||||
);
|
||||
selection = selection.filter((item) => {
|
||||
if (props.selectionUseKeys) {
|
||||
return item !== event.item[props.itemKey];
|
||||
}
|
||||
|
||||
selection.splice(itemIndex, 1);
|
||||
return item[props.itemKey] !== event.item[props.itemKey];
|
||||
});
|
||||
}
|
||||
|
||||
emit('select', selection);
|
||||
}
|
||||
|
||||
function getSelectedState(item: Item) {
|
||||
const selectedKeys = props.selection.map((item: Item) => item[props.itemKey]);
|
||||
const selectedKeys = props.selectionUseKeys
|
||||
? props.selection
|
||||
: props.selection.map((item: any) => item[props.itemKey]);
|
||||
return selectedKeys.includes(item[props.itemKey]);
|
||||
}
|
||||
|
||||
@@ -319,7 +331,7 @@ export default defineComponent({
|
||||
moved?: {
|
||||
oldIndex: number;
|
||||
newIndex: number;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
element: Record<string, any>;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,9 +13,9 @@ export type CollectionPreset = {
|
||||
search_query: string | null;
|
||||
title: string | null;
|
||||
user: number | null;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
view_options: Record<string, any>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
view_query: Record<string, any>;
|
||||
view_type: string | null;
|
||||
};
|
||||
|
||||
@@ -35,7 +35,6 @@ export function useCollectionPreset(
|
||||
watch(collection, initLocalPreset);
|
||||
watch(bookmark, initLocalPreset);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const viewOptions = computed<Record<string, any>>({
|
||||
get() {
|
||||
if (!localPreset.value.view_type) return null;
|
||||
@@ -56,7 +55,6 @@ export function useCollectionPreset(
|
||||
},
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const viewQuery = computed<Record<string, any>>({
|
||||
get() {
|
||||
if (!localPreset.value.view_type) return null;
|
||||
|
||||
@@ -18,9 +18,11 @@ export function useCollection(collection: Ref<string>) {
|
||||
});
|
||||
|
||||
const primaryKeyField = computed(() => {
|
||||
// Every collection has a primary key; rules of the land
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
return fields.value?.find(
|
||||
(field) => field.collection === collection.value && field.primary_key === true
|
||||
);
|
||||
)!;
|
||||
});
|
||||
|
||||
const ownerField = computed(() => {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Ref, ref, computed, watch } from '@vue/composition-api';
|
||||
import { nanoid } from 'nanoid';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type EmitFunction = (event: string, ...args: any[]) => void;
|
||||
|
||||
type Items = Readonly<
|
||||
|
||||
@@ -4,7 +4,6 @@ import { ResizeObserver as ResizeObserverPolyfill } from 'resize-observer';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
ResizeObserver: any;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +132,6 @@ export function useItem(collection: Ref<string>, primaryKey: Ref<string | number
|
||||
async function saveAsCopy() {
|
||||
saving.value = true;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const newItem: { [field: string]: any } = {
|
||||
...(item.value || {}),
|
||||
...edits.value,
|
||||
@@ -249,7 +248,6 @@ export function useItem(collection: Ref<string>, primaryKey: Ref<string | number
|
||||
} else {
|
||||
const valuesThatAreEqual = { ...response.data.data[0] };
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
response.data.data.forEach((existingItem: any) => {
|
||||
for (const [key, value] of Object.entries(existingItem)) {
|
||||
if (valuesThatAreEqual[key] !== value) {
|
||||
|
||||
@@ -31,7 +31,6 @@ export function useItems(collection: Ref<string>, query: Query) {
|
||||
: `/${currentProjectKey}/items/${collection.value}`;
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const items = ref<any>([]);
|
||||
const loading = ref(false);
|
||||
const error = ref(null);
|
||||
@@ -176,7 +175,6 @@ export function useItems(collection: Ref<string>, query: Query) {
|
||||
* pretend there is a file m2o, so we can use the regular layout logic for files as well
|
||||
*/
|
||||
if (collection.value === 'directus_files') {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
fetchedItems = fetchedItems.map((file: any) => ({
|
||||
...file,
|
||||
$file: file,
|
||||
@@ -231,7 +229,6 @@ export function useItems(collection: Ref<string>, query: Query) {
|
||||
}
|
||||
|
||||
type ManualSortData = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
item: Record<string, any>;
|
||||
oldIndex: number;
|
||||
newIndex: number;
|
||||
@@ -245,7 +242,6 @@ export function useItems(collection: Ref<string>, query: Query) {
|
||||
const selectionRange =
|
||||
move === 'down' ? [oldIndex + 1, newIndex + 1] : [newIndex, oldIndex];
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const updates = items.value.slice(...selectionRange).map((toBeUpdatedItem: any) => {
|
||||
const sortValue = getPositionForItem(toBeUpdatedItem);
|
||||
|
||||
@@ -271,12 +267,11 @@ export function useItems(collection: Ref<string>, query: Query) {
|
||||
|
||||
// Used as default value for the sort position. This is the index of the given item in the array
|
||||
// of items, offset by the page count and current page
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
function getPositionForItem(item: any) {
|
||||
const pk = primaryKeyField.value?.field;
|
||||
if (!pk) return;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const index = items.value.findIndex((existingItem: any) => existingItem[pk] === item[pk]);
|
||||
|
||||
return index + 1 + limit.value * (page.value - 1);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
function useSync<T, K extends keyof T>(
|
||||
props: T,
|
||||
key: K,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
emit: (event: string, ...args: any[]) => void
|
||||
): Ref<Readonly<T[K]>>
|
||||
```
|
||||
|
||||
@@ -3,7 +3,7 @@ import { computed, Ref } from '@vue/composition-api';
|
||||
export default function useSync<T, K extends keyof T>(
|
||||
props: T,
|
||||
key: K,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
emit: (event: string, ...args: any[]) => void
|
||||
): Ref<Readonly<T[K]>> {
|
||||
return computed<T[K]>({
|
||||
|
||||
@@ -2,14 +2,13 @@ import VueI18n from 'vue-i18n';
|
||||
import { Component } from 'vue';
|
||||
import { Field } from '@/stores/fields/types';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type DisplayHandlerFunction = (value: any, options: any) => string | null;
|
||||
|
||||
export type DisplayConfig = {
|
||||
id: string;
|
||||
icon: string;
|
||||
name: string | VueI18n.TranslateResult;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
handler: DisplayHandlerFunction | Component;
|
||||
options: null | Partial<Field>[] | Component;
|
||||
types: string[];
|
||||
|
||||
@@ -15,7 +15,7 @@ type GenericStore = {
|
||||
id: string;
|
||||
hydrate?: () => Promise<void>;
|
||||
dehydrate?: () => Promise<void>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import InterfaceStatus from './status';
|
||||
import InterfaceDateTime from './datetime';
|
||||
import InterfaceImage from './image';
|
||||
import InterfaceIcon from './icon';
|
||||
import InterfaceManyToOne from './many-to-one';
|
||||
|
||||
export const interfaces = [
|
||||
InterfaceTextInput,
|
||||
@@ -30,6 +31,7 @@ export const interfaces = [
|
||||
InterfaceDateTime,
|
||||
InterfaceImage,
|
||||
InterfaceIcon,
|
||||
InterfaceManyToOne,
|
||||
];
|
||||
|
||||
export default interfaces;
|
||||
|
||||
17
src/interfaces/many-to-one/index.ts
Normal file
17
src/interfaces/many-to-one/index.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { defineInterface } from '../define';
|
||||
import InterfaceManyToOne from './many-to-one.vue';
|
||||
|
||||
export default defineInterface(({ i18n }) => ({
|
||||
id: 'many-to-one',
|
||||
name: i18n.t('many-to-one'),
|
||||
icon: 'arrow_right_alt',
|
||||
component: InterfaceManyToOne,
|
||||
options: [
|
||||
{
|
||||
field: 'template',
|
||||
name: i18n.t('display_template'),
|
||||
width: 'half',
|
||||
interface: 'text-input',
|
||||
},
|
||||
],
|
||||
}));
|
||||
579
src/interfaces/many-to-one/many-to-one.vue
Normal file
579
src/interfaces/many-to-one/many-to-one.vue
Normal file
@@ -0,0 +1,579 @@
|
||||
<template>
|
||||
<v-notice warning v-if="!relation">
|
||||
{{ $t('relationship_not_setup') }}
|
||||
</v-notice>
|
||||
<v-notice warning v-else-if="!displayTemplate">
|
||||
{{ $t('display_template_not_setup') }}
|
||||
</v-notice>
|
||||
<div class="many-to-one" v-else>
|
||||
<v-menu v-model="menuActive" attached close-on-content-click>
|
||||
<template #activator="{ active }">
|
||||
<v-skeleton-loader type="input" v-if="loadingCurrent" />
|
||||
<v-input
|
||||
:active="active"
|
||||
@click="onPreviewClick"
|
||||
v-else
|
||||
:placeholder="$t('select_an_item')"
|
||||
>
|
||||
<template #input v-if="currentItem">
|
||||
<div class="preview">
|
||||
<render-template
|
||||
:collection="collection"
|
||||
:item="currentItem"
|
||||
:template="displayTemplate"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #append>
|
||||
<template v-if="currentItem">
|
||||
<v-icon
|
||||
name="open_in_new"
|
||||
class="edit"
|
||||
v-tooltip="$t('edit')"
|
||||
@click.stop="startEditing"
|
||||
/>
|
||||
<v-icon
|
||||
name="close"
|
||||
class="deselect"
|
||||
@click.stop="$emit('input', null)"
|
||||
v-tooltip="$t('deselect')"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<v-icon
|
||||
class="add"
|
||||
name="add"
|
||||
v-tooltip="$t('add_new_item')"
|
||||
@click.stop="startEditing"
|
||||
/>
|
||||
<v-icon class="expand" :class="{ active }" name="expand_more" />
|
||||
</template>
|
||||
</template>
|
||||
</v-input>
|
||||
</template>
|
||||
|
||||
<v-list dense>
|
||||
<template v-if="itemsLoading">
|
||||
<v-list-item v-for="n in 10" :key="`loader-${n}`">
|
||||
<v-list-item-content>
|
||||
<v-skeleton-loader type="text" />
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<v-list-item
|
||||
v-for="item in items"
|
||||
:key="item[primaryKeyField.field]"
|
||||
:active="value === item[primaryKeyField.field]"
|
||||
@click="setCurrent(item)"
|
||||
>
|
||||
<v-list-item-content>
|
||||
<render-template
|
||||
:collection="collection"
|
||||
:template="displayTemplate"
|
||||
:item="item"
|
||||
/>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</template>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
|
||||
<v-modal
|
||||
v-model="editModalActive"
|
||||
:title="$t('editing_in', { collection: relatedCollection.name })"
|
||||
persistent
|
||||
>
|
||||
<v-form
|
||||
:loading="editLoading"
|
||||
:initial-values="existingItem"
|
||||
:collection="relatedCollection.collection"
|
||||
v-model="edits"
|
||||
/>
|
||||
|
||||
<template #footer>
|
||||
<v-button @click="cancelEditing" secondary>{{ $t('cancel') }}</v-button>
|
||||
<v-button @click="stopEditing">{{ $t('save') }}</v-button>
|
||||
</template>
|
||||
</v-modal>
|
||||
|
||||
<v-modal v-model="selectModalActive" :title="$t('select_item')" no-padding>
|
||||
<layout-tabular
|
||||
class="layout"
|
||||
:collection="relatedCollection.collection"
|
||||
:selection="selection"
|
||||
@update:selection="onSelect"
|
||||
select-mode
|
||||
/>
|
||||
|
||||
<template #footer>
|
||||
<v-button @click="cancelSelecting" secondary>{{ $t('cancel') }}</v-button>
|
||||
<v-button @click="stopSelecting">{{ $t('save') }}</v-button>
|
||||
</template>
|
||||
</v-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, ref, toRefs, watch, PropType } from '@vue/composition-api';
|
||||
import { useRelationsStore } from '@/stores/relations';
|
||||
import useCollection from '@/composables/use-collection';
|
||||
import getFieldsFromTemplate from '@/utils/get-fields-from-template';
|
||||
import api from '@/api';
|
||||
import useProjectsStore from '@/stores/projects';
|
||||
import useCollectionsStore from '@/stores/collections';
|
||||
|
||||
/**
|
||||
* @NOTE
|
||||
*
|
||||
* The value of a many to one can be one of three things: A primary key (number/string), a nested
|
||||
* object of edits (including primary key = editing existing) or an object with new values (no
|
||||
* primary key)
|
||||
*/
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
value: {
|
||||
type: [Number, String, Object],
|
||||
default: null,
|
||||
},
|
||||
collection: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
field: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
template: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
selectMode: {
|
||||
type: String as PropType<'auto' | 'dropdown' | 'modal'>,
|
||||
default: 'auto',
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const { collection } = toRefs(props);
|
||||
|
||||
const projectsStore = useProjectsStore();
|
||||
const relationsStore = useRelationsStore();
|
||||
const collectionsStore = useCollectionsStore();
|
||||
|
||||
const { relation, relatedCollection } = useRelation();
|
||||
const { usesMenu, menuActive } = useMenu();
|
||||
const { info: collectionInfo, primaryKeyField } = useCollection(collection);
|
||||
const { displayTemplate, onPreviewClick, requiredFields } = usePreview();
|
||||
const { totalCount, loading: itemsLoading, fetchItems, items } = useItems();
|
||||
const { setCurrent, currentItem, loading: loadingCurrent } = useCurrent();
|
||||
const {
|
||||
edits,
|
||||
editModalActive,
|
||||
startEditing,
|
||||
stopEditing,
|
||||
loading: editLoading,
|
||||
error: editError,
|
||||
existingItem,
|
||||
cancelEditing,
|
||||
} = useEdit();
|
||||
|
||||
const {
|
||||
startSelecting,
|
||||
stopSelecting,
|
||||
cancelSelecting,
|
||||
active: selectModalActive,
|
||||
selection,
|
||||
onSelect,
|
||||
} = useSelectionModal();
|
||||
|
||||
return {
|
||||
cancelEditing,
|
||||
cancelSelecting,
|
||||
collectionInfo,
|
||||
currentItem,
|
||||
displayTemplate,
|
||||
editError,
|
||||
editLoading,
|
||||
editModalActive,
|
||||
edits,
|
||||
existingItem,
|
||||
items,
|
||||
itemsLoading,
|
||||
loadingCurrent,
|
||||
menuActive,
|
||||
onPreviewClick,
|
||||
primaryKeyField,
|
||||
relatedCollection,
|
||||
relation,
|
||||
selection,
|
||||
selectModalActive,
|
||||
setCurrent,
|
||||
startEditing,
|
||||
startSelecting,
|
||||
stopEditing,
|
||||
stopSelecting,
|
||||
totalCount,
|
||||
onSelect,
|
||||
useMenu,
|
||||
};
|
||||
|
||||
function useCurrent() {
|
||||
const currentItem = ref<Record<string, any>>(null);
|
||||
const loading = ref(false);
|
||||
const error = ref(null);
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
(newValue) => {
|
||||
// When the newly configured value is a primitive, assume it's the primary key
|
||||
// of the item and fetch it from the API to render the preview
|
||||
if (
|
||||
newValue !== null &&
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
newValue !== currentItem.value?.[primaryKeyField.value!.field] &&
|
||||
(typeof newValue === 'string' || typeof newValue === 'number')
|
||||
) {
|
||||
fetchCurrent();
|
||||
}
|
||||
|
||||
// If the value isn't a primary key, the current value will be set by the editing
|
||||
// handlers in useEdit()
|
||||
|
||||
if (newValue === null) {
|
||||
currentItem.value = null;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return { setCurrent, currentItem, loading };
|
||||
|
||||
function setCurrent(item: Record<string, any>) {
|
||||
currentItem.value = item;
|
||||
emit('input', item[primaryKeyField.value.field]);
|
||||
}
|
||||
|
||||
async function fetchCurrent() {
|
||||
const { currentProjectKey } = projectsStore.state;
|
||||
loading.value = true;
|
||||
|
||||
const fields = requiredFields.value || [];
|
||||
|
||||
if (fields.includes(primaryKeyField.value.field) === false) {
|
||||
fields.push(primaryKeyField.value.field);
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await api.get(
|
||||
`/${currentProjectKey}/items/${relatedCollection.value.collection}/${props.value}`,
|
||||
{
|
||||
params: {
|
||||
fields: fields,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
currentItem.value = response.data.data;
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function useItems() {
|
||||
const totalCount = ref<number>(null);
|
||||
|
||||
const items = ref<Record<string, any>[]>(null);
|
||||
const loading = ref(false);
|
||||
const error = ref(null);
|
||||
|
||||
watch(relatedCollection, () => {
|
||||
fetchTotalCount();
|
||||
items.value = null;
|
||||
});
|
||||
|
||||
return { totalCount, fetchItems, items, loading };
|
||||
|
||||
async function fetchItems() {
|
||||
if (items.value !== null) return;
|
||||
|
||||
const { currentProjectKey } = projectsStore.state;
|
||||
loading.value = true;
|
||||
|
||||
const fields = requiredFields.value || [];
|
||||
|
||||
if (fields.includes(primaryKeyField.value.field) === false) {
|
||||
fields.push(primaryKeyField.value.field);
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await api.get(
|
||||
`/${currentProjectKey}/items/${relatedCollection.value.collection}`,
|
||||
{
|
||||
params: {
|
||||
fields: fields,
|
||||
limit: -1,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
items.value = response.data.data;
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchTotalCount() {
|
||||
const { currentProjectKey } = projectsStore.state;
|
||||
const response = await api.get(
|
||||
`/${currentProjectKey}/items/${relatedCollection.value.collection}`,
|
||||
{
|
||||
params: {
|
||||
limit: 0,
|
||||
meta: 'total_count',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
totalCount.value = response.data.meta.total_count;
|
||||
}
|
||||
}
|
||||
|
||||
function useEdit() {
|
||||
const loading = ref(false);
|
||||
const error = ref(null);
|
||||
const existingItem = ref<any>(null);
|
||||
|
||||
const edits = ref<any>(null);
|
||||
const editModalActive = ref(false);
|
||||
|
||||
return {
|
||||
edits,
|
||||
editModalActive,
|
||||
startEditing,
|
||||
stopEditing,
|
||||
loading,
|
||||
error,
|
||||
existingItem,
|
||||
cancelEditing,
|
||||
};
|
||||
|
||||
async function startEditing() {
|
||||
editModalActive.value = true;
|
||||
loading.value = true;
|
||||
|
||||
// If the current value is an object, it's the previously created changes to the item
|
||||
if (props.value && typeof props.value === 'object') {
|
||||
edits.value = props.value;
|
||||
}
|
||||
// if not, it's the primary key of the existing item (fresh load). It's important for
|
||||
// us to stage the existing ID back up in the object of edits, otherwise the API will
|
||||
// treat the edits as a creation of a new item instead of editing the values of an
|
||||
// existing one
|
||||
else if (
|
||||
props.value &&
|
||||
(typeof props.value === 'number' || typeof props.value === 'string')
|
||||
) {
|
||||
edits.value = {
|
||||
[primaryKeyField.value.field]: props.value,
|
||||
};
|
||||
}
|
||||
|
||||
// If the current item has a primary key, it means that it's an existing item we're
|
||||
// about to edit. In that case, we want to fetch the whole existing item, so we can
|
||||
// render the full form inline
|
||||
if (currentItem.value?.hasOwnProperty(primaryKeyField.value.field)) {
|
||||
loading.value = true;
|
||||
|
||||
const { currentProjectKey } = projectsStore.state;
|
||||
|
||||
try {
|
||||
const response = await api.get(
|
||||
`/${currentProjectKey}/items/${relatedCollection.value.collection}/${
|
||||
currentItem.value[primaryKeyField.value.field]
|
||||
}`
|
||||
);
|
||||
existingItem.value = response.data.data;
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
// When the current item doesn't have a primary key, it means it's new. In that case
|
||||
// we don't have to bother fetching anything, as all the edits are already stored
|
||||
// in current item
|
||||
else {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function cancelEditing() {
|
||||
editModalActive.value = false;
|
||||
error.value = null;
|
||||
loading.value = false;
|
||||
existingItem.value = null;
|
||||
edits.value = null;
|
||||
}
|
||||
|
||||
function stopEditing() {
|
||||
emit('input', edits.value);
|
||||
|
||||
// Merging the previously fetched existing current item makes sure we don't remove
|
||||
// any fields in the preview that wasn't edited, but still used in the preview
|
||||
currentItem.value = {
|
||||
...currentItem.value,
|
||||
...edits.value,
|
||||
};
|
||||
|
||||
cancelEditing();
|
||||
}
|
||||
}
|
||||
|
||||
function useRelation() {
|
||||
const relation = computed(() => {
|
||||
return relationsStore.getRelationsForField(props.collection, props.field)?.[0];
|
||||
});
|
||||
|
||||
const relatedCollection = computed(() => {
|
||||
if (!relation.value) return null;
|
||||
return collectionsStore.getCollection(relation.value.collection_one);
|
||||
});
|
||||
|
||||
return { relation, relatedCollection };
|
||||
}
|
||||
|
||||
function useMenu() {
|
||||
const menuActive = ref(false);
|
||||
const usesMenu = computed(() => {
|
||||
if (props.selectMode === 'modal') return false;
|
||||
if (props.selectMode === 'dropdown') return true;
|
||||
|
||||
// auto
|
||||
if (totalCount.value && totalCount.value > 100) return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
return { menuActive, usesMenu };
|
||||
}
|
||||
|
||||
function usePreview() {
|
||||
const displayTemplate = computed(() => {
|
||||
if (props.template !== null) return props.template;
|
||||
return collectionInfo.value?.display_template;
|
||||
});
|
||||
|
||||
const requiredFields = computed(() => {
|
||||
if (!displayTemplate.value) return null;
|
||||
return getFieldsFromTemplate(displayTemplate.value);
|
||||
});
|
||||
|
||||
return { onPreviewClick, displayTemplate, requiredFields };
|
||||
|
||||
function onPreviewClick() {
|
||||
if (usesMenu.value === true) {
|
||||
const newActive = !menuActive.value;
|
||||
menuActive.value = newActive;
|
||||
if (newActive === true) fetchItems();
|
||||
} else {
|
||||
startSelecting();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function useSelectionModal() {
|
||||
const active = ref(false);
|
||||
const selection = ref<any[]>([]);
|
||||
|
||||
return { active, selection, onSelect, startSelecting, stopSelecting, cancelSelecting };
|
||||
|
||||
function onSelect(newSelection: any[]) {
|
||||
if (newSelection.length > 0) {
|
||||
selection.value = [newSelection[newSelection.length - 1]];
|
||||
} else {
|
||||
selection.value = [];
|
||||
}
|
||||
}
|
||||
|
||||
function startSelecting() {
|
||||
active.value = true;
|
||||
|
||||
if (props.value) {
|
||||
if (
|
||||
typeof props.value === 'object' &&
|
||||
props.value.hasOwnProperty(primaryKeyField.value.field)
|
||||
) {
|
||||
selection.value = [props.value[primaryKeyField.value.field]];
|
||||
} else if (typeof props.value === 'string' || typeof props.value === 'number') {
|
||||
selection.value = [props.value];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function stopSelecting() {
|
||||
if (!selection.value[0]) {
|
||||
emit('input', null);
|
||||
} else {
|
||||
emit('input', selection.value[0]);
|
||||
}
|
||||
cancelSelecting();
|
||||
}
|
||||
|
||||
function cancelSelecting() {
|
||||
active.value = false;
|
||||
selection.value = [];
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.many-to-one {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.v-skeleton-loader {
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.preview {
|
||||
display: block;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.expand {
|
||||
transition: transform var(--fast) var(--transition);
|
||||
|
||||
&.active {
|
||||
transform: scaleY(-1);
|
||||
}
|
||||
}
|
||||
|
||||
.edit {
|
||||
margin-right: 4px;
|
||||
|
||||
&:hover {
|
||||
--v-icon-color: var(--foreground-normal);
|
||||
}
|
||||
}
|
||||
|
||||
.add:hover {
|
||||
--v-icon-color: var(--primary);
|
||||
}
|
||||
|
||||
.deselect:hover {
|
||||
--v-icon-color: var(--danger);
|
||||
}
|
||||
|
||||
.layout {
|
||||
--layout-offset-top: 0px;
|
||||
}
|
||||
</style>
|
||||
0
src/interfaces/many-to-one/readme.md
Normal file
0
src/interfaces/many-to-one/readme.md
Normal file
@@ -81,7 +81,6 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const editorElement = ref<any>(null);
|
||||
|
||||
const _value = computed({
|
||||
|
||||
@@ -140,6 +140,8 @@
|
||||
|
||||
"comments": "Comments",
|
||||
|
||||
"select_item": "Select Item",
|
||||
|
||||
"item_count": "No Items | One Item | {count} Items",
|
||||
"no_items_copy": "It looks like you don’t have any items in this collection. You can click the button below to add an item.",
|
||||
"all_items": "All Items",
|
||||
@@ -152,6 +154,9 @@
|
||||
"radio_buttons": "Radio Buttons",
|
||||
"checkboxes": "Checkboxes",
|
||||
|
||||
"relationship_not_setup": "The relationship hasn't been configured correctly.",
|
||||
"display_template_not_setup": "The display template hasn't been configured correctly.",
|
||||
|
||||
"choose_status": "Choose Status...",
|
||||
|
||||
"users": "Users",
|
||||
@@ -174,6 +179,9 @@
|
||||
"bookmark_doesnt_exist_copy": "The bookmark you're trying to open couldn't be found.",
|
||||
"bookmark_doesnt_exist_cta": "Return to collection",
|
||||
|
||||
"select_an_item": "Select an item...",
|
||||
"edit": "Edit",
|
||||
|
||||
"errors": {
|
||||
"3": "Only super admins have access to this",
|
||||
"4": "Super Admin Token not provided",
|
||||
@@ -325,6 +333,9 @@
|
||||
|
||||
"add_new_item": "Add New Item",
|
||||
|
||||
"many-to-one": "Many to One",
|
||||
"display_template": "Display Template",
|
||||
|
||||
"n_items_selected": "No Items Selected | 1 Item Selected | {n} Items Selected",
|
||||
"per_page": "Per Page",
|
||||
"all_files": "All Files",
|
||||
@@ -796,7 +807,6 @@
|
||||
"related_entries": "Has related entries",
|
||||
"relational": "Relational",
|
||||
"relationship": "Relationship",
|
||||
"relationship_not_setup": "The relationship hasn't been configured correctly.",
|
||||
"remove": "Remove",
|
||||
"remove_related": "Remove Related Item",
|
||||
"report_issue": "Report Issue",
|
||||
|
||||
@@ -150,7 +150,6 @@ import CardsHeader from './components/header.vue';
|
||||
import i18n from '@/lang';
|
||||
import adjustFieldsForDisplays from '@/utils/adjust-fields-for-displays';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type Item = Record<string, any>;
|
||||
|
||||
type ViewOptions = {
|
||||
@@ -317,7 +316,6 @@ export default defineComponent({
|
||||
|
||||
return { size, icon, imageSource, title, subtitle, imageFit };
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function createViewOption<T>(key: keyof ViewOptions, defaultValue: any) {
|
||||
return computed<T>({
|
||||
get() {
|
||||
@@ -375,7 +373,6 @@ export default defineComponent({
|
||||
|
||||
return { sort, limit, page, fields };
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function createViewQueryOption<T>(key: keyof ViewQuery, defaultValue: any) {
|
||||
return computed<T>({
|
||||
get() {
|
||||
@@ -392,7 +389,6 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function getLinkForItem(item: Record<string, any>) {
|
||||
const currentProjectKey = projectsStore.state.currentProjectKey;
|
||||
|
||||
|
||||
@@ -40,7 +40,6 @@ import { defineComponent, PropType, computed } from '@vue/composition-api';
|
||||
import router from '@/router';
|
||||
|
||||
type File = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[key: string]: any;
|
||||
type: string;
|
||||
data: {
|
||||
@@ -71,12 +70,10 @@ export default defineComponent({
|
||||
default: false,
|
||||
},
|
||||
item: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type: Object as PropType<Record<string, any>>,
|
||||
default: null,
|
||||
},
|
||||
value: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type: Array as PropType<Record<string, any>[]>,
|
||||
default: () => [],
|
||||
},
|
||||
|
||||
@@ -65,7 +65,6 @@ export default defineComponent({
|
||||
required: true,
|
||||
},
|
||||
selection: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type: Array as PropType<Record<string, any>>,
|
||||
default: () => [],
|
||||
},
|
||||
|
||||
@@ -76,7 +76,8 @@
|
||||
:item-key="primaryKeyField.field"
|
||||
:show-manual-sort="_filters && _filters.length === 0 && sortField !== null"
|
||||
:manual-sort-key="sortField && sortField.field"
|
||||
@click:row="readonly ? null : onRowClick"
|
||||
selection-use-keys
|
||||
@click:row="onRowClick"
|
||||
@update:sort="onSortChange"
|
||||
@manual-sort="changeManualSort"
|
||||
>
|
||||
@@ -471,11 +472,13 @@ export default defineComponent({
|
||||
};
|
||||
|
||||
function onRowClick(item: Item) {
|
||||
if (props.readonly === true) return;
|
||||
|
||||
if (props.selectMode || _selection.value?.length > 0) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(table.value as any).onItemSelected({
|
||||
item,
|
||||
value: _selection.value?.includes(item) === false,
|
||||
value:
|
||||
_selection.value?.includes(item[primaryKeyField.value.field]) === false,
|
||||
});
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
|
||||
@@ -32,7 +32,6 @@ import { LayoutComponent } from '@/layouts/types';
|
||||
import useCollectionPreset from '@/composables/use-collection-preset';
|
||||
|
||||
type Item = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[field: string]: any;
|
||||
};
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ import useItem from '@/composables/use-item';
|
||||
import SaveOptions from '@/views/private/components/save-options';
|
||||
|
||||
type Values = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[field: string]: any;
|
||||
};
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
</template>
|
||||
|
||||
<template #drawer>
|
||||
<layout-drawer-detail v-model="viewType" />
|
||||
<layout-drawer-detail @input="viewType = $event" :value="viewType || 'tabular'" />
|
||||
<portal-target name="drawer" />
|
||||
</template>
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
v-else
|
||||
class="layout"
|
||||
ref="layout"
|
||||
:is="`layout-${viewType}`"
|
||||
:is="`layout-${viewType || 'tabular'}`"
|
||||
:collection="collection"
|
||||
:selection.sync="selection"
|
||||
:view-options.sync="viewOptions"
|
||||
@@ -140,7 +140,6 @@ const redirectIfNeeded: NavigationGuard = async (to, from, next) => {
|
||||
};
|
||||
|
||||
type Item = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[field: string]: any;
|
||||
};
|
||||
|
||||
@@ -190,10 +189,6 @@ export default defineComponent({
|
||||
|
||||
const { addBookmarkActive, creatingBookmark, createBookmark } = useBookmarks();
|
||||
|
||||
if (viewType.value === null) {
|
||||
viewType.value = 'tabular';
|
||||
}
|
||||
|
||||
return {
|
||||
addNewLink,
|
||||
batchDelete,
|
||||
@@ -267,10 +262,7 @@ export default defineComponent({
|
||||
|
||||
const batchLink = computed<string>(() => {
|
||||
const currentProjectKey = projectsStore.state.currentProjectKey;
|
||||
const batchPrimaryKeys = selection.value
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
.map((item) => item[primaryKeyField.value!.field])
|
||||
.join();
|
||||
const batchPrimaryKeys = selection.value.join();
|
||||
return `/${currentProjectKey}/collections/${props.collection}/${batchPrimaryKeys}`;
|
||||
});
|
||||
|
||||
|
||||
@@ -163,7 +163,6 @@ import useItem from '@/composables/use-item';
|
||||
import SaveOptions from '@/views/private/components/save-options';
|
||||
|
||||
type Values = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[field: string]: any;
|
||||
};
|
||||
|
||||
@@ -250,7 +249,6 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
async function saveAndStay() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const savedItem: Record<string, any> = await save();
|
||||
|
||||
if (props.primaryKey === '+') {
|
||||
|
||||
@@ -16,7 +16,7 @@ export type Folder = {
|
||||
|
||||
let loading: Ref<boolean> | null = null;
|
||||
let folders: Ref<Folder[] | null> | null = null;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
let error: Ref<any> | null = null;
|
||||
|
||||
export default function useFolders() {
|
||||
|
||||
@@ -84,7 +84,6 @@ import AddFolder from '../../components/add-folder';
|
||||
import SearchInput from '@/views/private/components/search-input';
|
||||
|
||||
type Item = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[field: string]: any;
|
||||
};
|
||||
|
||||
|
||||
@@ -126,7 +126,6 @@ import { nanoid } from 'nanoid';
|
||||
import FileLightbox from '@/views/private/components/file-lightbox';
|
||||
|
||||
type Values = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[field: string]: any;
|
||||
};
|
||||
|
||||
@@ -220,7 +219,6 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
async function saveAndStay() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const savedItem: Record<string, any> = await save();
|
||||
|
||||
if (props.primaryKey === '+') {
|
||||
|
||||
@@ -29,7 +29,6 @@ export default defineComponent({
|
||||
default: null,
|
||||
},
|
||||
options: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type: Object as PropType<any>,
|
||||
default: null,
|
||||
},
|
||||
|
||||
@@ -29,7 +29,6 @@ export default defineComponent({
|
||||
default: null,
|
||||
},
|
||||
options: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type: Object as PropType<any>,
|
||||
default: null,
|
||||
},
|
||||
|
||||
@@ -120,7 +120,6 @@ export default defineComponent({
|
||||
|
||||
const isNew = computed(() => props.existingField === null);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const edits = ref<Partial<Field>>({});
|
||||
|
||||
watch(
|
||||
@@ -302,7 +301,6 @@ export default defineComponent({
|
||||
() => localType.value && ['relational', 'file', 'files'].includes(localType.value)
|
||||
);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const relationships: any[] = [];
|
||||
|
||||
return { needsRelationalSetup, relationships };
|
||||
@@ -325,7 +323,7 @@ export default defineComponent({
|
||||
get() {
|
||||
return edits.value.options || props.existingField?.options;
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
set(newOptions: { [key: string]: any } | null) {
|
||||
edits.value = {
|
||||
...edits.value,
|
||||
@@ -354,7 +352,7 @@ export default defineComponent({
|
||||
get() {
|
||||
return edits.value.display_options || props.existingField?.display_options;
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
set(newOptions: { [key: string]: any } | null) {
|
||||
edits.value = {
|
||||
...edits.value,
|
||||
|
||||
@@ -36,7 +36,6 @@ export default defineComponent({
|
||||
|
||||
const initialValues = settingsStore.formatted;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const edits = ref<{ [key: string]: any }>(null);
|
||||
|
||||
const noEdits = computed<boolean>(
|
||||
|
||||
@@ -101,9 +101,9 @@ type FormattedPreset = {
|
||||
collection: string;
|
||||
layout: string | null;
|
||||
name: string | null;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
view_query: Record<string, any> | null;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
view_options: Record<string, any> | null;
|
||||
filters: readonly Filter[] | null;
|
||||
};
|
||||
@@ -228,7 +228,6 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
function useValues() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const edits = ref<any>({});
|
||||
|
||||
const hasEdits = computed(() => Object.keys(edits.value).length > 0);
|
||||
@@ -363,7 +362,6 @@ export default defineComponent({
|
||||
},
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
users.value = response.data.data.map((user: any) => ({
|
||||
name: user.first_name + ' ' + user.last_name,
|
||||
id: user.id,
|
||||
|
||||
@@ -71,7 +71,6 @@ import { LayoutComponent } from '@/layouts/types';
|
||||
import useCollectionPreset from '@/composables/use-collection-preset';
|
||||
|
||||
type Item = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[field: string]: any;
|
||||
};
|
||||
|
||||
|
||||
@@ -72,7 +72,6 @@ export default defineComponent({
|
||||
const allowedStatuses = ref<string[]>([]);
|
||||
const indeterminate = ref<string[]>([]);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const statusKeys = computed(() => props.statuses.map((status: any) => status.value));
|
||||
|
||||
const allAllowed = computed(() => {
|
||||
|
||||
@@ -101,7 +101,6 @@ import SaveOptions from '@/views/private/components/save-options';
|
||||
import PermissionsManagement from './components/permissions-management';
|
||||
|
||||
type Values = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[field: string]: any;
|
||||
};
|
||||
|
||||
|
||||
@@ -71,7 +71,6 @@ import { LayoutComponent } from '@/layouts/types';
|
||||
import useCollectionPreset from '@/composables/use-collection-preset';
|
||||
|
||||
type Item = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[field: string]: any;
|
||||
};
|
||||
|
||||
|
||||
@@ -91,7 +91,6 @@ import useItem from '@/composables/use-item';
|
||||
import SaveOptions from '@/views/private/components/save-options';
|
||||
|
||||
type Values = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[field: string]: any;
|
||||
};
|
||||
|
||||
|
||||
@@ -81,7 +81,6 @@ import LayoutDrawerDetail from '@/views/private/components/layout-drawer-detail'
|
||||
import SearchInput from '@/views/private/components/search-input';
|
||||
|
||||
type Item = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[field: string]: any;
|
||||
};
|
||||
|
||||
|
||||
@@ -91,7 +91,6 @@ import useItem from '@/composables/use-item';
|
||||
import SaveOptions from '@/views/private/components/save-options';
|
||||
|
||||
type Values = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[field: string]: any;
|
||||
};
|
||||
|
||||
@@ -164,7 +163,6 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
async function saveAndStay() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const savedItem: Record<string, any> = await save();
|
||||
|
||||
if (props.primaryKey === '+') {
|
||||
|
||||
@@ -37,8 +37,8 @@ export type CollectionPreset = {
|
||||
search_query: string | null;
|
||||
filters: readonly Filter[] | null;
|
||||
view_type: string | null;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
view_query: { [view_type: string]: any } | null;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
view_options: { [view_type: string]: any } | null;
|
||||
};
|
||||
|
||||
@@ -35,7 +35,7 @@ export const useSettingsStore = createStore({
|
||||
async dehydrate() {
|
||||
this.reset();
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
async updateSettings(updates: { [key: string]: any }) {
|
||||
const projectsStore = useProjectsStore();
|
||||
const currentProjectKey = projectsStore.state.currentProjectKey;
|
||||
@@ -79,7 +79,6 @@ export const useSettingsStore = createStore({
|
||||
|
||||
this.state.settings = this.state.settings.map((setting) => {
|
||||
const updated = response.data.data.find(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(update: any) => update.id === setting.id
|
||||
);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export type Setting = {
|
||||
id: number;
|
||||
key: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
value: any;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Filter } from '@/stores/collection-presets/types';
|
||||
|
||||
export default function filtersToQuery(filters: readonly Filter[]) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const query: Record<string, any> = {};
|
||||
|
||||
filters.forEach((filter) => {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export default function moveInArray(array: readonly any[], fromIndex: number, toIndex: number) {
|
||||
const item = array[fromIndex];
|
||||
const length = array.length;
|
||||
|
||||
@@ -2,7 +2,7 @@ import Vue, { Component } from 'vue';
|
||||
|
||||
function registerComponent(id: string, component: Component): void;
|
||||
function registerComponent(id: string, component: Parameters<typeof Vue.component>[1]): void;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
function registerComponent(id: string, component: any) {
|
||||
Vue.component(id, component);
|
||||
}
|
||||
|
||||
@@ -162,7 +162,6 @@ export default defineComponent({
|
||||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
activity.value = records;
|
||||
} catch (error) {
|
||||
error.value = error;
|
||||
|
||||
@@ -32,7 +32,6 @@ export default defineComponent({
|
||||
required: true,
|
||||
},
|
||||
item: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type: Object as PropType<Record<string, any>>,
|
||||
required: true,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user