Skip to content

Conversation

NoOne7135
Copy link
Contributor

No description provided.

@NoOne7135 NoOne7135 requested a review from Copilot August 20, 2025 14:36
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces a new bulk action component system that allows attaching custom Vue components to actions. The main purpose is to enable custom wrappers around action triggers, such as requiring 2FA verification before executing actions.

Key changes:

  • Adds support for customComponent property in action definitions
  • Introduces CallActionWrapper component for handling action clicks
  • Updates action handling across multiple views to support custom components

Reviewed Changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
dev-demo/resources/users.ts Updates user resource configuration to use new actions format and adds demo action
dev-demo/custom/ShadowLoginButton.vue Adds new custom component file for testing
adminforth/types/Back.ts Extends action interface to include optional customComponent property
adminforth/spa/src/views/ShowView.vue Updates action button rendering to support custom components
adminforth/spa/src/components/ThreeDotsMenu.vue Adds custom component support to dropdown menu actions
adminforth/spa/src/components/ResourceListTableVirtual.vue Integrates custom components in virtual table action buttons
adminforth/spa/src/components/ResourceListTable.vue Integrates custom components in regular table action buttons
adminforth/spa/src/components/CallActionWrapper.vue New wrapper component for handling action clicks with disabled state
adminforth/modules/configValidator.ts Adds validation for custom components in action definitions
adminforth/documentation/docs/tutorial/07-Plugins/02-TwoFactorsAuth.md Documents how to use custom components with 2FA plugin

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

function onClick() {
if (props.disabled) return;
const extra = { someData: 'example' };
emit('callAction', extra);
Copy link
Preview

Copilot AI Aug 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hardcoded 'extra' object with example data appears to be placeholder code that should be removed or made configurable.

Suggested change
emit('callAction', extra);
const props = defineProps<{ disabled?: boolean; extra?: any }>();
const emit = defineEmits<{ (e: 'callAction', extra?: any ): void }>();
function onClick() {
if (props.disabled) return;
emit('callAction', props.extra);

Copilot uses AI. Check for mistakes.

</component>
</li>
<li v-for="action in bulkActions.filter(a => a.showInThreeDotsDropdown)" :key="action.id">
<li v-for="action in bulkActions?.filter(a => a.showInThreeDotsDropdown)" :key="action.id">
Copy link
Preview

Copilot AI Aug 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding optional chaining (?.) to bulkActions suggests it might be undefined, but this could cause silent failures. Consider checking if bulkActions exists before filtering or provide a default empty array.

Suggested change
<li v-for="action in bulkActions?.filter(a => a.showInThreeDotsDropdown)" :key="action.id">
<li v-for="action in (bulkActions ?? []).filter(a => a.showInThreeDotsDropdown)" :key="action.id">

Copilot uses AI. Check for mistakes.

<button
type="button"
:disabled="rowActionLoadingStates?.[action.id]"
@click.stop.prevent="startCustomAction(action.id, row)"
Copy link
Preview

Copilot AI Aug 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The click handler uses both .stop and .prevent modifiers which might be overly restrictive. Consider if both are necessary or if this could interfere with expected event behavior.

Suggested change
@click.stop.prevent="startCustomAction(action.id, row)"
@click.stop="startCustomAction(action.id, row)"

Copilot uses AI. Check for mistakes.

<button
type="button"
:disabled="rowActionLoadingStates?.[action.id]"
@click.stop.prevent="startCustomAction(action.id, row)"
Copy link
Preview

Copilot AI Aug 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The click handler uses both .stop and .prevent modifiers which might be overly restrictive. Consider if both are necessary or if this could interfere with expected event behavior.

Suggested change
@click.stop.prevent="startCustomAction(action.id, row)"
@click.prevent="startCustomAction(action.id, row)"

Copilot uses AI. Check for mistakes.

@NoOne7135 NoOne7135 requested a review from ivictbor August 20, 2025 14:50
import { computed } from 'vue';

const props = defineProps<{ meta?: { color?: string; radius?: number; padding?: number } }>();
const emit = defineEmits<{ (e: 'callAction', payload?: unknown): void }>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@NoOne7135 payload any here?

<template>
<!-- Keep the slot: AdminForth renders the default action button/icon here -->
<!-- Emit `callAction` to trigger the action when the wrapper is clicked -->
<div :style="styleObj" @click="emit('callAction')">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@NoOne7135 for verbosity can we use emit('callAction', {}) ? so user will understand maybe better that he will pass payload

</script>
```
Now we need to read code entered on fronted on backend and verify that is is valid and not expired, on backend action handler:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now we need to use code which we got from user on fronted, inside of backend action handler and verify that is is valid and not expired:

If you want to style an action's button/icon without changing its behavior, attach a custom UI wrapper via `customComponent`.
The file points to your SFC in the custom folder (alias `@@/`), and `meta` lets you pass lightweight styling options (e.g., border color, radius).

```ts title="./resources/apartments.ts"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@NoOne7135 lets use mark as listed example as base (with link to original mark as listed action)

borderRadius: (props.meta?.radius ?? 8) + 'px',
padding: (props.meta?.padding ?? 2) + 'px',
}));
</script>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@NoOne7135 lets add one more example which demonstrates how to pass dynamic value from frontend to backend.

@click="emit('callAction', {asListed: true})"

@click="emit('callAction', {asListed: false})"

and how to access it on backend in handler - using extra

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants