Skip to content

Commit 39c1d43

Browse files
authored
Merge pull request #51 from seatable/html-page
Html page
2 parents 3cf6502 + 971ca5e commit 39c1d43

10 files changed

Lines changed: 751 additions & 0 deletions

File tree

‎.gitignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Mkdocs build directory
22
/site/
3+
/.cache/
34
/.idea
45
.vscode
56
__pycache__/

‎docs/html-pages/example.md‎

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
---
2+
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.
3+
---
4+
5+
# Example: a form that reads and writes
6+
7+
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.
8+
9+
The data flow is always the same four steps:
10+
11+
```mermaid
12+
flowchart LR
13+
A[init SDK] --> B[listRows: load data]
14+
B --> C[render UI]
15+
C --> D[on submit: batchAddRows + addRow]
16+
```
17+
18+
It assumes the three tables described in [Getting Started](getting-started.md): **Products**, **OrderItems** (link to Products), and **Orders** (link to OrderItems).
19+
20+
## Single-file version (non-modular)
21+
22+
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.
23+
24+
```html
25+
<!DOCTYPE html>
26+
<html lang="en">
27+
<head>
28+
<meta charset="UTF-8" />
29+
<title>Product Order</title>
30+
</head>
31+
<body>
32+
<div id="products"></div>
33+
<button id="submit">Submit order</button>
34+
<p id="status"></p>
35+
36+
<script src="https://unpkg.com/seatable-html-page-sdk@latest/dist/index.min.js"></script>
37+
<script>
38+
// Quantities chosen by the user: { productRowId: quantity }
39+
const order = {};
40+
let sdk;
41+
42+
async function init() {
43+
// In production __HTML_PAGE_DEV_CONFIG__ is undefined and the SDK
44+
// derives its context from the host app. In development the Vite
45+
// server injects it from src/setting.js. The same line works in both.
46+
sdk = new HTMLPageSDK(window.__HTML_PAGE_DEV_CONFIG__ || null);
47+
await sdk.init();
48+
49+
// Read rows. The payload is under res.data (see SDK Reference > Rows).
50+
const res = await sdk.listRows({ tableName: "Products" });
51+
renderProducts(res.data.results);
52+
}
53+
54+
function renderProducts(products) {
55+
const container = document.getElementById("products");
56+
container.innerHTML = "";
57+
58+
products.forEach((product) => {
59+
const row = document.createElement("div");
60+
row.innerHTML = `
61+
<span>${product.Product_name} (${product.Unit_price})</span>
62+
<input type="number" min="0" value="0" />
63+
`;
64+
// product._id is the row ID — we need it to create the link later.
65+
row.querySelector("input").addEventListener("input", (e) => {
66+
order[product._id] = parseInt(e.target.value, 10) || 0;
67+
});
68+
container.appendChild(row);
69+
});
70+
}
71+
72+
async function submitOrder() {
73+
const status = document.getElementById("status");
74+
75+
// Build one OrderItems row per chosen product.
76+
const orderItemsData = Object.keys(order)
77+
.filter((productId) => order[productId] > 0)
78+
.map((productId) => ({
79+
Quantity: order[productId],
80+
// Link columns take an array of linked row IDs.
81+
Product: [productId],
82+
}));
83+
84+
if (orderItemsData.length === 0) {
85+
status.textContent = "Please choose at least one product.";
86+
return;
87+
}
88+
89+
try {
90+
// 1. Create the order items in one batch call.
91+
const itemsRes = await sdk.batchAddRows({
92+
tableName: "OrderItems",
93+
rowsData: orderItemsData,
94+
});
95+
96+
// batchAddRows returns the created rows under res.data.rows.
97+
// Collect their new IDs to link them to the order.
98+
const orderItemIds = itemsRes.data.rows.map((row) => row._id);
99+
100+
// 2. Create the order, linking the items we just created.
101+
await sdk.addRow({
102+
tableName: "Orders",
103+
rowData: { OrderItems: orderItemIds },
104+
});
105+
106+
status.textContent = "Order submitted.";
107+
} catch (error) {
108+
// A 403 here usually means the API token lacks write permission.
109+
status.textContent = "Submit failed: " + error.message;
110+
}
111+
}
112+
113+
document.getElementById("submit").addEventListener("click", submitOrder);
114+
document.addEventListener("DOMContentLoaded", init);
115+
</script>
116+
</body>
117+
</html>
118+
```
119+
120+
!!! tip "Linking rows"
121+
122+
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).
123+
124+
## Modular version
125+
126+
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:
127+
128+
```js
129+
import { HTMLPageSDK } from "seatable-html-page-sdk";
130+
131+
export default class Context {
132+
async init(options) {
133+
this.sdk = new HTMLPageSDK(options);
134+
await this.sdk.init();
135+
}
136+
137+
async loadProducts() {
138+
const res = await this.sdk.listRows({ tableName: "Products" });
139+
return {
140+
columns: res.data.metadata,
141+
rows: res.data.results,
142+
};
143+
}
144+
145+
async submitOrder(orderItemsData) {
146+
const itemsRes = await this.sdk.batchAddRows({
147+
tableName: "OrderItems",
148+
rowsData: orderItemsData,
149+
});
150+
const orderItemIds = itemsRes.data.rows.map((row) => row._id);
151+
152+
await this.sdk.addRow({
153+
tableName: "Orders",
154+
rowData: { OrderItems: orderItemIds },
155+
});
156+
}
157+
}
158+
```
159+
160+
The entry point reads the injected dev config and starts the app:
161+
162+
```js
163+
// index.js
164+
import Context from "./context";
165+
166+
document.addEventListener("DOMContentLoaded", async () => {
167+
const context = new Context();
168+
await context.init(window.__HTML_PAGE_DEV_CONFIG__ || null);
169+
const { rows } = await context.loadProducts();
170+
// ... render rows, wire up submit -> context.submitOrder(...)
171+
});
172+
```
173+
174+
## Next steps
175+
176+
- [SDK Reference: Rows](sdk/rows.md) — full parameters and return shapes for every row method.
177+
- [SDK Reference: Files & Images](sdk/files.md) — add file and image uploads to your form.

‎docs/html-pages/getting-started.md‎

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
---
2+
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.
3+
---
4+
5+
# Getting Started
6+
7+
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.
8+
9+
The template ships the build configuration and project structure you need. It supports two development styles:
10+
11+
- **Modular** — HTML, JavaScript and CSS split across multiple files. Suited for complex pages.
12+
- **Non-modular** — everything in a single `index.html`. Suited for simple pages.
13+
14+
Choose the approach that fits the complexity of your page. Both produce the same kind of uploadable package.
15+
16+
!!! note "Base requirements for this example"
17+
18+
The form reads products from the base and writes orders back, so it expects three tables:
19+
20+
- **Products** — with `Product_name` (text) and `Unit_price` (number)
21+
- **OrderItems** — with `Product` (link to Products) and `Quantity` (number)
22+
- **Orders** — with `OrderItems` (link to OrderItems)
23+
24+
Create them in your base before running the page, or adapt the table and field names in the template's source.
25+
26+
## Before you start: add an HTML Page in the app
27+
28+
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)).
29+
30+
![Adding an HTML Page to a Universal App](../media/html-page-add.png)
31+
32+
## 1. Get the template
33+
34+
```shell
35+
git clone https://github.com/seatable/seatable-html-page-template-simple-form.git
36+
cd seatable-html-page-template-simple-form
37+
npm install
38+
```
39+
40+
## 2. Choose a development style
41+
42+
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`.
43+
44+
=== "Modular"
45+
46+
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"`).
47+
48+
Enable it by setting `ESM=true`. Either edit `.env` directly, or copy `.env` to `.env.local` (git-ignored) and set it there.
49+
50+
=== "Non-modular"
51+
52+
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.
53+
54+
## 3. Configure local development
55+
56+
During local development, the page needs to know which base to talk to. Create `src/setting.js` based on `setting.template.js`:
57+
58+
```js
59+
export default {
60+
server: "",
61+
appUuid: "",
62+
pageId: "",
63+
accountToken: "",
64+
};
65+
```
66+
67+
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.
68+
69+
!!! note "Where the values come from"
70+
71+
- `accountToken` — an API token you generate in the base in advance. See [how to create API tokens](https://seatable.com/help/create-api-tokens/).
72+
- `server`, `appUuid` and `pageId` — shown under **HTML Page development information** in the page's configuration (see below).
73+
74+
The HTML Page configuration shows these values ready to copy, alongside the upload area you will use later:
75+
76+
![HTML Page configuration with development information and upload area](../media/html-page-config.png)
77+
78+
## 4. Run the development server
79+
80+
```shell
81+
npm run dev
82+
```
83+
84+
Open the local development URL in your browser to preview and test the page. Changes reload live.
85+
86+
## 5. Build and package
87+
88+
Generate the uploadable ZIP:
89+
90+
```shell
91+
npm run build-page
92+
```
93+
94+
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.
95+
96+
!!! note "Inspecting the build"
97+
98+
`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.
99+
100+
## 6. Upload and test
101+
102+
1. In SeaTable, open the configuration of the HTML Page in your Universal App.
103+
2. Drag the generated ZIP into the **Upload HTML page file** area (the same configuration shown in [step 3](#3-configure-local-development)).
104+
3. Open the app preview, fill in the form, and submit it.
105+
4. Return to the base and confirm that the corresponding records were created.
106+
107+
Once the submitted records appear in the base, the page is fully integrated and ready to use.
108+
109+
## Next steps
110+
111+
- [SDK Reference: Initialization](sdk/initialization.md)
112+
- [SDK Reference: Rows](sdk/rows.md)
113+
- [SDK Reference: Files & Images](sdk/files.md)

‎docs/html-pages/index.md‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
description: Build interactive HTML Pages for SeaTable Universal Apps. Learn how a page reads and writes base data through the seatable-html-page-sdk.
3+
---
4+
5+
# HTML Pages
6+
7+
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.
8+
9+
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).
10+
11+
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.
12+
13+
## Architecture
14+
15+
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.
16+
17+
```mermaid
18+
flowchart LR
19+
A[HTML Page] -->|seatable-html-page-sdk| B[Universal App]
20+
B -->|API| C[(Base)]
21+
C --> B
22+
B --> A
23+
```
24+
25+
The SDK offers:
26+
27+
- **Data APIs** — list, add, update and delete rows; upload files and images.
28+
- **Event propagation** — bidirectional events (mouse, keyboard, drag-and-drop) between the page and the app.
29+
30+
## Prerequisites
31+
32+
- SeaTable 6.2 or later.
33+
- A Universal App with an HTML Page added to it.
34+
- An API token generated in the base (used during local development).
35+
- Node.js and npm for building the page.
36+
37+
## Where to start
38+
39+
- [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.
40+
- [SDK Reference](sdk/initialization.md) — installation, initialization, and the full API for rows, files and images.
41+
42+
!!! tip "Example project"
43+
44+
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.

0 commit comments

Comments
 (0)