# Create Custom App

## Creating a Custom App

This guide walks you through creating a custom app that runs inside the 0r-smarttab tablet.

***

### Step 1: Register the App

Open the admin panel in-game:

```
/tabadmin
```

Click **"Add New App"** and fill in the form:

| Field           | Description                             | Example                                         |
| --------------- | --------------------------------------- | ----------------------------------------------- |
| **App Name**    | Display name on the tablet              | `Stock Market`                                  |
| **App ID**      | Unique identifier (no spaces)           | `app-stocks`                                    |
| **Description** | Short description (optional)            | `Real-time stock tracker`                       |
| **Category**    | Dropdown: General, Job, Services, etc.  | `Services`                                      |
| **Color**       | Pick a color for the icon background    | *(click a circle)*                              |
| **Icon Path**   | Path to icon image                      | `/appicons/stocks.png`                          |
| **Iframe URL**  | Your app's NUI URL                      | `https://cfx-nui-my-stocks/web/dist/index.html` |
| **Job Filter**  | Comma-separated jobs (empty = everyone) | `police, ems`                                   |

Click **"Create App"**. The app appears on all tablets immediately.

> **Icon tip:** Place your `.png` icon file in `0r-smarttab/web/dist/appicons/`. Use a square image (128x128 recommended).

***

### Step 2: Set Up the Resource

Download the **0r-customapp** template from GitHub. This is a ready-to-use FiveM resource with React + TypeScript.

#### File Structure

```
my-custom-app/
├── fxmanifest.lua
├── client.lua
├── server.lua
└── web/
    ├── src/
    │   ├── main.tsx
    │   ├── App.tsx
    │   └── hooks/
    │       └── useTabletContext.ts
    └── dist/
        └── index.html
```

#### fxmanifest.lua

```lua
fx_version 'cerulean'
game 'gta5'
lua54 'yes'

description 'My Custom App'
version '1.0.0'

client_scripts { 'client.lua' }
server_scripts { 'server.lua' }

ui_page 'web/dist/index.html'

files {
    'web/dist/index.html',
    'web/dist/**/*.*'
}
```

#### Iframe URL Format

The URL you entered in the admin panel must point to your resource:

```
https://cfx-nui-my-custom-app/web/dist/index.html
```

Replace `my-custom-app` with your resource folder name.

***

### Step 3: Receive Tablet Context

When your app opens inside the tablet, it automatically receives context data via `postMessage`. Use the included hook:

```tsx
import { useTabletContext } from './hooks/useTabletContext'

function App() {
  const ctx = useTabletContext()

  if (!ctx) return null // Not inside tablet yet

  return (
    <div>
      <p>Serial: {ctx.serial}</p>
      <p>Player Job: {ctx.playerJob}</p>
      <p>Language: {ctx.language}</p>
    </div>
  )
}
```

#### Context Fields

| Field       | Type     | Description                              |
| ----------- | -------- | ---------------------------------------- |
| `serial`    | `string` | Tablet serial number (unique per device) |
| `appId`     | `string` | App action ID (e.g. `app-stocks`)        |
| `appFullId` | `string` | Full app ID from apps.json               |
| `playerJob` | `string` | Player's current job name                |
| `language`  | `string` | Tablet language code (`en`, `tr`, etc.)  |
| `config`    | `object` | Server config values                     |

***

### Step 4: Use Tablet Features

Custom apps can use tablet notifications and badges through **Lua exports**. No JavaScript needed for these — just call them from your `client.lua` or `server.lua`.

#### Send a Notification

Shows a notification banner on the tablet (with sound, peek when closed, notification history).

**From client.lua:**

```lua
exports['0r-smarttab']:SendNotification({
    title    = 'Stock Market',
    description = 'BTC is up 12% today!',
    app      = 'app-stocks',   -- your app ID (for icon & peek)
    duration = 5000             -- ms, default 4000
})
```

**From server.lua:**

```lua
-- Send to a specific player
exports['0r-smarttab']:SendNotification(source, {
    title       = 'Stock Market',
    description = 'Your order has been filled.',
    app         = 'app-stocks',
    duration    = 5000
})
```

#### Set App Badge

Shows a red badge (number) on your app's icon on the home screen.

**From client.lua:**

```lua
-- Set badge to 3
exports['0r-smarttab']:SetAppBadge('app-stocks', 3)

-- Clear badge
exports['0r-smarttab']:SetAppBadge('app-stocks', 0)
```

**From server.lua:**

```lua
exports['0r-smarttab']:SetAppBadge(source, 'app-stocks', 5)
```

***

### Step 5: Listen for Tablet Events (Optional)

Your app's JavaScript can listen for theme and visibility changes from the tablet:

```js
window.addEventListener('message', (event) => {
  const { type, payload } = event.data || {}

  if (type === '0r-smarttab-theme-change') {
    // payload.theme = 'dark' or 'light'
    document.body.className = payload.theme
  }

  if (type === '0r-smarttab-visibility') {
    // payload.visible = true (tablet opened) or false (tablet closed)
  }
})
```

Your app can also request to close itself:

```js
window.parent.postMessage({ type: '0r-smarttab-close' }, '*')
```

***

### Quick Reference

#### Lua Exports

| Export              | Side   | Signature                                                     |
| ------------------- | ------ | ------------------------------------------------------------- |
| `SendNotification`  | Client | `exports['0r-smarttab']:SendNotification(data)`               |
| `SendNotification`  | Server | `exports['0r-smarttab']:SendNotification(targetSrc, data)`    |
| `SetAppBadge`       | Client | `exports['0r-smarttab']:SetAppBadge(appId, count)`            |
| `SetAppBadge`       | Server | `exports['0r-smarttab']:SetAppBadge(targetSrc, appId, count)` |
| `OpenTabletWithApp` | Client | `exports['0r-smarttab']:OpenTabletWithApp(appId)`             |

#### Iframe Messages (JS)

| Message                    | Direction    | Payload                                          |
| -------------------------- | ------------ | ------------------------------------------------ |
| `0r-smarttab-context`      | Tablet → App | `{ serial, appId, playerJob, language, config }` |
| `0r-smarttab-theme-change` | Tablet → App | `{ theme: 'dark' \| 'light' }`                   |
| `0r-smarttab-visibility`   | Tablet → App | `{ visible: boolean }`                           |
| `0r-smarttab-close`        | App → Tablet | *(no payload)*                                   |

#### Notification Data Fields

| Field         | Type     | Required | Default |
| ------------- | -------- | -------- | ------- |
| `title`       | `string` | Yes      | —       |
| `description` | `string` | Yes      | —       |
| `app`         | `string` | No       | —       |
| `icon`        | `string` | No       | —       |
| `duration`    | `number` | No       | `4000`  |
