Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Mkdocs build directory
/site/
/.cache/
/.idea
.vscode
__pycache__/
Expand Down
177 changes: 177 additions & 0 deletions docs/html-pages/example.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
---
description: A complete, minimal HTML Page example — initialize the SDK, read rows from the base, render them, and write new linked rows back on submit.
---

# Example: a form that reads and writes

This page ties the SDK together into one working flow: load data on start, render it, and write new rows back on submit. It is a distilled version of the [simple form template](https://github.com/seatable/seatable-html-page-template-simple-form) — a form that lists products from the base, lets the user pick quantities, and stores the result as an order.

The data flow is always the same four steps:

```mermaid
flowchart LR
A[init SDK] --> B[listRows: load data]
B --> C[render UI]
C --> D[on submit: batchAddRows + addRow]
```

It assumes the three tables described in [Getting Started](getting-started.md): **Products**, **OrderItems** (link to Products), and **Orders** (link to OrderItems).

## Single-file version (non-modular)

This is the complete page. It loads the SDK from a CDN, so everything lives in one `index.html` — the non-modular style. Styling is omitted for clarity.

```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Product Order</title>
</head>
<body>
<div id="products"></div>
<button id="submit">Submit order</button>
<p id="status"></p>

<script src="https://unpkg.com/seatable-html-page-sdk@latest/dist/index.min.js"></script>
<script>
// Quantities chosen by the user: { productRowId: quantity }
const order = {};
let sdk;

async function init() {
// In production __HTML_PAGE_DEV_CONFIG__ is undefined and the SDK
// derives its context from the host app. In development the Vite
// server injects it from src/setting.js. The same line works in both.
sdk = new HTMLPageSDK(window.__HTML_PAGE_DEV_CONFIG__ || null);
await sdk.init();

// Read rows. The payload is under res.data (see SDK Reference > Rows).
const res = await sdk.listRows({ tableName: "Products" });
renderProducts(res.data.results);
}

function renderProducts(products) {
const container = document.getElementById("products");
container.innerHTML = "";

products.forEach((product) => {
const row = document.createElement("div");
row.innerHTML = `
<span>${product.Product_name} (${product.Unit_price})</span>
<input type="number" min="0" value="0" />
`;
// product._id is the row ID — we need it to create the link later.
row.querySelector("input").addEventListener("input", (e) => {
order[product._id] = parseInt(e.target.value, 10) || 0;
});
container.appendChild(row);
});
}

async function submitOrder() {
const status = document.getElementById("status");

// Build one OrderItems row per chosen product.
const orderItemsData = Object.keys(order)
.filter((productId) => order[productId] > 0)
.map((productId) => ({
Quantity: order[productId],
// Link columns take an array of linked row IDs.
Product: [productId],
}));

if (orderItemsData.length === 0) {
status.textContent = "Please choose at least one product.";
return;
}

try {
// 1. Create the order items in one batch call.
const itemsRes = await sdk.batchAddRows({
tableName: "OrderItems",
rowsData: orderItemsData,
});

// batchAddRows returns the created rows under res.data.rows.
// Collect their new IDs to link them to the order.
const orderItemIds = itemsRes.data.rows.map((row) => row._id);

// 2. Create the order, linking the items we just created.
await sdk.addRow({
tableName: "Orders",
rowData: { OrderItems: orderItemIds },
});

status.textContent = "Order submitted.";
} catch (error) {
// A 403 here usually means the API token lacks write permission.
status.textContent = "Submit failed: " + error.message;
}
}

document.getElementById("submit").addEventListener("click", submitOrder);
document.addEventListener("DOMContentLoaded", init);
</script>
</body>
</html>
```

!!! tip "Linking rows"

Notice the two-step write: first create the `OrderItems` rows with `batchAddRows`, then read their `_id` values from `res.data.rows` and pass them as an array to the `Orders` row's link column. Link columns always take an array of linked row IDs — see [`addRow`](sdk/rows.md#add-rows).

## Modular version

For anything beyond a simple page, split the logic into modules and import the SDK from the npm package. The template's `src/esm` directory does exactly this. A common pattern is to wrap all base access in a single `Context` class, keeping SDK calls out of your UI code:

```js
import { HTMLPageSDK } from "seatable-html-page-sdk";

export default class Context {
async init(options) {
this.sdk = new HTMLPageSDK(options);
await this.sdk.init();
}

async loadProducts() {
const res = await this.sdk.listRows({ tableName: "Products" });
return {
columns: res.data.metadata,
rows: res.data.results,
};
}

async submitOrder(orderItemsData) {
const itemsRes = await this.sdk.batchAddRows({
tableName: "OrderItems",
rowsData: orderItemsData,
});
const orderItemIds = itemsRes.data.rows.map((row) => row._id);

await this.sdk.addRow({
tableName: "Orders",
rowData: { OrderItems: orderItemIds },
});
}
}
```

The entry point reads the injected dev config and starts the app:

```js
// index.js
import Context from "./context";

document.addEventListener("DOMContentLoaded", async () => {
const context = new Context();
await context.init(window.__HTML_PAGE_DEV_CONFIG__ || null);
const { rows } = await context.loadProducts();
// ... render rows, wire up submit -> context.submitOrder(...)
});
```

## Next steps

- [SDK Reference: Rows](sdk/rows.md) — full parameters and return shapes for every row method.
- [SDK Reference: Files & Images](sdk/files.md) — add file and image uploads to your form.
113 changes: 113 additions & 0 deletions docs/html-pages/getting-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
---
description: Set up, develop, build and upload a SeaTable HTML Page. Walks through the simple form template with Vite, the SDK, and the uploadable ZIP package.
---

# Getting Started

This guide walks through building an HTML Page from the official [simple form template](https://github.com/seatable/seatable-html-page-template-simple-form): a form that writes submitted records into a base. By the end you will have a packaged ZIP that you can upload to a Universal App.

The template ships the build configuration and project structure you need. It supports two development styles:

- **Modular** — HTML, JavaScript and CSS split across multiple files. Suited for complex pages.
- **Non-modular** — everything in a single `index.html`. Suited for simple pages.

Choose the approach that fits the complexity of your page. Both produce the same kind of uploadable package.

!!! note "Base requirements for this example"

The form reads products from the base and writes orders back, so it expects three tables:

- **Products** — with `Product_name` (text) and `Unit_price` (number)
- **OrderItems** — with `Product` (link to Products) and `Quantity` (number)
- **Orders** — with `OrderItems` (link to OrderItems)

Create them in your base before running the page, or adapt the table and field names in the template's source.

## Before you start: add an HTML Page in the app

In your Universal App, add a new page and choose **Add HTML page**. This creates the empty page you will later upload your build to, and it surfaces the `server`, `appUuid` and `pageId` values you need for local development ([step 3](#3-configure-local-development)).

![Adding an HTML Page to a Universal App](../media/html-page-add.png)

## 1. Get the template

```shell
git clone https://github.com/seatable/seatable-html-page-template-simple-form.git
cd seatable-html-page-template-simple-form
npm install
```

## 2. Choose a development style

The active style is controlled by the `ESM` variable in `.env`. Vite derives its `root` from it automatically (`src/esm` when `true`, `src/classic` when `false`) — you do not edit `vite.config.js`.

=== "Modular"

JavaScript and CSS are split into multiple files under `src/esm`, and the entry file is imported into `index.html` with `type="module"`. The SDK is used via the npm package (`import { HTMLPageSDK } from "seatable-html-page-sdk"`).

Enable it by setting `ESM=true`. Either edit `.env` directly, or copy `.env` to `.env.local` (git-ignored) and set it there.

=== "Non-modular"

All logic lives in a single `index.html` under `src/classic`, with the SDK loaded from a CDN. This is the template default (`ESM=false`), so no extra configuration is required.

## 3. Configure local development

During local development, the page needs to know which base to talk to. Create `src/setting.js` based on `setting.template.js`:

```js
export default {
server: "",
appUuid: "",
pageId: "",
accountToken: "",
};
```

These values are registered to `window.__HTML_PAGE_DEV_CONFIG__` and picked up by the SDK when you call `init()` in development. See [Initialization](sdk/initialization.md) for how the SDK consumes them.

!!! note "Where the values come from"

- `accountToken` — an API token you generate in the base in advance. See [how to create API tokens](https://seatable.com/help/create-api-tokens/).
- `server`, `appUuid` and `pageId` — shown under **HTML Page development information** in the page's configuration (see below).

The HTML Page configuration shows these values ready to copy, alongside the upload area you will use later:

![HTML Page configuration with development information and upload area](../media/html-page-config.png)

## 4. Run the development server

```shell
npm run dev
```

Open the local development URL in your browser to preview and test the page. Changes reload live.

## 5. Build and package

Generate the uploadable ZIP:

```shell
npm run build-page
```

This single command cleans the output, runs the Vite build (into `dist`), and packages it into a ZIP under the `page-zip` directory. The ZIP is named after the project, for example `seatable-html-page-template-simple-form-0.0.1.zip` — bump the `version` in `package.json` before building so you can tell uploads apart.

!!! note "Inspecting the build"

`npm run build-page` already runs the build, so you do not call `npm run build` separately. Run `npm run build` on its own only if you want to inspect the unpackaged output in `dist`, or `npm run dev` to preview it live.

## 6. Upload and test

1. In SeaTable, open the configuration of the HTML Page in your Universal App.
2. Drag the generated ZIP into the **Upload HTML page file** area (the same configuration shown in [step 3](#3-configure-local-development)).
3. Open the app preview, fill in the form, and submit it.
4. Return to the base and confirm that the corresponding records were created.

Once the submitted records appear in the base, the page is fully integrated and ready to use.

## Next steps

- [SDK Reference: Initialization](sdk/initialization.md)
- [SDK Reference: Rows](sdk/rows.md)
- [SDK Reference: Files & Images](sdk/files.md)
44 changes: 44 additions & 0 deletions docs/html-pages/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
description: Build interactive HTML Pages for SeaTable Universal Apps. Learn how a page reads and writes base data through the seatable-html-page-sdk.
---

# HTML Pages

Starting with SeaTable 6.2, a Universal App can contain a new page type: the **HTML Page**. You upload a packaged HTML/JavaScript/CSS bundle, and SeaTable renders it as a full page inside the app.

A static bundle on its own can only display fixed content. To turn an HTML Page into a real application — a custom form, a dashboard, a calculator — it needs to read from and write to the base. That data exchange runs through the [`seatable-html-page-sdk`](https://www.npmjs.com/package/seatable-html-page-sdk).

This section is written for developers. It covers how to set up a project, how to develop and package a page, and the full SDK reference.

## Architecture

An HTML Page is rendered in a sandboxed context inside the Universal App. It never talks to the base directly. Instead, the SDK provides a messaging bridge to the app, which performs the actual data operations.

```mermaid
flowchart LR
A[HTML Page] -->|seatable-html-page-sdk| B[Universal App]
B -->|API| C[(Base)]
C --> B
B --> A
```

The SDK offers:

- **Data APIs** — list, add, update and delete rows; upload files and images.
- **Event propagation** — bidirectional events (mouse, keyboard, drag-and-drop) between the page and the app.

## Prerequisites

- SeaTable 6.2 or later.
- A Universal App with an HTML Page added to it.
- An API token generated in the base (used during local development).
- Node.js and npm for building the page.

## Where to start

- [Getting Started](getting-started.md) — set up the project, develop locally, build and upload a page. We follow the official [simple form template](https://github.com/seatable/seatable-html-page-template-simple-form) end to end.
- [SDK Reference](sdk/initialization.md) — installation, initialization, and the full API for rows, files and images.

!!! tip "Example project"

The [`seatable-html-page-template-simple-form`](https://github.com/seatable/seatable-html-page-template-simple-form) repository contains a complete, buildable form page. It is the running example used throughout this section.
Loading
Loading