Comprehensive UX audit fixes: navigation, feedback, affordances, and accessibility
CI / build-and-push (push) Successful in 28s
CI / build-and-push (push) Successful in 28s
Address 18 issues across high/medium/low impact tiers identified in a full interface review. Key changes: Models page decomposed into tabs, confirmation dialogs for irreversible actions (deploy/open-source/acquire), chart Y-axes made visible, hash router extended for Market tab persistence, collapsible sidebar, keyboard navigation shortcuts (g+key chords), notification bulk actions, achievement progress bars, and ARIA label improvements. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
import { useState } from 'react';
|
||||
import { Swords, TrendingUp, Shield, Users, Brain, ShoppingCart } from 'lucide-react';
|
||||
import { useGameStore } from '@/store';
|
||||
import { ConfirmModal } from '@/components/common/ConfirmModal';
|
||||
import { Tooltip } from '@/components/common/Tooltip';
|
||||
import { formatMoney, formatNumber } from '@ai-tycoon/shared';
|
||||
import type { Era } from '@ai-tycoon/shared';
|
||||
|
||||
@@ -27,6 +30,7 @@ export function CompetitorsPage() {
|
||||
const money = useGameStore((s) => s.economy.money);
|
||||
const acquireCompetitor = useGameStore((s) => s.acquireCompetitor);
|
||||
const canAcquire = (era: Era) => era === 'bigtech' || era === 'agi';
|
||||
const [acquireConfirm, setAcquireConfirm] = useState<{ id: string; name: string; cost: number } | null>(null);
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
@@ -55,10 +59,15 @@ export function CompetitorsPage() {
|
||||
key={rival.id}
|
||||
className="absolute top-0 h-full w-0.5 bg-danger"
|
||||
style={{ left: `${rival.estimatedCapability}%` }}
|
||||
title={`${rival.name}: ${rival.estimatedCapability.toFixed(1)}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex items-center gap-4 mt-1.5 text-[10px]">
|
||||
<span className="flex items-center gap-1"><span className="w-2 h-2 rounded-full bg-accent inline-block" />You: {playerBest.toFixed(1)}</span>
|
||||
{rivals.filter(r => r.status === 'active').map(rival => (
|
||||
<span key={rival.id} className="flex items-center gap-1"><span className="w-2 h-0.5 bg-danger inline-block" />{rival.name}: {rival.estimatedCapability.toFixed(1)}</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -84,7 +93,7 @@ export function CompetitorsPage() {
|
||||
return (
|
||||
<div className="text-right">
|
||||
<button
|
||||
onClick={() => acquireCompetitor(rival.id)}
|
||||
onClick={() => setAcquireConfirm({ id: rival.id, name: rival.name, cost })}
|
||||
disabled={money < cost}
|
||||
className="flex items-center gap-1 bg-blue-600/20 hover:bg-blue-600/30 text-blue-400 border border-blue-600/30 rounded px-3 py-1.5 text-xs disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
>
|
||||
@@ -134,6 +143,17 @@ export function CompetitorsPage() {
|
||||
<p>No competitors detected yet.</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{acquireConfirm && (
|
||||
<ConfirmModal
|
||||
title="Acquire Competitor"
|
||||
message={`Acquire "${acquireConfirm.name}" for ${formatMoney(acquireConfirm.cost)}? This will absorb their users and technology. This cannot be undone.`}
|
||||
confirmLabel={`Acquire (${formatMoney(acquireConfirm.cost)})`}
|
||||
danger
|
||||
onConfirm={() => { acquireCompetitor(acquireConfirm.id); setAcquireConfirm(null); }}
|
||||
onCancel={() => setAcquireConfirm(null)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user