Files
whatshooked/tooldoc/ORANGURU.md
Hein f9773bd07f
Some checks failed
CI / Test (1.23) (push) Failing after -22m46s
CI / Test (1.22) (push) Failing after -22m32s
CI / Build (push) Failing after -23m30s
CI / Lint (push) Failing after -23m12s
refactor(API): Relspect integration
2026-02-05 13:39:43 +02:00

7.6 KiB

Oranguru Integration Guide

Overview

Oranguru is a React component library that provides enhanced Mantine-based components with advanced features and state management capabilities. For WhatsHooked, we'll use it to build data grids and forms for the admin interface.

Installation

npm install @warkypublic/oranguru

Peer Dependencies

npm install react zustand @mantine/core @mantine/hooks @warkypublic/artemis-kit @warkypublic/zustandsyncstore use-sync-external-store

Core Concepts

Enhanced Context Menus

Oranguru provides better menu positioning and visibility control than standard Mantine menus.

Custom Rendering

Support for custom menu item renderers and complete menu rendering.

State Management

Uses Zustand for component state management with sync capabilities.

Basic Setup

import { MantineBetterMenusProvider } from '@warkypublic/oranguru';
import { MantineProvider } from '@mantine/core';

function App() {
  return (
    <MantineProvider>
      <MantineBetterMenusProvider>
        {/* Your app content */}
      </MantineBetterMenusProvider>
    </MantineProvider>
  );
}

Using Context Menus

import { useMantineBetterMenus } from '@warkypublic/oranguru';

function DataGrid() {
  const { show, hide } = useMantineBetterMenus();
  
  const handleContextMenu = (e: React.MouseEvent, record: any) => {
    e.preventDefault();
    show('record-menu', {
      x: e.clientX,
      y: e.clientY,
      items: [
        {
          label: 'Edit',
          onClick: () => handleEdit(record)
        },
        {
          label: 'Delete',
          onClick: () => handleDelete(record)
        },
        {
          isDivider: true
        },
        {
          label: 'View Details',
          onClick: () => handleViewDetails(record)
        }
      ]
    });
  };

  return (
    <table>
      {records.map(record => (
        <tr key={record.id} onContextMenu={(e) => handleContextMenu(e, record)}>
          <td>{record.name}</td>
        </tr>
      ))}
    </table>
  );
}

Async Actions

const asyncMenuItem = {
  label: 'Sync Data',
  onClickAsync: async () => {
    await fetch('/api/sync', { method: 'POST' });
    // Shows loading state automatically
  }
};

Custom Menu Items

const customItem = {
  renderer: ({ loading }: any) => (
    <div style={{ padding: '8px 12px' }}>
      {loading ? (
        <Loader size="xs" />
      ) : (
        <Group>
          <IconCheck size={16} />
          <Text>Custom Action</Text>
        </Group>
      )}
    </div>
  ),
  onClickAsync: async () => {
    await performAction();
  }
};

Integration with Data Grids

While Oranguru doesn't provide a built-in data grid component yet, it works excellently with Mantine's DataTable or custom table implementations:

import { useMantineBetterMenus } from '@warkypublic/oranguru';
import { DataTable } from '@mantine/datatable';

function UserGrid() {
  const { show } = useMantineBetterMenus();
  const [users, setUsers] = useState([]);

  const columns = [
    { accessor: 'name', title: 'Name' },
    { accessor: 'email', title: 'Email' },
    { accessor: 'status', title: 'Status' }
  ];

  const handleRowContextMenu = (e: React.MouseEvent, user: User) => {
    e.preventDefault();
    show('user-menu', {
      x: e.clientX,
      y: e.clientY,
      items: [
        {
          label: 'Edit User',
          onClick: () => navigate(`/users/${user.id}/edit`)
        },
        {
          label: 'Deactivate',
          onClickAsync: async () => {
            await fetch(`/api/users/${user.id}/deactivate`, { method: 'POST' });
            await refreshUsers();
          }
        },
        {
          isDivider: true
        },
        {
          label: 'Delete',
          onClick: () => handleDelete(user.id)
        }
      ]
    });
  };

  return (
    <DataTable
      columns={columns}
      records={users}
      onRowContextMenu={({ event, record }) => handleRowContextMenu(event, record)}
    />
  );
}

Form Integration

For forms, use Mantine's form components with Oranguru's menu system for enhanced UX:

import { useForm } from '@mantine/form';
import { TextInput, Select, Button } from '@mantine/core';

function UserForm() {
  const form = useForm({
    initialValues: {
      name: '',
      email: '',
      role: ''
    },
    validate: {
      email: (value) => (/^\S+@\S+$/.test(value) ? null : 'Invalid email'),
      name: (value) => (value.length < 2 ? 'Name too short' : null)
    }
  });

  const handleSubmit = async (values: typeof form.values) => {
    try {
      await fetch('/api/users', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(values)
      });
      notifications.show({ message: 'User created successfully' });
    } catch (error) {
      notifications.show({ 
        message: 'Failed to create user',
        color: 'red'
      });
    }
  };

  return (
    <form onSubmit={form.onSubmit(handleSubmit)}>
      <TextInput
        label="Name"
        placeholder="John Doe"
        {...form.getInputProps('name')}
      />
      <TextInput
        label="Email"
        placeholder="john@example.com"
        {...form.getInputProps('email')}
      />
      <Select
        label="Role"
        data={['Admin', 'User', 'Viewer']}
        {...form.getInputProps('role')}
      />
      <Button type="submit">Create User</Button>
    </form>
  );
}

Best Practices

  1. Provider Placement: Place MantineBetterMenusProvider at the root of your app
  2. Menu IDs: Use descriptive, unique IDs for each menu type
  3. Context Menus: Always prevent default on right-click events
  4. Loading States: Use onClickAsync for async operations to get automatic loading states
  5. Custom Renderers: Use custom renderers for complex menu items with icons, badges, etc.
  6. Portal Rendering: Oranguru uses portals for proper z-index handling
  7. State Management: Leverage Zustand store for complex menu state
  8. Accessibility: Ensure keyboard navigation works with context menus

Common Patterns

Multi-Select Context Menu

const handleBulkContextMenu = (e: React.MouseEvent, selectedIds: string[]) => {
  e.preventDefault();
  show('bulk-menu', {
    x: e.clientX,
    y: e.clientY,
    items: [
      {
        label: `Delete ${selectedIds.length} items`,
        onClickAsync: async () => {
          await bulkDelete(selectedIds);
        }
      },
      {
        label: 'Export selection',
        onClick: () => exportItems(selectedIds)
      }
    ]
  });
};

Conditional Menu Items

const menuItems = [
  {
    label: 'Edit',
    onClick: () => handleEdit(record)
  },
  canDelete(record) && {
    label: 'Delete',
    onClick: () => handleDelete(record)
  },
  {
    isDivider: true
  },
  isAdmin && {
    label: 'Admin Actions',
    onClick: () => showAdminActions(record)
  }
].filter(Boolean); // Remove falsy items

Nested Menus (Future Feature)

While not yet supported, Oranguru is designed to support nested menus in future versions.

Integration with WhatsHooked

For WhatsHooked's admin interface:

  1. User Management Grid: Use DataTable with context menus for user actions
  2. Hook Configuration: Form with validation and async submission
  3. WhatsApp Account Management: Grid with QR code display and pairing actions
  4. API Key Management: Grid with copy-to-clipboard and revoke actions
  5. Event Logs: Read-only grid with filtering and export

References