Skip to content

Commit 7f891d7

Browse files
committed
Merge branch 'next' of https://github.com/devforth/adminforth into next
2 parents ae10643 + b39df80 commit 7f891d7

File tree

9 files changed

+966
-46
lines changed

9 files changed

+966
-46
lines changed

adminforth/documentation/blog/2025-11-04-k3s-ec2-deployment/index.md

Lines changed: 849 additions & 0 deletions
Large diffs are not rendered by default.

adminforth/documentation/blog/authors.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,9 @@ ypechorkin:
1717
name: Yaroslav Pechorkin
1818
title: Developer of AdminForth
1919
url: https://github.com/yaroslav8765
20-
image_url: https://avatars.githubusercontent.com/u/189334989?v=4
20+
image_url: https://avatars.githubusercontent.com/u/189334989?v=4
21+
kirilldorr:
22+
name: Kyrylo Doropii
23+
title: DevOps Engineer of AdminForth
24+
url: https://github.com/kirilldorr
25+
image_url: https://avatars.githubusercontent.com/u/181721742?s=96&v=4

adminforth/documentation/blog/tags.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,13 @@ authentication:
5252
label: Auth
5353
permalink: /auth
5454
description: Authentication is the process of verifying the identity of a user or system. Authentication is a critical component of security in software applications and systems.
55+
56+
k3s:
57+
label: k3s
58+
permalink: /k3s
59+
description: k3s is a lightweight version of k8s (kubernetes) that is also used for container orchestration but uses fewer resources.
60+
61+
helm:
62+
label: Helm
63+
permalink: /helm
64+
description: The package manager for Kubernetes

adminforth/documentation/docs/tutorial/03-Customization/07-alert.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ Next variants are supported:
2424
* `warning`
2525
* `info`
2626

27-
// ...existing code...
2827
### Making alert responsive
2928
You can pass buttons in the alert method and receive a response like:
3029

adminforth/spa/src/afcl/Dropzone.vue

Lines changed: 94 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -16,37 +16,78 @@
1616
'h-32': !props.multiple,
1717
}"
1818
>
19-
<div class="flex flex-col items-center justify-center pt-5 pb-6">
20-
21-
22-
<svg v-if="!selectedFiles.length" class="w-8 h-8 mb-4 text-lightDropzoneIcon dark:text-darkDropzoneIcon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 16">
23-
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 13h3a3 3 0 0 0 0-6h-.025A5.56 5.56 0 0 0 16 6.5 5.5 5.5 0 0 0 5.207 5.021C5.137 5.017 5.071 5 5 5a4 4 0 0 0 0 8h2.167M10 15V6m0 0L8 8m2-2 2 2"/>
24-
</svg>
25-
<div v-else class="flex items-center justify-center flex-wrap gap-1 w-full mt-1 mb-4">
26-
<template v-for="file in selectedFiles">
27-
<p class="text-sm text-lightDropzoneIcon dark:text-darkDropzoneIcon flex items-center gap-1">
28-
<IconFileSolid class="w-5 h-5" />
29-
{{ file.name }} ({{ humanifySize(file.size) }})
30-
</p>
31-
</template>
32-
19+
<input
20+
:id="id"
21+
type="file"
22+
class="hidden"
23+
:accept="props.extensions.join(',')"
24+
@change="$event.target && doEmit(($event.target as HTMLInputElement).files!)"
25+
:multiple="props.multiple || false"
26+
/>
27+
28+
<div class="flex flex-col items-center justify-center pt-5 pb-6">
29+
<svg
30+
v-if="!selectedFiles.length"
31+
class="w-8 h-8 mb-4 text-lightDropzoneIcon dark:text-darkDropzoneIcon"
32+
xmlns="http://www.w3.org/2000/svg"
33+
fill="none"
34+
viewBox="0 0 20 16"
35+
>
36+
<path
37+
stroke="currentColor"
38+
stroke-linecap="round"
39+
stroke-linejoin="round"
40+
stroke-width="2"
41+
d="M13 13h3a3 3 0 0 0 0-6h-.025A5.56 5.56 0 0 0 16 6.5
42+
5.5 5.5 0 0 0 5.207 5.021C5.137 5.017 5.071 5 5 5a4 4 0
43+
0 0 0 8h2.167M10 15V6m0 0L8 8m2-2 2 2"
44+
/>
45+
</svg>
46+
47+
<div
48+
v-else
49+
class="flex items-center justify-center py-1 flex-wrap gap-2 w-full gap-2 mt-1 mb-4 px-4"
50+
>
51+
<template v-for="(file, index) in selectedFiles" :key="index">
52+
<div
53+
class="text-sm text-lightDropzoneIcon dark:text-darkDropzoneIcon bg-lightDropzoneBackgroundHover dark:bg-darkDropzoneBackgroundHover rounded-md
54+
flex items-center gap-1 px-2 py-1 group"
55+
>
56+
<IconFileSolid class="w-4 h-4 flex-shrink-0" />
57+
<span class="truncate max-w-[200px]">{{ file.name }}</span>
58+
<span class="text-xs">({{ humanifySize(file.size) }})</span>
59+
<button
60+
type="button"
61+
@click.prevent.stop="removeFile(index)"
62+
class="text-lightDropzoneIcon dark:text-darkDropzoneIcon hover:text-red-600 dark:hover:text-red-400
63+
opacity-70 hover:opacity-100 transition-all"
64+
:title="$t('Remove file')"
65+
>
66+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
67+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
68+
</svg>
69+
</button>
3370
</div>
34-
35-
<p v-if="!selectedFiles.length" class="mb-2 text-sm text-lightDropzoneText dark:text-darkDropzoneText"><span class="font-semibold">{{ $t('Click to upload') }}</span> {{ $t('or drag and drop') }}</p>
36-
<p class="text-xs text-lightDropzoneText dark:text-darkDropzoneText">
37-
{{ props.extensions.join(', ').toUpperCase().replace(/\./g, '') }}
38-
<template v-if="props.maxSizeBytes">
39-
(Max size: {{ humanifySize(props.maxSizeBytes) }})
40-
</template>
41-
</p>
71+
</template>
4272
</div>
43-
<input :id="id" type="file" class="hidden"
44-
:accept="props.extensions.join(', ')"
45-
@change="$event.target && doEmit(($event.target as HTMLInputElement).files!)"
46-
:multiple="props.multiple || false"
47-
/>
73+
74+
<p
75+
v-if="!selectedFiles.length"
76+
class="mb-2 text-sm text-lightDropzoneText dark:text-darkDropzoneText"
77+
>
78+
<span class="font-semibold">{{ $t('Click to upload') }}</span>
79+
{{ $t('or drag and drop') }}
80+
</p>
81+
82+
<p class="text-xs text-lightDropzoneText dark:text-darkDropzoneText">
83+
{{ props.extensions.join(', ').toUpperCase().replace(/\./g, '') }}
84+
<template v-if="props.maxSizeBytes">
85+
(Max size: {{ humanifySize(props.maxSizeBytes) }})
86+
</template>
87+
</p>
88+
</div>
4889
</label>
49-
</form>
90+
</form>
5091
</template>
5192

5293
<script setup lang="ts">
@@ -73,12 +114,17 @@ const selectedFiles: Ref<{
73114
mime: string,
74115
}[]> = ref([]);
75116
117+
const storedFiles: Ref<File[]> = ref([]);
118+
76119
watch(() => props.modelValue, (files) => {
77-
selectedFiles.value = Array.from(files).map(file => ({
78-
name: file.name,
79-
size: file.size,
80-
mime: file.type,
81-
}));
120+
if (files && files.length > 0) {
121+
selectedFiles.value = Array.from(files).map(file => ({
122+
name: file.name,
123+
size: file.size,
124+
mime: file.type,
125+
}));
126+
storedFiles.value = Array.from(files);
127+
}
82128
});
83129
84130
function doEmit(filesIn: FileList) {
@@ -110,26 +156,37 @@ function doEmit(filesIn: FileList) {
110156
});
111157
return;
112158
}
113-
159+
114160
validFiles.push(file);
115161
});
116162
117163
if (!multiple) {
118-
validFiles.splice(1);
164+
storedFiles.value = validFiles.slice(0, 1);
165+
} else {
166+
storedFiles.value = [...storedFiles.value, ...validFiles];
119167
}
120-
selectedFiles.value = validFiles.map(file => ({
168+
169+
selectedFiles.value = storedFiles.value.map(file => ({
121170
name: file.name,
122171
size: file.size,
123172
mime: file.type,
124173
}));
125174
126-
emit('update:modelValue', validFiles);
175+
emit('update:modelValue', storedFiles.value);
176+
127177
}
128178
129179
const dragging = ref(false);
130180
181+
function removeFile(index: number) {
182+
storedFiles.value = storedFiles.value.filter((_, i) => i !== index);
183+
selectedFiles.value = selectedFiles.value.filter((_, i) => i !== index);
184+
emit('update:modelValue', storedFiles.value);
185+
}
186+
131187
function clear() {
132188
selectedFiles.value = [];
189+
storedFiles.value = [];
133190
emit('update:modelValue', []);
134191
const form = document.getElementById(id)?.closest('form');
135192
form?.reset();

adminforth/spa/src/views/ShowView.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
<template v-for="action in coreStore.resource.options.actions.filter(a => a.showIn?.showButton)" :key="action.id">
1616
<component
17-
:is="getCustomComponent(action.customComponent) || CallActionWrapper"
17+
:is="action?.customComponent ? getCustomComponent(action.customComponent) : CallActionWrapper"
1818
:meta="action.customComponent?.meta"
1919
@callAction="(payload?) => startCustomAction(action.id, payload)"
2020
:disabled="actionLoadingStates[action.id]"

live-demo/app/package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

live-demo/app/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"@adminforth/two-factors-auth": "^1.1.2",
3333
"@adminforth/upload": "^2.3.6",
3434
"@prisma/client": "^6.6.0",
35-
"adminforth": "^2.4.0-next.280",
35+
"adminforth": "^2.4.0-next.298",
3636
"better-sqlite3": "^10.0.0",
3737
"express": "^4.19.2"
3838
},

live-demo/app/resources/apartments.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ export default {
248248
}
249249
}),
250250
new BulkAiFlowPlugin({
251-
actionName: 'Process',
251+
actionName: 'Process with AI',
252252
attachFiles: async ({ record }: { record: any }) => {
253253
if (!record.apartment_image) {
254254
return [];

0 commit comments

Comments
 (0)