@burdenoff/microfe-planmagnet 2026.1001.5 → 2026.1001.7
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/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
- package/dist/planmagnet/PlanMagnetRoutes.js +10 -6
- package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
- package/dist/planmagnet/automation/automationRuleTypes.d.ts +31 -19
- package/dist/planmagnet/automation/automationRuleTypes.d.ts.map +1 -1
- package/dist/planmagnet/automation/automationRuleTypes.js +4 -8
- package/dist/planmagnet/automation/automationRuleTypes.js.map +1 -1
- package/dist/planmagnet/controls/CompetitorControls.d.ts +71 -0
- package/dist/planmagnet/controls/CompetitorControls.d.ts.map +1 -1
- package/dist/planmagnet/controls/CompetitorControls.js +346 -12
- package/dist/planmagnet/controls/CompetitorControls.js.map +1 -1
- package/dist/planmagnet/controls/FeatureControls.d.ts +23 -1
- package/dist/planmagnet/controls/FeatureControls.d.ts.map +1 -1
- package/dist/planmagnet/controls/FeatureControls.js +431 -229
- package/dist/planmagnet/controls/FeatureControls.js.map +1 -1
- package/dist/planmagnet/controls/FeedbackControls.d.ts +70 -0
- package/dist/planmagnet/controls/FeedbackControls.d.ts.map +1 -0
- package/dist/planmagnet/controls/FeedbackControls.js +153 -0
- package/dist/planmagnet/controls/FeedbackControls.js.map +1 -0
- package/dist/planmagnet/controls/Pickers.d.ts +15 -0
- package/dist/planmagnet/controls/Pickers.d.ts.map +1 -1
- package/dist/planmagnet/controls/Pickers.js +61 -1
- package/dist/planmagnet/controls/Pickers.js.map +1 -1
- package/dist/planmagnet/controls/PrioritizationControls.d.ts +36 -0
- package/dist/planmagnet/controls/PrioritizationControls.d.ts.map +1 -1
- package/dist/planmagnet/controls/PrioritizationControls.js +118 -1
- package/dist/planmagnet/controls/PrioritizationControls.js.map +1 -1
- package/dist/planmagnet/controls/SyncControls.d.ts.map +1 -1
- package/dist/planmagnet/controls/SyncControls.js +1 -0
- package/dist/planmagnet/controls/SyncControls.js.map +1 -1
- package/dist/planmagnet/controls/WebhookSubscriptionControls.d.ts +47 -0
- package/dist/planmagnet/controls/WebhookSubscriptionControls.d.ts.map +1 -0
- package/dist/planmagnet/controls/WebhookSubscriptionControls.js +542 -0
- package/dist/planmagnet/controls/WebhookSubscriptionControls.js.map +1 -0
- package/dist/planmagnet/dashboards/DashboardUI.js +8 -8
- package/dist/planmagnet/dashboards/cards/RiskSeverityCard.js +4 -4
- package/dist/planmagnet/dashboards/cards/SprintCapacityCard.d.ts.map +1 -1
- package/dist/planmagnet/dashboards/cards/SprintCapacityCard.js +67 -59
- package/dist/planmagnet/dashboards/cards/SprintCapacityCard.js.map +1 -1
- package/dist/planmagnet/forecast/DeliveryForecastCard.js +8 -8
- package/dist/planmagnet/forecast/ProjectOutlookCard.js +10 -10
- package/dist/planmagnet/forecast/ProjectRiskSummaryCard.js +9 -9
- package/dist/planmagnet/forecast/ResourceForecastCard.js +6 -6
- package/dist/planmagnet/integrations/syncTypes.d.ts +2 -1
- package/dist/planmagnet/integrations/syncTypes.d.ts.map +1 -1
- package/dist/planmagnet/integrations/syncTypes.js +19 -9
- package/dist/planmagnet/integrations/syncTypes.js.map +1 -1
- package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.js +290 -283
- package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/CompetitorsCompetitorDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/CompetitorsCompetitorDetailPagePage.js +126 -63
- package/dist/planmagnet/pages/CompetitorsCompetitorDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/CompetitorsCompetitorsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/CompetitorsCompetitorsPagePage.js +14 -0
- package/dist/planmagnet/pages/CompetitorsCompetitorsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/CompetitorsCreateCompetitorPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/CompetitorsCreateCompetitorPagePage.js +93 -57
- package/dist/planmagnet/pages/CompetitorsCreateCompetitorPagePage.js.map +1 -1
- package/dist/planmagnet/pages/CompetitorsEditCompetitorPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/CompetitorsEditCompetitorPagePage.js +125 -83
- package/dist/planmagnet/pages/CompetitorsEditCompetitorPagePage.js.map +1 -1
- package/dist/planmagnet/pages/DashboardDashboardPagePage.js +23 -23
- package/dist/planmagnet/pages/DocsDocDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/DocsDocDetailPagePage.js +340 -248
- package/dist/planmagnet/pages/DocsDocDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/IntegrationsSyncDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/IntegrationsSyncDetailPagePage.js +172 -167
- package/dist/planmagnet/pages/IntegrationsSyncDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/IntegrationsSyncsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/IntegrationsSyncsPagePage.js +92 -83
- package/dist/planmagnet/pages/IntegrationsSyncsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/IntegrationsWebhooksPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/IntegrationsWebhooksPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/IntegrationsWebhooksPagePage.js +175 -0
- package/dist/planmagnet/pages/IntegrationsWebhooksPagePage.js.map +1 -0
- package/dist/planmagnet/pages/PortfoliosPortfolioDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/PortfoliosPortfolioDetailPagePage.js +226 -126
- package/dist/planmagnet/pages/PortfoliosPortfolioDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js +151 -126
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.js +169 -122
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js +165 -109
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsProductsPagePage.js +13 -13
- package/dist/planmagnet/pages/ProjectsAtRiskPagePage.js +9 -9
- package/dist/planmagnet/pages/ProjectsBudgetsPagePage.js +8 -8
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js +15 -3
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsProjectsPagePage.js +12 -12
- package/dist/planmagnet/pages/ReportsReportsPagePage.js +23 -23
- package/dist/planmagnet/pages/TasksMyDashboardPagePage.js +18 -18
- package/dist/planmagnet/pages/TasksMyWorkPagePage.js +10 -10
- package/dist/planmagnet/pages/TasksTimeEntriesPagePage.js +5 -5
- package/dist/planmagnet/pages/TeamTeamPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TeamTeamPagePage.js +59 -53
- package/dist/planmagnet/pages/TeamTeamPagePage.js.map +1 -1
- package/dist/planmagnet/pages/analytics/DeliveryHealthCards.js +8 -8
- package/dist/planmagnet/pages/analytics/DeliveryKpisCard.js +6 -6
- package/dist/planmagnet/pages/analytics/MetricsCard.d.ts +8 -0
- package/dist/planmagnet/pages/analytics/MetricsCard.d.ts.map +1 -0
- package/dist/planmagnet/pages/analytics/MetricsCard.js +164 -0
- package/dist/planmagnet/pages/analytics/MetricsCard.js.map +1 -0
- package/dist/planmagnet/pages/analytics/QualityMetricsCard.js +6 -6
- package/dist/planmagnet/pages/analytics/SprintDeliveryCards.js +8 -8
- package/dist/planmagnet/pages/analytics/StatusFlowCard.js +7 -7
- package/dist/planmagnet/pages/analytics/ThroughputCard.js +7 -7
- package/dist/planmagnet/pages/analytics/WorkBreakdownCard.js +7 -7
- package/dist/planmagnet/people/People.d.ts +14 -0
- package/dist/planmagnet/people/People.d.ts.map +1 -1
- package/dist/planmagnet/people/People.js +36 -22
- package/dist/planmagnet/people/People.js.map +1 -1
- package/dist/planmagnet/team/UnmatchedCrewIdentities.d.ts +2 -0
- package/dist/planmagnet/team/UnmatchedCrewIdentities.d.ts.map +1 -0
- package/dist/planmagnet/team/UnmatchedCrewIdentities.js +155 -0
- package/dist/planmagnet/team/UnmatchedCrewIdentities.js.map +1 -0
- package/dist/planmagnet/webhooks/webhookTypes.d.ts +54 -0
- package/dist/planmagnet/webhooks/webhookTypes.d.ts.map +1 -0
- package/dist/planmagnet/webhooks/webhookTypes.js +17 -0
- package/dist/planmagnet/webhooks/webhookTypes.js.map +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProductsFeatureDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsFeatureDetailPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge } from '@burdenoff/fe-libs/ui';\nimport { Star, ThumbsUp, MessageSquare, Target, Loader2 } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { detailPath } from '../utils/rowNavigation';\nimport { FeatureTriageActions, isOpenRequest, isSnoozed } from '../controls/FeatureTriage';\nimport { useParams } from 'react-router';\nimport { PersonName } from '../people/People';\nimport {\n FeatureHeaderActions,\n FeaturePrioritizationCard,\n FeatureVoteButton,\n} from '../controls/FeatureControls';\nimport { CompetitorLinkCard } from '../controls/CompetitorControls';\nimport { FeatureInitiativeCard } from '../roadmaps/FeatureInitiativeCard';\n\nconst GET_FEATURE_REQUEST = gql`\n query GetFeatureRequest($id: ID!) {\n planmagnetFeatureRequest(id: $id) {\n id\n workspaceId\n projectId\n title\n description\n status\n businessValue\n effortEstimate\n priorityScore\n voteCount\n submittedBy\n workItemId\n tags\n snoozedUntil\n moscow\n kano\n valueScore\n effortScore\n valueEffortQuadrant\n declineReason\n duplicateOfId\n duplicateOf {\n id\n title\n }\n triagedAt\n triagedBy\n createdAt\n updatedAt\n productId\n roadmapItem {\n id\n title\n roadmap {\n id\n name\n }\n }\n votes {\n id\n voterId\n weight\n comment\n createdAt\n }\n competitors {\n id\n name\n description\n website\n }\n }\n }\n`;\n\ninterface FeatureVoteNode {\n id: string;\n voterId: string;\n weight: number;\n comment: string | null;\n createdAt: string;\n}\n\ninterface CompetitorNode {\n id: string;\n name: string;\n description: string | null;\n website: string | null;\n}\n\nexport function ProductsFeatureDetailPagePage() {\n const { workspaceId, currentUser } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n // Snooze state is read against the moment the page opened (Date.now() may not run during render).\n const [now] = useState(() => Date.now());\n const params = useParams<{ id: string }>();\n const featureId = params.id ?? '';\n\n const {\n data,\n loading,\n error,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(GET_FEATURE_REQUEST, {\n variables: { id: featureId },\n skip: !featureId,\n });\n\n // Only before the first load: a refetch must not blank the page (it\n\n // unmounts every component on it and loses their state).\n\n if (loading && !data) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n </div>\n );\n }\n\n if (error) {\n return <div className=\"p-6 text-destructive\">Failed to load feature: {error.message}</div>;\n }\n\n const feature = data?.planmagnetFeatureRequest;\n if (!feature) return <div className=\"p-6 text-muted-foreground\">Feature not found.</div>;\n\n const votes: FeatureVoteNode[] = feature.votes ?? [];\n const competitors: CompetitorNode[] = feature.competitors ?? [];\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">{feature.title}</h1>\n <p className=\"text-sm text-muted-foreground\">\n Submitted by: <PersonName id={feature.submittedBy} /> · Effort:{' '}\n {feature.effortEstimate ?? 'N/A'}\n </p>\n </div>\n {/* Vote, status, Edit and Delete (BOFF-7951): the request was read-only. */}\n <div className=\"flex flex-col items-end gap-2\">\n <FeatureVoteButton\n featureId={feature.id}\n voted={votes.some((v) => v.voterId === currentUser?.id)}\n />\n <FeatureHeaderActions feature={feature} />\n </div>\n </div>\n\n {/* Triage (P1.3): an undecided request is accepted, declined, snoozed or merged from here. */}\n {isOpenRequest(feature.status) && (\n <Card data-testid=\"triage-card\">\n <CardHeader>\n <CardTitle>Triage</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n <p className=\"text-sm text-muted-foreground\">\n {isSnoozed(feature.snoozedUntil, now)\n ? `Snoozed until ${new Date(feature.snoozedUntil).toLocaleString()} — it returns to the triage queue then.`\n : feature.status === 'SUBMITTED'\n ? 'Waiting for a decision.'\n : 'Under review: accept it as planned, decline it, or merge it into its original.'}\n </p>\n <FeatureTriageActions feature={feature} now={now} />\n </CardContent>\n </Card>\n )}\n {feature.status === 'DECLINED' && feature.declineReason && (\n <Card data-testid=\"decline-reason\">\n <CardContent className=\"pt-4 text-sm\">\n <span className=\"font-medium\">Declined: </span>\n {feature.declineReason}\n </CardContent>\n </Card>\n )}\n {feature.status === 'DUPLICATE' && feature.duplicateOfId && (\n <Card data-testid=\"duplicate-of\">\n <CardContent className=\"pt-4 text-sm\">\n <span className=\"font-medium\">Duplicate of </span>\n {feature.duplicateOf ? (\n <button\n type=\"button\"\n className=\"text-primary underline-offset-2 hover:underline\"\n onClick={() =>\n navigate(detailPath('/products/feature-detail-page', feature.duplicateOf.id))\n }\n >\n {feature.duplicateOf.title}\n </button>\n ) : (\n <span className=\"text-muted-foreground\">a request that has since been deleted</span>\n )}{' '}\n — its votes were moved there.\n </CardContent>\n </Card>\n )}\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <Target className=\"h-5 w-5 mx-auto text-primary\" />\n <p className=\"text-2xl font-bold mt-1\">{feature.priorityScore ?? 0}</p>\n <p className=\"text-xs text-muted-foreground\">Priority Score</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <ThumbsUp className=\"h-5 w-5 mx-auto text-green-500\" />\n <p className=\"text-2xl font-bold mt-1\">{feature.voteCount}</p>\n <p className=\"text-xs text-muted-foreground\">Votes</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <Star className=\"h-5 w-5 mx-auto text-yellow-500\" />\n <p className=\"text-2xl font-bold mt-1\">{feature.businessValue ?? 0}</p>\n <p className=\"text-xs text-muted-foreground\">Business Value</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <MessageSquare className=\"h-5 w-5 mx-auto text-blue-500\" />\n <p className=\"text-2xl font-bold mt-1\">{competitors.length}</p>\n <p className=\"text-xs text-muted-foreground\">Competitors</p>\n </CardContent>\n </Card>\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Description</CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm\">{feature.description ?? 'No description provided.'}</p>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Votes ({votes.length})</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {votes.length === 0 && <p className=\"text-sm text-muted-foreground\">No votes yet.</p>}\n {votes.map((vote) => (\n <div\n key={vote.id}\n className=\"flex items-start justify-between border rounded-md p-3\"\n >\n <div>\n <p className=\"text-sm font-medium\">\n Voter: <PersonName id={vote.voterId} />\n </p>\n {vote.comment && (\n <p className=\"text-xs text-muted-foreground mt-1\">{vote.comment}</p>\n )}\n </div>\n <div className=\"flex items-center gap-1\">\n <ThumbsUp className=\"h-3 w-3 text-green-500\" />\n <span className=\"text-sm font-bold\">{vote.weight}</span>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n\n {/* Competitor tracking (BOFF-8047): add, remove and browse to the competitors linked here. */}\n <Card>\n <CardHeader>\n <CardTitle>Competitors ({competitors.length})</CardTitle>\n </CardHeader>\n <CardContent>\n <CompetitorLinkCard featureRequestId={feature.id} competitors={competitors} />\n </CardContent>\n </Card>\n </div>\n\n <div className=\"space-y-6\">\n {/* PRM-24: the roadmap initiative that addresses this request. */}\n <FeatureInitiativeCard\n featureId={feature.id}\n productId={feature.productId ?? null}\n initiative={feature.roadmapItem ?? null}\n />\n {/* PRM-27: MoSCoW/Kano/Value-Effort scoring, edited in place with the computed quadrant read back\n from the save's own response. */}\n <FeaturePrioritizationCard feature={feature} />\n <Card>\n <CardHeader>\n <CardTitle>Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3 text-sm\">\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Status</span>\n <Badge variant=\"secondary\" className=\"capitalize\">\n {feature.status.toLowerCase().replace('_', ' ')}\n </Badge>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Priority Score</span>\n <Badge variant=\"outline\">{feature.priorityScore ?? 'N/A'}</Badge>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Effort</span>\n <Badge variant=\"outline\" className=\"capitalize\">\n {feature.effortEstimate ?? 'N/A'}\n </Badge>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Business Value</span>\n <span className=\"font-medium\">{feature.businessValue ?? 'N/A'}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Submitted By</span>\n <PersonName id={feature.submittedBy} />\n </div>\n {feature.triagedAt && (\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Decided</span>\n <span>\n {new Date(feature.triagedAt).toLocaleDateString()}\n {feature.triagedBy && (\n <>\n {' by '}\n <PersonName id={feature.triagedBy} />\n </>\n )}\n </span>\n </div>\n )}\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Updated</span>\n <span>{new Date(feature.updatedAt).toLocaleDateString()}</span>\n </div>\n </CardContent>\n </Card>\n\n {feature.tags?.length > 0 && (\n <Card>\n <CardHeader>\n <CardTitle>Tags</CardTitle>\n </CardHeader>\n <CardContent className=\"flex flex-wrap gap-1\">\n {feature.tags.map((tag: string) => (\n <Badge key={tag} variant=\"outline\" className=\"text-xs\">\n {tag}\n </Badge>\n ))}\n </CardContent>\n </Card>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ProductsFeatureDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsFeatureDetailPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge } from '@burdenoff/fe-libs/ui';\nimport { Star, ThumbsUp, MessageSquare, Target, Loader2 } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { detailPath } from '../utils/rowNavigation';\nimport { FeatureTriageActions, isOpenRequest, isSnoozed } from '../controls/FeatureTriage';\nimport { useParams } from 'react-router';\nimport { PersonName } from '../people/People';\nimport {\n FeatureHeaderActions,\n FeaturePrioritizationCard,\n FeatureVoteButton,\n} from '../controls/FeatureControls';\nimport { CompetitorLinkCard } from '../controls/CompetitorControls';\nimport { FeatureInitiativeCard } from '../roadmaps/FeatureInitiativeCard';\nimport { CategoryBadge, RatingDisplay } from '../controls/FeedbackControls';\n\nconst GET_FEATURE_REQUEST = gql`\n query GetFeatureRequest($id: ID!) {\n planmagnetFeatureRequest(id: $id) {\n id\n workspaceId\n projectId\n title\n description\n status\n businessValue\n effortEstimate\n priorityScore\n voteCount\n submittedBy\n workItemId\n tags\n snoozedUntil\n rating\n category\n isAnonymous\n moscow\n kano\n valueScore\n effortScore\n valueEffortQuadrant\n reach\n impact\n confidence\n riceScore\n declineReason\n duplicateOfId\n duplicateOf {\n id\n title\n }\n triagedAt\n triagedBy\n createdAt\n updatedAt\n productId\n roadmapItem {\n id\n title\n roadmap {\n id\n name\n }\n }\n votes {\n id\n voterId\n weight\n comment\n createdAt\n }\n competitors {\n id\n name\n description\n website\n }\n }\n }\n`;\n\ninterface FeatureVoteNode {\n id: string;\n voterId: string;\n weight: number;\n comment: string | null;\n createdAt: string;\n}\n\ninterface CompetitorNode {\n id: string;\n name: string;\n description: string | null;\n website: string | null;\n}\n\nexport function ProductsFeatureDetailPagePage() {\n const { workspaceId, currentUser } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n // Snooze state is read against the moment the page opened (Date.now() may not run during render).\n const [now] = useState(() => Date.now());\n const params = useParams<{ id: string }>();\n const featureId = params.id ?? '';\n\n const {\n data,\n loading,\n error,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(GET_FEATURE_REQUEST, {\n variables: { id: featureId },\n skip: !featureId,\n });\n\n // Only before the first load: a refetch must not blank the page (it\n\n // unmounts every component on it and loses their state).\n\n if (loading && !data) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n </div>\n );\n }\n\n if (error) {\n return <div className=\"p-6 text-destructive\">Failed to load feature: {error.message}</div>;\n }\n\n const feature = data?.planmagnetFeatureRequest;\n if (!feature) return <div className=\"p-6 text-muted-foreground\">Feature not found.</div>;\n\n const votes: FeatureVoteNode[] = feature.votes ?? [];\n const competitors: CompetitorNode[] = feature.competitors ?? [];\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">{feature.title}</h1>\n <p className=\"text-sm text-muted-foreground\">\n Submitted by: <PersonName id={feature.submittedBy} /> · Effort:{' '}\n {feature.effortEstimate ?? 'N/A'}\n </p>\n </div>\n {/* Vote, status, Edit and Delete (BOFF-7951): the request was read-only. */}\n <div className=\"flex flex-col items-end gap-2\">\n <FeatureVoteButton\n key={feature.id}\n featureId={feature.id}\n voted={votes.some((v) => v.voterId === currentUser?.id)}\n />\n <FeatureHeaderActions feature={feature} />\n </div>\n </div>\n\n {/* Triage (P1.3): an undecided request is accepted, declined, snoozed or merged from here. */}\n {isOpenRequest(feature.status) && (\n <Card data-testid=\"triage-card\">\n <CardHeader>\n <CardTitle>Triage</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n <p className=\"text-sm text-muted-foreground\">\n {isSnoozed(feature.snoozedUntil, now)\n ? `Snoozed until ${new Date(feature.snoozedUntil).toLocaleString()} — it returns to the triage queue then.`\n : feature.status === 'SUBMITTED'\n ? 'Waiting for a decision.'\n : 'Under review: accept it as planned, decline it, or merge it into its original.'}\n </p>\n <FeatureTriageActions feature={feature} now={now} />\n </CardContent>\n </Card>\n )}\n {feature.status === 'DECLINED' && feature.declineReason && (\n <Card data-testid=\"decline-reason\">\n <CardContent className=\"pt-4 text-sm\">\n <span className=\"font-medium\">Declined: </span>\n {feature.declineReason}\n </CardContent>\n </Card>\n )}\n {feature.status === 'DUPLICATE' && feature.duplicateOfId && (\n <Card data-testid=\"duplicate-of\">\n <CardContent className=\"pt-4 text-sm\">\n <span className=\"font-medium\">Duplicate of </span>\n {feature.duplicateOf ? (\n <button\n type=\"button\"\n className=\"text-primary underline-offset-2 hover:underline\"\n onClick={() =>\n navigate(detailPath('/products/feature-detail-page', feature.duplicateOf.id))\n }\n >\n {feature.duplicateOf.title}\n </button>\n ) : (\n <span className=\"text-muted-foreground\">a request that has since been deleted</span>\n )}{' '}\n — its votes were moved there.\n </CardContent>\n </Card>\n )}\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <Target className=\"h-5 w-5 mx-auto text-primary\" />\n <p className=\"text-2xl font-bold mt-1\">{feature.priorityScore ?? 0}</p>\n <p className=\"text-xs text-muted-foreground\">Priority Score</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <ThumbsUp className=\"h-5 w-5 mx-auto text-green-500\" />\n <p className=\"text-2xl font-bold mt-1\">{feature.voteCount}</p>\n <p className=\"text-xs text-muted-foreground\">Votes</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <Star className=\"h-5 w-5 mx-auto text-yellow-500\" />\n <p className=\"text-2xl font-bold mt-1\">{feature.businessValue ?? 0}</p>\n <p className=\"text-xs text-muted-foreground\">Business Value</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <MessageSquare className=\"h-5 w-5 mx-auto text-blue-500\" />\n <p className=\"text-2xl font-bold mt-1\">{competitors.length}</p>\n <p className=\"text-xs text-muted-foreground\">Competitors</p>\n </CardContent>\n </Card>\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Description</CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm\">{feature.description ?? 'No description provided.'}</p>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Votes ({votes.length})</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {votes.length === 0 && <p className=\"text-sm text-muted-foreground\">No votes yet.</p>}\n {votes.map((vote) => (\n <div\n key={vote.id}\n className=\"flex items-start justify-between border rounded-md p-3\"\n >\n <div>\n <p className=\"text-sm font-medium\">\n Voter: <PersonName id={vote.voterId} />\n </p>\n {vote.comment && (\n <p className=\"text-xs text-muted-foreground mt-1\">{vote.comment}</p>\n )}\n </div>\n <div className=\"flex items-center gap-1\">\n <ThumbsUp className=\"h-3 w-3 text-green-500\" />\n <span className=\"text-sm font-bold\">{vote.weight}</span>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n\n {/* Competitor tracking (BOFF-8047): add, remove and browse to the competitors linked here. */}\n <Card>\n <CardHeader>\n <CardTitle>Competitors ({competitors.length})</CardTitle>\n </CardHeader>\n <CardContent>\n <CompetitorLinkCard featureRequestId={feature.id} competitors={competitors} />\n </CardContent>\n </Card>\n </div>\n\n <div className=\"space-y-6\">\n {/* PRM-24: the roadmap initiative that addresses this request. */}\n <FeatureInitiativeCard\n featureId={feature.id}\n productId={feature.productId ?? null}\n initiative={feature.roadmapItem ?? null}\n />\n {/* PRM-27: MoSCoW/Kano/Value-Effort scoring, edited in place with the computed quadrant read back\n from the save's own response. */}\n <FeaturePrioritizationCard feature={feature} />\n <Card>\n <CardHeader>\n <CardTitle>Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3 text-sm\">\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Status</span>\n <Badge variant=\"secondary\" className=\"capitalize\">\n {feature.status.toLowerCase().replace('_', ' ')}\n </Badge>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Priority Score</span>\n <Badge variant=\"outline\">{feature.priorityScore ?? 'N/A'}</Badge>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Effort</span>\n <Badge variant=\"outline\" className=\"capitalize\">\n {feature.effortEstimate ?? 'N/A'}\n </Badge>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Business Value</span>\n <span className=\"font-medium\">{feature.businessValue ?? 'N/A'}</span>\n </div>\n {/* CWF-15 */}\n <div className=\"flex justify-between items-center\">\n <span className=\"text-muted-foreground\">Rating</span>\n <RatingDisplay rating={feature.rating ?? null} />\n </div>\n <div className=\"flex justify-between items-center\">\n <span className=\"text-muted-foreground\">Category</span>\n {feature.category ? (\n <CategoryBadge category={feature.category} />\n ) : (\n <span className=\"text-sm text-muted-foreground\">Not set</span>\n )}\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Submitted By</span>\n <PersonName id={feature.submittedBy} />\n </div>\n {feature.triagedAt && (\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Decided</span>\n <span>\n {new Date(feature.triagedAt).toLocaleDateString()}\n {feature.triagedBy && (\n <>\n {' by '}\n <PersonName id={feature.triagedBy} />\n </>\n )}\n </span>\n </div>\n )}\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Updated</span>\n <span>{new Date(feature.updatedAt).toLocaleDateString()}</span>\n </div>\n </CardContent>\n </Card>\n\n {feature.tags?.length > 0 && (\n <Card>\n <CardHeader>\n <CardTitle>Tags</CardTitle>\n </CardHeader>\n <CardContent className=\"flex flex-wrap gap-1\">\n {feature.tags.map((tag: string) => (\n <Badge key={tag} variant=\"outline\" className=\"text-xs\">\n {tag}\n </Badge>\n ))}\n </CardContent>\n </Card>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProductsFeaturesPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsFeaturesPagePage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ProductsFeaturesPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsFeaturesPagePage.tsx"],"names":[],"mappings":"AA8GA,wBAAgB,wBAAwB,4CAkXvC"}
|
|
@@ -3,15 +3,16 @@ import { PersonName as n } from "../people/People.js";
|
|
|
3
3
|
import { countLabel as r, useFeatureCounts as i } from "../hooks/useCounts.js";
|
|
4
4
|
import { detailPath as a, rowLinkProps as o } from "../utils/rowNavigation.js";
|
|
5
5
|
import { FeatureTriageActions as s, isSnoozed as c } from "../controls/FeatureTriage.js";
|
|
6
|
-
import { KANO_META as l, KANO_VALUES as u, MOSCOW_META as d, MOSCOW_VALUES as
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
6
|
+
import { KANO_META as l, KANO_VALUES as u, MOSCOW_META as d, MOSCOW_VALUES as ee, QUADRANT_META as f, QUADRANT_VALUES as p, QuadrantBadge as m, riceScoreMissingHint as h } from "../controls/PrioritizationControls.js";
|
|
7
|
+
import { CategoryBadge as te, RatingDisplay as g } from "../controls/FeedbackControls.js";
|
|
8
|
+
import { useState as _ } from "react";
|
|
9
|
+
import { ArrowUpDown as v, Filter as y, Gauge as b, Loader2 as ne, Plus as re, Star as ie, Target as ae, ThumbsUp as x } from "lucide-react";
|
|
10
|
+
import { gql as S } from "@apollo/client";
|
|
11
|
+
import { useQuery as C } from "@apollo/client/react";
|
|
12
|
+
import { jsx as w, jsxs as T } from "react/jsx-runtime";
|
|
13
|
+
import { Badge as E, Button as D, Card as O, CardContent as k } from "@burdenoff/fe-libs/ui";
|
|
13
14
|
//#region src/planmagnet/pages/ProductsFeaturesPagePage.tsx
|
|
14
|
-
var
|
|
15
|
+
var A = S`
|
|
15
16
|
query ListFeatureRequests(
|
|
16
17
|
$workspaceId: ID!
|
|
17
18
|
$filter: PlanmagnetFeatureRequestFilter
|
|
@@ -40,13 +41,20 @@ var j = C`
|
|
|
40
41
|
moscow
|
|
41
42
|
kano
|
|
42
43
|
valueEffortQuadrant
|
|
44
|
+
reach
|
|
45
|
+
impact
|
|
46
|
+
confidence
|
|
47
|
+
effortScore
|
|
48
|
+
riceScore
|
|
49
|
+
rating
|
|
50
|
+
category
|
|
43
51
|
createdAt
|
|
44
52
|
updatedAt
|
|
45
53
|
}
|
|
46
54
|
totalCount
|
|
47
55
|
}
|
|
48
56
|
}
|
|
49
|
-
`,
|
|
57
|
+
`, j = {
|
|
50
58
|
SUBMITTED: "bg-gray-100 text-gray-800",
|
|
51
59
|
UNDER_REVIEW: "bg-yellow-100 text-yellow-800",
|
|
52
60
|
PLANNED: "bg-blue-100 text-blue-800",
|
|
@@ -55,30 +63,30 @@ var j = C`
|
|
|
55
63
|
DECLINED: "bg-red-100 text-red-800",
|
|
56
64
|
DUPLICATE: "bg-orange-100 text-orange-800"
|
|
57
65
|
};
|
|
58
|
-
function
|
|
59
|
-
let
|
|
60
|
-
...
|
|
61
|
-
...
|
|
62
|
-
...
|
|
63
|
-
...
|
|
64
|
-
}, { data:
|
|
66
|
+
function M() {
|
|
67
|
+
let S = t(), { workspaceId: M } = e(), [N, P] = _("all"), [F] = _(() => Date.now()), [I, L] = _("NEWEST"), [R, z] = _(!1), [B, V] = _(""), [H, U] = _(""), [W, G] = _(""), K = i(), q = {
|
|
68
|
+
...N === "TRIAGE" ? { triage: !0 } : N === "SNOOZED" ? { snoozed: !0 } : N === "all" ? {} : { status: N },
|
|
69
|
+
...B ? { moscow: B } : {},
|
|
70
|
+
...H ? { kano: H } : {},
|
|
71
|
+
...W ? { quadrant: W } : {}
|
|
72
|
+
}, { data: J, loading: oe, error: Y } = C(A, {
|
|
65
73
|
variables: {
|
|
66
|
-
workspaceId:
|
|
67
|
-
filter: Object.keys(
|
|
74
|
+
workspaceId: M,
|
|
75
|
+
filter: Object.keys(q).length > 0 ? q : void 0,
|
|
68
76
|
pagination: { first: 50 },
|
|
69
|
-
orderBy:
|
|
77
|
+
orderBy: I
|
|
70
78
|
},
|
|
71
|
-
skip: !
|
|
79
|
+
skip: !M
|
|
72
80
|
});
|
|
73
|
-
if (
|
|
81
|
+
if (oe && !J) return /* @__PURE__ */ w("div", {
|
|
74
82
|
className: "flex items-center justify-center p-12",
|
|
75
|
-
children: /* @__PURE__ */
|
|
83
|
+
children: /* @__PURE__ */ w(ne, { className: "h-8 w-8 animate-spin text-muted-foreground" })
|
|
76
84
|
});
|
|
77
|
-
if (
|
|
85
|
+
if (Y) return /* @__PURE__ */ T("div", {
|
|
78
86
|
className: "p-6 text-destructive",
|
|
79
|
-
children: ["Failed to load features: ",
|
|
87
|
+
children: ["Failed to load features: ", Y.message]
|
|
80
88
|
});
|
|
81
|
-
let
|
|
89
|
+
let X = J?.planmagnetFeatureRequests?.items ?? [], Z = J?.planmagnetFeatureRequests?.totalCount ?? 0, Q = R ? [...X].sort((e, t) => e.riceScore == null && t.riceScore == null ? 0 : e.riceScore == null ? 1 : t.riceScore == null ? -1 : t.riceScore - e.riceScore) : X, se = [
|
|
82
90
|
"all",
|
|
83
91
|
"TRIAGE",
|
|
84
92
|
"SNOOZED",
|
|
@@ -90,257 +98,296 @@ function N() {
|
|
|
90
98
|
"DECLINED",
|
|
91
99
|
"DUPLICATE"
|
|
92
100
|
], $ = {
|
|
93
|
-
total: r(
|
|
94
|
-
open: r(
|
|
95
|
-
inProgress: r(
|
|
96
|
-
completed: r(
|
|
101
|
+
total: r(K?.all),
|
|
102
|
+
open: r(K?.open),
|
|
103
|
+
inProgress: r(K?.byStatus.IN_PROGRESS),
|
|
104
|
+
completed: r(K?.byStatus.COMPLETED)
|
|
97
105
|
};
|
|
98
|
-
return /* @__PURE__ */
|
|
106
|
+
return /* @__PURE__ */ T("div", {
|
|
99
107
|
className: "space-y-6 p-6",
|
|
100
108
|
children: [
|
|
101
|
-
/* @__PURE__ */
|
|
109
|
+
/* @__PURE__ */ T("div", {
|
|
102
110
|
className: "flex items-center justify-between",
|
|
103
|
-
children: [/* @__PURE__ */
|
|
111
|
+
children: [/* @__PURE__ */ T("div", { children: [/* @__PURE__ */ w("h1", {
|
|
104
112
|
className: "text-2xl font-semibold",
|
|
105
113
|
children: "Features"
|
|
106
|
-
}), /* @__PURE__ */
|
|
114
|
+
}), /* @__PURE__ */ w("p", {
|
|
107
115
|
className: "text-sm text-muted-foreground",
|
|
108
|
-
children:
|
|
109
|
-
})] }), /* @__PURE__ */
|
|
116
|
+
children: N === "all" ? `${$.total} feature requests` : N === "TRIAGE" ? `${Z} waiting for triage` : `${Z} ${N.toLowerCase().replace("_", " ")} match`
|
|
117
|
+
})] }), /* @__PURE__ */ T(D, {
|
|
110
118
|
size: "sm",
|
|
111
|
-
onClick: () =>
|
|
112
|
-
children: [/* @__PURE__ */
|
|
119
|
+
onClick: () => S("/products/new-feature-page"),
|
|
120
|
+
children: [/* @__PURE__ */ w(re, { className: "mr-1 h-4 w-4" }), "New Feature"]
|
|
113
121
|
})]
|
|
114
122
|
}),
|
|
115
|
-
/* @__PURE__ */
|
|
123
|
+
/* @__PURE__ */ T("div", {
|
|
116
124
|
className: "grid grid-cols-2 md:grid-cols-4 gap-4",
|
|
117
125
|
children: [
|
|
118
|
-
/* @__PURE__ */
|
|
126
|
+
/* @__PURE__ */ w(O, { children: /* @__PURE__ */ T(k, {
|
|
119
127
|
className: "pt-4",
|
|
120
|
-
children: [/* @__PURE__ */
|
|
128
|
+
children: [/* @__PURE__ */ w("p", {
|
|
121
129
|
className: "text-xs text-muted-foreground",
|
|
122
130
|
children: "Total Features"
|
|
123
|
-
}), /* @__PURE__ */
|
|
131
|
+
}), /* @__PURE__ */ w("p", {
|
|
124
132
|
className: "text-xl font-bold",
|
|
125
133
|
children: $.total
|
|
126
134
|
})]
|
|
127
135
|
}) }),
|
|
128
|
-
/* @__PURE__ */
|
|
136
|
+
/* @__PURE__ */ w(O, { children: /* @__PURE__ */ T(k, {
|
|
129
137
|
className: "pt-4",
|
|
130
|
-
children: [/* @__PURE__ */
|
|
138
|
+
children: [/* @__PURE__ */ w("p", {
|
|
131
139
|
className: "text-xs text-muted-foreground",
|
|
132
140
|
children: "In Progress"
|
|
133
|
-
}), /* @__PURE__ */
|
|
141
|
+
}), /* @__PURE__ */ w("p", {
|
|
134
142
|
className: "text-xl font-bold",
|
|
135
143
|
children: $.inProgress
|
|
136
144
|
})]
|
|
137
145
|
}) }),
|
|
138
|
-
/* @__PURE__ */
|
|
146
|
+
/* @__PURE__ */ w(O, { children: /* @__PURE__ */ T(k, {
|
|
139
147
|
className: "pt-4",
|
|
140
|
-
children: [/* @__PURE__ */
|
|
148
|
+
children: [/* @__PURE__ */ w("p", {
|
|
141
149
|
className: "text-xs text-muted-foreground",
|
|
142
150
|
children: "Completed"
|
|
143
|
-
}), /* @__PURE__ */
|
|
151
|
+
}), /* @__PURE__ */ w("p", {
|
|
144
152
|
className: "text-xl font-bold",
|
|
145
153
|
children: $.completed
|
|
146
154
|
})]
|
|
147
155
|
}) }),
|
|
148
|
-
/* @__PURE__ */
|
|
156
|
+
/* @__PURE__ */ w(O, { children: /* @__PURE__ */ T(k, {
|
|
149
157
|
className: "pt-4",
|
|
150
|
-
children: [/* @__PURE__ */
|
|
158
|
+
children: [/* @__PURE__ */ w("p", {
|
|
151
159
|
className: "text-xs text-muted-foreground",
|
|
152
160
|
children: "Open"
|
|
153
|
-
}), /* @__PURE__ */
|
|
161
|
+
}), /* @__PURE__ */ w("p", {
|
|
154
162
|
className: "text-xl font-bold",
|
|
155
163
|
children: $.open
|
|
156
164
|
})]
|
|
157
165
|
}) })
|
|
158
166
|
]
|
|
159
167
|
}),
|
|
160
|
-
/* @__PURE__ */
|
|
168
|
+
/* @__PURE__ */ T("div", {
|
|
161
169
|
className: "flex items-center justify-between flex-wrap gap-2",
|
|
162
|
-
children: [/* @__PURE__ */
|
|
170
|
+
children: [/* @__PURE__ */ T("div", {
|
|
163
171
|
className: "flex items-center gap-2",
|
|
164
|
-
children: [/* @__PURE__ */
|
|
165
|
-
onClick: () =>
|
|
166
|
-
className: `px-2 py-1 text-xs rounded-md capitalize ${
|
|
167
|
-
children: e === "all" ? "All" : e === "TRIAGE" ? `Triage (${r(
|
|
172
|
+
children: [/* @__PURE__ */ w(y, { className: "h-4 w-4 text-muted-foreground" }), se.map((e) => /* @__PURE__ */ w("button", {
|
|
173
|
+
onClick: () => P(e),
|
|
174
|
+
className: `px-2 py-1 text-xs rounded-md capitalize ${N === e ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
|
|
175
|
+
children: e === "all" ? "All" : e === "TRIAGE" ? `Triage (${r(K?.triage)})` : e === "SNOOZED" ? `Snoozed (${r(K?.snoozed)})` : e.toLowerCase().replace("_", " ")
|
|
168
176
|
}, e))]
|
|
169
|
-
}), /* @__PURE__ */
|
|
177
|
+
}), /* @__PURE__ */ T("div", {
|
|
170
178
|
className: "flex items-center gap-2",
|
|
171
|
-
children: [
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
179
|
+
children: [
|
|
180
|
+
/* @__PURE__ */ w(v, { className: "h-4 w-4 text-muted-foreground" }),
|
|
181
|
+
["NEWEST", "MOST_VOTED"].map((e) => /* @__PURE__ */ w("button", {
|
|
182
|
+
onClick: () => L(e),
|
|
183
|
+
className: `px-2 py-1 text-xs rounded-md ${!R && I === e ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
|
|
184
|
+
children: e === "NEWEST" ? "Newest" : "Most voted"
|
|
185
|
+
}, e)),
|
|
186
|
+
/* @__PURE__ */ w("button", {
|
|
187
|
+
type: "button",
|
|
188
|
+
"aria-pressed": R,
|
|
189
|
+
"data-testid": "feature-sort-rice",
|
|
190
|
+
onClick: () => z((e) => !e),
|
|
191
|
+
className: `px-2 py-1 text-xs rounded-md ${R ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
|
|
192
|
+
children: "RICE score"
|
|
193
|
+
})
|
|
194
|
+
]
|
|
176
195
|
})]
|
|
177
196
|
}),
|
|
178
|
-
/* @__PURE__ */
|
|
197
|
+
R && /* @__PURE__ */ T("p", {
|
|
198
|
+
className: "text-xs text-muted-foreground",
|
|
199
|
+
"data-testid": "feature-sort-rice-note",
|
|
200
|
+
children: [
|
|
201
|
+
"Sorted by RICE score within the ",
|
|
202
|
+
Q.length,
|
|
203
|
+
" loaded results above — not a server-wide sort."
|
|
204
|
+
]
|
|
205
|
+
}),
|
|
206
|
+
/* @__PURE__ */ T("div", {
|
|
179
207
|
className: "flex flex-wrap items-center gap-3",
|
|
180
208
|
children: [
|
|
181
|
-
/* @__PURE__ */
|
|
209
|
+
/* @__PURE__ */ T("div", {
|
|
182
210
|
className: "flex items-center gap-1",
|
|
183
|
-
children: [/* @__PURE__ */
|
|
211
|
+
children: [/* @__PURE__ */ w("label", {
|
|
184
212
|
htmlFor: "feature-filter-moscow",
|
|
185
213
|
className: "text-xs text-muted-foreground",
|
|
186
214
|
children: "MoSCoW"
|
|
187
|
-
}), /* @__PURE__ */
|
|
215
|
+
}), /* @__PURE__ */ T("select", {
|
|
188
216
|
id: "feature-filter-moscow",
|
|
189
217
|
"data-testid": "feature-filter-moscow",
|
|
190
218
|
className: "rounded-md border border-input bg-background px-2 py-1 text-xs",
|
|
191
|
-
value:
|
|
192
|
-
onChange: (e) =>
|
|
193
|
-
children: [/* @__PURE__ */
|
|
219
|
+
value: B,
|
|
220
|
+
onChange: (e) => V(e.target.value),
|
|
221
|
+
children: [/* @__PURE__ */ w("option", {
|
|
194
222
|
value: "",
|
|
195
223
|
children: "All"
|
|
196
|
-
}),
|
|
224
|
+
}), ee.map((e) => /* @__PURE__ */ w("option", {
|
|
197
225
|
value: e,
|
|
198
226
|
children: d[e].label
|
|
199
227
|
}, e))]
|
|
200
228
|
})]
|
|
201
229
|
}),
|
|
202
|
-
/* @__PURE__ */
|
|
230
|
+
/* @__PURE__ */ T("div", {
|
|
203
231
|
className: "flex items-center gap-1",
|
|
204
|
-
children: [/* @__PURE__ */
|
|
232
|
+
children: [/* @__PURE__ */ w("label", {
|
|
205
233
|
htmlFor: "feature-filter-kano",
|
|
206
234
|
className: "text-xs text-muted-foreground",
|
|
207
235
|
children: "Kano"
|
|
208
|
-
}), /* @__PURE__ */
|
|
236
|
+
}), /* @__PURE__ */ T("select", {
|
|
209
237
|
id: "feature-filter-kano",
|
|
210
238
|
"data-testid": "feature-filter-kano",
|
|
211
239
|
className: "rounded-md border border-input bg-background px-2 py-1 text-xs",
|
|
212
|
-
value:
|
|
213
|
-
onChange: (e) =>
|
|
214
|
-
children: [/* @__PURE__ */
|
|
240
|
+
value: H,
|
|
241
|
+
onChange: (e) => U(e.target.value),
|
|
242
|
+
children: [/* @__PURE__ */ w("option", {
|
|
215
243
|
value: "",
|
|
216
244
|
children: "All"
|
|
217
|
-
}), u.map((e) => /* @__PURE__ */
|
|
245
|
+
}), u.map((e) => /* @__PURE__ */ w("option", {
|
|
218
246
|
value: e,
|
|
219
247
|
children: l[e].label
|
|
220
248
|
}, e))]
|
|
221
249
|
})]
|
|
222
250
|
}),
|
|
223
|
-
/* @__PURE__ */
|
|
251
|
+
/* @__PURE__ */ T("div", {
|
|
224
252
|
className: "flex items-center gap-1",
|
|
225
|
-
children: [/* @__PURE__ */
|
|
253
|
+
children: [/* @__PURE__ */ w("label", {
|
|
226
254
|
htmlFor: "feature-filter-quadrant",
|
|
227
255
|
className: "text-xs text-muted-foreground",
|
|
228
256
|
children: "Quadrant"
|
|
229
|
-
}), /* @__PURE__ */
|
|
257
|
+
}), /* @__PURE__ */ T("select", {
|
|
230
258
|
id: "feature-filter-quadrant",
|
|
231
259
|
"data-testid": "feature-filter-quadrant",
|
|
232
260
|
className: "rounded-md border border-input bg-background px-2 py-1 text-xs",
|
|
233
|
-
value:
|
|
234
|
-
onChange: (e) =>
|
|
235
|
-
children: [/* @__PURE__ */
|
|
261
|
+
value: W,
|
|
262
|
+
onChange: (e) => G(e.target.value),
|
|
263
|
+
children: [/* @__PURE__ */ w("option", {
|
|
236
264
|
value: "",
|
|
237
265
|
children: "All"
|
|
238
|
-
}),
|
|
266
|
+
}), p.map((e) => /* @__PURE__ */ w("option", {
|
|
239
267
|
value: e,
|
|
240
|
-
children:
|
|
268
|
+
children: f[e].label
|
|
241
269
|
}, e))]
|
|
242
270
|
})]
|
|
243
271
|
})
|
|
244
272
|
]
|
|
245
273
|
}),
|
|
246
|
-
/* @__PURE__ */
|
|
274
|
+
/* @__PURE__ */ w("div", {
|
|
247
275
|
className: "space-y-3",
|
|
248
|
-
children:
|
|
249
|
-
...o(() =>
|
|
250
|
-
children: /* @__PURE__ */
|
|
276
|
+
children: Q.map((e) => /* @__PURE__ */ w(O, {
|
|
277
|
+
...o(() => S(a("/products/feature-detail-page", e.id)), e.title),
|
|
278
|
+
children: /* @__PURE__ */ w(k, {
|
|
251
279
|
className: "py-4",
|
|
252
|
-
children: /* @__PURE__ */
|
|
280
|
+
children: /* @__PURE__ */ T("div", {
|
|
253
281
|
className: "flex items-start justify-between gap-4",
|
|
254
|
-
children: [/* @__PURE__ */
|
|
282
|
+
children: [/* @__PURE__ */ T("div", {
|
|
255
283
|
className: "flex-1 space-y-2",
|
|
256
284
|
children: [
|
|
257
|
-
/* @__PURE__ */
|
|
285
|
+
/* @__PURE__ */ T("div", {
|
|
258
286
|
className: "flex items-center gap-2",
|
|
259
287
|
children: [
|
|
260
|
-
/* @__PURE__ */
|
|
288
|
+
/* @__PURE__ */ w("span", {
|
|
261
289
|
className: "text-sm font-medium",
|
|
262
290
|
children: e.title
|
|
263
291
|
}),
|
|
264
|
-
/* @__PURE__ */
|
|
265
|
-
className: `text-[10px] px-1.5 py-0.5 rounded-full capitalize ${
|
|
292
|
+
/* @__PURE__ */ w("span", {
|
|
293
|
+
className: `text-[10px] px-1.5 py-0.5 rounded-full capitalize ${j[e.status] ?? "bg-gray-100"}`,
|
|
266
294
|
children: e.status.toLowerCase().replace("_", " ")
|
|
267
295
|
}),
|
|
268
|
-
/* @__PURE__ */
|
|
296
|
+
/* @__PURE__ */ w(m, {
|
|
269
297
|
quadrant: e.valueEffortQuadrant,
|
|
270
298
|
hideWhenUnscored: !0
|
|
271
299
|
})
|
|
272
300
|
]
|
|
273
301
|
}),
|
|
274
|
-
/* @__PURE__ */
|
|
302
|
+
/* @__PURE__ */ w("p", {
|
|
275
303
|
className: "text-xs text-muted-foreground",
|
|
276
304
|
children: e.description
|
|
277
305
|
}),
|
|
278
|
-
/* @__PURE__ */
|
|
306
|
+
/* @__PURE__ */ T("div", {
|
|
279
307
|
className: "flex items-center gap-4 text-xs text-muted-foreground",
|
|
280
308
|
children: [
|
|
281
|
-
/* @__PURE__ */
|
|
282
|
-
/* @__PURE__ */
|
|
283
|
-
/* @__PURE__ */
|
|
309
|
+
/* @__PURE__ */ T("span", { children: ["Effort: ", e.effortEstimate ?? "N/A"] }),
|
|
310
|
+
/* @__PURE__ */ T("span", { children: ["Submitted by: ", /* @__PURE__ */ w(n, { id: e.submittedBy })] }),
|
|
311
|
+
/* @__PURE__ */ w("span", { children: new Date(e.updatedAt).toLocaleDateString() })
|
|
284
312
|
]
|
|
285
313
|
}),
|
|
286
|
-
|
|
314
|
+
(e.rating != null || e.category) && /* @__PURE__ */ T("div", {
|
|
315
|
+
className: "flex items-center gap-2",
|
|
316
|
+
children: [e.rating != null && /* @__PURE__ */ w(g, { rating: e.rating }), /* @__PURE__ */ w(te, { category: e.category })]
|
|
317
|
+
}),
|
|
318
|
+
c(e.snoozedUntil, F) && /* @__PURE__ */ T("p", {
|
|
287
319
|
className: "text-xs text-muted-foreground",
|
|
288
320
|
children: ["Snoozed until ", new Date(e.snoozedUntil).toLocaleDateString()]
|
|
289
321
|
}),
|
|
290
|
-
/* @__PURE__ */
|
|
322
|
+
/* @__PURE__ */ w(s, {
|
|
291
323
|
feature: e,
|
|
292
|
-
now:
|
|
324
|
+
now: F
|
|
293
325
|
}),
|
|
294
|
-
e.tags.length > 0 && /* @__PURE__ */
|
|
326
|
+
e.tags.length > 0 && /* @__PURE__ */ w("div", {
|
|
295
327
|
className: "flex gap-1",
|
|
296
|
-
children: e.tags.map((e) => /* @__PURE__ */
|
|
328
|
+
children: e.tags.map((e) => /* @__PURE__ */ w(E, {
|
|
297
329
|
variant: "outline",
|
|
298
330
|
className: "text-[10px]",
|
|
299
331
|
children: e
|
|
300
332
|
}, e))
|
|
301
333
|
})
|
|
302
334
|
]
|
|
303
|
-
}), /* @__PURE__ */
|
|
335
|
+
}), /* @__PURE__ */ T("div", {
|
|
304
336
|
className: "flex items-center gap-4 shrink-0",
|
|
305
337
|
children: [
|
|
306
|
-
/* @__PURE__ */
|
|
338
|
+
/* @__PURE__ */ T("div", {
|
|
307
339
|
className: "text-center",
|
|
308
|
-
children: [/* @__PURE__ */
|
|
340
|
+
children: [/* @__PURE__ */ T("div", {
|
|
309
341
|
className: "flex items-center gap-1",
|
|
310
|
-
children: [/* @__PURE__ */
|
|
342
|
+
children: [/* @__PURE__ */ w(ae, { className: "h-3 w-3 text-primary" }), /* @__PURE__ */ w("span", {
|
|
311
343
|
className: "text-sm font-bold",
|
|
312
344
|
children: e.priorityScore ?? 0
|
|
313
345
|
})]
|
|
314
|
-
}), /* @__PURE__ */
|
|
346
|
+
}), /* @__PURE__ */ w("p", {
|
|
315
347
|
className: "text-[10px] text-muted-foreground",
|
|
316
348
|
children: "Priority"
|
|
317
349
|
})]
|
|
318
350
|
}),
|
|
319
|
-
/* @__PURE__ */
|
|
351
|
+
/* @__PURE__ */ T("div", {
|
|
320
352
|
className: "text-center",
|
|
321
|
-
children: [/* @__PURE__ */
|
|
353
|
+
children: [/* @__PURE__ */ T("div", {
|
|
322
354
|
className: "flex items-center gap-1",
|
|
323
|
-
children: [/* @__PURE__ */
|
|
355
|
+
children: [/* @__PURE__ */ w(x, { className: "h-3 w-3 text-green-500" }), /* @__PURE__ */ w("span", {
|
|
324
356
|
className: "text-sm font-bold",
|
|
325
357
|
children: e.voteCount
|
|
326
358
|
})]
|
|
327
|
-
}), /* @__PURE__ */
|
|
359
|
+
}), /* @__PURE__ */ w("p", {
|
|
328
360
|
className: "text-[10px] text-muted-foreground",
|
|
329
361
|
children: "Votes"
|
|
330
362
|
})]
|
|
331
363
|
}),
|
|
332
|
-
/* @__PURE__ */
|
|
364
|
+
/* @__PURE__ */ T("div", {
|
|
333
365
|
className: "text-center",
|
|
334
|
-
children: [/* @__PURE__ */
|
|
366
|
+
children: [/* @__PURE__ */ T("div", {
|
|
335
367
|
className: "flex items-center gap-1",
|
|
336
|
-
children: [/* @__PURE__ */
|
|
368
|
+
children: [/* @__PURE__ */ w(ie, { className: "h-3 w-3 text-yellow-500" }), /* @__PURE__ */ w("span", {
|
|
337
369
|
className: "text-sm font-bold",
|
|
338
370
|
children: e.businessValue ?? 0
|
|
339
371
|
})]
|
|
340
|
-
}), /* @__PURE__ */
|
|
372
|
+
}), /* @__PURE__ */ w("p", {
|
|
341
373
|
className: "text-[10px] text-muted-foreground",
|
|
342
374
|
children: "Value"
|
|
343
375
|
})]
|
|
376
|
+
}),
|
|
377
|
+
/* @__PURE__ */ T("div", {
|
|
378
|
+
className: "text-center",
|
|
379
|
+
title: e.riceScore == null ? h(e) ?? void 0 : void 0,
|
|
380
|
+
children: [/* @__PURE__ */ T("div", {
|
|
381
|
+
className: "flex items-center gap-1",
|
|
382
|
+
children: [/* @__PURE__ */ w(b, { className: "h-3 w-3 text-primary" }), /* @__PURE__ */ w("span", {
|
|
383
|
+
className: "text-sm font-bold",
|
|
384
|
+
"data-testid": "feature-rice-score-cell",
|
|
385
|
+
children: e.riceScore == null ? "—" : e.riceScore.toFixed(1)
|
|
386
|
+
})]
|
|
387
|
+
}), /* @__PURE__ */ w("p", {
|
|
388
|
+
className: "text-[10px] text-muted-foreground",
|
|
389
|
+
children: "RICE"
|
|
390
|
+
})]
|
|
344
391
|
})
|
|
345
392
|
]
|
|
346
393
|
})]
|
|
@@ -348,14 +395,14 @@ function N() {
|
|
|
348
395
|
})
|
|
349
396
|
}, e.id))
|
|
350
397
|
}),
|
|
351
|
-
|
|
398
|
+
Q.length === 0 && /* @__PURE__ */ w("p", {
|
|
352
399
|
className: "text-center text-muted-foreground py-8",
|
|
353
|
-
children:
|
|
400
|
+
children: N === "TRIAGE" ? "Nothing waits for triage." : "No features match the selected filters."
|
|
354
401
|
})
|
|
355
402
|
]
|
|
356
403
|
});
|
|
357
404
|
}
|
|
358
405
|
//#endregion
|
|
359
|
-
export {
|
|
406
|
+
export { M as ProductsFeaturesPagePage };
|
|
360
407
|
|
|
361
408
|
//# sourceMappingURL=ProductsFeaturesPagePage.js.map
|