# Actions

### <mark style="color:red;">Overview</mark>

Actions are mechanisms through which clients can request the shell to perform certain operations, such as displaying messages, navigating between pages, or invoking custom logic.

### <mark style="color:red;">Action Types</mark>

AppShell supports two types of actions:

1. **Default Actions** - Predefined UI operations (toast, modal, confirmation dialog)
2. **Custom Actions** - Application-specific operations defined by the shell

### <mark style="color:red;">Default Actions</mark>

Default actions are predefined operations that ensure a consistent user experience across micro-frontends. These actions cover common functionalities that clients frequently need.

#### Available Default Actions

| Action                   | Parameters                                       | Description                                   |
| ------------------------ | ------------------------------------------------ | --------------------------------------------- |
| `showModalDialog`        | `(title, content)`                               | Display a modal dialog with title and content |
| `showConfirmationDialog` | `(title, content)`                               | Show a confirmation dialog, returns boolean   |
| `showToast`              | `(content, type)`                                | Show a brief toast notification               |
| `showErrorMessage`       | `(title, content)`                               | Display an error message prominently          |
| `showRichModal`          | `(uuid, path, context?, size?, closeIconColor?)` | Open a rich modal with iframe content         |
| `removeSearchParams`     | `(keys)`                                         | Remove specified URL search parameters        |
| `setSearchParams`        | `(searchParams)`                                 | Set or update URL search parameters           |

#### Implementing Default Actions

```typescript
const defaultActions = {
  showModalDialog: (title: string, content: string) => {
    Modal.info({ title, content });
  },

  showConfirmationDialog: (title: string, content: string): boolean => {
    return window.confirm(`${title}\n${content}`);
  },

  showToast: (content: string, type: 'success' | 'warning' | 'error' | 'loading' | 'destroy') => {
    if (type === 'destroy') {
      message.destroy();
    } else {
      message[type](content);
    }
  },

  showErrorMessage: (title: string, content: string) => {
    Modal.error({ title, content });
  },

  showRichModal: (uuid: string, path: string, context?: unknown) => {
    // Open a modal with iframe content
    openRichModal({ uuid, path, context });
  },

  removeSearchParams: (keys: string[]) => {
    const params = new URLSearchParams(window.location.search);
    keys.forEach(key => params.delete(key));
    window.history.replaceState({}, '', `?${params.toString()}`);
  },

  setSearchParams: (searchParams: Record<string, string | number>) => {
    const params = new URLSearchParams(window.location.search);
    Object.entries(searchParams).forEach(([key, value]) => {
      params.set(key, String(value));
    });
    window.history.replaceState({}, '', `?${params.toString()}`);
  }
};
```

### <mark style="color:red;">Custom Actions</mark>

Beyond default actions, you can define custom actions specific to your application. These are operations that micro-frontends can invoke to interact with the shell.

#### Defining Custom Actions

```typescript
const customActions = {
  performLogout: () => {
    authService.logout();
    window.location.href = '/login';
  },

  fetchUserData: async (userId: number) => {
    const response = await api.get(`/users/${userId}`);
    return response.data;
  },

  openProductDetail: (productId: string) => {
    navigate(`/products/${productId}`);
  },

  refreshPermissions: async () => {
    const permissions = await api.get('/permissions');
    updatePermissions(permissions.data);
  }
};
```

### <mark style="color:red;">Combining Actions</mark>

Pass both default and custom actions to `AppShellProvider`:

```tsx
import { AppShellProvider, type ApplicationActions } from '@akinon/app-shell';

const App = () => {
  const actions: ApplicationActions = {
    // Default actions
    showModalDialog: (title, content) => Modal.info({ title, content }),
    showConfirmationDialog: (title, content) => window.confirm(`${title}\n${content}`),
    showToast: (content, type) => message[type](content),
    showErrorMessage: (title, content) => Modal.error({ title, content }),
    showRichModal: (uuid, path, context) => openRichModal({ uuid, path, context }),
    removeSearchParams: (keys) => { /* ... */ },
    setSearchParams: (params) => { /* ... */ },
    
    // Custom actions
    actions: {
      performLogout: () => authService.logout(),
      fetchUserData: async (userId) => api.get(`/users/${userId}`),
      refreshPermissions: async () => { /* ... */ }
    }
  };

  return (
    <AppShellProvider
      apps={registeredApps}
      navigation={navigationConfig}
      data={sharedData}
      actions={actions}
    >
      {/* Your shell application */}
    </AppShellProvider>
  );
};
```

### <mark style="color:red;">ApplicationActions Type</mark>

```typescript
interface ApplicationActions {
  // Default actions
  showModalDialog?: (title: string, content: string) => void;
  showConfirmationDialog?: (title: string, content: string) => boolean;
  showToast?: (
    content: string,
    type: 'success' | 'warning' | 'error' | 'loading' | 'destroy'
  ) => void;
  showErrorMessage?: (title: string, content: string) => void;
  showRichModal?: (
    uuid: string,
    path: string,
    context?: unknown,
    size?: ApplicationModalSize,
    closeIconColor?: string
  ) => void;
  removeSearchParams?: (keys: string[]) => void;
  setSearchParams?: (searchParams: Record<string, string | number>) => void;
  
  // Custom actions
  actions: {
    [key: string]: (...args: any[]) => any;
  };
}

type ApplicationModalSize = 'small' | 'medium' | 'large' | 'full';
```

### <mark style="color:red;">Invoking Actions (Client Side)</mark>

Clients invoke actions via the `useAppClient` hook:

```tsx
// In the client application
import { useAppClient } from '@akinon/app-client';

const ClientComponent = () => {
  const { actions } = useAppClient();

  const handleSave = async () => {
    try {
      await saveData();
      actions.showToast('Saved successfully!', 'success');
    } catch (error) {
      actions.showErrorMessage('Save Failed', error.message);
    }
  };

  const handleLogout = () => {
    actions.actions.performLogout();
  };

  return (
    <div>
      <button onClick={handleSave}>Save</button>
      <button onClick={handleLogout}>Logout</button>
    </div>
  );
};
```

{% hint style="info" %}
For detailed information about using actions on the client side, see the Client Application documentation.
{% endhint %}

### <mark style="color:red;">Async Actions</mark>

Actions can be asynchronous. The client will await the result:

```typescript
// Shell
const actions: ApplicationActions = {
  actions: {
    validateOrder: async (orderId: string) => {
      const result = await orderService.validate(orderId);
      return result; // This will be returned to the client
    }
  }
};

// Client
const { actions } = useAppClient();
const isValid = await actions.actions.validateOrder('order-123');
```

### <mark style="color:red;">Best Practices</mark>

#### 1. Keep Actions Simple

Actions should perform a single, well-defined operation.

```typescript
// ❌ Bad - Action does too much
actions: {
  doEverything: async () => {
    await fetchData();
    await updateUI();
    await sendAnalytics();
    await notifyUser();
  }
}

// ✅ Good - Separate, focused actions
actions: {
  fetchData: async () => { /* ... */ },
  sendAnalytics: (event) => { /* ... */ },
  showNotification: (message) => { /* ... */ }
}
```

#### 2. Return Values When Needed

If clients need a result, make sure to return it:

```typescript
actions: {
  // Returns a boolean
  confirmDelete: async (itemId) => {
    const confirmed = await showConfirmDialog('Delete this item?');
    if (confirmed) {
      await deleteItem(itemId);
    }
    return confirmed;
  }
}
```

#### 3. Handle Errors Gracefully

Catch and handle errors within actions:

```typescript
actions: {
  saveData: async (data) => {
    try {
      await api.save(data);
      return { success: true };
    } catch (error) {
      console.error('Save failed:', error);
      return { success: false, error: error.message };
    }
  }
}
```

### <mark style="color:red;">Next Steps</mark>

* [Navigation](/akinon-ui/ui-protocol/shell-application/configuration/navigation.md) - Configure navigation helpers
* [Data Sharing](/akinon-ui/ui-protocol/shell-application/configuration/data-sharing.md) - Share data with clients


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.akinon.com/akinon-ui/ui-protocol/shell-application/configuration/actions.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
