Platform UI bridge
useAppContext().ui cho phép app-owned UI yêu cầu host thực hiện presentation thuộc host. Đây là primitives, không phải business workflow.
| API | Hiệu ứng |
|---|---|
toast(message, options?) | Hiển thị notice info, success, warning hoặc error. |
confirm(options) | Trả promise cho quyết định confirm của host. |
navigate(to) | Điều hướng qua host router. |
openArtifact(kind, context?) | Mở artifact trong cùng app lane bằng renderer hiện tại. |
closeArtifact() | Đóng artifact host hiện tại nếu có. |
openImageLightbox(images, startIndex?) | Mở image viewer chung, có caption và source attribution tùy chọn. |
import { useAppContext, useAppFetch } from '@sota/platform';
export function DeleteReportButton({ reportId }: { reportId: string }) {
const appFetch = useAppFetch();
const { ui } = useAppContext();
async function remove() {
const confirmed = await ui.confirm({
title: 'Delete report?', description: 'This cannot be undone.',
confirmLabel: 'Delete', destructive: true,
});
if (!confirmed) return;
const response = await appFetch('/reports/' + encodeURIComponent(reportId), {
method: 'DELETE',
});
ui.toast(response.ok ? 'Report deleted' : 'Delete failed', {
kind: response.ok ? 'success' : 'error',
});
}
return <button type="button" onClick={remove}>Delete</button>;
}import { useAppContext } from '@sota/platform';
export function PreviewButton({ previewUrl }: { previewUrl: string }) {
const { ui } = useAppContext();
return <button type="button" onClick={() => ui.openImageLightbox([{
url: previewUrl,
alt: 'Report preview',
caption: 'Page 1',
sourceTitle: 'Quarterly report',
sourceUrl: 'https://reports.example.com/q3',
}], 0)}>Preview</button>;
}Method có thể no-op an toàn nếu host hiện tại không thực hiện được action. State nghiệp vụ vẫn phải có authority ở app backend.