Skip to content

Comments

feat: i18n integration, cancel confirmation, and save draft for App Creation Wizard#745

Merged
hotlong merged 4 commits intomainfrom
copilot/add-app-creation-wizard
Feb 23, 2026
Merged

feat: i18n integration, cancel confirmation, and save draft for App Creation Wizard#745
hotlong merged 4 commits intomainfrom
copilot/add-app-creation-wizard

Conversation

Copy link
Contributor

Copilot AI commented Feb 23, 2026

AppCreationWizard and NavigationDesigner hardcoded English strings despite i18n keys existing in all 10 locale files. Also missing: cancel confirmation on unsaved changes and save-as-draft support.

i18n Integration

  • Added @object-ui/i18n dependency to @object-ui/plugin-designer
  • Created useDesignerTranslation safe wrapper hook (falls back to English defaults when no I18nProvider — same pattern as useListViewTranslation in plugin-list)
  • Replaced all hardcoded strings in AppCreationWizard.tsx and NavigationDesigner.tsx with t() calls
  • Added 40 new keys to appDesigner section across all 10 locale files (en, zh, ja, ko, de, fr, es, ar, ru, pt)

Cancel Confirmation Dialog

  • Detects unsaved changes (name or title filled) before allowing cancel
  • Integrates existing useConfirmDialog hook — renders inline modal with Discard/Keep Editing actions
<AppCreationWizard
  onCancel={() => navigate('/apps')}     // now guarded by confirm dialog
  onSaveDraft={(draft) => saveDraft(draft)} // new: partial progress save
  onComplete={(draft) => createApp(draft)}
/>

NavigationDesigner i18n

  • NAV_TYPE_META and QUICK_ADD_TYPES switched from hardcoded label to labelKey references resolved via t()
  • t threaded through recursive NavItemRow for type badges, aria-labels, and empty states

Tests

  • 12 new tests: cancel confirmation (4), save draft (3), i18n fallback rendering (5)
  • 365 tests pass across 19 affected files (plugin-designer, i18n, types)
Original prompt

This section details on the original issue you should resolve

<issue_title>App Creation Wizard(多步应用向导)</issue_title>
<issue_description>实现"新建应用"多步引导流程,按Airtable/Notion体验设计,符合@objectstack/spec协议:

开发内容

  1. 新建应用入口(可从首页/项目仪表盘触达)
  2. 向导步骤设计:
  • 【基本信息】填写名称、icon、描述、选择模板(关联AppSchema字段校验,支持snake_case强校验)
  • 【请选择业务对象】从Object List中勾选,将自动生成导航节点,可新建/导入对象
  • 【导航设置】可视化拖拽构建导航树结构(recursive,支持嵌套/分组/分隔)
  • 【品牌个性化】Logo上传、主题色选择、Favicon预览
  1. 步骤状态管理(保存为draft、返回历史步骤、取消确认、自定义校验)
  2. 按协议调用 DefineApp,提交全部schema数据
  3. 移动端兼容,完善 i18n

交付要求

  • 交互稿+UI效果Mock(Figma推送到wiki)
  • 前端React组件分步实现(Tailwind + cva样式)
  • Storybook用例与vitest单元测试
  • pnpm test 必须通过
  • 完善开发文档及DEMO
  • Roadmap同步更新
    </issue_description>

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@vercel
Copy link

vercel bot commented Feb 23, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
objectui Ready Ready Preview, Comment Feb 23, 2026 5:32am
objectui-demo Error Error Feb 23, 2026 5:32am
objectui-storybook Error Error Feb 23, 2026 5:32am

Request Review

… cancel confirmation and save draft

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
… ROADMAP

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copilot AI changed the title [WIP] Add multi-step app creation wizard implementation feat: i18n integration, cancel confirmation, and save draft for App Creation Wizard Feb 23, 2026
Copilot AI requested a review from hotlong February 23, 2026 05:27
@hotlong hotlong marked this pull request as ready for review February 23, 2026 05:34
Copilot AI review requested due to automatic review settings February 23, 2026 05:34
@hotlong hotlong merged commit 105491f into main Feb 23, 2026
3 of 5 checks passed
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR integrates @object-ui/i18n into @object-ui/plugin-designer so AppCreationWizard and NavigationDesigner can render localized UI text (with an English fallback when no I18nProvider is present), and adds UX support for cancel confirmation and draft saving in the app creation flow.

Changes:

  • Add useDesignerTranslation hook and wire t() through AppCreationWizard + NavigationDesigner.
  • Add cancel confirmation dialog (using useConfirmDialog) and new onSaveDraft callback to AppCreationWizard.
  • Extend i18n locale packs (appDesigner keys) and update/expand unit tests and roadmap notes.

Reviewed changes

Copilot reviewed 18 out of 19 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
pnpm-lock.yaml Adds workspace link for @object-ui/i18n dependency.
packages/plugin-designer/package.json Declares @object-ui/i18n dependency for plugin-designer.
packages/plugin-designer/src/hooks/useDesignerTranslation.ts Adds safe translation hook with English fallback defaults for designer UIs.
packages/plugin-designer/src/hooks/index.ts Re-exports useDesignerTranslation.
packages/plugin-designer/src/AppCreationWizard.tsx Replaces UI strings with t(), adds cancel confirm dialog + onSaveDraft.
packages/plugin-designer/src/NavigationDesigner.tsx Replaces UI strings with t(), converts type/quick-add labels to translation keys.
packages/plugin-designer/src/tests/AppCreationWizard.test.tsx Adds coverage for cancel confirm, save draft, and i18n fallback rendering.
packages/plugin-designer/src/tests/NavigationDesigner.test.tsx Adds coverage for i18n fallback text in NavigationDesigner.
packages/i18n/src/locales/en.ts Adds new appDesigner translation keys (English).
packages/i18n/src/locales/zh.ts Adds new appDesigner translation keys (Chinese).
packages/i18n/src/locales/ja.ts Adds new appDesigner translation keys (Japanese).
packages/i18n/src/locales/ko.ts Adds new appDesigner translation keys (Korean).
packages/i18n/src/locales/de.ts Adds new appDesigner translation keys (German).
packages/i18n/src/locales/fr.ts Adds new appDesigner translation keys (French).
packages/i18n/src/locales/es.ts Adds new appDesigner translation keys (Spanish).
packages/i18n/src/locales/ar.ts Adds new appDesigner translation keys (Arabic).
packages/i18n/src/locales/ru.ts Adds new appDesigner translation keys (Russian).
packages/i18n/src/locales/pt.ts Adds new appDesigner translation keys (Portuguese).
ROADMAP.md Updates roadmap checklist/status to reflect i18n + UX enhancements + test counts.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

Comment on lines +836 to +840
if (confirmed) onCancel();
} else {
onCancel?.();
}
}, [draft.name, draft.title, onCancel, confirmDialog, t]);
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handleCancel depends on the entire confirmDialog object, which is recreated every render (the hook returns a new object literal). This makes the useCallback ineffective and can cause unnecessary re-renders downstream. Prefer destructuring stable members (e.g. confirmDialog.confirm) and depending on those, or drop useCallback if memoization isn’t needed.

Copilot uses AI. Check for mistakes.
Comment on lines +414 to +418
(parentId: string, type: NavigationItemType) => {
const newItem: NavigationItem = {
id: createId(type),
type,
label: type === 'separator' ? '' : `New ${NAV_TYPE_META[type].label}`,
label: type === 'separator' ? '' : `New ${t(NAV_TYPE_META[type].labelKey)}`,
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addChild uses t(...) but the useCallback dependency list doesn’t include t, so it can capture a stale translation function (and typically triggers hooks-lint warnings). Add t to the dependency array (or avoid useCallback here).

Copilot uses AI. Check for mistakes.
id: createId(type),
type,
label: type === 'separator' ? '' : `New ${NAV_TYPE_META[type].label}`,
label: type === 'separator' ? '' : `New ${t(NAV_TYPE_META[type].labelKey)}`,
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default label is built with a hardcoded English prefix (New ${...}), so non-English locales will still display “New …”. Consider using a dedicated translation key (optionally with interpolation) instead of string concatenation.

Suggested change
label: type === 'separator' ? '' : `New ${t(NAV_TYPE_META[type].labelKey)}`,
label:
type === 'separator'
? ''
: t('navigationDesigner.newItemLabel', {
label: t(NAV_TYPE_META[type].labelKey),
}),

Copilot uses AI. Check for mistakes.
id: createId(type),
type,
label: type === 'separator' ? '' : `New ${NAV_TYPE_META[type].label}`,
label: type === 'separator' ? '' : `New ${t(NAV_TYPE_META[type].labelKey)}`,
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This path also hardcodes the English New ${...} prefix, which won’t localize correctly. Use a dedicated translation key (optionally with interpolation) rather than concatenating an English prefix.

Suggested change
label: type === 'separator' ? '' : `New ${t(NAV_TYPE_META[type].labelKey)}`,
label:
type === 'separator'
? ''
: t('navigationDesigner.newItemLabel', {
item: t(NAV_TYPE_META[type].labelKey),
}),

Copilot uses AI. Check for mistakes.
@@ -290,9 +288,9 @@ function BasicInfoStep({ draft, templates, readOnly, onChange }: BasicInfoStepPr
className="block w-full rounded-md border border-gray-300 px-3 py-2 text-sm shadow-sm outline-none transition-colors focus:border-blue-500 focus:ring-1 focus:ring-blue-500 disabled:cursor-not-allowed disabled:bg-gray-50"
>
<option value="">None</option>
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The template select’s empty option label is still hardcoded as “None”. If the wizard is intended to be fully i18n-integrated, this should come from t(...) (you may need to add a shared key like common.none or an appDesigner.none).

Suggested change
<option value="">None</option>
<option value="">{t('common.none')}</option>

Copilot uses AI. Check for mistakes.
</div>
)}
<span className="text-sm font-semibold text-gray-800">{title || 'App Title'}</span>
<span className="text-sm font-semibold text-gray-800">{title || t('appDesigner.appTitle')}</span>
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the branding preview, the fallback title uses t('appDesigner.appTitle') (which translates to “Title”), so the preview will show “Title” instead of a meaningful placeholder like “App Title”. Consider adding a separate placeholder key (e.g. appDesigner.appTitlePlaceholder) and using that here.

Suggested change
<span className="text-sm font-semibold text-gray-800">{title || t('appDesigner.appTitle')}</span>
<span className="text-sm font-semibold text-gray-800">
{title || t('appDesigner.appTitlePlaceholder')}
</span>

Copilot uses AI. Check for mistakes.
@@ -661,7 +664,7 @@ function BrandingStep({ branding, title, readOnly, onChange }: BrandingStepProps
Logo
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The branding preview still hardcodes the “Logo” placeholder text. If the wizard is intended to be fully i18n-integrated, this should be translated via t(...).

Suggested change
Logo
{t('appDesigner.logoPlaceholder')}

Copilot uses AI. Check for mistakes.
Comment on lines +871 to +875
data-testid="cancel-confirm-dialog"
className="fixed inset-0 z-50 flex items-center justify-center bg-black/40"
role="dialog"
aria-modal="true"
>
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The confirm modal uses role="dialog"/aria-modal="true" but doesn’t provide an accessible name/description via aria-labelledby/aria-describedby. Add IDs for the title/message and wire them up (or add an aria-label) so screen readers announce the dialog correctly.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

App Creation Wizard(多步应用向导)

2 participants