Skip to content

Commit 22099a0

Browse files
authored
Merge pull request #644 from devforth/feature/AdminForth/1680/update-accept-modal
fix: add dangerous option for modal to change color from primary to red
2 parents 083a37b + 366d11f commit 22099a0

12 files changed

Lines changed: 57 additions & 22 deletions

File tree

adminforth/modules/configValidator.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -250,10 +250,9 @@ export default class ConfigValidator implements IConfigValidator {
250250
bulkActions.push({
251251
label: `Delete checked`,
252252
icon: 'flowbite:trash-bin-outline',
253-
confirm: {
254-
title: 'Are you sure you want to delete selected items?',
255-
message: 'Deleting {count} item. This process is irreversible. | Deleting {count} items. This process is irreversible.',
256-
},
253+
confirm: 'Are you sure you want to delete selected items?',
254+
message: 'Deleting {count} item. This process is irreversible. | Deleting {count} items. This process is irreversible.',
255+
dangerous: true,
257256
allowed: async ({ resource, adminUser, allowedActions }) => { return allowedActions.delete },
258257
action: async ({ selectedIds, adminUser, response }) => {
259258
const connector = this.adminforth.connectors[res.dataSource];

adminforth/spa/src/adminforth.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@ class FrontendAPI implements FrontendAPIInterface {
127127
content: params.message,
128128
contentHTML: params.messageHtml,
129129
acceptText: params.yes || 'Yes',
130-
cancelText: params.no || 'Cancel'
130+
cancelText: params.no || 'Cancel',
131+
dangerous: params.dangerous ?? false,
131132
})
132133
this.modalStore.onAcceptFunction = resolve
133134
this.modalStore.onCancelFunction = reject

adminforth/spa/src/components/AcceptModal.vue

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
backgroundCustomClasses="z-[998]"
77
modalCustomClasses="z-[999] flex items-center justify-center"
88
>
9-
<div class="relative p-6 sm:p-8 sm:w-[440px] rounded-lg shadow-xl dark:bg-gray-800" >
9+
<div class="relative p-6 sm:p-8 sm:w-[440px] rounded-lg shadow-xl bg-lightAcceptModalBackground dark:bg-darkAcceptModalBackground" >
1010

1111
<button type="button" @click="modalStore.togleModal()" class="absolute top-4 right-4 text-gray-400 hover:text-gray-900 transition-colors dark:hover:text-white" >
1212
<svg class="w-3.5 h-3.5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
@@ -15,10 +15,13 @@
1515
</button>
1616

1717
<div class="text-center flex flex-col">
18-
<div class="mx-auto flex items-center justify-center h-16 w-16 rounded-full bg-red-50 mb-4 relative dark:bg-red-900/20">
19-
<IconClipboardDocumentSolid class="w-10 h-10 text-red-500" />
20-
<div class="absolute bottom-1 right-1 bg-red-500 rounded-full w-[18px] h-[18px] flex items-center justify-center border-2 border-white dark:border-gray-800">
21-
<span class="text-white text-[10px] font-medium">!</span>
18+
<div class="mx-auto flex items-center justify-center h-16 w-16 rounded-full mb-4 relative"
19+
:class="modalStore.modalContent.dangerous ? 'bg-red-50 dark:bg-red-900/20' : 'bg-lightPrimaryOpacity dark:bg-darkPrimaryOpacity'">
20+
<IconClipboardDocumentSolid class="w-10 h-10"
21+
:class="modalStore.modalContent.dangerous ? 'text-red-500' : 'text-lightButtonsBackground dark:text-darkButtonsBackground'" />
22+
<div class="absolute bottom-1 right-1 rounded-default w-[18px] h-[18px] flex items-center justify-center border-2 border-white dark:border-gray-800"
23+
:class="modalStore.modalContent.dangerous ? 'bg-red-500' : 'bg-lightButtonsBackground dark:bg-darkButtonsBackground'">
24+
<span class="text-lightButtonsText dark:text-darkButtonsText text-[10px] font-medium">!</span>
2225
</div>
2326
</div>
2427

@@ -33,21 +36,21 @@
3336
</div>
3437
</div>
3538

36-
<hr class="border-t-2 border-gray-300 dark:border-gray-700 my-6">
39+
<hr class="border-t-2 border-gray-300 dark:border-gray-500 my-6">
3740

3841
<div class="flex justify-center gap-4 w-full">
39-
<button @click="()=>{modalStore.onAcceptFunction(false);modalStore.togleModal()}" type="button" class="flex-1 py-2.5 px-4 text-sm font-medium text-gray-700 bg-white rounded-lg border border-gray-200 hover:bg-gray-50 focus:ring-4 focus:ring-gray-100 transition-all dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-700">
42+
<button @click="()=>{modalStore.onAcceptFunction(false);modalStore.togleModal()}" type="button" class="flex-1 py-2.5 px-4 text-sm font-medium text-gray-700 bg-white rounded-default border border-gray-200 hover:bg-gray-50 focus:ring-4 focus:ring-gray-100 transition-all dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-700">
4043
{{ modalStore?.modalContent?.cancelText }}
4144
</button>
4245

43-
<button
44-
@click="()=>{ modalStore.onAcceptFunction(true);modalStore.togleModal()}"
45-
type="button"
46-
class="flex-1 flex items-center justify-center py-2.5 px-4 text-sm font-medium transition-all focus:outline-none
47-
text-white bg-red-600 hover:bg-red-700
48-
dark:bg-red-500 dark:hover:bg-red-600
49-
border-none rounded-lg shadow-sm focus:z-10 focus:ring-4
50-
focus:ring-red-100 dark:focus:ring-red-900 gap-1">
46+
<button
47+
@click="()=>{ modalStore.onAcceptFunction(true);modalStore.togleModal()}"
48+
type="button"
49+
class="flex-1 flex items-center justify-center py-2.5 px-4 text-sm font-medium transition-all focus:outline-none
50+
border-none rounded-default shadow-sm focus:z-10 focus:ring-4 gap-1"
51+
:class="modalStore.modalContent.dangerous
52+
? 'text-lightAcceptModalConfirmButtonText dark:text-darkAcceptModalConfirmButtonText bg-lightAcceptModalConfirmButtonBackground dark:bg-darkAcceptModalConfirmButtonBackground hover:bg-lightAcceptModalConfirmButtonBackgroundHover dark:hover:bg-darkAcceptModalConfirmButtonBackgroundHover focus:ring-lightAcceptModalConfirmButtonFocus dark:focus:ring-darkAcceptModalConfirmButtonFocus'
53+
: 'text-lightButtonsText dark:text-darkButtonsText bg-lightButtonsBackground dark:bg-darkButtonsBackground hover:bg-lightButtonsHover dark:hover:bg-darkButtonsHover focus:ring-lightButtonFocusRing dark:focus:ring-darkButtonFocusRing'">
5154
{{ modalStore?.modalContent?.acceptText }}
5255
</button>
5356
</div>

adminforth/spa/src/components/ResourceListTable.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,7 @@ async function deleteRecord(row: any) {
641641
message: t(`This process is irreversible.`),
642642
yes: t('Delete'),
643643
no: t('Cancel'),
644+
dangerous: true,
644645
});
645646
if (data) {
646647
try {

adminforth/spa/src/stores/modal.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { defineStore } from 'pinia'
88
contentHTML?: string;
99
acceptText?: string;
1010
cancelText?: string;
11+
dangerous?: boolean;
1112
}
1213

1314

@@ -19,6 +20,7 @@ export const useModalStore = defineStore('modal', () => {
1920
contentHTML: '',
2021
acceptText: 'acceptText',
2122
cancelText: 'cancelText',
23+
dangerous: false,
2224
});
2325
const isOpened = ref(false);
2426
const onAcceptFunction: any = ref(()=>{});
@@ -40,6 +42,7 @@ export const useModalStore = defineStore('modal', () => {
4042
contentHTML: content.contentHTML || '',
4143
acceptText: content.acceptText || 'acceptText',
4244
cancelText: content.cancelText || 'cancelText',
45+
dangerous: content.dangerous ?? false,
4346
};
4447
}
4548
function resetmodalState() {
@@ -51,6 +54,7 @@ export const useModalStore = defineStore('modal', () => {
5154
contentHTML: '',
5255
acceptText: 'acceptText',
5356
cancelText: 'cancelText',
57+
dangerous: false,
5458
};
5559
setOnAcceptFunction(()=>{});
5660

adminforth/spa/src/utils/listUtils.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,15 @@ export async function startBulkAction(actionId: string, resource: AdminForthReso
6464
if (action?.confirm) {
6565
const confirmed = await confirm(
6666
typeof action.confirm === 'string'
67-
? { title: action.confirm }
67+
? {
68+
title: action.confirm,
69+
message: t('Deleting {count} item. This process is irreversible. | Deleting {count} items. This process is irreversible.', { count: checkboxes.value.length }),
70+
dangerous: action.dangerous ?? false,
71+
}
6872
: {
6973
...action.confirm,
7074
message: action.confirm.message && t(action.confirm.message, { count: checkboxes.value.length }),
75+
dangerous: action.dangerous ?? false,
7176
}
7277
);
7378
if (!confirmed) {

adminforth/spa/src/utils/utils.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,7 @@ export async function onBeforeRouteLeaveCreateEditViewGuard(initialValues: any,
792792
messageHtml,
793793
yes: t('Leave without saving'),
794794
no: t('Stay and continue'),
795+
dangerous: true,
795796
});
796797

797798
return answer;
@@ -875,6 +876,7 @@ export async function executeCustomBulkAction({
875876
onError,
876877
setLoadingState,
877878
confirmMessage,
879+
confirmDangerous,
878880
resource,
879881
}: {
880882
actionId: string | number | undefined,
@@ -885,6 +887,7 @@ export async function executeCustomBulkAction({
885887
onError?: (error: string) => void,
886888
setLoadingState?: (loading: boolean) => void,
887889
confirmMessage?: string,
890+
confirmDangerous?: boolean,
888891
resource?: AdminForthResourceFrontend,
889892
}): Promise<any> {
890893
if (!recordIds || recordIds.length === 0) {
@@ -898,6 +901,7 @@ export async function executeCustomBulkAction({
898901
const { confirm } = useAdminforth();
899902
const confirmed = await confirm({
900903
message: confirmMessage,
904+
dangerous: confirmDangerous ?? false,
901905
});
902906
if (!confirmed) {
903907
return { cancelled: true };

adminforth/spa/src/views/ListView.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,7 @@ async function startCustomBulkActionInner(actionId: string | number) {
361361
resourceId: route.params.resourceId as string,
362362
recordIds: checkboxes.value,
363363
confirmMessage: action?.bulkConfirmationMessage,
364+
confirmDangerous: action?.bulkDangerous ?? false,
364365
resource: coreStore.resource!,
365366
setLoadingState: (loading: boolean) => {
366367
customActionLoadingStates.value[actionId] = loading;

adminforth/spa/src/views/ShowView.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ async function deleteRecord() {
314314
message: t(`This process is irreversible.`),
315315
yes: t('Delete'),
316316
no: t('Cancel'),
317+
dangerous: true,
317318
});
318319
if (data) {
319320
try {

adminforth/types/Back.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1469,6 +1469,10 @@ interface AdminForthInputConfigCustomization {
14691469
export interface AdminForthActionInput {
14701470
name: string;
14711471
bulkConfirmationMessage?: string;
1472+
/**
1473+
* When true, the bulk confirmation dialog renders in red/danger style.
1474+
*/
1475+
bulkDangerous?: boolean;
14721476
bulkSuccessMessage?: string;
14731477
showIn?: {
14741478
list?: boolean,

0 commit comments

Comments
 (0)