Skip to content

Commit 24a517b

Browse files
committed
feat: implement attachment file retrieval and zoom functionality in image generator
1 parent 7c4dcb0 commit 24a517b

File tree

2 files changed

+99
-5
lines changed

2 files changed

+99
-5
lines changed

custom/imageGenerator.vue

Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,41 @@
2121
</div>
2222
<!-- Modal body -->
2323
<div class="p-4 md:p-5 space-y-4">
24-
<textarea id="message" rows="3" class="block p-2.5 w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
25-
:placeholder="$t('Prompt which will be passed to AI network')" v-model="prompt"
24+
<!-- PROMPT TEXTAREA -->
25+
<!-- Textarea -->
26+
<textarea
27+
id="message"
28+
rows="3"
29+
class="block p-2.5 w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
30+
:placeholder="$t('Prompt which will be passed to AI network')"
31+
v-model="prompt"
2632
:title="$t('Prompt which will be passed to AI network')"
27-
></textarea>
33+
></textarea>
34+
35+
<!-- Thumbnails -->
36+
<div class="mt-2 flex flex-wrap gap-2">
37+
<img
38+
v-for="(img, idx) in attachmentFiles"
39+
:key="idx"
40+
:src="img"
41+
class="w-20 h-20 object-cover rounded cursor-pointer border hover:border-blue-500 transition"
42+
:alt="`Generated image ${idx + 1}`"
43+
@click="zoomImage(img)"
44+
/>
45+
</div>
46+
47+
<!-- Fullscreen Modal -->
48+
<div
49+
v-if="zoomedImage"
50+
class="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-80"
51+
@click.self="closeZoom"
52+
>
53+
<img
54+
:src="zoomedImage"
55+
ref="zoomedImg"
56+
class="max-w-full max-h-full rounded-lg object-contain cursor-grab"
57+
/>
58+
</div>
2859

2960
<div class="flex flex-col items-center justify-center w-full relative">
3061
<div
@@ -144,7 +175,7 @@
144175

145176
<script setup lang="ts">
146177
147-
import { ref, onMounted, nextTick, Ref, h, computed } from 'vue'
178+
import { ref, onMounted, nextTick, Ref, h, computed, watch, reactive } from 'vue'
148179
import { Carousel } from 'flowbite';
149180
import { callAdminForthApi } from '@/utils';
150181
import { useI18n } from 'vue-i18n';
@@ -158,6 +189,7 @@ const emit = defineEmits(['close', 'uploadImage']);
158189
const props = defineProps(['meta', 'record']);
159190
const images = ref([]);
160191
const loading = ref(false);
192+
const attachmentFiles = ref<string[]>([])
161193
162194
function minifyField(field: string): string {
163195
if (field.length > 100) {
@@ -167,7 +199,7 @@ function minifyField(field: string): string {
167199
}
168200
169201
const caurosel = ref(null);
170-
onMounted(() => {
202+
onMounted(async () => {
171203
// Initialize carousel
172204
const context = {
173205
field: props.meta.pathColumnLabel,
@@ -203,7 +235,24 @@ onMounted(() => {
203235
prompt.value = template.replace(regex, (_, field) => {
204236
return context[field.trim()] || '';
205237
});
238+
239+
const recordId = props.record[props.meta.recorPkFieldName];
240+
if (!recordId) return;
241+
242+
try {
243+
const resp = await callAdminForthApi({
244+
path: `/plugin/${props.meta.pluginInstanceId}/get_attachment_files`,
245+
method: 'POST',
246+
body: { recordId },
247+
});
206248
249+
if (resp?.files?.length) {
250+
attachmentFiles.value = resp.files;
251+
console.log('attachmentFiles', attachmentFiles.value);
252+
}
253+
} catch (err) {
254+
console.error('Failed to fetch attachment files', err);
255+
}
207256
});
208257
209258
async function slide(direction: number) {
@@ -357,6 +406,27 @@ async function generateImages() {
357406
loading.value = false;
358407
}
359408
409+
import mediumZoom from 'medium-zoom'
360410
411+
const zoomedImage = ref(null)
412+
const zoomedImg = ref(null)
413+
414+
function zoomImage(img) {
415+
zoomedImage.value = img
416+
}
361417
418+
function closeZoom() {
419+
zoomedImage.value = null
420+
}
421+
422+
watch(zoomedImage, async (val) => {
423+
await nextTick()
424+
if (val && zoomedImg.value) {
425+
mediumZoom(zoomedImg.value, {
426+
margin: 24,
427+
background: 'rgba(0, 0, 0, 0.9)',
428+
scrollOffset: 150
429+
}).show()
430+
}
431+
})
362432
</script>

index.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,30 @@ export default class UploadPlugin extends AdminForthPlugin {
423423
return null
424424
}
425425
});
426+
427+
server.endpoint({
428+
method: 'POST',
429+
path: `/plugin/${this.pluginInstanceId}/get_attachment_files`,
430+
handler: async ({ body, adminUser }) => {
431+
const { recordId } = body;
432+
433+
if (!recordId) return { error: 'Missing recordId' };
434+
435+
const record = await this.adminforth.resource(this.resourceConfig.resourceId).get([
436+
Filters.EQ(this.resourceConfig.columns.find((col: any) => col.primaryKey)?.name, recordId),
437+
]);
438+
439+
if (!record) return { error: 'Record not found' };
440+
441+
if (!this.options.generation.attachFiles) return { files: [] };
442+
443+
const files = await this.options.generation.attachFiles({ record, adminUser });
444+
445+
return {
446+
files: Array.isArray(files) ? files : [files],
447+
};
448+
},
449+
});
426450

427451
}
428452

0 commit comments

Comments
 (0)