Bỏ qua để đến nội dung

Dựng view

Viết màn hình React/TSX gọi action qua @app/sdk, dùng component của @app/ui, xem preview trực tiếp, dựng bằng kéo-thả và publish sau bước kiểm tra kiểu.

7 phút đọc

View là màn hình tuỳ biến viết bằng React (TSX). Sau khi publish, người dùng mở view ở mục Màn hình. View chạy trong một khung cách ly và chỉ lấy dữ liệu bằng cách gọi action, với quyền của người đang xem.

Người dựng màn hình kéo khối số liệu và bảng dữ liệu vào bố cục, rồi gắn action cho bảng.

Phần lớn nhu cầu đã được đáp ứng bằng form tự sinh của trang Chạy action, kết hợp với transformer để thêm link, ảnh, badge. Dùng view khi form tự sinh không đủ, ví dụ:

  • Bảng ghép dữ liệu từ nhiều action.
  • Dashboard có nhiều số liệu và bảng trên cùng một trang.
  • Luồng nhiều bước, ví dụ chọn một dòng rồi bấm nút để chạy action ghi trên dòng đó.

Với màn hình quản lý chuẩn cho một bảng, dùng Action CRUD tự sinh với tuỳ chọn Tạo kèm view trước.

Cần vai trò Editor trở lên để tạo, sửa, publish, rollback và xoá view. Người xem màn hình chỉ cần vai trò Viewer và quyền chạy các action mà view gọi.

  1. Vào Xây dựng > View, bấm Tạo view.
  2. Nhập Tên, Slug (không đổi được sau khi publish) và Mô tả (tuỳ chọn), sau đó bấm Tạo.
  3. Trang chi tiết mở ở tab Code & Preview với một đoạn code khởi đầu chạy được ngay.

Giống action, view mới áp dụng ở mọi môi trường trừ production.

Trang view ở tab Code & Preview: editor TSX bên trái, preview bên phảiTrang view ở tab Code & Preview: editor TSX bên trái, preview bên phải

  • Editor (Monaco) gợi ý kiểu theo các action của project. Gõ useAction(' để xem danh sách action có quyền chạy; tên và kiểu param được kiểm tra ngay khi gõ. Lỗi kiểu không chặn preview nhưng chặn publish.
  • Preview bên phải cập nhật khi sửa code. Preview chạy trong khung cách ly, với quyền của người đang sửa, trên dữ liệu của môi trường đang chọn.
  • Code được tự lưu nháp sau 2 giây ngừng gõ; trạng thái hiện Nháp lưu lúc…. Nếu người khác vừa sửa cùng bản nháp, trang yêu cầu chọn Lấy bản của họ hoặc Giữ bản của tôi, ghi đè.

Để bắt đầu mà không viết code từ đầu, bấm Dựng bằng kéo-thả ở đầu thẻ Code.

  1. Kéo khối từ cột Khối vào Bố cục. Cũng có thể bấm một khối để thêm vào cuối, hoặc chọn một bố cục mẫu: Một bảng, Form + bảng, Bảng điều khiển.
  2. Bấm một khối để chọn action đã publish và đặt tiêu đề ở cột Thuộc tính.
  3. Chọn số cột (1–4). Kéo tay nắm ⋮⋮ để đổi vị trí; đặt Độ rộng cho khối cần rộng hơn.
  4. Bấm Tạo code. Code được đưa vào editor. Kiểm tra Preview, sau đó bấm Publish.

Hộp Dựng view bằng kéo-thả: cột Khối, lưới Bố cục và cột Thuộc tínhHộp Dựng view bằng kéo-thả: cột Khối, lưới Bố cục và cột Thuộc tính

Các loại khối: Bảng dữ liệu, Số liệu (số dòng action trả về), Form chạy action, Ghi chú và Người dùng hiện tại. Khối bảng có thêm Tương tác với danh sách (Ô tìm kiếm, Sắp theo cột, Lọc theo cột, Phân trang) và Nút trên từng dòng (tối đa 4 nút; param bắt buộc lấy từ cột cùng tên của dòng; action ghi yêu cầu xác nhận trước khi chạy). Có thể thao tác bằng bàn phím: Tab tới tay nắm ⋮⋮, Space để nhấc, phím mũi tên để di chuyển, Space để thả.

Trình kéo-thả chỉ sinh code một lần. Bố cục không được lưu lại; sau khi sinh code, mọi chỉnh sửa thực hiện trong code. Nếu editor đã có code, hệ thống hỏi trước khi thay (Thay code); phần chưa publish sẽ mất.

Mỗi view là một file TSX với hai export:

  • export const manifest: tiêu đề và danh sách action mà view được gọi.
  • export default: component React của màn hình.

View chỉ được import react, react/jsx-runtime, react-dom/client, @app/ui và @app/sdk. Không có fetch; mọi dữ liệu đều đi qua action.

import { useState } from 'react';
import { Alert, Button, Card, Field, Input, Row, Spinner, Stack, Table, View } from '@app/ui';
import { runAction, toast, useAction, useCanRunAction } from '@app/sdk';
export const manifest = {
title: 'Đơn chờ thanh toán',
actions: ['orders-by-status', 'mark-order-paid'],
};
export default function Screen() {
const orders = useAction('orders-by-status', { status: 'pending' });
const canMarkPaid = useCanRunAction('mark-order-paid');
const [orderId, setOrderId] = useState('');
async function markPaid() {
try {
await runAction('mark-order-paid', { id: Number(orderId) });
toast('Đã cập nhật đơn hàng', { tone: 'success' });
orders.refetch();
} catch (error) {
toast(String(error), { tone: 'error' });
}
}
return (
<View>
<Stack>
<Card title="Đơn chờ thanh toán">
{orders.loading ? <Spinner /> : null}
{orders.error ? <Alert tone="danger">{orders.error.message}</Alert> : null}
<Table rows={orders.data?.rows ?? []} searchable sortable pageSize={20} />
</Card>
{canMarkPaid ? (
<Card title="Đánh dấu đã thanh toán">
<Row>
<Field label="Mã đơn">
<Input value={orderId} onChange={(event) => setOrderId(event.target.value)} />
</Field>
<Button onClick={markPaid} disabled={!orderId}>
Cập nhật
</Button>
</Row>
</Card>
) : null}
</Stack>
</View>
);
}

Ví dụ giả định project đã publish action orders-by-status (param status) và mark-order-paid (param id).

APICông dụng
useAction(slug, params)Chạy action khi component hiển thị; trả về { data, loading, error, refetch }. data có rows, columns, rowCount.
runAction(slug, params)Chạy action khi người dùng bấm (ví dụ action ghi); trả về Promise với rows, columns, rowCount, ném ViewSdkError nếu bị từ chối.
useCanRunAction(slug)Cho biết người xem có chạy được action hay không. Chỉ dùng để ẩn hoặc hiện nút, không phải lớp bảo vệ.
useUser()id, email của người đang xem (không phải người viết view).
useEnvironment()id, slug, isProduction của môi trường đang chọn.
toast(message, { tone })Thông báo ngắn; tone là info, success hoặc error.
navigate({ view }) / navigate({ action })Chuyển tới một view khác hoặc tới trang chạy một action.

Bố cục: View, Stack, Row, Grid (1–4 cột) và GridItem (span), Card. Nội dung: Text, Badge, Alert, Spinner, EmptyState. Nhập liệu: Button (primary, secondary, danger, danger-outline), Field, Input, Select. Dữ liệu: Table. Nếu bỏ columns, Table lấy cột từ dòng đầu; bật searchable, sortable, filterable, pageSize để tìm kiếm, sắp xếp, lọc, phân trang ngay trong view. Component tự theo màu và chế độ sáng/tối của giao diện admin.

Các tuỳ chọn searchable, sortable, filterable, pageSize của Table chỉ xử lý các dòng đã tải. Nếu action tự phân trang bằng param limit / offset (như action Danh sách tự sinh), truyền param cho action thay vì bật các tuỳ chọn này.

Bấm Publish. Server chạy lại mọi bước kiểm tra; kết quả ở preview (chạy trong trình duyệt) không được tính:

  • Kiểm tra kiểu bằng TypeScript với kiểu sinh từ action của project. Sai slug, sai tên hoặc kiểu param đều bị từ chối, kèm số dòng.
  • Allowlist import: import gói ngoài danh sách hoặc import file khác đều bị từ chối.
  • File phải có export default là một component.
  • Mọi useAction / runAction có slug cố định phải nằm trong manifest.actions.
  • Quyền trên từng action trong manifest: người publish phải chạy được mọi action mà view khai báo.

Nếu view áp dụng ở môi trường production, hộp Publish lên môi trường yêu cầu xác nhận. Sau khi publish, người dùng tải lại trang là thấy bản mới, không cần đăng nhập lại.

Tab Cài đặt & version gồm:

  • Thông tin chung: tên và mô tả (hiện trong danh sách view). Thay đổi có hiệu lực ngay và không tạo version.
  • Môi trường áp dụng: view chỉ hiện ở mục Màn hình và chỉ gọi được action khi người xem đang ở môi trường đã bật. Bật production luôn yêu cầu xác nhận.
  • Lịch sử version: Xem code của từng version, so sánh với bản đang chạy và Quay về bản này. Rollback không tạo version mới. Người dùng tải lại trang sẽ chạy version đã chọn, và có thể chuyển lại bản mới hơn bất cứ lúc nào.
  • Xoá view: view biến mất khỏi mục Màn hình của mọi người; các action mà view gọi không bị ảnh hưởng. Thao tác này không hoàn tác được.

Bấm Mở màn hình để xem view như người dùng.