Skip to content

Commit f56439e

Browse files
committed
feat: add ability to set readonlyColumns for create form
1 parent 6c662bf commit f56439e

File tree

11 files changed

+83
-33
lines changed

11 files changed

+83
-33
lines changed

adminforth/documentation/docs/tutorial/03-Customization/13-standardPagesTuning.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,30 @@ const coreStore = useCoreStore();
360360
</script>
361361
```
362362

363+
ALso if you want to disable ability to change such fields (but keep them as readonly) you can add `readonlyColumns` to the link:
364+
365+
```html title="./resources/Dashboard.vue
366+
<template>
367+
...
368+
<LinkButton
369+
:to="{
370+
name: 'resource-create',
371+
params: {
372+
resourceId: 'aparts',
373+
},
374+
query: {
375+
values: encodeURIComponent(JSON.stringify({
376+
realtor_id: coreStore?.adminUser.dbUser.id
377+
})),
378+
//diff-add
379+
readonlyColumns: encodeURIComponent(JSON.stringify(['realtor_id'])),
380+
},
381+
}"
382+
>
383+
{{$t('Create new apartment')}}
384+
</LinkButton>
385+
...
386+
```
363387

364388
## Editing
365389

adminforth/modules/restApi.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ export async function interpretResource(
2929
source: ActionCheckSource,
3030
adminforth: IAdminForth
3131
): Promise<{allowedActions: AllowedActionsResolved}> {
32-
// if (process.env.HEAVY_DEBUG) {
33-
// console.log('🪲Interpreting resource', resource.resourceId, source, 'adminUser', adminUser);
34-
// }
32+
if (process.env.HEAVY_DEBUG) {
33+
console.log('🪲Interpreting resource', resource.resourceId, source, 'adminUser', adminUser);
34+
}
3535
const allowedActions = {} as AllowedActionsResolved;
3636

3737
// we need to compute only allowed actions for this source:
@@ -53,6 +53,8 @@ export async function interpretResource(
5353
[ActionCheckSource.CustomActionRequest]: ['show', 'edit', 'delete', 'create', 'filter'],
5454
}[source];
5555

56+
console.log('🎉🎉🎉 ', JSON.stringify(resource.options.allowedActions, null, 2));
57+
5658
await Promise.all(
5759
Object.entries(resource.options.allowedActions).map(
5860
async ([key, value]: [string, AllowedActionValue]) => {
@@ -67,6 +69,7 @@ export async function interpretResource(
6769
// if callable then call
6870
if (typeof value === 'function') {
6971
allowedActions[key] = await value({ adminUser, resource, meta, source, adminforth });
72+
console.log(`🪲🚥🚥${resource.resourceId}: allowed ${key} (function): ${allowedActions[key]}`);
7073
} else {
7174
allowedActions[key] = value;
7275
}

adminforth/spa/src/afcl/Input.vue

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<template>
22

3-
<div class="flex z-0">
3+
<div class="flex z-0" :class="{'opacity-50' : readonly}">
44
<span
55
v-if="$slots.prefix || prefix"
66
class="inline-flex items-center px-3 text-sm text-gray-900 bg-gray-200 border border-s-0 border-gray-300 rounded-s-md dark:bg-gray-600 dark:text-gray-400 dark:border-gray-600">
@@ -18,6 +18,7 @@
1818
class="inline-flex bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-0 focus:ring-lightPrimary focus:border-lightPrimary dark:focus:ring-darkPrimary dark:focus:border-darkPrimary
1919
blue-500 focus:border-blue-500 block w-20 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white translate-y-0"
2020
:class="{'rounded-l-md': !$slots.prefix && !prefix, 'rounded-r-md': !$slots.suffix && !suffix, 'w-full': fullWidth}"
21+
:disabled="readonly"
2122
>
2223

2324

@@ -34,13 +35,14 @@
3435
3536
import { ref } from 'vue';
3637
37-
const props = defineProps({
38-
type: String,
39-
fullWidth: Boolean,
40-
modelValue: String,
41-
suffix: String,
42-
prefix: String,
43-
})
38+
const props = defineProps<{
39+
type: string,
40+
fullWidth: boolean,
41+
modelValue: string,
42+
suffix: string,
43+
prefix: string,
44+
readonly?: boolean,
45+
}>()
4446
4547
const input = ref<HTMLInputElement | null>(null)
4648

adminforth/spa/src/afcl/Select.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<template>
2-
<div class="relative inline-block afcl-select" ref="internalSelect">
2+
<div class="relative inline-block afcl-select" ref="internalSelect"
3+
:class="{'opacity-50': readonly}"
4+
>
35
<div class="relative">
46
<input
57
ref="inputEl"

adminforth/spa/src/components/ColumnValueInput.vue

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
:record="currentValues"
1111
:resource="coreStore.resource"
1212
:adminUser="coreStore.adminUser"
13+
:readonly="(column.editReadonly && source === 'edit') || readonly"
1314
@update:inValidity="$emit('update:inValidity', $event)"
1415
@update:emptiness="$emit('update:emptiness', $event)"
1516
/>
@@ -21,7 +22,7 @@
2122
teleportToBody
2223
:placeholder = "columnOptions[column.name]?.length ?$t('Select...'): $t('There are no options available')"
2324
:modelValue="value"
24-
:readonly="column.editReadonly && source === 'edit'"
25+
:readonly="(column.editReadonly && source === 'edit') || readonly"
2526
@update:modelValue="$emit('update:modelValue', $event)"
2627
/>
2728
<Select
@@ -31,7 +32,7 @@
3132
:options="column.enum"
3233
teleportToBody
3334
:modelValue="value"
34-
:readonly="column.editReadonly && source === 'edit'"
35+
:readonly="(column.editReadonly && source === 'edit') || readonly"
3536
@update:modelValue="$emit('update:modelValue', $event)"
3637
/>
3738
<Select
@@ -41,7 +42,7 @@
4142
:options="getBooleanOptions(column)"
4243
teleportToBody
4344
:modelValue="value"
44-
:readonly="column.editReadonly && source === 'edit'"
45+
:readonly="(column.editReadonly && source === 'edit') || readonly"
4546
@update:modelValue="$emit('update:modelValue', $event)"
4647
/>
4748
<Input
@@ -55,7 +56,7 @@
5556
:max="![undefined, null].includes(column.maxValue) ? column.maxValue : ''"
5657
:prefix="column.inputPrefix"
5758
:suffix="column.inputSuffix"
58-
:readonly="column.editReadonly && source === 'edit'"
59+
:readonly="(column.editReadonly && source === 'edit') || readonly"
5960
:modelValue="value"
6061
@update:modelValue="$emit('update:modelValue', $event)"
6162
/>
@@ -66,7 +67,7 @@
6667
:valueStart="value"
6768
auto-hide
6869
@update:valueStart="$emit('update:modelValue', $event)"
69-
:readonly="column.editReadonly && source === 'edit'"
70+
:readonly="(column.editReadonly && source === 'edit') || readonly"
7071
/>
7172
<Input
7273
v-else-if="['decimal', 'float'].includes(type || column.type)"
@@ -81,7 +82,7 @@
8182
:suffix="column.inputSuffix"
8283
:modelValue="value"
8384
@update:modelValue="$emit('update:modelValue', $event)"
84-
:readonly="column.editReadonly && source === 'edit'"
85+
:readonly="(column.editReadonly && source === 'edit') || readonly"
8586
/>
8687
<textarea
8788
v-else-if="['text', 'richtext'].includes(type || column.type)"
@@ -90,7 +91,7 @@
9091
:placeholder="$t('Text')"
9192
:value="value"
9293
@input="$emit('update:modelValue', $event.target.value)"
93-
:readonly="column.editReadonly && source === 'edit'"
94+
:readonly="(column.editReadonly && source === 'edit') || readonly"
9495
/>
9596
<textarea
9697
v-else-if="['json'].includes(type || column.type)"
@@ -112,7 +113,7 @@
112113
@update:modelValue="$emit('update:modelValue', $event)"
113114
autocomplete="false"
114115
data-lpignore="true"
115-
readonly
116+
:readonly="(column.editReadonly && source === 'edit') || readonly"
116117
@focus="onFocusHandler($event, column, source)"
117118
/>
118119

@@ -161,10 +162,12 @@
161162
columnOptions: any,
162163
unmasked: any,
163164
deletable?: boolean,
165+
readonly?: boolean,
164166
}>(),
165167
{
166168
type: undefined,
167169
deletable: false,
170+
readonly: false,
168171
}
169172
);
170173

adminforth/spa/src/components/ColumnValueInputWrapper.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,10 @@
3434
</button>
3535
</div>
3636
</template>
37+
3738
<ColumnValueInput
3839
v-else
40+
:readonly="props.readonly"
3941
:source="source"
4042
:column="column"
4143
:value="currentValues[column.name]"
@@ -62,7 +64,8 @@
6264
mode: string,
6365
columnOptions: any,
6466
unmasked: any,
65-
setCurrentValue: Function
67+
setCurrentValue: Function,
68+
readonly?: boolean,
6669
}>();
6770
6871
const emit = defineEmits(['update:unmasked', 'update:inValidity', 'update:emptiness', 'focus-last-input']);

adminforth/spa/src/components/GroupsTable.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
@update:unmasked="unmasked[$event] = !unmasked[$event]"
5454
@update:inValidity="customComponentsInValidity[$event.name] = $event.value"
5555
@update:emptiness="customComponentsEmptiness[$event.name] = $event.value"
56+
:readonly="readonlyColumns?.includes(column.name)"
5657
/>
5758
<div v-if="columnError(column) && validating" class="mt-1 text-xs text-red-500 dark:text-red-400">{{ columnError(column) }}</div>
5859
<div v-if="column.editingNote && column.editingNote[mode]" class="mt-1 text-xs text-gray-400 dark:text-gray-500">{{ column.editingNote[mode] }}</div>
@@ -82,6 +83,7 @@
8283
columnError: (column: any) => string,
8384
setCurrentValue: (columnName: string, value: any) => void,
8485
columnOptions: any,
86+
readonlyColumns?: string[],
8587
}>();
8688
8789
const customComponentsInValidity: Ref<Record<string, boolean>> = ref({});

adminforth/spa/src/components/ResourceForm.vue

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
<form autocomplete="off" @submit.prevent>
66
<div v-if="!groups || groups.length === 0">
77
<GroupsTable
8+
:readonlyColumns="props.readonlyColumns"
89
:source="source"
910
:group="{groupName: '', columns: editableColumns}"
1011
:currentValues="currentValues"
@@ -22,6 +23,7 @@
2223
<div v-else class="flex flex-col gap-4">
2324
<template v-for="group in groupedColumns" :key="group.groupName">
2425
<GroupsTable
26+
:readonlyColumns="props.readonlyColumns"
2527
:source="source"
2628
:group="group"
2729
:currentValues="currentValues"
@@ -36,8 +38,9 @@
3638
@update:customComponentsEmptiness="(data) => customComponentsEmptiness = { ...customComponentsEmptiness, ...data }"
3739
/>
3840
</template>
39-
<div v-if="otherColumns.length > 0">
41+
<div v-if="otherColumns?.length || 0 > 0">
4042
<GroupsTable
43+
:readonlyColumns="props.readonlyColumns"
4144
:source="source"
4245
:group="{groupName: $t('Other'), columns: otherColumns}"
4346
:currentValues="currentValues"
@@ -58,7 +61,7 @@
5861

5962
</template>
6063

61-
<script setup>
64+
<script setup lang="ts">
6265
6366
import { applyRegexValidation, callAdminForthApi} from '@/utils';
6467
import { computedAsync } from '@vueuse/core';
@@ -67,18 +70,20 @@ import { useRouter, useRoute } from 'vue-router';
6770
import { useCoreStore } from "@/stores/core";
6871
import GroupsTable from '@/components/GroupsTable.vue';
6972
import { useI18n } from 'vue-i18n';
73+
import { type AdminForthResourceCommon } from '@/types/Common';
7074
7175
const { t } = useI18n();
7276
7377
const coreStore = useCoreStore();
7478
const router = useRouter();
7579
const route = useRoute();
76-
const props = defineProps({
77-
resource: Object,
78-
record: Object,
79-
validating: Boolean,
80-
source: String,
81-
});
80+
const props = defineProps<{
81+
resource: AdminForthResourceCommon,
82+
record: any,
83+
validating: boolean,
84+
source: 'create' | 'edit',
85+
readonlyColumns?: string[],
86+
}>();
8287
8388
const unmasked = ref({});
8489

adminforth/spa/src/views/CreateView.vue

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
@update:isValid="isValid = $event"
5757
:validating="validating"
5858
:source="'create'"
59+
:readonlyColumns="readonlyColumns"
5960
>
6061
</ResourceForm>
6162

@@ -105,6 +106,8 @@ const { t } = useI18n();
105106
106107
const initialValues = ref({});
107108
109+
const readonlyColumns = ref([]);
110+
108111
109112
async function onUpdateRecord(newRecord) {
110113
console.log('newRecord', newRecord);
@@ -125,6 +128,9 @@ onMounted(async () => {
125128
if (route.query.values) {
126129
initialValues.value = { ...initialValues.value, ...JSON.parse(decodeURIComponent(route.query.values)) };
127130
}
131+
if (route.query.readonlyColumns) {
132+
readonlyColumns.value = JSON.parse(decodeURIComponent(route.query.readonlyColumns));
133+
}
128134
record.value = initialValues.value;
129135
loading.value = false;
130136
checkAcessByAllowedActions(coreStore.resourceOptions.allowedActions,'create');

dev-demo/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ const port = process.env.PORT || 3000;
341341
(async () => {
342342
console.log('🅿️ Bundling AdminForth...');
343343
// needed to compile SPA. Call it here or from a build script e.g. in Docker build time to reduce downtime
344-
await admin.bundleNow({ hotReload: process.env.NODE_ENV === 'development1'});
344+
await admin.bundleNow({ hotReload: process.env.NODE_ENV === 'development'});
345345
console.log('Bundling AdminForth SPA done.');
346346
})();
347347

0 commit comments

Comments
 (0)