Skip to content

feat(plugin-gantt): ownership-aware reschedule + confirm-first auto-schedule, export fixes, business time zone#2683

Merged
baozhoutao merged 4 commits into
mainfrom
feat/gantt-reschedule-export-timezone
Jul 19, 2026
Merged

feat(plugin-gantt): ownership-aware reschedule + confirm-first auto-schedule, export fixes, business time zone#2683
baozhoutao merged 4 commits into
mainfrom
feat/gantt-reschedule-export-timezone

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Delivers #2682 — the follow-up batch to #2677. These four commits were originally pushed to the #2677 branch after it was squash-merged, so they never reached main; cherry-picked clean onto main here. Each was verified live on the 4-level 排班计划 tree; suite 340 green (17 new tests across the batch).

eacde79 — reschedule follows date ownership + auto-schedule confirms first

  • computeProjectRescheduleDetailed: under summaryExtent: 'self', an own-dates unlocked summary shifts like a task and carries its unlocked subtree by the same delta (group-drag semantics; descendants pushed further by their own links keep the later position — 顺延-only max rule). Locked violators are reported in skippedLocked instead of moving. snapStart hook snaps pushed starts onto shift-band boundaries. Back-compat wrapper keeps the old signature.
  • Toolbar auto-schedule now CONFIRMS before the bulk write — "shift N task(s)? (M locked skipped)" — mirroring the drag-conflict dialog's contract; a clean graph shows a transient "nothing to reschedule" notice. Both cascade paths share the semantics.
  • Live: dialog "将顺延 4 个任务"; cancel leaves the DB untouched; confirm pushed a child-bearing plan +10d (band-snapped, duration kept), cascaded its successor, left locked dispatch children and the predecessor in place.

2f2334c — export skips group rows + business-context timestamped filenames

PNG/PDF export drew phantom rollup bars on pure grouping rows (type 'group') the live chart renders bar-less. Filenames upgrade from gantt-week.png to <base>-<yyyyMMdd-HHmm>.<ext> with base from the new gantt.exportFileName view config (host schemas reach the component stripped of label), falling back to object label → API name.

42718da — export draws in-bar titles (条上标题)

Summary bars show the plan title, leaf bars the executor label, ellipsis-truncated to bar width (CJK-aware per-char estimate); narrow leaf bars fall back to the progress number. Live: 甘特计划A 组焊 / 甘特计划B 涂装 in-bar, narrow bars truncate to 甘特… / Dev….

423ce17 — business time zone rendering via gantt.timeZone

The chart rendered all calendar math in the VIEWER's browser zone — bands and dates misalign for viewers outside the business zone. New IANA gantt.timeZone renders the configured zone's wall time for every viewer via a shifted-clock shim; emitted date changes translate back so persisted data stays real instants; DST-safe per-instant offsets; invalid names warn and fall back; omitted = unchanged behavior. Live from a UTC-7 browser against Asia/Shanghai: task-list dates read Beijing, bar left edge aligns pixel-exact with the 7/20 白班 band start, and a +1-band drag persists 7/20 20:00 → 7/24 08:00 Beijing exactly.

Refs #2682, #2676

🤖 Generated with Claude Code

@vercel

vercel Bot commented Jul 19, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectui Ignored Ignored Jul 19, 2026 12:03pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 28.0 KB 350 KB
Entry file index-Dg7M4Lcd.js
Status PASS

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 8.17KB 2.95KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 7.57KB 2.97KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 21.70KB 4.21KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.12KB 3.41KB
auth (LoginForm.js) 17.86KB 5.29KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.43KB 2.09KB
auth (SocialSignInButtons.js) 9.60KB 3.89KB
auth (UserMenu.js) 3.40KB 1.22KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 33.74KB 8.53KB
auth (createAuthenticatedFetch.js) 3.93KB 1.55KB
auth (index.js) 1.83KB 0.79KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 4.86KB 0.85KB
auth (useIsWorkspaceAdmin.js) 1.61KB 0.85KB
collaboration (CommentThread.js) 18.38KB 4.49KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 3.65KB 1.42KB
collaboration (PresenceProvider.js) 2.42KB 0.96KB
collaboration (index.js) 1.25KB 0.53KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 450.03KB 97.83KB
core (index.js) 1.82KB 0.62KB
create-plugin (index.js) 9.28KB 2.98KB
data-objectstack (index.js) 115.42KB 28.53KB
fields (index.js) 208.98KB 51.09KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.32KB 1.77KB
i18n (index.js) 2.46KB 0.96KB
i18n (pickLocalized.js) 1.70KB 0.83KB
i18n (provider.js) 5.37KB 1.72KB
i18n (useObjectLabel.js) 21.22KB 4.69KB
i18n (useSafeTranslation.js) 2.87KB 1.28KB
layout (index.js) 38.45KB 10.67KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.74KB
mobile (index.js) 1.50KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 4.42KB 1.27KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.71KB 0.42KB
mobile (useResponsiveConfig.js) 1.36KB 0.63KB
mobile (useSpecGesture.js) 1.77KB 0.77KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 5.90KB 2.15KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 3.46KB 1.03KB
permissions (evaluator.js) 4.00KB 1.23KB
permissions (index.js) 0.91KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.52KB
permissions (usePermissions.js) 1.50KB 0.70KB
plugin-ai (index.js) 15.71KB 3.79KB
plugin-calendar (index.js) 45.12KB 12.34KB
plugin-charts (index.js) 46.31KB 13.06KB
plugin-chatbot (index.js) 179.58KB 42.81KB
plugin-dashboard (index.js) 117.37KB 29.18KB
plugin-designer (index.js) 213.43KB 42.95KB
plugin-detail (index.js) 213.28KB 51.94KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 105.08KB 25.47KB
plugin-gantt (index.js) 165.40KB 40.20KB
plugin-grid (index.js) 174.72KB 46.05KB
plugin-kanban (index.js) 47.23KB 12.88KB
plugin-list (index.js) 98.22KB 23.21KB
plugin-map (index.js) 16.80KB 5.24KB
plugin-markdown (index.js) 13.65KB 4.67KB
plugin-report (index.js) 37.81KB 9.98KB
plugin-timeline (index.js) 25.37KB 7.20KB
plugin-tree (index.js) 8.36KB 2.81KB
plugin-view (index.js) 85.72KB 20.86KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.55KB 0.67KB
providers (UploadProvider.js) 11.71KB 3.53KB
providers (index.js) 0.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 3.19KB 1.38KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 18.70KB 6.09KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 0.76KB 0.42KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 2.16KB 0.94KB
sdui-parser (parse.js) 10.04KB 2.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 4.69KB 1.48KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 0.77KB 0.41KB
types (disclosure.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (index.js) 1.67KB 0.75KB
types (layout.js) 0.20KB 0.18KB
types (mobile.js) 0.20KB 0.18KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (spec-report.js) 5.26KB 1.96KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 0.75KB 0.46KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@baozhoutao
baozhoutao merged commit adc0318 into main Jul 19, 2026
14 checks passed
@baozhoutao
baozhoutao deleted the feat/gantt-reschedule-export-timezone branch July 19, 2026 12:07
@baozhoutao

Copy link
Copy Markdown
Contributor Author

Added commit 6de32edgantt.dependencyTypes switch: id-only dependency stores (bare predecessor ids, no type slot) declare false and the link context menu hides the fs/ss/ff/sf switcher — previously picking a non-FS type flashed on the arrow and was silently reverted by the refetch after the persist dropped the type. Drag-created links are pinned to FS under the switch. Live-verified: menu shows only title + 移除依赖. 342 tests green.

baozhoutao and others added 4 commits July 19, 2026 07:59
… confirms first

computeProjectReschedule skipped EVERY parent task as a derived rollup, so
under summaryExtent 'self' a plan that had children was silently immune to
auto-schedule and to the post-drag conflict cascade — the scheduling twin
of the summary-editability gap fixed earlier in this branch.

- scheduling: computeProjectRescheduleDetailed with RescheduleOptions.
  summaryExtent 'self' lets an own-dates, unlocked summary shift like a
  task and carries its unlocked subtree by the same delta (group-drag
  semantics; a descendant pushed further by its own links keeps the later
  position — 顺延-only max rule). Locked tasks that violate a link are
  reported in skippedLocked instead of moving. snapStart hook snaps pushed
  starts onto a grid. Back-compat wrapper keeps the old signature/behavior.
- GanttView: both cascade paths (toolbar wand + conflict dialog) share the
  new semantics, with pushed starts snapping to shift-band boundaries
  (班次边界) when shiftSegments are configured.
- Toolbar auto-schedule now CONFIRMS before the bulk write — compute, show
  "shift N task(s)? (M locked skipped)", apply on confirm only; a clean
  graph shows a transient "nothing to reschedule" notice. Mirrors the
  conflict dialog's existing interaction contract; i18n en/zh.

Verified live on the 4-level 排班计划 tree: wand → dialog "将顺延 4 个任务"
→ cancel leaves the DB untouched → confirm pushes the child-bearing plan
+10d (start snapped to a band boundary, duration kept), cascades its
successor, and leaves both locked dispatch children and the predecessor
exactly in place. Suite: 335 tests green (12 new).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…mped filenames

PNG/PDF export drew a rollup bar for EVERY parent row, including pure
grouping levels (type 'group' — 项目/产品) that the live chart renders
bar-less: the exported image showed phantom full-span bars on those rows
(导出内容异常). The export row loop now mirrors the live renderer's
group branch and draws nothing for them.

Filenames were an opaque `gantt-week.png`. Exports are now
`<base>-<yyyyMMdd-HHmm>.<ext>` — base from the new `gantt.exportFileName`
view config (the host strips `label` off the schema it hands the
component, so views declare their display name explicitly), falling back
to the object schema label, then the object API name; the timestamp keeps
repeated exports from overwriting each other.

Verified live: exported PNG shows group rows bar-less with plan bars,
links, dispatch bars and the today line intact; filename renders as
排班计划甘特图-20260719-0642.png.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The exported PNG/PDF rendered summary and leaf bars as bare color blocks —
the live chart shows the task title inside every bar (bar_label: 计划名 on
summaries, executor name on dispatch leaves), so exports lost that context.
The export loop now draws the title inside summary and leaf bars, ellipsis-
truncated to the bar width via a per-char width estimate (CJK ≈ 9px, latin
≈ 5.5px at font-size 9); narrow leaf bars fall back to the progress number
when even a truncated title can't fit.

Verified live: exported image shows 甘特计划A 组焊 / 甘特计划B 涂装 in
their bars, with narrow bars truncating to 甘特… / Dev… — matching the
on-screen rendering.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…eZone

The chart rendered ALL calendar math — shift bands, day columns, drag
snapping, the today line, start/end labels — in the VIEWER's browser zone.
A viewer whose machine isn't in the business zone saw every band and date
shifted (班次错位: a plan starting 7/20 08:00 Beijing displayed as 7/19
evening in a UTC-7 browser) and could drag bars onto misaligned instants.

New `gantt.timeZone` (IANA name, e.g. 'Asia/Shanghai') renders the
configured zone's wall time for every viewer via a "shifted clock" shim:
incoming dates translate into a display space where the browser clock
reads the business zone's wall time — all existing local-clock logic
becomes zone-correct unchanged — and emitted date changes translate back,
so persisted data stays real instants. Per-instant offsets keep DST zones
correct; invalid names warn and fall back to the browser zone; omitting
the config keeps today's behavior.

Verified live in a UTC-7 browser with timeZone 'Asia/Shanghai': task-list
dates read Beijing dates, plan bar left edge aligns pixel-exact with the
7/20 白班 band start, and dragging +1 band persists 7/20 20:00 → 7/24
08:00 Beijing (exact band boundary, duration kept). Suite: 340 green
(5 new tz-shift tests incl. DST round-trips).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant