mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-03-01 15:03:57 +08:00
Add E2E tests for internationalization across multiple pages
- Implemented navigation.spec.ts to test language switching and translation of navigation elements. - Created sessions-page.spec.ts to verify translations on the sessions page, including headers, status badges, and date formatting. - Developed settings-page.spec.ts to ensure settings page content is translated and persists across sessions. - Added skills-page.spec.ts to validate translations for skill categories, action buttons, and empty states.
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
// Card component for displaying issues with actions
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
import {
|
||||
AlertCircle,
|
||||
AlertTriangle,
|
||||
@@ -41,31 +42,64 @@ export interface IssueCardProps {
|
||||
|
||||
// ========== Priority Helpers ==========
|
||||
|
||||
const priorityConfig: Record<Issue['priority'], { icon: React.ElementType; color: string; label: string }> = {
|
||||
critical: { icon: AlertCircle, color: 'destructive', label: 'Critical' },
|
||||
high: { icon: AlertTriangle, color: 'warning', label: 'High' },
|
||||
medium: { icon: Info, color: 'info', label: 'Medium' },
|
||||
low: { icon: Info, color: 'secondary', label: 'Low' },
|
||||
// Priority icon and color configuration (without labels for i18n)
|
||||
const priorityVariantConfig: Record<Issue['priority'], { icon: React.ElementType; color: string }> = {
|
||||
critical: { icon: AlertCircle, color: 'destructive' },
|
||||
high: { icon: AlertTriangle, color: 'warning' },
|
||||
medium: { icon: Info, color: 'info' },
|
||||
low: { icon: Info, color: 'secondary' },
|
||||
};
|
||||
|
||||
const statusConfig: Record<Issue['status'], { icon: React.ElementType; color: string; label: string }> = {
|
||||
open: { icon: AlertCircle, color: 'info', label: 'Open' },
|
||||
in_progress: { icon: Clock, color: 'warning', label: 'In Progress' },
|
||||
resolved: { icon: CheckCircle, color: 'success', label: 'Resolved' },
|
||||
closed: { icon: XCircle, color: 'muted', label: 'Closed' },
|
||||
completed: { icon: CheckCircle, color: 'success', label: 'Completed' },
|
||||
// Priority label keys for i18n
|
||||
const priorityLabelKeys: Record<Issue['priority'], string> = {
|
||||
critical: 'issues.priority.critical',
|
||||
high: 'issues.priority.high',
|
||||
medium: 'issues.priority.medium',
|
||||
low: 'issues.priority.low',
|
||||
};
|
||||
|
||||
// Status icon and color configuration (without labels for i18n)
|
||||
const statusVariantConfig: Record<Issue['status'], { icon: React.ElementType; color: string }> = {
|
||||
open: { icon: AlertCircle, color: 'info' },
|
||||
in_progress: { icon: Clock, color: 'warning' },
|
||||
resolved: { icon: CheckCircle, color: 'success' },
|
||||
closed: { icon: XCircle, color: 'muted' },
|
||||
completed: { icon: CheckCircle, color: 'success' },
|
||||
};
|
||||
|
||||
// Status label keys for i18n
|
||||
const statusLabelKeys: Record<Issue['status'], string> = {
|
||||
open: 'issues.status.open',
|
||||
in_progress: 'issues.status.inProgress',
|
||||
resolved: 'issues.status.resolved',
|
||||
closed: 'issues.status.closed',
|
||||
completed: 'issues.status.completed',
|
||||
};
|
||||
|
||||
// ========== Priority Badge ==========
|
||||
|
||||
export function PriorityBadge({ priority }: { priority: Issue['priority'] }) {
|
||||
const config = priorityConfig[priority];
|
||||
const { formatMessage } = useIntl();
|
||||
const config = priorityVariantConfig[priority];
|
||||
|
||||
// Defensive check: handle unknown priority values
|
||||
if (!config) {
|
||||
return (
|
||||
<Badge variant="secondary" className="gap-1">
|
||||
{priority}
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
const Icon = config.icon;
|
||||
const label = priorityLabelKeys[priority]
|
||||
? formatMessage({ id: priorityLabelKeys[priority] })
|
||||
: priority;
|
||||
|
||||
return (
|
||||
<Badge variant={config.color as 'default' | 'secondary' | 'destructive' | 'outline'} className="gap-1">
|
||||
<Icon className="w-3 h-3" />
|
||||
{config.label}
|
||||
{label}
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
@@ -73,13 +107,27 @@ export function PriorityBadge({ priority }: { priority: Issue['priority'] }) {
|
||||
// ========== Status Badge ==========
|
||||
|
||||
export function StatusBadge({ status }: { status: Issue['status'] }) {
|
||||
const config = statusConfig[status];
|
||||
const { formatMessage } = useIntl();
|
||||
const config = statusVariantConfig[status];
|
||||
|
||||
// Defensive check: handle unknown status values
|
||||
if (!config) {
|
||||
return (
|
||||
<Badge variant="outline" className="gap-1">
|
||||
{status}
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
const Icon = config.icon;
|
||||
const label = statusLabelKeys[status]
|
||||
? formatMessage({ id: statusLabelKeys[status] })
|
||||
: status;
|
||||
|
||||
return (
|
||||
<Badge variant="outline" className="gap-1">
|
||||
<Icon className="w-3 h-3" />
|
||||
{config.label}
|
||||
{label}
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
@@ -99,6 +147,7 @@ export function IssueCard({
|
||||
dragHandleProps,
|
||||
innerRef,
|
||||
}: IssueCardProps) {
|
||||
const { formatMessage } = useIntl();
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
|
||||
const handleClick = () => {
|
||||
@@ -176,19 +225,19 @@ export function IssueCard({
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={handleEdit}>
|
||||
<Edit className="w-4 h-4 mr-2" />
|
||||
Edit
|
||||
{formatMessage({ id: 'issues.actions.edit' })}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => onStatusChange?.(issue, 'in_progress')}>
|
||||
<Clock className="w-4 h-4 mr-2" />
|
||||
Start Progress
|
||||
{formatMessage({ id: 'issues.actions.startProgress' })}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => onStatusChange?.(issue, 'resolved')}>
|
||||
<CheckCircle className="w-4 h-4 mr-2" />
|
||||
Mark Resolved
|
||||
{formatMessage({ id: 'issues.actions.markResolved' })}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={handleDelete} className="text-destructive">
|
||||
<Trash2 className="w-4 h-4 mr-2" />
|
||||
Delete
|
||||
{formatMessage({ id: 'issues.actions.delete' })}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
@@ -228,7 +277,10 @@ export function IssueCard({
|
||||
{issue.solutions && issue.solutions.length > 0 && (
|
||||
<div className="flex items-center gap-1 mt-2 text-xs text-muted-foreground">
|
||||
<ExternalLink className="w-3 h-3" />
|
||||
{issue.solutions.length} solution{issue.solutions.length !== 1 ? 's' : ''}
|
||||
{issue.solutions.length} {formatMessage(
|
||||
{ id: 'issues.card.solutions' },
|
||||
{ count: issue.solutions.length }
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
Reference in New Issue
Block a user