Merge pull request #1817 from AndyMik90/auto-claude/226-add-archive-button-to-done-tasks

auto-claude: 226-add-archive-button-to-done-tasks
This commit is contained in:
Andy
2026-02-20 11:50:21 +01:00
committed by GitHub
11 changed files with 301 additions and 84 deletions
@@ -1,4 +1,6 @@
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Archive } from 'lucide-react';
import { RoadmapGenerationProgress } from './RoadmapGenerationProgress';
import { CompetitorAnalysisDialog } from './CompetitorAnalysisDialog';
import { ExistingCompetitorAnalysisDialog } from './ExistingCompetitorAnalysisDialog';
@@ -8,17 +10,30 @@ import { RoadmapHeader } from './roadmap/RoadmapHeader';
import { RoadmapEmptyState } from './roadmap/RoadmapEmptyState';
import { RoadmapTabs } from './roadmap/RoadmapTabs';
import { FeatureDetailPanel } from './roadmap/FeatureDetailPanel';
import {
AlertDialog,
AlertDialogContent,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogCancel,
AlertDialogAction,
} from './ui/alert-dialog';
import { useRoadmapData, useFeatureActions, useRoadmapGeneration, useRoadmapSave, useFeatureDelete } from './roadmap/hooks';
import { getCompetitorInsightsForFeature } from './roadmap/utils';
import type { RoadmapFeature } from '../../shared/types';
import type { RoadmapProps } from './roadmap/types';
export function Roadmap({ projectId, onGoToTask }: RoadmapProps) {
const { t } = useTranslation('common');
// State management
const [selectedFeature, setSelectedFeature] = useState<RoadmapFeature | null>(null);
const [activeTab, setActiveTab] = useState('kanban');
const [showAddFeatureDialog, setShowAddFeatureDialog] = useState(false);
const [showCompetitorViewer, setShowCompetitorViewer] = useState(false);
const [pendingArchiveFeatureId, setPendingArchiveFeatureId] = useState<string | null>(null);
// Custom hooks
const { roadmap, competitorAnalysis, generationStatus } = useRoadmapData(projectId);
@@ -54,6 +69,22 @@ export function Roadmap({ projectId, onGoToTask }: RoadmapProps) {
}
};
const handleArchiveFeature = (featureId: string) => {
setPendingArchiveFeatureId(featureId);
};
const confirmArchiveFeature = async () => {
if (!pendingArchiveFeatureId) return;
try {
await deleteFeature(pendingArchiveFeatureId);
if (selectedFeature?.id === pendingArchiveFeatureId) {
setSelectedFeature(null);
}
} finally {
setPendingArchiveFeatureId(null);
}
};
// Show generation progress
if (generationStatus.phase !== 'idle' && generationStatus.phase !== 'complete') {
return (
@@ -116,6 +147,7 @@ export function Roadmap({ projectId, onGoToTask }: RoadmapProps) {
onConvertToSpec={handleConvertToSpec}
onGoToTask={handleGoToTask}
onSave={saveRoadmap}
onArchive={handleArchiveFeature}
/>
</div>
@@ -127,6 +159,7 @@ export function Roadmap({ projectId, onGoToTask }: RoadmapProps) {
onConvertToSpec={handleConvertToSpec}
onGoToTask={handleGoToTask}
onDelete={deleteFeature}
onArchive={handleArchiveFeature}
competitorInsights={getCompetitorInsightsForFeature(selectedFeature, competitorAnalysis)}
/>
)}
@@ -165,6 +198,34 @@ export function Roadmap({ projectId, onGoToTask }: RoadmapProps) {
open={showAddFeatureDialog}
onOpenChange={setShowAddFeatureDialog}
/>
{/* Archive Confirmation Dialog */}
<AlertDialog
open={!!pendingArchiveFeatureId}
onOpenChange={(open) => { if (!open) setPendingArchiveFeatureId(null); }}
>
<AlertDialogContent>
<AlertDialogHeader>
<div className="flex items-center gap-2">
<Archive className="h-5 w-5 text-muted-foreground" />
<AlertDialogTitle>{t('roadmap.archiveFeatureConfirmTitle')}</AlertDialogTitle>
</div>
<AlertDialogDescription>
{t('roadmap.archiveFeatureConfirmDescription', {
title: pendingArchiveFeatureId
? roadmap.features.find((f) => f.id === pendingArchiveFeatureId)?.title ?? ''
: '',
})}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>{t('buttons.cancel')}</AlertDialogCancel>
<AlertDialogAction onClick={confirmArchiveFeature}>
{t('roadmap.archiveFeature')}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
);
}
@@ -36,6 +36,7 @@ interface RoadmapKanbanViewProps {
onConvertToSpec?: (feature: RoadmapFeature) => void;
onGoToTask?: (specId: string) => void;
onSave?: () => void;
onArchive?: (featureId: string) => void;
}
interface DroppableStatusColumnProps {
@@ -45,6 +46,7 @@ interface DroppableStatusColumnProps {
onFeatureClick: (feature: RoadmapFeature) => void;
onConvertToSpec?: (feature: RoadmapFeature) => void;
onGoToTask?: (specId: string) => void;
onArchive?: (featureId: string) => void;
isOver: boolean;
}
@@ -71,6 +73,7 @@ function DroppableStatusColumn({
onFeatureClick,
onConvertToSpec,
onGoToTask,
onArchive,
isOver
}: DroppableStatusColumnProps) {
const { setNodeRef } = useDroppable({
@@ -158,6 +161,7 @@ function DroppableStatusColumn({
onClick={() => onFeatureClick(feature)}
onConvertToSpec={onConvertToSpec}
onGoToTask={onGoToTask}
onArchive={onArchive}
/>
))
)}
@@ -174,7 +178,8 @@ export function RoadmapKanbanView({
onFeatureClick,
onConvertToSpec,
onGoToTask,
onSave
onSave,
onArchive
}: RoadmapKanbanViewProps) {
const [activeFeature, setActiveFeature] = useState<RoadmapFeature | null>(null);
const [overColumnId, setOverColumnId] = useState<string | null>(null);
@@ -300,6 +305,7 @@ export function RoadmapKanbanView({
onFeatureClick={onFeatureClick}
onConvertToSpec={onConvertToSpec}
onGoToTask={onGoToTask}
onArchive={onArchive}
isOver={overColumnId === column.id}
/>
))}
@@ -9,7 +9,8 @@ import {
TooltipContent,
TooltipTrigger
} from './ui/tooltip';
import { Play, ExternalLink, TrendingUp, Layers, ThumbsUp } from 'lucide-react';
import { Play, ExternalLink, TrendingUp, Layers, ThumbsUp, Archive } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { TaskOutcomeBadge, getTaskOutcomeColorClass } from './roadmap/TaskOutcomeBadge';
import {
ROADMAP_PRIORITY_COLORS,
@@ -25,6 +26,7 @@ interface SortableFeatureCardProps {
onClick: () => void;
onConvertToSpec?: (feature: RoadmapFeature) => void;
onGoToTask?: (specId: string) => void;
onArchive?: (featureId: string) => void;
}
export function SortableFeatureCard({
@@ -32,8 +34,10 @@ export function SortableFeatureCard({
roadmap,
onClick,
onConvertToSpec,
onGoToTask
onGoToTask,
onArchive
}: SortableFeatureCardProps) {
const { t } = useTranslation('common');
const {
attributes,
listeners,
@@ -120,7 +124,7 @@ export function SortableFeatureCard({
</div>
<h3 className="font-medium text-sm leading-snug line-clamp-2">{feature.title}</h3>
</div>
<div className="shrink-0">
<div className="shrink-0 flex items-center gap-1">
{feature.taskOutcome ? (
<Badge
variant="outline"
@@ -139,7 +143,7 @@ export function SortableFeatureCard({
}}
>
<ExternalLink className="h-3 w-3 mr-1" />
Task
{t('roadmap.task')}
</Button>
) : (
feature.status !== 'done' &&
@@ -154,10 +158,25 @@ export function SortableFeatureCard({
}}
>
<Play className="h-3 w-3 mr-1" />
Build
{t('roadmap.build')}
</Button>
)
)}
{feature.status === 'done' && onArchive && (
<Button
variant="ghost"
size="sm"
className="h-7 px-2"
title={t('roadmap.archiveFeature')}
aria-label={t('accessibility.archiveFeatureAriaLabel')}
onClick={(e) => {
e.stopPropagation();
onArchive(feature.id);
}}
>
<Archive className="h-3 w-3" />
</Button>
)}
</div>
</div>
@@ -1,4 +1,5 @@
import { ExternalLink, Play, TrendingUp } from 'lucide-react';
import { Archive, ExternalLink, Play, TrendingUp } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { TaskOutcomeBadge, getTaskOutcomeColorClass } from './TaskOutcomeBadge';
import { Badge } from '../ui/badge';
import { Button } from '../ui/button';
@@ -17,8 +18,10 @@ export function FeatureCard({
onClick,
onConvertToSpec,
onGoToTask,
onArchive,
hasCompetitorInsight = false,
}: FeatureCardProps) {
const { t } = useTranslation('common');
return (
<Card className="p-4 hover:bg-muted/50 cursor-pointer transition-colors" onClick={onClick}>
@@ -55,37 +58,53 @@ export function FeatureCard({
<h3 className="font-medium">{feature.title}</h3>
<p className="text-sm text-muted-foreground line-clamp-2">{feature.description}</p>
</div>
{feature.taskOutcome ? (
<Badge variant="outline" className={`text-xs ${getTaskOutcomeColorClass(feature.taskOutcome)}`}>
<TaskOutcomeBadge outcome={feature.taskOutcome} size="md" />
</Badge>
) : feature.linkedSpecId ? (
<Button
variant="outline"
size="sm"
onClick={(e) => {
e.stopPropagation();
onGoToTask(feature.linkedSpecId!);
}}
>
<ExternalLink className="h-3 w-3 mr-1" />
Go to Task
</Button>
) : (
feature.status !== 'done' && (
<div className="flex items-center gap-1">
{feature.taskOutcome ? (
<Badge variant="outline" className={`text-xs ${getTaskOutcomeColorClass(feature.taskOutcome)}`}>
<TaskOutcomeBadge outcome={feature.taskOutcome} size="md" />
</Badge>
) : feature.linkedSpecId ? (
<Button
variant="outline"
size="sm"
onClick={(e) => {
e.stopPropagation();
onConvertToSpec(feature);
onGoToTask(feature.linkedSpecId!);
}}
>
<Play className="h-3 w-3 mr-1" />
Build
<ExternalLink className="h-3 w-3 mr-1" />
{t('roadmap.goToTask')}
</Button>
)
)}
) : (
feature.status !== 'done' && (
<Button
variant="outline"
size="sm"
onClick={(e) => {
e.stopPropagation();
onConvertToSpec(feature);
}}
>
<Play className="h-3 w-3 mr-1" />
{t('roadmap.build')}
</Button>
)
)}
{feature.status === 'done' && onArchive && (
<Button
variant="ghost"
size="sm"
title={t('roadmap.archiveFeature')}
aria-label={t('accessibility.archiveFeatureAriaLabel')}
onClick={(e) => {
e.stopPropagation();
onArchive(feature.id);
}}
>
<Archive className="h-3 w-3" />
</Button>
)}
</div>
</div>
</Card>
);
@@ -11,6 +11,7 @@ import {
ExternalLink,
TrendingUp,
Trash2,
Archive,
} from 'lucide-react';
import { TaskOutcomeBadge } from './TaskOutcomeBadge';
import { Badge } from '../ui/badge';
@@ -31,11 +32,16 @@ export function FeatureDetailPanel({
onConvertToSpec,
onGoToTask,
onDelete,
onArchive,
competitorInsights = [],
}: FeatureDetailPanelProps) {
const { t } = useTranslation('common');
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
const handleArchive = () => {
onArchive?.(feature.id);
};
const handleDelete = () => {
if (onDelete) {
onDelete(feature.id);
@@ -215,29 +221,53 @@ export function FeatureDetailPanel({
</ScrollArea>
{/* Actions */}
{feature.taskOutcome ? (
<div className="shrink-0 p-4 border-t border-border">
<div className="flex items-center justify-center gap-2 py-2">
<TaskOutcomeBadge outcome={feature.taskOutcome} size="lg" />
</div>
</div>
) : feature.linkedSpecId ? (
<div className="shrink-0 p-4 border-t border-border">
<Button className="w-full" onClick={() => onGoToTask(feature.linkedSpecId!)}>
<ExternalLink className="h-4 w-4 mr-2" />
Go to Task
{(() => {
const archiveButton = feature.status === 'done' && (
<Button
variant="outline"
className="w-full"
onClick={handleArchive}
aria-label={t('accessibility.archiveFeatureAriaLabel')}
>
<Archive className="h-4 w-4 mr-2" />
{t('roadmap.archiveFeature')}
</Button>
</div>
) : (
feature.status !== 'done' && (
);
if (feature.taskOutcome) return (
<div className="shrink-0 p-4 border-t border-border space-y-3">
<div className="flex items-center justify-center gap-2 py-2">
<TaskOutcomeBadge outcome={feature.taskOutcome} size="lg" />
</div>
{archiveButton}
</div>
);
if (feature.linkedSpecId) return (
<div className="shrink-0 p-4 border-t border-border space-y-2">
<Button className="w-full" onClick={() => onGoToTask(feature.linkedSpecId!)}>
<ExternalLink className="h-4 w-4 mr-2" />
{t('roadmap.goToTask')}
</Button>
{archiveButton}
</div>
);
if (feature.status === 'done') return (
<div className="shrink-0 p-4 border-t border-border">
{archiveButton}
</div>
);
return (
<div className="shrink-0 p-4 border-t border-border">
<Button className="w-full" onClick={() => onConvertToSpec(feature)}>
<Zap className="h-4 w-4 mr-2" />
Convert to Auto-Build Task
{t('roadmap.convertToTask')}
</Button>
</div>
)
)}
);
})()}
{/* Delete Confirmation */}
{showDeleteConfirm && (
@@ -1,5 +1,5 @@
import { useState } from 'react';
import { CheckCircle2, ChevronDown, ChevronUp, Circle, ExternalLink, Play, TrendingUp } from 'lucide-react';
import { Archive, CheckCircle2, ChevronDown, ChevronUp, Circle, ExternalLink, Play, TrendingUp } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { TaskOutcomeBadge } from './TaskOutcomeBadge';
import { Badge } from '../ui/badge';
@@ -18,6 +18,7 @@ export function PhaseCard({
onFeatureSelect,
onConvertToSpec,
onGoToTask,
onArchive,
}: PhaseCardProps) {
const { t } = useTranslation('common');
const [isExpanded, setIsExpanded] = useState(false);
@@ -96,7 +97,24 @@ export function PhaseCard({
<div>
<h4 className="text-sm font-medium mb-2">Features ({features.length})</h4>
<div className="grid gap-2">
{visibleFeatures.map((feature) => (
{visibleFeatures.map((feature) => {
const isDone = feature.status === 'done';
const archiveButton = isDone && onArchive && (
<Button
variant="ghost"
size="sm"
className="h-6 px-2"
title={t('roadmap.archiveFeature')}
aria-label={t('accessibility.archiveFeatureAriaLabel')}
onClick={(e) => {
e.stopPropagation();
onArchive(feature.id);
}}
>
<Archive className="h-3 w-3" />
</Button>
);
return (
<div
key={feature.id}
className="flex items-center justify-between p-2 rounded-md bg-muted/50 hover:bg-muted transition-colors"
@@ -118,11 +136,15 @@ export function PhaseCard({
)}
</button>
{feature.taskOutcome ? (
<span className="flex-shrink-0">
<span className="flex items-center gap-1 flex-shrink-0">
<TaskOutcomeBadge outcome={feature.taskOutcome} size="lg" showLabel={false} />
{archiveButton}
</span>
) : isDone ? (
<span className="flex items-center gap-1 flex-shrink-0">
<CheckCircle2 className="h-4 w-4 text-success" />
{archiveButton}
</span>
) : feature.status === 'done' ? (
<CheckCircle2 className="h-4 w-4 text-success flex-shrink-0" />
) : feature.linkedSpecId ? (
<Button
variant="ghost"
@@ -134,7 +156,7 @@ export function PhaseCard({
}}
>
<ExternalLink className="h-3 w-3 mr-1" />
View Task
{t('roadmap.viewTask')}
</Button>
) : (
<Button
@@ -147,11 +169,12 @@ export function PhaseCard({
}}
>
<Play className="h-3 w-3 mr-1" />
Build
{t('roadmap.build')}
</Button>
)}
</div>
))}
);
})}
{hasMoreFeatures && (
<Button
type="button"
@@ -1,5 +1,7 @@
import { TrendingUp } from 'lucide-react';
import { Archive, TrendingUp, CheckCircle2 } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { Badge } from '../ui/badge';
import { Button } from '../ui/button';
import { Card } from '../ui/card';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '../ui/tabs';
import { PhaseCard } from './PhaseCard';
@@ -23,8 +25,10 @@ export function RoadmapTabs({
onFeatureSelect,
onConvertToSpec,
onGoToTask,
onArchive,
onSave,
}: RoadmapTabsProps) {
const { t } = useTranslation('common');
return (
<Tabs value={activeTab} onValueChange={onTabChange} className="h-full flex flex-col">
<TabsList className="shrink-0 mx-4 mt-4">
@@ -42,6 +46,7 @@ export function RoadmapTabs({
onFeatureClick={onFeatureSelect}
onConvertToSpec={onConvertToSpec}
onGoToTask={onGoToTask}
onArchive={onArchive}
onSave={onSave}
/>
</TabsContent>
@@ -58,6 +63,7 @@ export function RoadmapTabs({
onFeatureSelect={onFeatureSelect}
onConvertToSpec={onConvertToSpec}
onGoToTask={onGoToTask}
onArchive={onArchive}
/>
))}
</div>
@@ -73,6 +79,7 @@ export function RoadmapTabs({
onClick={() => onFeatureSelect(feature)}
onConvertToSpec={onConvertToSpec}
onGoToTask={onGoToTask}
onArchive={onArchive}
hasCompetitorInsight={hasCompetitorInsight(feature)}
/>
))}
@@ -93,35 +100,64 @@ export function RoadmapTabs({
<span className="text-sm text-muted-foreground">{features.length} features</span>
</div>
<div className="space-y-2">
{features.map((feature: RoadmapFeature) => (
<div
key={feature.id}
className="p-2 rounded-md bg-muted/50 hover:bg-muted cursor-pointer transition-colors"
onClick={() => onFeatureSelect(feature)}
>
<div className="font-medium text-sm">{feature.title}</div>
<div className="flex items-center gap-2 mt-1 flex-wrap">
<Badge
variant="outline"
className={`text-xs ${ROADMAP_COMPLEXITY_COLORS[feature.complexity]}`}
{features.map((feature: RoadmapFeature) => {
const isDone = feature.status === 'done';
return (
<div
key={feature.id}
className="p-2 rounded-md bg-muted/50 hover:bg-muted transition-colors"
>
<button
type="button"
className="w-full text-left cursor-pointer rounded-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
onClick={() => onFeatureSelect(feature)}
>
{feature.complexity}
</Badge>
<Badge
variant="outline"
className={`text-xs ${ROADMAP_IMPACT_COLORS[feature.impact]}`}
>
{feature.impact} impact
</Badge>
{hasCompetitorInsight(feature) && (
<Badge variant="outline" className="text-xs text-primary border-primary/50">
<TrendingUp className="h-3 w-3 mr-1" />
Insight
</Badge>
<div className="font-medium text-sm">{feature.title}</div>
<div className="flex items-center gap-2 mt-1 flex-wrap">
<Badge
variant="outline"
className={`text-xs ${ROADMAP_COMPLEXITY_COLORS[feature.complexity]}`}
>
{feature.complexity}
</Badge>
<Badge
variant="outline"
className={`text-xs ${ROADMAP_IMPACT_COLORS[feature.impact]}`}
>
{feature.impact} impact
</Badge>
{hasCompetitorInsight(feature) && (
<Badge variant="outline" className="text-xs text-primary border-primary/50">
<TrendingUp className="h-3 w-3 mr-1" />
Insight
</Badge>
)}
</div>
</button>
{isDone && onArchive && (
<div className="flex items-center justify-between mt-2 pt-2 border-t border-border/50">
<span className="flex items-center gap-1 text-xs text-muted-foreground">
<CheckCircle2 className="h-3 w-3 text-success" />
Completed
</span>
<Button
variant="ghost"
size="sm"
className="h-6 px-2"
title={t('roadmap.archiveFeature')}
onClick={(e) => {
e.stopPropagation();
onArchive(feature.id);
}}
>
<Archive className="h-3 w-3 mr-1" />
Archive
</Button>
</div>
)}
</div>
</div>
))}
);
})}
</div>
</Card>
);
@@ -114,6 +114,7 @@ export function useFeatureDelete(projectId: string) {
return { deleteFeature: handleDeleteFeature };
}
/**
* Hook to manage roadmap generation actions
*
@@ -12,6 +12,7 @@ export interface PhaseCardProps {
onFeatureSelect: (feature: RoadmapFeature) => void;
onConvertToSpec: (feature: RoadmapFeature) => void;
onGoToTask: (specId: string) => void;
onArchive?: (featureId: string) => void;
}
export interface FeatureCardProps {
@@ -20,6 +21,7 @@ export interface FeatureCardProps {
onConvertToSpec: (feature: RoadmapFeature) => void;
onGoToTask: (specId: string) => void;
hasCompetitorInsight?: boolean;
onArchive?: (featureId: string) => void;
}
export interface FeatureDetailPanelProps {
@@ -28,6 +30,7 @@ export interface FeatureDetailPanelProps {
onConvertToSpec: (feature: RoadmapFeature) => void;
onGoToTask: (specId: string) => void;
onDelete?: (featureId: string) => void;
onArchive?: (featureId: string) => void;
competitorInsights?: CompetitorPainPoint[];
}
@@ -51,4 +54,5 @@ export interface RoadmapTabsProps {
onConvertToSpec: (feature: RoadmapFeature) => void;
onGoToTask: (specId: string) => void;
onSave?: () => void;
onArchive?: (featureId: string) => void;
}
@@ -29,6 +29,7 @@
},
"accessibility": {
"deleteFeatureAriaLabel": "Delete feature",
"archiveFeatureAriaLabel": "Archive feature",
"closeFeatureDetailsAriaLabel": "Close feature details",
"regenerateRoadmapAriaLabel": "Regenerate Roadmap",
"repositoryOwnerAriaLabel": "Repository owner",
@@ -625,7 +626,15 @@
"taskArchived": "Archived",
"showMoreFeatures": "Show {{count}} more feature",
"showMoreFeatures_plural": "Show {{count}} more features",
"showLessFeatures": "Show less"
"showLessFeatures": "Show less",
"archiveFeature": "Archive",
"archiveFeatureConfirmTitle": "Archive Feature?",
"archiveFeatureConfirmDescription": "This will remove \"{{title}}\" from your roadmap.",
"goToTask": "Go to Task",
"convertToTask": "Convert to Auto-Build Task",
"build": "Build",
"task": "Task",
"viewTask": "View Task"
},
"roadmapGeneration": {
"progress": "Progress",
@@ -29,6 +29,7 @@
},
"accessibility": {
"deleteFeatureAriaLabel": "Supprimer la fonctionnalité",
"archiveFeatureAriaLabel": "Archiver la fonctionnalité",
"closeFeatureDetailsAriaLabel": "Fermer les détails de la fonctionnalité",
"regenerateRoadmapAriaLabel": "Régénérer la feuille de route",
"repositoryOwnerAriaLabel": "Propriétaire du dépôt",
@@ -625,7 +626,15 @@
"taskArchived": "Archivé",
"showMoreFeatures": "Afficher {{count}} fonctionnalité supplémentaire",
"showMoreFeatures_plural": "Afficher {{count}} fonctionnalités supplémentaires",
"showLessFeatures": "Afficher moins"
"showLessFeatures": "Afficher moins",
"archiveFeature": "Archiver",
"archiveFeatureConfirmTitle": "Archiver la fonctionnalité ?",
"archiveFeatureConfirmDescription": "Cela supprimera \"{{title}}\" de votre feuille de route.",
"goToTask": "Aller à la tâche",
"convertToTask": "Convertir en tâche Auto-Build",
"build": "Construire",
"task": "Tâche",
"viewTask": "Voir la tâche"
},
"roadmapGeneration": {
"progress": "Progression",