Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions adminforth/spa/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions adminforth/spa/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"@tailwindcss/typography": "^0.5.19",
"@tsconfig/node20": "^20.1.4",
"@types/node": "^20.12.5",
"@types/sanitize-html": "^2.16.1",
"@vitejs/plugin-vue": "^5.0.4",
"@vue/eslint-config-typescript": "^13.0.0",
"@vue/tsconfig": "^0.5.1",
Expand Down
20 changes: 20 additions & 0 deletions adminforth/spa/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 17 additions & 29 deletions adminforth/spa/src/adminforth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,13 @@ class FrontendAPI implements FrontendAPIInterface {
public modalStore:any
public filtersStore:any
public coreStore:any
private saveInterceptors: Record<string, Array<(ctx: { action: 'create'|'edit'; values: any; resource: any; resourceId: string; }) => Promise<{ ok: boolean; error?: string | null; extra?: object; }>>> = {};

public list: {
refresh(): Promise<{ error? : string }>;
silentRefresh(): Promise<{ error? : string }>;
silentRefreshRow(pk: any): Promise<{ error? : string }>;
closeThreeDotsDropdown(): Promise<{ error? : string }>;
closeUserMenuDropdown: () => void;
setFilter: (filter: FilterParams) => void;
updateFilter: (filter: FilterParams) => void;
clearFilters: () => void;
}
private saveInterceptors: Record<string, Array<Parameters<FrontendAPIInterface['registerSaveInterceptor']>[0]>> = {};

public menu: {
refreshMenuBadges: () => void;
}
public list: FrontendAPIInterface['list'];

public show: {
refresh(): void;
}
public menu: FrontendAPIInterface['menu'];

public show: FrontendAPIInterface['show'];

closeUserMenuDropdown(): void {
console.log('closeUserMenuDropdown')
Expand Down Expand Up @@ -70,9 +57,6 @@ class FrontendAPI implements FrontendAPIInterface {
console.log('closeThreeDotsDropdown')
return { error: 'Not implemented' }
},
closeUserMenuDropdown: () => {
console.log('closeUserMenuDropdown')
},
setFilter: this.setListFilter.bind(this),
updateFilter: this.updateListFilter.bind(this),
clearFilters: this.clearListFilters.bind(this),
Expand All @@ -83,11 +67,15 @@ class FrontendAPI implements FrontendAPIInterface {
console.log('show.refresh')
}
}

this.closeUserMenuDropdown = () => {
console.log('closeUserMenuDropdown')
};
}

registerSaveInterceptor(
handler: (ctx: { action: 'create'|'edit'; values: any; resource: any; }) => Promise<{ ok: boolean; error?: string | null; extra?: object; }>,
): void {
handler: Parameters<FrontendAPIInterface['registerSaveInterceptor']>[0]
): ReturnType<FrontendAPIInterface['registerSaveInterceptor']> {
const rid = router.currentRoute.value?.params?.resourceId as string;
if (!rid) {
return;
Expand All @@ -98,7 +86,7 @@ class FrontendAPI implements FrontendAPIInterface {
this.saveInterceptors[rid].push(handler);
}

async runSaveInterceptors(params: { action: 'create'|'edit'; values: any; resource: any; resourceId: string; }): Promise<{ ok: boolean; error?: string | null; extra?: object; }> {
async runSaveInterceptors(params: Parameters<FrontendAPIInterface['runSaveInterceptors']>[0]): ReturnType<FrontendAPIInterface['runSaveInterceptors']> {
const list = this.saveInterceptors[params.resourceId] || [];
const aggregatedExtra: Record<string, any> = {};
for (const fn of list) {
Expand All @@ -120,15 +108,15 @@ class FrontendAPI implements FrontendAPIInterface {
return { ok: true, extra: aggregatedExtra };
}

clearSaveInterceptors(resourceId?: string): void {
clearSaveInterceptors(resourceId?: Parameters<FrontendAPIInterface['clearSaveInterceptors']>[0]): ReturnType<FrontendAPIInterface['clearSaveInterceptors']> {
if (resourceId) {
delete this.saveInterceptors[resourceId];
} else {
this.saveInterceptors = {};
}
}

confirm(params: ConfirmParams): Promise<boolean> {
confirm(params: Parameters<FrontendAPIInterface['confirm']>[0]): ReturnType<FrontendAPIInterface['confirm']> {
return new Promise((resolve, reject) => {
this.modalStore.setModalContent({
content: params.message,
Expand All @@ -142,7 +130,7 @@ class FrontendAPI implements FrontendAPIInterface {
})
}

alert(params: AlertParams): void | Promise<string> | string {
alert(params: Parameters<FrontendAPIInterface['alert']>[0]): ReturnType<FrontendAPIInterface['alert']> {
const toats = {
message: params.message,
messageHtml: params.messageHtml,
Expand All @@ -162,14 +150,14 @@ class FrontendAPI implements FrontendAPIInterface {
}
}

listFilterValidation(filter: FilterParams): boolean {
listFilterValidation(filter: Parameters<FrontendAPIInterface['list']['setFilter']>[0]): boolean {
if(router.currentRoute.value.meta.type !== 'list'){
throw new Error(`Cannot use ${this.setListFilter.name} filter on a list page`)
}
return true
}

setListFilter(filter: FilterParams): void {
setListFilter(filter: Parameters<FrontendAPIInterface['list']['setFilter']>[0]): ReturnType<FrontendAPIInterface['list']['setFilter']> {
if(this.listFilterValidation(filter)){
const existingFilterIndex = this.filtersStore.filters.findIndex((f: any) => {
return f.field === filter.field && f.operator === filter.operator
Expand Down
2 changes: 1 addition & 1 deletion adminforth/spa/src/afcl/Input.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const isIos = coreStore.isIos;

const props = defineProps<{
type: string,
fullWidth: boolean,
fullWidth?: boolean,
modelValue: string,
suffix?: string,
prefix?: string,
Expand Down
6 changes: 4 additions & 2 deletions adminforth/spa/src/afcl/Select.vue
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,12 @@ import { ref, computed, onMounted, onUnmounted, watch, nextTick,type PropType, t
import { IconCaretDownSolid } from '@iconify-prerendered/vue-flowbite';
import { useElementSize } from '@vueuse/core'

type ISingleSelectModelValue = string | number;

const props = defineProps({
options: Array,
modelValue: {
type: Array as PropType<(string | number)[] | (string | number)>,
type: Array as PropType<(ISingleSelectModelValue)[] | ISingleSelectModelValue>,
default: () => [],
},
multiple: {
Expand Down Expand Up @@ -201,7 +203,7 @@ function updateFromProps() {
selectedItems.value = [];
}
} else {
selectedItems.value = props.options?.filter((item: any) => props.modelValue?.includes(item.value)) || [];
selectedItems.value = props.options?.filter((item: any) => (props.modelValue as (ISingleSelectModelValue)[])?.includes(item.value)) || [];
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion adminforth/spa/src/afcl/Table.vue
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ function sortArrayData(data:any[], sortField?:string, dir:'asc'|'desc'='asc') {
});
}

function tableRowClick(row) {
function tableRowClick(row: any) {
emit("clickTableRow", row)
}
</script>
14 changes: 11 additions & 3 deletions adminforth/spa/src/components/ColumnValueInputWrapper.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@
:unmasked="unmasked"
:deletable="!column.editReadonly"
@update:modelValue="setCurrentValue(column.name, $event, arrayItemIndex)"
@update:recordFieldValue="({ fieldName, fieldValue }: { fieldName: string; fieldValue: any }) => setCurrentValue(fieldName, fieldValue)"
@update:recordFieldValue="recordFieldValueUpdate"
@update:unmasked="$emit('update:unmasked', column.name)"
@update:inValidity="$emit('update:inValidity', { name: column.name, value: $event })"
@update:emptiness="$emit('update:emptiness', { name: column.name, value: $event })"
@delete="setCurrentValue(column.name, currentValues[column.name].filter((_: any, index: any) => index !== arrayItemIndex))"
@delete="deleteHandler(arrayItemIndex)"
/>
</div>
<div class="flex items-center">
Expand Down Expand Up @@ -48,7 +48,7 @@
:columnOptions="columnOptions"
:unmasked="unmasked"
@update:modelValue="setCurrentValue(column.name, $event)"
@update:recordFieldValue="({ fieldName, fieldValue }: { fieldName: string; fieldValue: any }) => setCurrentValue(fieldName, fieldValue)"
@update:recordFieldValue="recordFieldValueUpdate"
@update:unmasked="$emit('update:unmasked', column.name)"
@update:inValidity="$emit('update:inValidity', { name: column.name, value: $event })"
@update:emptiness="$emit('update:emptiness', { name: column.name, value: $event })"
Expand Down Expand Up @@ -80,4 +80,12 @@
await nextTick();
arrayItemRefs.value[arrayItemRefs.value.length - 1].focus();
}

function recordFieldValueUpdate({ fieldName, fieldValue }: { fieldName: string; fieldValue: any }) {
props.setCurrentValue(fieldName, fieldValue);
}

function deleteHandler(arrayItemIndex: number | string) {
props.setCurrentValue(props.column.name, props.currentValues[props.column.name].filter((_: any, index: any) => index !== arrayItemIndex));
}
</script>
17 changes: 9 additions & 8 deletions adminforth/spa/src/components/ListActionsThreeDots.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,14 @@
{{ $t('Delete item') }}
</button>
</template>
<div v-for="action in (resourceOptions.actions ?? []).filter(a => a.showIn?.listThreeDotsMenu)" :key="action.id" >
<div v-for="action in (resourceOptions.actions ?? []).filter((a: AdminForthActionInput) => a.showIn?.listThreeDotsMenu)" :key="action.id" >
<button class="flex text-nowrap p-1 hover:bg-gray-100 dark:hover:bg-gray-800 w-full text-left px-4 py-2 text-sm text-gray-700 dark:text-gray-300" @click="() => { startCustomAction(action.id, record); showMenu = false; }">
<component
:is="action.customComponent ? getCustomComponent(action.customComponent) : CallActionWrapper"
:meta="action.customComponent?.meta"
:row="record"
:resource="resource"
:adminUser="adminUser"
:resource="coreStore.resource"
:adminUser="coreStore.adminUser"
@callAction="(payload? : Object) => startCustomAction(action.id, record, payload)"
>
<component
Expand All @@ -79,8 +79,8 @@
<template v-if="customActionIconsThreeDotsMenuItems">
<component
v-for="c in customActionIconsThreeDotsMenuItems"
:is="getCustomComponent(c)"
:meta="c.meta"
:is="getCustomComponent(formatComponent(c))"
:meta="formatComponent(c).meta"
:resource="coreStore.resource"
:adminUser="coreStore.adminUser"
:record="record"
Expand All @@ -100,9 +100,10 @@ import {
IconDotsHorizontalOutline
} from '@iconify-prerendered/vue-flowbite';
import { onMounted, onBeforeUnmount, ref, nextTick, watch } from 'vue';
import { getIcon, getCustomComponent } from '@/utils';
import { getIcon, getCustomComponent, formatComponent } from '@/utils';
import { useCoreStore } from '@/stores/core';
import CallActionWrapper from '@/components/CallActionWrapper.vue'
import { type AdminForthActionInput, type AdminForthComponentDeclaration, type AdminForthComponentDeclarationFull } from '@/types/Common';

const coreStore = useCoreStore();
const showMenu = ref(false);
Expand All @@ -113,11 +114,11 @@ const menuStyles = ref<Record<string, string>>({});
const props = defineProps<{
resourceOptions: any;
record: any;
customActionIconsThreeDotsMenuItems: any[];
customActionIconsThreeDotsMenuItems: AdminForthComponentDeclaration[];
resourceId: string;
deleteRecord: (record: any) => void;
updateRecords: () => void;
startCustomAction: (actionId: string, record: any) => void;
startCustomAction: (actionId: string, row: any, extraData?: Record<string, any>) => void;
}>();

onMounted(() => {
Expand Down
Loading