@taskforcehq/taskforce 0.3.306 → 0.3.308
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -2
- package/dist/Taskforce.module.css +317 -27
- package/dist/TaskforceCore.js +1 -0
- package/dist/TaskforceCore.test.js +178 -0
- package/dist/components/activity/EntityActivityTimeline.d.ts +34 -0
- package/dist/components/activity/EntityActivityTimeline.js +81 -0
- package/dist/components/context/ContextAttachmentManager.js +15 -3
- package/dist/components/features/AgentsModule.d.ts +5 -1
- package/dist/components/features/AgentsModule.js +142 -39
- package/dist/components/features/AgentsModule.test.js +260 -11
- package/dist/components/features/AnnotatedAttachmentWorkspace.d.ts +3 -1
- package/dist/components/features/AnnotatedAttachmentWorkspace.js +118 -4
- package/dist/components/features/AnnotatedAttachmentWorkspace.test.js +52 -0
- package/dist/components/features/DocumentIndex.d.ts +2 -2
- package/dist/components/features/DocumentIndex.js +3 -4
- package/dist/components/features/DocumentViewer.d.ts +3 -1
- package/dist/components/features/DocumentViewer.js +6 -5
- package/dist/components/features/DocumentWorkspace.d.ts +10 -2
- package/dist/components/features/DocumentWorkspace.js +15 -4
- package/dist/components/features/DocumentWorkspace.test.js +20 -3
- package/dist/components/features/TaskSettings.d.ts +2 -0
- package/dist/components/features/TaskSettings.js +83 -78
- package/dist/components/features/TaskSettings.test.js +171 -82
- package/dist/components/task/TaskActionHeader.js +11 -5
- package/dist/components/task/TaskActionHeader.test.js +10 -0
- package/dist/components/task/TaskCard.js +6 -2
- package/dist/components/task/TaskContextUpload.test.js +3 -3
- package/dist/components/task/TaskForm.js +14 -64
- package/dist/components/task/TaskKanban.js +0 -8
- package/dist/components/task/TaskList.js +5 -1
- package/dist/components/task/TaskList.test.js +4 -0
- package/dist/components/ui/HierarchyActionBadge.d.ts +10 -0
- package/dist/components/ui/HierarchyActionBadge.js +5 -0
- package/dist/components/views/AppShellHeader.d.ts +3 -1
- package/dist/components/views/AppShellHeader.js +27 -11
- package/dist/components/views/AppShellHeader.test.js +18 -1
- package/dist/components/views/PlanComparisonPage.d.ts +20 -15
- package/dist/components/views/PlanComparisonPage.js +221 -82
- package/dist/components/views/PlanComparisonPage.test.js +268 -14
- package/dist/components/views/PlansPage.js +16 -10
- package/dist/components/views/PlansPage.test.js +4 -2
- package/dist/components/views/StandaloneLayout.js +256 -69
- package/dist/components/views/WidgetView.js +10 -8
- package/dist/components/views/panels/PlanningDrawer.d.ts +19 -6
- package/dist/components/views/panels/PlanningDrawer.js +259 -51
- package/dist/components/views/panels/PlanningDrawer.test.js +392 -4
- package/dist/config/envSchema.js +1 -0
- package/dist/core/AiProfileService.d.ts +7 -0
- package/dist/core/AiProfileService.js +460 -35
- package/dist/core/AiProfiles.test.js +648 -2
- package/dist/core/AttachmentLinkService.d.ts +3 -0
- package/dist/core/AttachmentLinkService.js +58 -13
- package/dist/core/GlobalSettingsService.js +30 -0
- package/dist/core/GlobalSettingsService.test.d.ts +1 -0
- package/dist/core/GlobalSettingsService.test.js +45 -0
- package/dist/core/McpTokenService.d.ts +12 -0
- package/dist/core/McpTokenService.js +44 -1
- package/dist/core/PlanEntitlementService.d.ts +20 -0
- package/dist/core/PlanEntitlementService.js +215 -19
- package/dist/core/PlanFeatureCatalog.test.js +55 -0
- package/dist/core/PlanningEntities.test.js +366 -4
- package/dist/core/TaskActivityService.d.ts +28 -1
- package/dist/core/TaskActivityService.js +227 -46
- package/dist/core/TaskActivitySync.test.d.ts +1 -0
- package/dist/core/TaskActivitySync.test.js +90 -0
- package/dist/core/TaskAuditTimeline.test.js +96 -0
- package/dist/core/Taskforce.d.ts +204 -26
- package/dist/core/Taskforce.ids.test.js +58 -0
- package/dist/core/Taskforce.js +781 -71
- package/dist/core/Taskforce.mcpAuth.test.js +109 -0
- package/dist/core/types.d.ts +10 -0
- package/dist/hooks/auth/useTaskforceAuthBootstrap.js +2 -1
- package/dist/hooks/bootstrapTimeouts.d.ts +4 -0
- package/dist/hooks/bootstrapTimeouts.js +8 -0
- package/dist/hooks/bootstrapTimeouts.test.d.ts +1 -0
- package/dist/hooks/bootstrapTimeouts.test.js +12 -0
- package/dist/hooks/sync/orchestratorShared.d.ts +3 -3
- package/dist/hooks/sync/orchestratorShared.js +61 -1
- package/dist/hooks/sync/recovery.js +8 -0
- package/dist/hooks/sync/recovery.test.js +5 -5
- package/dist/hooks/sync/transfers.d.ts +2 -1
- package/dist/hooks/sync/transfers.js +36 -9
- package/dist/hooks/sync/transfers.test.js +534 -2
- package/dist/hooks/useFilterSort.js +13 -13
- package/dist/hooks/useFilterSort.test.js +87 -1
- package/dist/hooks/useSyncOrchestrator.js +50 -5
- package/dist/hooks/useTaskforce.d.ts +85 -50
- package/dist/hooks/useTaskforce.js +44 -14
- package/dist/hooks/useTaskforce.runtime-routing.test.js +94 -0
- package/dist/hooks/useTaskforce.sync-behavior.test.js +99 -0
- package/dist/hooks/useWorkspaceSyncController.js +7 -1
- package/dist/hooks/useWorkspaceSyncController.test.js +48 -0
- package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +14 -7
- package/dist/mcp/canonicalAssetHelpers.d.ts +16 -4
- package/dist/mcp/canonicalAssetHelpers.js +18 -10
- package/dist/mcp/canonicalAssetHelpers.test.d.ts +1 -0
- package/dist/mcp/canonicalAssetHelpers.test.js +67 -0
- package/dist/mcp/clientIntegrations.d.ts +1 -0
- package/dist/mcp/clientIntegrations.js +25 -0
- package/dist/mcp/clientIntegrations.test.js +14 -3
- package/dist/mcp/clientProfileDefaults.js +3 -3
- package/dist/mcp/collaborationRegistrar.js +7 -7
- package/dist/mcp/documentAssetRegistrar.js +113 -7
- package/dist/mcp/runtime.d.ts +1 -0
- package/dist/mcp/runtime.js +969 -122
- package/dist/mcp/runtime.test.js +1083 -11
- package/dist/mcp/taskAttachmentTypes.d.ts +1 -1
- package/dist/mcp/taskPlanningRegistrar.js +48 -0
- package/dist/mcp/toolRegistry.d.ts +56 -0
- package/dist/mcp/toolRegistry.js +8 -0
- package/dist/mcp/toolRegistry.test.js +4 -0
- package/dist/migrations/taskSchemaMigrations.js +58 -15
- package/dist/server/aiProfileAvatarAssets.d.ts +49 -0
- package/dist/server/aiProfileAvatarAssets.js +319 -0
- package/dist/server/aiProfileAvatarAssets.test.d.ts +1 -0
- package/dist/server/aiProfileAvatarAssets.test.js +213 -0
- package/dist/server/annotatedAttachmentsRoutes.test.js +66 -7
- package/dist/server/index.js +4 -0
- package/dist/server/index.test.js +6 -1
- package/dist/server/routes/admin.js +328 -9
- package/dist/server/routes/annotatedAttachments.js +51 -0
- package/dist/server/routes/auth.d.ts +11 -0
- package/dist/server/routes/auth.js +189 -27
- package/dist/server/routes/authSupport.d.ts +5 -0
- package/dist/server/routes/authSupport.js +13 -1
- package/dist/server/routes/billing.d.ts +1 -0
- package/dist/server/routes/billing.js +116 -63
- package/dist/server/routes/billing.test.js +162 -11
- package/dist/server/routes/documents.js +10 -0
- package/dist/server/routes/shared.js +0 -10
- package/dist/server/routes/sync.integration.test.js +221 -0
- package/dist/server/routes/sync.js +13 -3
- package/dist/server/routes/syncSnapshotRoutes.js +2 -0
- package/dist/server/routes/tasks.js +12 -9
- package/dist/server/routes.js +87 -8
- package/dist/server/routes.test.js +972 -23
- package/dist/shared/aiProfileSurfaceType.d.ts +1 -1
- package/dist/shared/aiProfileSurfaceType.js +10 -8
- package/dist/storage/objectStorage.d.ts +1 -0
- package/dist/storage/objectStorage.js +2 -1
- package/dist/storage/objectStorageClient.d.ts +1 -0
- package/dist/storage/objectStorageClient.js +41 -8
- package/dist/storage/objectStorageClient.test.js +26 -1
- package/dist/storage/objectStorageConfig.js +3 -1
- package/dist/storage/objectStorageConfig.test.js +13 -1
- package/dist/sync/collaborationSyncPayload.js +2 -0
- package/dist/sync/collaborationSyncPayload.test.js +1 -0
- package/dist/sync/collaborationSyncState.d.ts +3 -3
- package/dist/sync/collaborationSyncState.js +19 -12
- package/dist/sync/contentSyncPayload.d.ts +5 -1
- package/dist/sync/contentSyncPayload.js +9 -0
- package/dist/sync/contentSyncPayload.test.js +39 -0
- package/dist/sync/contentSyncState.d.ts +5 -1
- package/dist/sync/contentSyncState.js +231 -37
- package/dist/sync/planningSyncPayload.js +5 -0
- package/dist/sync/planningSyncPayload.test.js +24 -0
- package/dist/sync/syncApplyHandlers.d.ts +4 -4
- package/dist/sync/syncApplyHandlers.js +89 -31
- package/dist/sync/syncApplyHandlers.test.js +184 -4
- package/dist/sync/syncResourceAdapters.d.ts +2 -0
- package/dist/sync/syncResourceAdapters.js +2 -0
- package/dist/sync/syncService.d.ts +1 -0
- package/dist/sync/syncService.js +13 -1
- package/dist/sync/syncService.test.js +39 -0
- package/dist/sync/workspacePullFeed.d.ts +4 -0
- package/dist/sync/workspacePullFeed.js +129 -70
- package/dist/sync/workspacePullFeed.test.js +49 -0
- package/dist/sync/workspaceSyncModel.d.ts +28 -6
- package/dist/sync/workspaceSyncModel.js +159 -45
- package/dist/sync/workspaceSyncModel.test.js +269 -3
- package/dist/types.d.ts +33 -2
- package/dist/ui/agent-logos/ChatGPT.png +0 -0
- package/dist/ui/agent-logos/Gemini CLI.jpeg +0 -0
- package/dist/ui/agent-logos/antigravity.jpeg +0 -0
- package/dist/ui/agent-logos/claude-code.jpeg +0 -0
- package/dist/ui/agent-logos/codex.jpeg +0 -0
- package/dist/ui/agent-logos/cursor.png +0 -0
- package/dist/ui/agent-logos/openclaw.jpeg +0 -0
- package/dist/ui/agent-logos/windsurf.png +0 -0
- package/dist/ui/assets/AgentsModule-BS4Pi_nC.js +1 -0
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-BVKew-mH.css +1 -0
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-CCckzWYZ.js +3 -0
- package/dist/ui/assets/ContextAttachmentManager-CfG_ER7C.js +3 -0
- package/dist/ui/assets/DocumentWorkspace-BVaWPA25.js +252 -0
- package/dist/ui/assets/DocumentWorkspace-D4rJKXzd.css +1 -0
- package/dist/ui/assets/EntityActivityTimeline-DSj0ThaK.js +1 -0
- package/dist/ui/assets/EntityActivityTimeline-DyqvvhuF.css +1 -0
- package/dist/ui/assets/{InitiativesModule-9E6ParrY.js → InitiativesModule-AxMkWtxH.js} +1 -1
- package/dist/ui/assets/PlansPage-B_z-D6Nh.css +1 -0
- package/dist/ui/assets/PlansPage-Ndt7mYfo.js +1 -0
- package/dist/ui/assets/TaskContextUpload-Dw4rTF4i.js +1 -0
- package/dist/ui/assets/TaskSettings-f2ga42_V.js +9 -0
- package/dist/ui/assets/{WorkflowsModule-BA7jcEpH.js → WorkflowsModule-E4UL3mov.js} +4 -4
- package/dist/ui/assets/documentReferences-BOUcJm6-.js +1 -0
- package/dist/ui/assets/index-BvAbqx5Q.css +1 -0
- package/dist/ui/assets/index-MAHKvFqp.js +4 -0
- package/dist/ui/assets/{vendor-icons-DuEd_65c.js → vendor-icons-B32hGuF2.js} +1 -1
- package/dist/ui/index.html +3 -3
- package/dist/ui/og-image.png +0 -0
- package/dist/ui/product/agent manager.png +0 -0
- package/dist/ui/product/agent_manager_02.png +0 -0
- package/dist/utils/aiProfileEvents.d.ts +7 -0
- package/dist/utils/aiProfileEvents.js +6 -0
- package/dist/utils/assignees.d.ts +6 -0
- package/dist/utils/assignees.js +8 -1
- package/dist/utils/assignees.test.d.ts +1 -0
- package/dist/utils/assignees.test.js +25 -0
- package/dist/utils/avatarDisplay.d.ts +1 -0
- package/dist/utils/avatarDisplay.js +15 -0
- package/dist/utils/contextFiles.d.ts +3 -1
- package/dist/utils/contextFiles.js +2 -1
- package/dist/utils/taskActivity.d.ts +7 -1
- package/dist/utils/taskActivity.js +63 -1
- package/dist/utils/taskActivity.test.js +23 -1
- package/dist/utils/taskSearch.d.ts +22 -0
- package/dist/utils/taskSearch.js +87 -0
- package/dist/utils/taskSearch.test.d.ts +1 -0
- package/dist/utils/taskSearch.test.js +69 -0
- package/dist/utils/taskUtils.js +10 -3
- package/dist/utils/taskUtils.test.d.ts +1 -0
- package/dist/utils/taskUtils.test.js +19 -0
- package/package.json +1 -1
- package/dist/ui/assets/AgentsModule-DrLawwNl.js +0 -1
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-BaS2VwIr.css +0 -1
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-Z9_whf7F.js +0 -3
- package/dist/ui/assets/DocumentWorkspace-B03MaSkw.js +0 -252
- package/dist/ui/assets/DocumentWorkspace-Dhdso07p.css +0 -1
- package/dist/ui/assets/PlansPage-CvfnMiWq.css +0 -1
- package/dist/ui/assets/PlansPage-DRXscYxH.js +0 -1
- package/dist/ui/assets/TaskSettings-DZX4jk7e.js +0 -9
- package/dist/ui/assets/index-9eT8e0Lu.css +0 -1
- package/dist/ui/assets/index-C6k75jr8.js +0 -6
|
@@ -32,11 +32,11 @@ describe('TaskContextUpload drag and drop', () => {
|
|
|
32
32
|
const renderUpload = (contextFiles = [], taskId, extraProps) => render(_jsx(TaskContextUpload, { taskId: taskId, contextFiles: contextFiles, onAddContextFile: onAddContextFile, onRemoveContextFile: onRemoveContextFile, onUpdateContextCaption: onUpdateContextCaption, ...extraProps }));
|
|
33
33
|
const openSection = async (user) => {
|
|
34
34
|
const localUser = user ?? userEvent.setup();
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
if (!screen.queryByText('Drop files or URLs here')) {
|
|
36
|
+
const showToggle = await screen.findByTitle('Show context documents');
|
|
37
37
|
await localUser.click(showToggle);
|
|
38
38
|
}
|
|
39
|
-
return screen.
|
|
39
|
+
return screen.findByText('Drop files or URLs here');
|
|
40
40
|
};
|
|
41
41
|
beforeEach(() => {
|
|
42
42
|
vi.clearAllMocks();
|
|
@@ -14,12 +14,21 @@ import { Markdown } from "../ui/Markdown";
|
|
|
14
14
|
import { IconMap, resolveColor } from "../../utils/constants";
|
|
15
15
|
import { formatDate } from "../../utils/formatting";
|
|
16
16
|
import { t } from "../../localization";
|
|
17
|
-
import {
|
|
18
|
-
import { getTaskActivityItems, getTaskEventPrimaryChangeField, summarizeTaskChange, summarizeTaskEvent, } from "../../utils/taskActivity";
|
|
17
|
+
import { getTaskActivityItems } from "../../utils/taskActivity";
|
|
19
18
|
import { ensureAssigneeOptionPresent, getAssigneeKind, getAssigneeLabel, } from "../../utils/assignees";
|
|
20
19
|
import { getTaskReferenceLabel } from "../../utils/taskReferences";
|
|
21
20
|
import { getVisibleTaskFormTaxonomies } from "../../utils/taxonomyFormDefaults.js";
|
|
22
21
|
import { getInitiativeReferenceLabel, getWorkstreamReferenceLabel, } from "../../utils/planningReferences";
|
|
22
|
+
const DeferredEntityActivityTimeline = React.lazy(() => (import("../activity/EntityActivityTimeline").then((module) => ({
|
|
23
|
+
default: module.EntityActivityTimeline,
|
|
24
|
+
}))));
|
|
25
|
+
const DeferredTaskContextUpload = React.lazy(() => (import("./TaskContextUpload").then((module) => ({
|
|
26
|
+
default: module.TaskContextUpload,
|
|
27
|
+
}))));
|
|
28
|
+
function ActivityTimelineFallback({ count }) {
|
|
29
|
+
const placeholderCount = Math.max(2, Math.min(count || 3, 4));
|
|
30
|
+
return (_jsx("div", { className: `${formStyles.commentThread} ${formStyles.activityLoadingThread}`.trim(), "aria-busy": "true", "aria-live": "polite", "aria-label": "Loading activity", children: Array.from({ length: placeholderCount }, (_, index) => (_jsxs("div", { className: formStyles.activitySkeletonItem, children: [_jsx("div", { className: formStyles.activitySkeletonMeta }), _jsx("div", { className: formStyles.activitySkeletonBody })] }, index))) }));
|
|
31
|
+
}
|
|
23
32
|
function SortableChecklistRow({ id, item, onToggle, onRemove, }) {
|
|
24
33
|
const { attributes, listeners, setNodeRef, transform, transition, isDragging, } = useSortable({ id });
|
|
25
34
|
const style = {
|
|
@@ -31,12 +40,6 @@ function SortableChecklistRow({ id, item, onToggle, onRemove, }) {
|
|
|
31
40
|
? "Mark checklist item incomplete"
|
|
32
41
|
: "Mark checklist item complete", children: item.isCompleted ? _jsx(Check, { size: 14, strokeWidth: 2.1 }) : null }), _jsx("span", { className: `${formStyles.checklistItemText} ${item.isCompleted ? formStyles.checklistItemTextCompleted : ""}`.trim(), children: item.title }), _jsx("button", { type: "button", className: formStyles.checklistRemoveBtn, onClick: onRemove, title: "Remove checklist item", "aria-label": "Remove checklist item", children: _jsx(X, { size: 15, strokeWidth: 1.9 }) })] }));
|
|
33
42
|
}
|
|
34
|
-
function hexToRgba(hex, opacity) {
|
|
35
|
-
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
36
|
-
if (!result)
|
|
37
|
-
return "";
|
|
38
|
-
return `rgba(${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(result[3], 16)}, ${opacity})`;
|
|
39
|
-
}
|
|
40
43
|
export function TaskForm(props) {
|
|
41
44
|
const { editingTaskId, error, title, description, category, type, priority, complexity, manualComplexityEnabled = false, assignee, scheduledDate, dueDate, workstreamInput = "", checklistItems, comments, newCommentText, contextFiles, currentWorkspaceId, apiBaseUrl, descriptionFocused, showMarkdownHelp, showChecklist = false, checklistEnabled = true, formTaxonomies, onTaxonomyChange, onOpenSettings, categories, types, priorities, taxonomyDisplayLabels, assigneeOptions, taxonomies, workstreams = [], initiatives = [], copiedId, onTitleChange, onDescriptionChange, onCategoryChange, onTypeChange, onPriorityChange, onComplexityChange, onAssigneeChange, onScheduledDateChange, onDueDateChange, onWorkstreamInputChange = () => { }, onChecklistItemsChange, onNewCommentTextChange, onDescriptionFocusedChange, onShowMarkdownHelpChange, onShowChecklistChange = () => { }, onSubmit, onAddComment, onAddContextFile, onRemoveContextFile, onUpdateContextCaption, onCopyId, onToggleInProgress, onToggleReview, onToggleComplete, onToggleCancel, onSetStatus, onArchiveTask, onUnarchive, onOpenTaskById, currentTask, commentsEndRef, } = props;
|
|
42
45
|
const currentTaskReferenceLabel = getTaskReferenceLabel(currentTask);
|
|
@@ -381,65 +384,12 @@ export function TaskForm(props) {
|
|
|
381
384
|
const frame = window.requestAnimationFrame(scrollToLatest);
|
|
382
385
|
return () => window.cancelAnimationFrame(frame);
|
|
383
386
|
}, [activityCount, commentsEndRef, currentTask?.id, editingTaskId]);
|
|
384
|
-
const activitySidebar = editingTaskId ? (_jsxs("aside", { className: formStyles.activitySidebar, "aria-label": t("taskForm.commentsSectionTitle"), children: [_jsxs("div", { className: formStyles.activitySidebarHeader, children: [_jsx("h2", { className: formStyles.activitySidebarTitle, children: t("taskForm.commentsSectionTitle") }), _jsx("span", { className: formStyles.activitySidebarCount, children: activityCount })] }), _jsxs("div", { className: `${formStyles.commentPanel} ${formStyles.activitySidebarPanel}`.trim(), children: [_jsxs("div", { className: formStyles.
|
|
385
|
-
if (item.type === "comment") {
|
|
386
|
-
const c = item.comment;
|
|
387
|
-
const actorPresentation = resolveActivityActorPresentation({
|
|
388
|
-
actorId: c.author,
|
|
389
|
-
actorType: c.actor?.kind === "ai"
|
|
390
|
-
? "ai"
|
|
391
|
-
: c.actor?.kind === "human"
|
|
392
|
-
? "human"
|
|
393
|
-
: null,
|
|
394
|
-
actorProfile: c.actor,
|
|
395
|
-
});
|
|
396
|
-
const commentKind = actorPresentation.kind === "agent" ? "agent" : "member";
|
|
397
|
-
const { ActorIcon: CommentActorIcon } = actorPresentation;
|
|
398
|
-
const authorColor = actorPresentation.color;
|
|
399
|
-
const authorLabel = actorPresentation.label;
|
|
400
|
-
const commentAccentStyle = authorColor && hexToRgba(authorColor, 0.35)
|
|
401
|
-
? {
|
|
402
|
-
background: `linear-gradient(135deg, ${hexToRgba(authorColor, 0.18)} 0%, ${hexToRgba(authorColor, 0.1)} 100%)`,
|
|
403
|
-
borderColor: hexToRgba(authorColor, 0.35),
|
|
404
|
-
}
|
|
405
|
-
: undefined;
|
|
406
|
-
return (_jsxs("div", { className: `${formStyles.comment} ${commentKind === "agent" ? formStyles.commentAi : formStyles.commentUser}`, children: [_jsxs("div", { className: formStyles.commentHeader, children: [_jsx("span", { className: formStyles.commentAuthor, style: authorColor ? { color: authorColor } : undefined, children: _jsxs(_Fragment, { children: [_jsx(CommentActorIcon, { size: 12, strokeWidth: 1.5 }), " ", authorLabel] }) }), _jsx("span", { className: formStyles.commentTime, children: formatDate(c.timestamp, {
|
|
407
|
-
month: "short",
|
|
408
|
-
day: "numeric",
|
|
409
|
-
hour: "2-digit",
|
|
410
|
-
minute: "2-digit",
|
|
411
|
-
}) })] }), _jsx("div", { className: formStyles.commentText, style: commentAccentStyle, children: _jsx(Markdown, { onTaskIdClick: onOpenTaskById, children: c.text }) })] }, c.id));
|
|
412
|
-
}
|
|
413
|
-
const event = item.event;
|
|
414
|
-
const actorPresentation = resolveActivityActorPresentation({
|
|
415
|
-
actorId: event.actor,
|
|
416
|
-
actorType: event.actorType,
|
|
417
|
-
actorProfile: event.actorProfile,
|
|
418
|
-
fallbackKind: "system",
|
|
419
|
-
});
|
|
420
|
-
const { ActorIcon: EventActorIcon } = actorPresentation;
|
|
421
|
-
const authorColor = actorPresentation.color;
|
|
422
|
-
const authorLabel = actorPresentation.label;
|
|
423
|
-
const eventKind = actorPresentation.kind;
|
|
424
|
-
const changeEntries = Object.entries(event.details?.changes || {});
|
|
425
|
-
const primaryChangeField = getTaskEventPrimaryChangeField(event);
|
|
426
|
-
const displayedChangeEntries = changeEntries.length > 1 && primaryChangeField
|
|
427
|
-
? changeEntries.filter(([field]) => field !== primaryChangeField)
|
|
428
|
-
: changeEntries.length > 1
|
|
429
|
-
? changeEntries
|
|
430
|
-
: [];
|
|
431
|
-
return (_jsxs("div", { className: `${formStyles.comment} ${formStyles.activityEvent} ${eventKind === "system" ? formStyles.activityEventSystem : ""}`, children: [_jsxs("div", { className: `${formStyles.commentHeader} ${formStyles.activityEventHeader}`, children: [_jsx("span", { className: formStyles.commentAuthor, style: authorColor ? { color: authorColor } : undefined, children: _jsxs(_Fragment, { children: [_jsx(EventActorIcon, { size: 12, strokeWidth: 1.5 }), " ", authorLabel] }) }), _jsx("span", { className: formStyles.commentTime, children: formatDate(event.createdAt, {
|
|
432
|
-
month: "short",
|
|
433
|
-
day: "numeric",
|
|
434
|
-
hour: "2-digit",
|
|
435
|
-
minute: "2-digit",
|
|
436
|
-
}) })] }), _jsxs("div", { className: formStyles.activityEventText, children: [_jsx("div", { children: summarizeTaskEvent(event, formatActivityFieldValue) }), displayedChangeEntries.length > 0 && (_jsx("div", { className: formStyles.activityEventChanges, children: displayedChangeEntries.map(([field, change]) => (_jsx("div", { className: formStyles.activityEventChange, children: summarizeTaskChange(field, change, formatActivityFieldValue) }, field))) }))] })] }, item.id));
|
|
437
|
-
}), _jsx("div", { ref: commentsEndRef })] }), _jsxs("div", { className: formStyles.commentInputArea, children: [_jsx("textarea", { className: formStyles.commentInput, placeholder: t("taskForm.commentPlaceholder"), value: newCommentText, onChange: (e) => onNewCommentTextChange(e.target.value), onKeyDown: (e) => {
|
|
387
|
+
const activitySidebar = editingTaskId ? (_jsxs("aside", { className: formStyles.activitySidebar, "aria-label": t("taskForm.commentsSectionTitle"), children: [_jsxs("div", { className: formStyles.activitySidebarHeader, children: [_jsx("h2", { className: formStyles.activitySidebarTitle, children: t("taskForm.commentsSectionTitle") }), _jsx("span", { className: formStyles.activitySidebarCount, children: activityCount })] }), _jsxs("div", { className: `${formStyles.commentPanel} ${formStyles.activitySidebarPanel}`.trim(), children: [_jsx(React.Suspense, { fallback: _jsx(ActivityTimelineFallback, { count: activityCount }), children: _jsx(DeferredEntityActivityTimeline, { activity: currentTask?.activity, comments: comments, formatFieldValue: formatActivityFieldValue, resolveActorPresentation: resolveActivityActorPresentation, onOpenTaskById: onOpenTaskById, emptyMessage: t("taskForm.noComments"), listClassName: formStyles.commentThread, emptyClassName: formStyles.emptyComments, endRef: commentsEndRef }) }), _jsxs("div", { className: formStyles.commentInputArea, children: [_jsx("textarea", { className: formStyles.commentInput, placeholder: t("taskForm.commentPlaceholder"), value: newCommentText, onChange: (e) => onNewCommentTextChange(e.target.value), onKeyDown: (e) => {
|
|
438
388
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
439
389
|
e.preventDefault();
|
|
440
390
|
onAddComment();
|
|
441
391
|
}
|
|
442
|
-
}, rows:
|
|
392
|
+
}, rows: 4 }), _jsx("button", { type: "button", className: formStyles.sendCommentBtn, onClick: onAddComment, disabled: !newCommentText.trim(), "aria-label": "Send comment", title: "Send comment", children: _jsx(Send, { size: 16 }) })] })] })] })) : null;
|
|
443
393
|
return (_jsxs("form", { ref: formRef, onSubmit: onSubmit, className: `${styles.form} ${styles.appScrollbar} tf-scrollbar`, children: [error && _jsx("div", { className: formStyles.error, children: error }), isReadOnly && (_jsx("div", { className: `${formStyles.formNotice} tf-text-helper`, children: isDeletedReadOnly
|
|
444
394
|
? "Deleted tasks are read-only. Restore the task to make changes."
|
|
445
395
|
: "Archived tasks are read-only. Unarchive the task to make changes." })), _jsxs("fieldset", { disabled: isReadOnly, className: `${formStyles.formFieldsetReset} ${editingTaskId ? formStyles.formLayoutWithSidebar : formStyles.formLayout}`.trim(), children: [_jsxs("div", { className: formStyles.formMainColumn, children: [_jsxs("div", { className: styles.field, children: [_jsxs("div", { className: styles.labelRow, children: [_jsx("label", { className: styles.label, children: "Title" }), assignee && (_jsx("span", { title: `Assigned to: ${currentAssigneeLabel}`, className: [
|
|
@@ -560,7 +510,7 @@ export function TaskForm(props) {
|
|
|
560
510
|
.map((entry, entryIndex) => ({
|
|
561
511
|
...entry,
|
|
562
512
|
order: entryIndex,
|
|
563
|
-
}))) }, item.id || `checklist-${index}`))) }) })) : (_jsx("div", { className: `${formStyles.settingsHint} tf-text-helper`, children: "No checklist items yet." }))] }))] }))] }), _jsxs("div", { children: [_jsx(
|
|
513
|
+
}))) }, item.id || `checklist-${index}`))) }) })) : (_jsx("div", { className: `${formStyles.settingsHint} tf-text-helper`, children: "No checklist items yet." }))] }))] }))] }), _jsxs("div", { children: [_jsx(React.Suspense, { fallback: null, children: _jsx(DeferredTaskContextUpload, { taskId: editingTaskId, taskReferenceLabel: currentTaskReferenceLabel, workspaceId: currentWorkspaceId, apiBaseUrl: apiBaseUrl, contextFiles: contextFiles, onAddContextFile: onAddContextFile, onRemoveContextFile: onRemoveContextFile, onUpdateContextCaption: onUpdateContextCaption }) }), _jsx("div", { className: `${formStyles.taskFormLifecycle} ${formStyles.taskFormMetaDivider}`, children: _jsx("div", { className: formStyles.taskFormMetaGrid, children: currentTask && (_jsxs(_Fragment, { children: [_jsxs("div", { className: formStyles.taskFormDateRow, children: [_jsx("span", { className: formStyles.taskFormDateLabel, children: t("taskForm.createdLabel") }), _jsxs("span", { className: formStyles.taskFormMetaStack, children: [_jsx("span", { className: formStyles.taskFormDateValue, children: createdLabel || t("taskForm.emptyValue") }), _jsx("span", { className: formStyles.taskFormMetaActor, children: createdByLabel
|
|
564
514
|
? `by ${createdByLabel}`
|
|
565
515
|
: t("taskForm.emptyValue") })] })] }), _jsxs("div", { className: formStyles.taskFormDateRow, children: [_jsx("span", { className: formStyles.taskFormDateLabel, children: t("taskForm.updatedLabel") }), _jsxs("span", { className: formStyles.taskFormMetaStack, children: [_jsx("span", { className: formStyles.taskFormDateValue, children: updatedLabel || t("taskForm.emptyValue") }), _jsx("span", { className: formStyles.taskFormMetaActor, children: updatedByLabel
|
|
566
516
|
? `by ${updatedByLabel}`
|
|
@@ -520,20 +520,12 @@ export function TaskKanban({ tasks, columns: kanbanColumns, groupBy, searchQuery
|
|
|
520
520
|
void onAssignTaskToWorkstream?.(activeTaskDataId, overWorkstreamDataId);
|
|
521
521
|
return;
|
|
522
522
|
}
|
|
523
|
-
if (overType === 'planning-task-unlink' && activeTaskDataId) {
|
|
524
|
-
void onAssignTaskToWorkstream?.(activeTaskDataId, null);
|
|
525
|
-
return;
|
|
526
|
-
}
|
|
527
523
|
}
|
|
528
524
|
if (activeType === 'planning-workstream') {
|
|
529
525
|
if (overType === 'planning-initiative-target' && activeWorkstreamDataId && overInitiativeDataId) {
|
|
530
526
|
void onAssignWorkstreamToInitiative?.(activeWorkstreamDataId, overInitiativeDataId);
|
|
531
527
|
return;
|
|
532
528
|
}
|
|
533
|
-
if (overType === 'planning-workstream-unlink' && activeWorkstreamDataId) {
|
|
534
|
-
void onAssignWorkstreamToInitiative?.(activeWorkstreamDataId, null);
|
|
535
|
-
return;
|
|
536
|
-
}
|
|
537
529
|
return;
|
|
538
530
|
}
|
|
539
531
|
const activeId = String(active.id);
|
|
@@ -11,6 +11,7 @@ import { getVisibleTaxonomiesForTasks } from '../../utils/taxonomyLifecycle.js';
|
|
|
11
11
|
import { TaskCard } from './TaskCard';
|
|
12
12
|
import { t } from '../../localization';
|
|
13
13
|
import { DeleteAllDeletedAction, TaskScopeToggle } from '../views/panels/FilterToolbarControls';
|
|
14
|
+
import { normalizeTaskReferenceToken } from '../../utils/taskSearch';
|
|
14
15
|
export const TaskList = forwardRef((props, ref) => {
|
|
15
16
|
const { tasks, archivedTasks, categories, types, priorities, taxonomyDisplayLabels, assigneeOptions = [], workstreams = [], initiatives = [], searchQuery, filterCategories, filterTypes, filterPriorities, filterStatus, filterAssignees = [], filterTaxonomies, sortBy, sortOrder, showArchive, taskScope, collapsedCategories, loadingTasks, filteredTasks, filteredArchive, groupedTasks, filteredDeletedTasks = [], groupedDeletedTasks = {}, copiedId, recentlyChangedTaskIds = [], showTaskCardStatusLabel = true, onSearchChange, onFilterCategoriesChange, onFilterTypesChange, onFilterPrioritiesChange, onFilterStatusChange, onFilterAssigneesChange, onTaxonomyFilterChange, onSortByChange, onSortOrderChange, onShowArchiveChange, onTaskScopeChange, onClearFilters, onToggleCategory, onEditTask, onOpenTaskById, onCopyId, onToggleInProgress, onToggleReview, onToggleComplete, onToggleCancel, onSetStatus, onArchiveTask, onBulkArchive, onUnarchive, onDelete, onDeleteAllDeleted, onFetchArchive, onAddTaskToCategory, supplementalTasks = [], taxonomies, } = props;
|
|
16
17
|
const changedTaskIdSet = new Set(recentlyChangedTaskIds);
|
|
@@ -46,8 +47,11 @@ export const TaskList = forwardRef((props, ref) => {
|
|
|
46
47
|
: effectiveTaskScope === 'archived'
|
|
47
48
|
? 'archived'
|
|
48
49
|
: null;
|
|
50
|
+
const searchedTaskReference = normalizeTaskReferenceToken(searchQuery);
|
|
49
51
|
const emptyLabel = searchQuery
|
|
50
|
-
?
|
|
52
|
+
? searchedTaskReference
|
|
53
|
+
? `No task found for ${searchedTaskReference.toUpperCase()}`
|
|
54
|
+
: t('taskList.noMatchingTasks')
|
|
51
55
|
: effectiveTaskScope === 'archived'
|
|
52
56
|
? 'No archived tasks found.'
|
|
53
57
|
: effectiveTaskScope === 'deleted'
|
|
@@ -390,6 +390,10 @@ describe('TaskList Component', () => {
|
|
|
390
390
|
renderComponent({ filteredTasks: [], loadingTasks: false });
|
|
391
391
|
expect(screen.getByText('No active tasks')).toBeInTheDocument();
|
|
392
392
|
});
|
|
393
|
+
it('shows a task-reference-specific empty state for unmatched searches', () => {
|
|
394
|
+
renderComponent({ filteredTasks: [], loadingTasks: false, searchQuery: ' T - 123 ' });
|
|
395
|
+
expect(screen.getByText('No task found for T-123')).toBeInTheDocument();
|
|
396
|
+
});
|
|
393
397
|
it('2.13 Loading state', () => {
|
|
394
398
|
renderComponent({ loadingTasks: true });
|
|
395
399
|
expect(screen.queryByText('No active tasks')).not.toBeInTheDocument();
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface HierarchyActionBadgeProps {
|
|
3
|
+
ariaLabel: string;
|
|
4
|
+
children: React.ReactNode;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
onClick: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
|
7
|
+
title: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function HierarchyActionBadge({ ariaLabel, children, disabled, onClick, title, }: HierarchyActionBadgeProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import styles from '../../Taskforce.module.css';
|
|
3
|
+
export function HierarchyActionBadge({ ariaLabel, children, disabled = false, onClick, title, }) {
|
|
4
|
+
return (_jsx("button", { type: "button", className: `${styles.taskIdBadge} ${styles.taskHierarchyActionBtn}`, onClick: onClick, disabled: disabled, title: title, "aria-label": ariaLabel, children: children }));
|
|
5
|
+
}
|
|
@@ -4,10 +4,12 @@ interface AppShellHeaderProps {
|
|
|
4
4
|
projectName?: string | null;
|
|
5
5
|
currentWorkspaceId?: string | null;
|
|
6
6
|
brandLabel?: string;
|
|
7
|
+
brandHomeLabel?: string;
|
|
8
|
+
onBrandClick?: () => void;
|
|
7
9
|
runtimeMode?: 'local' | 'cloud';
|
|
8
10
|
theme?: TaskforceTheme;
|
|
9
11
|
meta?: React.ReactNode;
|
|
10
12
|
actions?: React.ReactNode;
|
|
11
13
|
}
|
|
12
|
-
export declare function AppShellHeader({ projectName, currentWorkspaceId, brandLabel, runtimeMode, theme, meta, actions }: AppShellHeaderProps): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export declare function AppShellHeader({ projectName, currentWorkspaceId, brandLabel, brandHomeLabel, onBrandClick, runtimeMode, theme, meta, actions }: AppShellHeaderProps): import("react/jsx-runtime").JSX.Element;
|
|
13
15
|
export {};
|
|
@@ -5,19 +5,35 @@ import logoLight from '../../assets/branding/logos/logo_light.svg';
|
|
|
5
5
|
import logoDark from '../../assets/branding/logos/logo_dark.svg';
|
|
6
6
|
import favicon from '../../assets/favicon/favicon.svg';
|
|
7
7
|
import { DEFAULT_TASKFORCE_THEME, isLightThemeFamily } from '../../utils/theme';
|
|
8
|
-
export function AppShellHeader({ projectName, currentWorkspaceId, brandLabel = 'TaskForce', runtimeMode = 'local', theme = DEFAULT_TASKFORCE_THEME, meta, actions }) {
|
|
8
|
+
export function AppShellHeader({ projectName, currentWorkspaceId, brandLabel = 'TaskForce', brandHomeLabel = 'Go to Kanban', onBrandClick, runtimeMode = 'local', theme = DEFAULT_TASKFORCE_THEME, meta, actions }) {
|
|
9
9
|
const logo = isLightThemeFamily(theme) ? logoDark : logoLight;
|
|
10
10
|
const normalizedProjectName = String(projectName || '').trim();
|
|
11
|
-
const
|
|
11
|
+
const workspaceIdForCopy = String(currentWorkspaceId || '').trim();
|
|
12
|
+
const normalizedWorkspaceId = workspaceIdForCopy.toLowerCase();
|
|
12
13
|
const showProjectName = Boolean(normalizedProjectName
|
|
13
14
|
&& (runtimeMode !== 'cloud' || (normalizedWorkspaceId && normalizedWorkspaceId !== 'default')));
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
const copyWorkspaceId = () => {
|
|
16
|
+
if (!workspaceIdForCopy || typeof navigator === 'undefined')
|
|
17
|
+
return;
|
|
18
|
+
const clipboard = navigator.clipboard;
|
|
19
|
+
if (!clipboard?.writeText)
|
|
20
|
+
return;
|
|
21
|
+
void clipboard.writeText(workspaceIdForCopy).catch(() => undefined);
|
|
22
|
+
};
|
|
23
|
+
const handleProjectNameKeyDown = (event) => {
|
|
24
|
+
if (event.key !== 'Enter' && event.key !== ' ')
|
|
25
|
+
return;
|
|
26
|
+
event.preventDefault();
|
|
27
|
+
copyWorkspaceId();
|
|
28
|
+
};
|
|
29
|
+
const brandContent = (_jsxs(_Fragment, { children: [_jsx("img", { src: logo, alt: "Taskforce Logo", className: styles.brandIcon, onError: (event) => {
|
|
30
|
+
const target = event.currentTarget;
|
|
31
|
+
if (target.src !== favicon) {
|
|
32
|
+
target.src = favicon;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
target.style.display = 'none';
|
|
36
|
+
}
|
|
37
|
+
} }), _jsx("span", { children: brandLabel }), runtimeMode === 'cloud' && (_jsx("span", { className: styles.brandCloudSuffix, children: "HQ" }))] }));
|
|
38
|
+
return (_jsxs("div", { className: `${styles.header} ${shellStyles.standaloneHeader}`, children: [_jsxs("div", { className: `${styles.headerTitle} ${shellStyles.standaloneTitle}`, children: [onBrandClick ? (_jsx("button", { type: "button", className: styles.brandHomeButton, onClick: onBrandClick, title: brandHomeLabel, "aria-label": brandHomeLabel, children: brandContent })) : (_jsx("span", { className: styles.brandHomeStatic, children: brandContent })), showProjectName && (_jsxs(_Fragment, { children: [_jsx("span", { className: styles.projectSlash, children: "/" }), _jsx("span", { className: styles.projectName, title: workspaceIdForCopy ? `Workspace ID: ${workspaceIdForCopy}` : undefined, role: workspaceIdForCopy ? 'button' : undefined, tabIndex: workspaceIdForCopy ? 0 : undefined, onClick: copyWorkspaceId, onMouseDown: (event) => event.preventDefault(), onKeyDown: handleProjectNameKeyDown, children: normalizedProjectName })] })), meta] }), _jsx("div", { className: styles.headerActions, style: { gap: '16px' }, children: actions })] }));
|
|
23
39
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { render, screen } from '@testing-library/react';
|
|
3
|
-
import
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
4
5
|
import { AppShellHeader } from './AppShellHeader';
|
|
5
6
|
import logoDark from '../../assets/branding/logos/logo_dark.svg';
|
|
6
7
|
describe('AppShellHeader', () => {
|
|
@@ -18,6 +19,22 @@ describe('AppShellHeader', () => {
|
|
|
18
19
|
render(_jsx(AppShellHeader, { runtimeMode: "local", theme: "dawn" }));
|
|
19
20
|
expect(screen.getByAltText('Taskforce Logo')).toHaveAttribute('src', logoDark);
|
|
20
21
|
});
|
|
22
|
+
it('calls the brand click handler from the logo button', async () => {
|
|
23
|
+
const onBrandClick = vi.fn();
|
|
24
|
+
render(_jsx(AppShellHeader, { runtimeMode: "local", onBrandClick: onBrandClick }));
|
|
25
|
+
await userEvent.click(screen.getByRole('button', { name: 'Go to Kanban' }));
|
|
26
|
+
expect(onBrandClick).toHaveBeenCalledTimes(1);
|
|
27
|
+
});
|
|
28
|
+
it('copies the workspace id from the project badge', async () => {
|
|
29
|
+
const writeText = vi.fn().mockResolvedValue(undefined);
|
|
30
|
+
Object.defineProperty(navigator, 'clipboard', {
|
|
31
|
+
configurable: true,
|
|
32
|
+
value: { writeText }
|
|
33
|
+
});
|
|
34
|
+
render(_jsx(AppShellHeader, { runtimeMode: "local", projectName: "Roadmap", currentWorkspaceId: "workspace-alpha" }));
|
|
35
|
+
await userEvent.click(screen.getByRole('button', { name: 'Roadmap' }));
|
|
36
|
+
expect(writeText).toHaveBeenCalledWith('workspace-alpha');
|
|
37
|
+
});
|
|
21
38
|
it('hides a stale project label while cloud runtime is still on the default workspace', () => {
|
|
22
39
|
render(_jsx(AppShellHeader, { runtimeMode: "cloud", projectName: "Old Workspace", currentWorkspaceId: "default" }));
|
|
23
40
|
expect(screen.queryByText('Old Workspace')).not.toBeInTheDocument();
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import type { TaskforceTheme } from '../../utils/theme';
|
|
2
3
|
export type BillingInterval = 'month' | 'year';
|
|
3
4
|
export type BillingPricingType = 'stripe' | 'free';
|
|
5
|
+
export type BillingPricingAudience = 'public' | 'campaign';
|
|
6
|
+
type PublicPricingValue = {
|
|
7
|
+
pricingType: BillingPricingType;
|
|
8
|
+
stripePriceId: string | null;
|
|
9
|
+
unitAmount: number | null;
|
|
10
|
+
currency: string | null;
|
|
11
|
+
interval: string;
|
|
12
|
+
} | null;
|
|
4
13
|
export interface PublicPricingPlan {
|
|
5
14
|
planId: string;
|
|
6
15
|
displayName: string;
|
|
@@ -20,22 +29,15 @@ export interface PublicPricingPlan {
|
|
|
20
29
|
description?: string | null;
|
|
21
30
|
publicLabel?: string | null;
|
|
22
31
|
publicDescription?: string | null;
|
|
32
|
+
publicDescriptionVisible?: boolean | null;
|
|
23
33
|
}>;
|
|
24
34
|
pricing: {
|
|
25
|
-
month:
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
} | null;
|
|
32
|
-
year: {
|
|
33
|
-
pricingType: BillingPricingType;
|
|
34
|
-
stripePriceId: string | null;
|
|
35
|
-
unitAmount: number | null;
|
|
36
|
-
currency: string | null;
|
|
37
|
-
interval: string;
|
|
38
|
-
} | null;
|
|
35
|
+
month: PublicPricingValue;
|
|
36
|
+
year: PublicPricingValue;
|
|
37
|
+
};
|
|
38
|
+
campaignPricing?: {
|
|
39
|
+
month: PublicPricingValue;
|
|
40
|
+
year: PublicPricingValue;
|
|
39
41
|
};
|
|
40
42
|
}
|
|
41
43
|
interface PricingSourceDetails {
|
|
@@ -47,12 +49,14 @@ export interface PlanSelectionIntent {
|
|
|
47
49
|
planVersionId: string;
|
|
48
50
|
interval: BillingInterval;
|
|
49
51
|
pricingType: BillingPricingType;
|
|
52
|
+
pricingAudience: BillingPricingAudience;
|
|
50
53
|
isCurrentPlan: boolean;
|
|
51
54
|
}
|
|
52
55
|
interface PlanComparisonPageProps {
|
|
53
56
|
heading: string;
|
|
54
57
|
subtitle?: string;
|
|
55
58
|
variant?: 'app' | 'site';
|
|
59
|
+
theme?: TaskforceTheme;
|
|
56
60
|
chrome?: React.ReactNode;
|
|
57
61
|
footer?: React.ReactNode;
|
|
58
62
|
isAuthenticated: boolean;
|
|
@@ -74,9 +78,10 @@ interface PlanComparisonPageProps {
|
|
|
74
78
|
shellOwnsScroll?: boolean;
|
|
75
79
|
showPageBackButton?: boolean;
|
|
76
80
|
showHeaderBarWithChrome?: boolean;
|
|
81
|
+
preferPricingPageTitle?: boolean;
|
|
77
82
|
currentPlanCardActionMode?: 'manage' | 'indicator' | 'hidden';
|
|
78
83
|
fallbackPricingSource?: PricingSourceDetails | null;
|
|
79
84
|
onSelectPlan: (intent: PlanSelectionIntent) => void | Promise<void>;
|
|
80
85
|
}
|
|
81
|
-
export declare function PlanComparisonPage({ heading, subtitle, variant, chrome, footer, isAuthenticated, authSessionResolved, currentPlanId, currentPlanVersionId, currentEntitlementState, markCurrentPlan, canManageCurrentPlan, pricingEndpoint, statusBanner, onBack, backLabel, backDisabled, topActions, shellOwnsScroll, showPageBackButton, showHeaderBarWithChrome, currentPlanCardActionMode, fallbackPricingSource, onSelectPlan }: PlanComparisonPageProps): import("react/jsx-runtime").JSX.Element;
|
|
86
|
+
export declare function PlanComparisonPage({ heading, subtitle, variant, theme, chrome, footer, isAuthenticated, authSessionResolved, currentPlanId, currentPlanVersionId, currentEntitlementState, markCurrentPlan, canManageCurrentPlan, pricingEndpoint, statusBanner, onBack, backLabel, backDisabled, topActions, shellOwnsScroll, showPageBackButton, showHeaderBarWithChrome, preferPricingPageTitle, currentPlanCardActionMode, fallbackPricingSource, onSelectPlan }: PlanComparisonPageProps): import("react/jsx-runtime").JSX.Element;
|
|
82
87
|
export {};
|