Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5,316 changes: 5,316 additions & 0 deletions frontend/pnpm-lock.yaml

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions frontend/pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
allowBuilds:
esbuild: true
Binary file added frontend/public/assets/icons/icon_attack_new.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/assets/icons/icon_bipndip_new.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/assets/icons/icon_planner_new.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/assets/icons/icon_raid_new.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/assets/icons/icon_trap_new.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ const CocaineCrush = React.lazy(() => import('./components/cocaine-crush/Co
const WeeklyUpdateRoute = React.lazy(() => import('./components/news/WeeklyUpdateRoute'));
const PhoneApp = React.lazy(() => import('./components/phone/PhoneApp'));
const AttackPlanner = React.lazy(() => import('./components/missions/AttackPlanner'));
const TrapApp = React.lazy(() => import('./components/trap/TrapApp'));

import './App.css';

Expand Down Expand Up @@ -179,6 +180,7 @@ const App: React.FC = () => {
case 'leaderboard': return <Suspense fallback={<LazyFallback />}><Leaderboard key="leaderboard" /></Suspense>;
case 'news': return <Suspense fallback={<LazyFallback />}><WeeklyUpdateRoute key="news" /></Suspense>;
case 'phone': return <Suspense fallback={<LazyFallback />}><PhoneApp key="phone" /></Suspense>;
case 'trap': return <Suspense fallback={<LazyFallback />}><TrapApp key="trap" /></Suspense>;
default:
return (
<OSShell
Expand Down
11 changes: 9 additions & 2 deletions frontend/src/components/common/GameSprite.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,15 @@ const APP_ICONS: Record<string, string> = {
driveby: '/assets/icons/icon_driveby_new.png',
graffiti: '/assets/icons/icon_graffiti_new.png',
news: '/assets/generated/ui/icons/icon_news_v001.png',
leaderboard: '/assets/icons/icon_slide_new.png', // fallback until dedicated icon
cocaine_crush: '/assets/icons/icon_casino_new.png', // fallback until dedicated icon
// Sprint: trap-app-icon-deemoji — dedicated icons replacing fallbacks
leaderboard: '/assets/icons/icon_leaderboard_new.png',
cocaine_crush: '/assets/icons/icon_casino_new.png',
trap: '/assets/icons/icon_trap_new.png',
contacts: '/assets/icons/icon_contacts_new.png',
bipndip: '/assets/icons/icon_bipndip_new.png',
attack: '/assets/icons/icon_attack_new.png',
planner: '/assets/icons/icon_planner_new.png',
raid: '/assets/icons/icon_raid_new.png',
};

// Role-to-frame mapping for gang members
Expand Down
94 changes: 62 additions & 32 deletions frontend/src/components/layout/OSShell.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// ============================================================
// OSShell - iOS-Style Desktop Interface for DEALT/SLIDE
// Live Dashboard: Heat meter, Morale indicator, Income ticker
// Sprint: trap-app-icon-deemoji — emoji purge + Trap app added
// ============================================================

import React, { useState, useMemo } from 'react';
Expand All @@ -20,8 +21,8 @@ import './OSShell.css';
interface AppIcon {
id: string;
label: string;
icon: string;
spriteIcon?: string; // GameSprite icon key
icon: string; // text fallback only — never rendered as emoji in UI
spriteIcon?: string; // GameSprite icon key (preferred)
colorClass: string;
available: boolean;
badge?: number | string;
Expand Down Expand Up @@ -60,7 +61,7 @@ const OSShell: React.FC<OSShellProps> = ({ gangMorale = 75, incomePerMinute = 0
{
id: 'map',
label: 'MAP',
icon: '📍',
icon: 'MAP',
spriteIcon: 'map',
colorClass: 'app-green',
available: true,
Expand All @@ -69,17 +70,16 @@ const OSShell: React.FC<OSShellProps> = ({ gangMorale = 75, incomePerMinute = 0
{
id: 'dealt_v2',
label: 'DEALT',
icon: '💊',
icon: 'DEALT',
spriteIcon: 'dealt',
colorClass: 'app-purple',
available: true,
badge: '🔥',
description: 'Drug Dealing',
},
{
id: 'slide',
label: 'SLIDE',
icon: '🎯',
icon: 'SLIDE',
spriteIcon: 'slide',
colorClass: 'app-red',
available: true,
Expand All @@ -88,7 +88,7 @@ const OSShell: React.FC<OSShellProps> = ({ gangMorale = 75, incomePerMinute = 0
{
id: 'driveby',
label: 'DRIVE',
icon: '🚗',
icon: 'DRIVE',
spriteIcon: 'driveby',
colorClass: 'app-orange',
available: true,
Expand All @@ -97,7 +97,7 @@ const OSShell: React.FC<OSShellProps> = ({ gangMorale = 75, incomePerMinute = 0
{
id: 'alchemy',
label: 'COOK',
icon: '⚗️',
icon: 'COOK',
spriteIcon: 'cook',
colorClass: 'app-cyan',
available: true,
Expand All @@ -106,17 +106,26 @@ const OSShell: React.FC<OSShellProps> = ({ gangMorale = 75, incomePerMinute = 0
{
id: 'gang_hq',
label: 'CREW',
icon: '👥',
icon: 'CREW',
spriteIcon: 'crew',
colorClass: 'app-blue',
available: true,
badge: crewAlerts > 0 ? crewAlerts : undefined,
description: 'Gang Management',
},
{
id: 'trap',
label: 'TRAP',
icon: 'TRAP',
spriteIcon: 'trap',
colorClass: 'app-gold',
available: true,
description: 'Stash & Inventory',
},
{
id: 'shoebox',
label: 'SHOEBOX',
icon: '💰',
icon: 'SHOEBOX',
spriteIcon: 'shoebox',
colorClass: 'app-gold',
available: true,
Expand All @@ -125,16 +134,25 @@ const OSShell: React.FC<OSShellProps> = ({ gangMorale = 75, incomePerMinute = 0
{
id: 'market',
label: 'MARKET',
icon: '🏪',
icon: 'MARKET',
spriteIcon: 'market',
colorClass: 'app-teal',
available: true,
description: 'Underworld Market',
},
{
id: 'contacts',
label: 'CONTACTS',
icon: 'CONTACTS',
spriteIcon: 'contacts',
colorClass: 'app-green',
available: true,
description: 'Gang Contacts',
},
{
id: 'missions',
label: 'OPS',
icon: '📋',
icon: 'OPS',
spriteIcon: 'ops',
colorClass: 'app-brown',
available: true,
Expand All @@ -143,7 +161,7 @@ const OSShell: React.FC<OSShellProps> = ({ gangMorale = 75, incomePerMinute = 0
{
id: 'casino',
label: 'CASINO',
icon: '🎰',
icon: 'CASINO',
spriteIcon: 'casino',
colorClass: 'app-pink',
available: true,
Expand All @@ -152,7 +170,7 @@ const OSShell: React.FC<OSShellProps> = ({ gangMorale = 75, incomePerMinute = 0
{
id: 'cocaine_crush',
label: 'CRUSH',
icon: '❄️',
icon: 'CRUSH',
spriteIcon: 'cocaine_crush',
colorClass: 'app-blue',
available: true,
Expand All @@ -161,23 +179,34 @@ const OSShell: React.FC<OSShellProps> = ({ gangMorale = 75, incomePerMinute = 0
{
id: 'bipndip',
label: 'BIP N DIP',
icon: '🚙',
icon: 'BIP',
spriteIcon: 'bipndip',
colorClass: 'app-red',
available: true,
description: 'Break into cars',
},
{
id: 'topdown',
label: 'ATTACK',
icon: '🔫',
icon: 'ATTACK',
spriteIcon: 'attack',
colorClass: 'app-red',
available: true,
description: 'Block Attack',
},
{
id: 'raid',
label: 'RAID',
icon: 'RAID',
spriteIcon: 'raid',
colorClass: 'app-red',
available: true,
description: 'Police Raid',
},
{
id: 'graffiti',
label: 'VANDALIZE',
icon: '🎨',
icon: 'TAG',
spriteIcon: 'graffiti',
colorClass: 'app-orange',
available: true,
Expand All @@ -186,7 +215,7 @@ const OSShell: React.FC<OSShellProps> = ({ gangMorale = 75, incomePerMinute = 0
{
id: 'leaderboard',
label: 'RANKINGS',
icon: '🏆',
icon: 'RANK',
spriteIcon: 'leaderboard',
colorClass: 'app-gold',
available: true,
Expand All @@ -195,7 +224,7 @@ const OSShell: React.FC<OSShellProps> = ({ gangMorale = 75, incomePerMinute = 0
{
id: 'news',
label: 'LOCAL NEWS',
icon: '📰',
icon: 'NEWS',
spriteIcon: 'news',
colorClass: 'app-cyan',
available: true,
Expand All @@ -204,23 +233,24 @@ const OSShell: React.FC<OSShellProps> = ({ gangMorale = 75, incomePerMinute = 0
{
id: 'phone',
label: 'PHONE',
icon: '📱',
icon: 'PHONE',
colorClass: 'app-green',
available: true,
description: 'Call Contacts',
},
{
id: 'planner',
label: 'OPS PLAN',
icon: '⚔️',
icon: 'PLAN',
spriteIcon: 'planner',
colorClass: 'app-red',
available: true,
description: 'Attack Planner',
},
{
id: 'settings',
label: 'SETTINGS',
icon: '⚙️',
icon: 'SETTINGS',
spriteIcon: 'settings',
colorClass: 'app-dark',
available: true,
Expand Down Expand Up @@ -267,7 +297,7 @@ const OSShell: React.FC<OSShellProps> = ({ gangMorale = 75, incomePerMinute = 0
onClick={() => setShowNotifications(!showNotifications)}
whileTap={{ scale: 0.9 }}
>
🔔
<span className="notif-bell-icon">NOTIF</span>
{getUnreadCount() > 0 && (
<span className="notification-badge">{getUnreadCount()}</span>
)}
Expand Down Expand Up @@ -318,7 +348,7 @@ const OSShell: React.FC<OSShellProps> = ({ gangMorale = 75, incomePerMinute = 0
</div>
{raidProb > 0 && (
<div className="raid-probability">
🚨 {Math.round(raidProb * 100)}% raid
{Math.round(raidProb * 100)}% RAID RISK
</div>
)}
</div>
Expand Down Expand Up @@ -442,9 +472,9 @@ const OSShell: React.FC<OSShellProps> = ({ gangMorale = 75, incomePerMinute = 0
{app.spriteIcon ? (
<GameSprite icon={app.spriteIcon as any} size={52} fallback={app.icon} />
) : (
<span className="icon-emoji">{app.icon}</span>
<span className="icon-text-fallback">{app.icon}</span>
)}
{!app.available && <div className="lock-overlay">🔒</div>}
{!app.available && <div className="lock-overlay">LOCKED</div>}
{app.badge && (
<div className={`app-badge ${typeof app.badge === 'number' ? 'app-badge-number' : ''}`}>
{app.badge}
Expand All @@ -459,16 +489,16 @@ const OSShell: React.FC<OSShellProps> = ({ gangMorale = 75, incomePerMinute = 0
{/* Quick Actions Dock */}
<div className="dock">
<motion.div className="dock-item" whileTap={{ scale: 0.9 }} onClick={() => navigateTo('dealt_v2')}>
💊
<GameSprite icon="dealt" size={40} fallback="DEALT" />
</motion.div>
<motion.div className="dock-item" whileTap={{ scale: 0.9 }} onClick={() => navigateTo('slide')}>
🎯
<GameSprite icon="slide" size={40} fallback="SLIDE" />
</motion.div>
<motion.div className="dock-item" whileTap={{ scale: 0.9 }} onClick={() => navigateTo('gang_hq')}>
👥
<GameSprite icon="crew" size={40} fallback="CREW" />
</motion.div>
<motion.div className="dock-item" whileTap={{ scale: 0.9 }} onClick={() => navigateTo('shoebox')}>
💰
<motion.div className="dock-item" whileTap={{ scale: 0.9 }} onClick={() => navigateTo('trap')}>
<GameSprite icon="trap" size={40} fallback="TRAP" />
</motion.div>
</div>

Expand All @@ -489,7 +519,7 @@ const OSShell: React.FC<OSShellProps> = ({ gangMorale = 75, incomePerMinute = 0
Mark All Read
</button>
)}
<button onClick={() => setShowNotifications(false)}></button>
<button onClick={() => setShowNotifications(false)}>X</button>
</div>
</div>
<div className="notification-list">
Expand Down
Loading
Loading