feat: initial commit — Falah Coach Portal v0.1
Deploy to Netlify / build-and-deploy (push) Failing after 14m42s
Deploy to Netlify / build-and-deploy (push) Failing after 14m42s
- React 18 + Vite 5 + TailwindCSS 3 SPA - Mission Control dashboard - AI Co-Pilot & Simulator view - Tactical Audio Briefings library - MS Loop Wiki integration view - Global audio player in sidebar - Netlify deployment config with SPA redirect - GitHub Actions CI/CD workflow
This commit is contained in:
@@ -0,0 +1,36 @@
|
|||||||
|
name: Deploy to Netlify
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
jobs:
|
||||||
|
build-and-deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout Code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: '18'
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: npm install
|
||||||
|
|
||||||
|
- name: Build React App
|
||||||
|
run: npm run build
|
||||||
|
|
||||||
|
- name: Deploy to Netlify
|
||||||
|
uses: nwtgck/actions-netlify@v2.0
|
||||||
|
with:
|
||||||
|
publish-dir: './dist'
|
||||||
|
production-branch: main
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
deploy-message: "Deploy from GitHub Actions"
|
||||||
|
enable-pull-request-comment: false
|
||||||
|
enable-commit-comment: true
|
||||||
|
overwrites-pull-request-comment: true
|
||||||
|
env:
|
||||||
|
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||||
|
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
.DS_Store
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
.vite/
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"siteId": "c40ab51c-80b9-4c30-b669-65e68c3bd836"
|
||||||
|
}
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Falah Coach Portal</title>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body class="bg-black text-slate-100 font-sans antialiased selection:bg-emerald-500/30">
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.jsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
[build]
|
||||||
|
command = "npm run build"
|
||||||
|
publish = "dist"
|
||||||
|
|
||||||
|
[[redirects]]
|
||||||
|
from = "/*"
|
||||||
|
to = "/index.html"
|
||||||
|
status = 200
|
||||||
Generated
+2661
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"name": "falah-coach-portal",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"lucide-react": "^0.368.0",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "^18.2.66",
|
||||||
|
"@types/react-dom": "^18.2.22",
|
||||||
|
"@vitejs/plugin-react": "^4.2.1",
|
||||||
|
"autoprefixer": "^10.4.19",
|
||||||
|
"postcss": "^8.4.38",
|
||||||
|
"tailwindcss": "^3.4.3",
|
||||||
|
"vite": "^5.2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export default {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
+452
@@ -0,0 +1,452 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import {
|
||||||
|
LayoutDashboard,
|
||||||
|
MessageSquareQuote,
|
||||||
|
Headphones,
|
||||||
|
BookOpen,
|
||||||
|
PlayCircle,
|
||||||
|
PauseCircle,
|
||||||
|
SkipForward,
|
||||||
|
SkipBack,
|
||||||
|
TerminalSquare,
|
||||||
|
Bot,
|
||||||
|
User,
|
||||||
|
Zap,
|
||||||
|
TrendingUp,
|
||||||
|
FileText,
|
||||||
|
ChevronRight,
|
||||||
|
FolderOpen
|
||||||
|
} from 'lucide-react';
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
MOCK DATA
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
const MOCK_AUDIO_TRACKS = [
|
||||||
|
{ id: 1, title: 'The 100-Day Turnaround', duration: '14:20', tag: 'Strategy', author: 'Senior Partner A' },
|
||||||
|
{ id: 2, title: 'Negotiating with Hostile Boards', duration: '22:15', tag: 'Leadership', author: 'Principal B' },
|
||||||
|
{ id: 3, title: 'M&A Synergy Identification', duration: '18:45', tag: 'Finance', author: 'Director C' },
|
||||||
|
{ id: 4, title: 'Digital Transformation Pitfalls', duration: '31:10', tag: 'Technology', author: 'Principal D' },
|
||||||
|
{ id: 5, title: 'Pricing Strategies in Inflation', duration: '12:55', tag: 'Economics', author: 'Partner E' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const MOCK_WIKI_DIRECTORY = [
|
||||||
|
{
|
||||||
|
folder: 'Core Frameworks',
|
||||||
|
items: ['Falah MECE Guidelines', 'Value Creation Matrix', 'Go-To-Market Playbook']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
folder: 'Banking Edition',
|
||||||
|
items: ['Retail Bank Cost Reduction', 'Fintech Threat Analysis', 'Regulatory Compliance 2026']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
folder: 'Telco Edition',
|
||||||
|
items: ['5G Monetization', 'Churn Reduction Models']
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const MOCK_CHAT_HISTORY = [
|
||||||
|
{ role: 'system', text: 'Falah Simulator initialized. Persona: Hostile Telco CIO. Objective: Justify £2M Agile Transformation.' },
|
||||||
|
{ role: 'user', text: 'Good morning. I\'d like to walk you through the projected ROI for the Agile Transformation initiative.' },
|
||||||
|
{ role: 'assistant', text: 'I don\'t have time for buzzwords today. You want £2M of my budget to "transform" teams that are already delivering. Where is the hard financial justification? I need EBITDA impact, not velocity charts.' },
|
||||||
|
];
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
SUB-VIEWS
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
const MissionControlView = ({ setActiveView, setActiveAudio }) => {
|
||||||
|
return (
|
||||||
|
<div className="space-y-8 animate-fade-in pr-6">
|
||||||
|
<header>
|
||||||
|
<h1 className="text-4xl font-bold tracking-tight text-white mb-2">Mission Control</h1>
|
||||||
|
<p className="text-slate-400">Welcome back. Your Falah Coach portal is ready.</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||||
|
{/* Featured Audio Widget */}
|
||||||
|
<div className="lg:col-span-2 bg-slate-900 border border-slate-800 rounded-2xl p-6 relative overflow-hidden group">
|
||||||
|
<div className="absolute top-0 right-0 p-32 bg-emerald-500/10 rounded-full blur-[100px] -mr-16 -mt-16 pointer-events-none"></div>
|
||||||
|
<div className="relative z-10 flex flex-col h-full justify-between">
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center gap-2 text-emerald-400 font-medium mb-4 text-sm tracking-wide uppercase">
|
||||||
|
<Zap size={16} /> Featured Briefing
|
||||||
|
</div>
|
||||||
|
<h2 className="text-3xl font-semibold text-white mb-3 tracking-tight">The 100-Day Turnaround</h2>
|
||||||
|
<p className="text-slate-400 max-w-lg mb-6 leading-relaxed">
|
||||||
|
Master the critical first 100 days of a private equity backed turnaround. Tactical insights from our top Senior Partners.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => setActiveAudio(MOCK_AUDIO_TRACKS[0])}
|
||||||
|
className="flex items-center gap-2 bg-emerald-500 hover:bg-emerald-400 text-slate-950 font-semibold py-3 px-6 rounded-lg transition-colors w-max"
|
||||||
|
>
|
||||||
|
<PlayCircle size={20} />
|
||||||
|
Play Briefing (14:20)
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Quick Starts */}
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
<div
|
||||||
|
onClick={() => setActiveView('simulator')}
|
||||||
|
className="bg-slate-900 border border-slate-800 hover:border-blue-500/50 rounded-2xl p-6 cursor-pointer transition-all hover:-translate-y-1 group"
|
||||||
|
>
|
||||||
|
<div className="bg-blue-500/10 text-blue-400 p-3 rounded-lg w-max mb-4 group-hover:bg-blue-500 group-hover:text-white transition-colors">
|
||||||
|
<Bot size={24} />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-xl font-semibold text-white mb-1">AI Simulator</h3>
|
||||||
|
<p className="text-slate-400 text-sm">Practice hostile boardroom scenarios.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
onClick={() => setActiveView('wiki')}
|
||||||
|
className="bg-slate-900 border border-slate-800 hover:border-emerald-500/50 rounded-2xl p-6 cursor-pointer transition-all hover:-translate-y-1 group"
|
||||||
|
>
|
||||||
|
<div className="bg-emerald-500/10 text-emerald-400 p-3 rounded-lg w-max mb-4 group-hover:bg-emerald-500 group-hover:text-white transition-colors">
|
||||||
|
<BookOpen size={24} />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-xl font-semibold text-white mb-1">Loop Wiki</h3>
|
||||||
|
<p className="text-slate-400 text-sm">Review the Falah MECE Guidelines.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const AICoPilotView = () => {
|
||||||
|
const [messages, setMessages] = useState(MOCK_CHAT_HISTORY);
|
||||||
|
const [input, setInput] = useState('');
|
||||||
|
|
||||||
|
const handleSend = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!input.trim()) return;
|
||||||
|
setMessages([...messages, { role: 'user', text: input }]);
|
||||||
|
setInput('');
|
||||||
|
// Mock response
|
||||||
|
setTimeout(() => {
|
||||||
|
setMessages(prev => [...prev, {
|
||||||
|
role: 'assistant',
|
||||||
|
text: 'That response lacks financial rigor. Reframe your answer focusing on cost-of-delay and projected OPEX reduction over the next 3 quarters.'
|
||||||
|
}]);
|
||||||
|
}, 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-full flex flex-col pb-4 animate-fade-in pr-6">
|
||||||
|
<header className="mb-6">
|
||||||
|
<h1 className="text-3xl font-bold tracking-tight text-white mb-1">AI Co-Pilot & Simulator</h1>
|
||||||
|
<p className="text-slate-400 text-sm">Engage in high-pressure executive roleplay to refine your delivery.</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div className="flex-1 bg-slate-900 border border-slate-800 rounded-2xl flex flex-col overflow-hidden">
|
||||||
|
<div className="flex-1 p-6 overflow-y-auto space-y-6">
|
||||||
|
{messages.map((msg, i) => (
|
||||||
|
<div key={i} className={`flex gap-4 ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}>
|
||||||
|
|
||||||
|
{msg.role !== 'user' && (
|
||||||
|
<div className={`w-8 h-8 rounded-full flex items-center justify-center shrink-0 ${msg.role === 'system' ? 'bg-slate-800 text-slate-400' : 'bg-blue-500/20 text-blue-400'}`}>
|
||||||
|
{msg.role === 'system' ? <TerminalSquare size={16} /> : <Bot size={16} />}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className={`max-w-[70%] p-4 rounded-2xl ${
|
||||||
|
msg.role === 'user'
|
||||||
|
? 'bg-blue-600 text-white rounded-tr-sm'
|
||||||
|
: msg.role === 'system'
|
||||||
|
? 'bg-slate-800 text-slate-300 font-mono text-sm border border-slate-700'
|
||||||
|
: 'bg-slate-800 text-slate-200 border border-slate-700 rounded-tl-sm'
|
||||||
|
}`}>
|
||||||
|
{msg.text}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{msg.role === 'user' && (
|
||||||
|
<div className="w-8 h-8 rounded-full bg-slate-700 text-slate-300 flex items-center justify-center shrink-0">
|
||||||
|
<User size={16} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="p-4 bg-slate-850 border-t border-slate-800">
|
||||||
|
<div className="flex gap-2 mb-3 overflow-x-auto pb-1 hide-scrollbar">
|
||||||
|
{['/simulate Telco CIO', '/mentor DevSecOps', '/grade'].map(cmd => (
|
||||||
|
<button
|
||||||
|
key={cmd}
|
||||||
|
onClick={() => setInput(cmd)}
|
||||||
|
className="whitespace-nowrap px-3 py-1.5 bg-slate-800 hover:bg-slate-700 text-blue-400 text-xs font-mono rounded-md border border-slate-700 transition-colors"
|
||||||
|
>
|
||||||
|
{cmd}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<form onSubmit={handleSend} className="relative">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={input}
|
||||||
|
onChange={(e) => setInput(e.target.value)}
|
||||||
|
placeholder="Deploy a response or command..."
|
||||||
|
className="w-full bg-slate-900 border border-slate-700 rounded-xl px-4 py-4 pr-12 text-slate-200 focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 transition-all placeholder:text-slate-500"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 hover:text-blue-400 p-2"
|
||||||
|
>
|
||||||
|
<Zap size={20} />
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const AudioLibraryView = ({ setActiveAudio }) => {
|
||||||
|
return (
|
||||||
|
<div className="animate-fade-in pr-6">
|
||||||
|
<header className="mb-8">
|
||||||
|
<h1 className="text-3xl font-bold tracking-tight text-white mb-2">Tactical Audio Briefings</h1>
|
||||||
|
<p className="text-slate-400">Exclusive micro-learning tracks from Falah's leadership network.</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
|
||||||
|
{MOCK_AUDIO_TRACKS.map(track => (
|
||||||
|
<div
|
||||||
|
key={track.id}
|
||||||
|
onClick={() => setActiveAudio(track)}
|
||||||
|
className="group bg-slate-900 border border-slate-800 hover:border-emerald-500/50 rounded-2xl p-6 cursor-pointer transition-all hover:shadow-lg hover:shadow-emerald-500/5 relative overflow-hidden"
|
||||||
|
>
|
||||||
|
<div className="flex justify-between items-start mb-12">
|
||||||
|
<span className="px-2.5 py-1 bg-slate-800 text-slate-300 text-xs font-semibold rounded-md uppercase tracking-wider">
|
||||||
|
{track.tag}
|
||||||
|
</span>
|
||||||
|
<div className="w-10 h-10 rounded-full bg-emerald-500/20 text-emerald-400 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity scale-75 group-hover:scale-100">
|
||||||
|
<PlayCircle size={24} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 className="text-xl font-semibold text-white mb-2 group-hover:text-emerald-400 transition-colors">{track.title}</h3>
|
||||||
|
<div className="flex justify-between items-center text-sm text-slate-500">
|
||||||
|
<span>{track.author}</span>
|
||||||
|
<span className="flex items-center gap-1 font-mono"><Headphones size={14}/> {track.duration}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const MSLoopWikiView = () => {
|
||||||
|
const [activePage, setActivePage] = useState('Falah MECE Guidelines');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-full flex flex-col pb-4 animate-fade-in pr-6">
|
||||||
|
<header className="mb-6">
|
||||||
|
<h1 className="text-3xl font-bold tracking-tight text-white mb-1">MS Loop Integration</h1>
|
||||||
|
<p className="text-slate-400 text-sm">Falah Playbooks & Living Intellectual Property.</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div className="flex-1 flex gap-6 h-[calc(100vh-140px)] overflow-hidden">
|
||||||
|
{/* Left Col: Directory */}
|
||||||
|
<div className="w-64 shrink-0 bg-slate-900 border border-slate-800 rounded-2xl p-4 overflow-y-auto custom-scrollbar">
|
||||||
|
<div className="text-xs font-semibold text-slate-500 uppercase tracking-wider mb-4 ml-2">Directory</div>
|
||||||
|
<div className="space-y-4">
|
||||||
|
{MOCK_WIKI_DIRECTORY.map((dir, idx) => (
|
||||||
|
<div key={idx}>
|
||||||
|
<div className="flex items-center gap-2 text-slate-300 font-medium text-sm mb-2 px-2">
|
||||||
|
<FolderOpen size={16} className="text-blue-400/70" />
|
||||||
|
{dir.folder}
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
{dir.items.map(item => (
|
||||||
|
<button
|
||||||
|
key={item}
|
||||||
|
onClick={() => setActivePage(item)}
|
||||||
|
className={`w-full flex items-center gap-2 px-2 py-1.5 text-sm rounded-md transition-colors text-left ${
|
||||||
|
activePage === item
|
||||||
|
? 'bg-blue-500/10 text-blue-400 font-medium'
|
||||||
|
: 'text-slate-400 hover:text-slate-200 hover:bg-slate-800'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<FileText size={14} />
|
||||||
|
<span className="truncate">{item}</span>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right Col: Iframe container */}
|
||||||
|
<div className="flex-1 bg-white rounded-2xl overflow-hidden flex flex-col shadow-2xl relative">
|
||||||
|
{/* Iframe Chrome */}
|
||||||
|
<div className="bg-slate-100 border-b border-slate-200 p-3 flex items-center gap-4">
|
||||||
|
<div className="flex gap-1.5">
|
||||||
|
<div className="w-3 h-3 rounded-full bg-slate-300"></div>
|
||||||
|
<div className="w-3 h-3 rounded-full bg-slate-300"></div>
|
||||||
|
<div className="w-3 h-3 rounded-full bg-slate-300"></div>
|
||||||
|
</div>
|
||||||
|
<div className="bg-white border border-slate-200 text-slate-400 text-xs px-3 py-1.5 rounded flex-1 flex justify-between items-center shadow-sm max-w-md">
|
||||||
|
<div className="truncate text-slate-500 font-mono text-[11px]"><span className="text-slate-400">loop.microsoft.com/falah/</span>{activePage.toLowerCase().replace(/ /g, '-')}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Simulated content area */}
|
||||||
|
<div className="flex-1 p-12 overflow-y-auto text-slate-800 bg-[#fbfbfb]">
|
||||||
|
<div className="max-w-2xl mx-auto">
|
||||||
|
<h1 className="text-4xl font-bold mb-8 text-slate-900 border-b border-slate-200 pb-4">{activePage}</h1>
|
||||||
|
|
||||||
|
<div className="prose prose-slate max-w-none">
|
||||||
|
<p className="text-lg leading-relaxed mb-6 text-slate-600">
|
||||||
|
This document serves as the canonical reference for exactly how our consultants should approach problem structuring in this domain.
|
||||||
|
</p>
|
||||||
|
<div className="grid grid-cols-2 gap-4 mb-8">
|
||||||
|
<div className="p-4 bg-blue-50 border border-blue-100 rounded-lg">
|
||||||
|
<h4 className="font-semibold text-blue-900 m-0 mb-2">Key Objective</h4>
|
||||||
|
<p className="text-sm text-blue-800 m-0">Drive unambiguous clarity to executive stakeholders within 3 minutes of engagement.</p>
|
||||||
|
</div>
|
||||||
|
<div className="p-4 bg-emerald-50 border border-emerald-100 rounded-lg">
|
||||||
|
<h4 className="font-semibold text-emerald-900 m-0 mb-2">Success Metric</h4>
|
||||||
|
<p className="text-sm text-emerald-800 m-0">Achieve client alignment before the first break in the agenda.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h3 className="text-2xl font-semibold mb-4 mt-8 text-slate-800">1. Executive Summary</h3>
|
||||||
|
<p className="text-slate-600 leading-relaxed mb-4">
|
||||||
|
The imperative here is not just structural elegance, but actionable intelligence. When framing the issue, prioritize the financial delta and the organizational capacity required to capture it.
|
||||||
|
</p>
|
||||||
|
<ul className="space-y-2 list-disc pl-5 text-slate-600 mb-8">
|
||||||
|
<li>Identify the core constraint (Capital, Talent, or Regulatory).</li>
|
||||||
|
<li>Map the adjacent capabilities required.</li>
|
||||||
|
<li>Draft the 100-day execution roadmap.</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
MAIN APP SHELL
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
const [activeView, setActiveView] = useState('missionControl');
|
||||||
|
const [activeAudio, setActiveAudio] = useState(null);
|
||||||
|
const [isPlaying, setIsPlaying] = useState(false);
|
||||||
|
|
||||||
|
const togglePlay = () => setIsPlaying(!isPlaying);
|
||||||
|
|
||||||
|
const navItems = [
|
||||||
|
{ id: 'missionControl', label: 'Mission Control', icon: LayoutDashboard },
|
||||||
|
{ id: 'simulator', label: 'AI Co-Pilot', icon: MessageSquareQuote },
|
||||||
|
{ id: 'audio', label: 'Audio Library', icon: Headphones },
|
||||||
|
{ id: 'wiki', label: 'Loop Wiki', icon: BookOpen },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex h-screen bg-slate-950 text-slate-100 font-sans overflow-hidden selection:bg-blue-500/30">
|
||||||
|
|
||||||
|
{/* Sidebar Layout */}
|
||||||
|
<div className="w-72 border-r border-slate-800 flex flex-col bg-slate-900 shadow-2xl relative z-20">
|
||||||
|
|
||||||
|
{/* Brand */}
|
||||||
|
<div className="p-6">
|
||||||
|
<div className="flex items-center gap-3 mb-1">
|
||||||
|
<div className="w-8 h-8 bg-white rounded-lg flex items-center justify-center">
|
||||||
|
<TrendingUp className="text-slate-950" size={20} strokeWidth={2.5}/>
|
||||||
|
</div>
|
||||||
|
<span className="font-bold text-xl tracking-wide text-white uppercase">FALAH</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-[10px] uppercase font-bold tracking-[0.2em] text-slate-500 ml-11">Coach Portal</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Navigation */}
|
||||||
|
<nav className="flex-1 px-4 mt-6 space-y-2 overflow-y-auto">
|
||||||
|
<div className="text-xs font-semibold text-slate-600 uppercase tracking-wider mb-4 ml-3">Platform</div>
|
||||||
|
{navItems.map((item) => {
|
||||||
|
const Icon = item.icon;
|
||||||
|
const isActive = activeView === item.id;
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={item.id}
|
||||||
|
onClick={() => setActiveView(item.id)}
|
||||||
|
className={`w-full flex items-center justify-between px-3 py-2.5 rounded-lg transition-all duration-200 group ${
|
||||||
|
isActive
|
||||||
|
? 'bg-blue-600 shadow-md shadow-blue-500/20'
|
||||||
|
: 'hover:bg-slate-800 text-slate-400 hover:text-slate-100'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<Icon size={18} className={isActive ? 'text-white' : 'text-slate-500 group-hover:text-blue-400 transition-colors'}/>
|
||||||
|
<span className={`font-medium ${isActive ? 'text-white' : ''}`}>{item.label}</span>
|
||||||
|
</div>
|
||||||
|
{isActive && <ChevronRight size={16} className="text-blue-200" />}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
{/* Global Audio Player */}
|
||||||
|
<div className="mt-auto border-t border-slate-800 p-4 bg-slate-950/50 backdrop-blur-md">
|
||||||
|
{activeAudio ? (
|
||||||
|
<div className="animate-fade-in-up">
|
||||||
|
<div className="flex justify-between items-center mb-2">
|
||||||
|
<span className="text-[10px] font-bold text-emerald-400 uppercase tracking-wider">Now Playing</span>
|
||||||
|
<span className="text-xs text-slate-500 font-mono">{isPlaying ? '02:14' : '00:00'} / {activeAudio.duration}</span>
|
||||||
|
</div>
|
||||||
|
<p className="font-semibold text-sm text-white truncate mb-1" title={activeAudio.title}>
|
||||||
|
{activeAudio.title}
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-slate-400 mb-3 truncate">{activeAudio.author}</p>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-between mt-3 px-2">
|
||||||
|
<button className="text-slate-400 hover:text-white transition-colors"><SkipBack size={18} /></button>
|
||||||
|
<button
|
||||||
|
onClick={togglePlay}
|
||||||
|
className="text-white bg-emerald-500 hover:bg-emerald-400 rounded-full w-10 h-10 flex items-center justify-center transition-all shadow-lg shadow-emerald-500/20"
|
||||||
|
>
|
||||||
|
{isPlaying ? <PauseCircle size={24} className="text-slate-950" /> : <PlayCircle size={24} className="text-slate-950" />}
|
||||||
|
</button>
|
||||||
|
<button className="text-slate-400 hover:text-white transition-colors"><SkipForward size={18} /></button>
|
||||||
|
</div>
|
||||||
|
{/* Fake progress bar */}
|
||||||
|
<div className="w-full bg-slate-800 h-1 mt-4 rounded-full overflow-hidden">
|
||||||
|
<div className="bg-emerald-500 h-full w-1/4 rounded-full transition-all duration-1000 ease-linear"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="py-4 text-center">
|
||||||
|
<Headphones className="mx-auto text-slate-700 mb-2" size={24} />
|
||||||
|
<p className="text-xs text-slate-500 font-medium">Select an audio brief to begin</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Main Content Area */}
|
||||||
|
<main className="flex-1 overflow-y-auto p-8 relative scroll-smooth overflow-x-hidden">
|
||||||
|
{/* Ambient background glow */}
|
||||||
|
<div className="absolute top-[-20%] left-[-10%] w-[50%] h-[50%] bg-blue-600/5 rounded-full blur-[120px] pointer-events-none"></div>
|
||||||
|
|
||||||
|
<div className="max-w-6xl mx-auto h-full relative z-10">
|
||||||
|
{activeView === 'missionControl' && <MissionControlView setActiveView={setActiveView} setActiveAudio={setActiveAudio}/>}
|
||||||
|
{activeView === 'simulator' && <AICoPilotView />}
|
||||||
|
{activeView === 'audio' && <AudioLibraryView setActiveAudio={setActiveAudio}/>}
|
||||||
|
{activeView === 'wiki' && <MSLoopWikiView />}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
html, body, #root {
|
||||||
|
@apply h-full;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer utilities {
|
||||||
|
.custom-scrollbar::-webkit-scrollbar {
|
||||||
|
width: 4px;
|
||||||
|
}
|
||||||
|
.custom-scrollbar::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
.custom-scrollbar::-webkit-scrollbar-thumb {
|
||||||
|
@apply bg-slate-800 rounded;
|
||||||
|
}
|
||||||
|
.custom-scrollbar:hover::-webkit-scrollbar-thumb {
|
||||||
|
@apply bg-slate-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hide-scrollbar::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.hide-scrollbar {
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
scrollbar-width: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import ReactDOM from 'react-dom/client'
|
||||||
|
import App from './App.jsx'
|
||||||
|
import './index.css'
|
||||||
|
|
||||||
|
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<App />
|
||||||
|
</React.StrictMode>,
|
||||||
|
)
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
export default {
|
||||||
|
content: [
|
||||||
|
"./index.html",
|
||||||
|
"./src/**/*.{js,ts,jsx,tsx}",
|
||||||
|
],
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
fontFamily: {
|
||||||
|
sans: ['Inter', 'sans-serif'],
|
||||||
|
},
|
||||||
|
colors: {
|
||||||
|
slate: {
|
||||||
|
850: '#151e2e',
|
||||||
|
900: '#0f172a',
|
||||||
|
950: '#020617',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
animation: {
|
||||||
|
'fade-in': 'fadeIn 0.5s ease-out forwards',
|
||||||
|
'fade-in-up': 'fadeInUp 0.5s ease-out forwards',
|
||||||
|
},
|
||||||
|
keyframes: {
|
||||||
|
fadeIn: {
|
||||||
|
'0%': { opacity: '0' },
|
||||||
|
'100%': { opacity: '1' },
|
||||||
|
},
|
||||||
|
fadeInUp: {
|
||||||
|
'0%': { opacity: '0', transform: 'translateY(10px)' },
|
||||||
|
'100%': { opacity: '1', transform: 'translateY(0)' },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import react from '@vitejs/plugin-react'
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
})
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// vite.config.js
|
||||||
|
import { defineConfig } from "file:///Users/wanjauhari24/Desktop/Projects/Antigravity/Coach%20Portal/node_modules/vite/dist/node/index.js";
|
||||||
|
import react from "file:///Users/wanjauhari24/Desktop/Projects/Antigravity/Coach%20Portal/node_modules/@vitejs/plugin-react/dist/index.js";
|
||||||
|
var vite_config_default = defineConfig({
|
||||||
|
plugins: [react()]
|
||||||
|
});
|
||||||
|
export {
|
||||||
|
vite_config_default as default
|
||||||
|
};
|
||||||
|
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcuanMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCIvVXNlcnMvd2FuamF1aGFyaTI0L0Rlc2t0b3AvUHJvamVjdHMvQW50aWdyYXZpdHkvQ29hY2ggUG9ydGFsXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ZpbGVuYW1lID0gXCIvVXNlcnMvd2FuamF1aGFyaTI0L0Rlc2t0b3AvUHJvamVjdHMvQW50aWdyYXZpdHkvQ29hY2ggUG9ydGFsL3ZpdGUuY29uZmlnLmpzXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ltcG9ydF9tZXRhX3VybCA9IFwiZmlsZTovLy9Vc2Vycy93YW5qYXVoYXJpMjQvRGVza3RvcC9Qcm9qZWN0cy9BbnRpZ3Jhdml0eS9Db2FjaCUyMFBvcnRhbC92aXRlLmNvbmZpZy5qc1wiO2ltcG9ydCB7IGRlZmluZUNvbmZpZyB9IGZyb20gJ3ZpdGUnXG5pbXBvcnQgcmVhY3QgZnJvbSAnQHZpdGVqcy9wbHVnaW4tcmVhY3QnXG5cbi8vIGh0dHBzOi8vdml0ZWpzLmRldi9jb25maWcvXG5leHBvcnQgZGVmYXVsdCBkZWZpbmVDb25maWcoe1xuICBwbHVnaW5zOiBbcmVhY3QoKV0sXG59KVxuIl0sCiAgIm1hcHBpbmdzIjogIjtBQUEyVyxTQUFTLG9CQUFvQjtBQUN4WSxPQUFPLFdBQVc7QUFHbEIsSUFBTyxzQkFBUSxhQUFhO0FBQUEsRUFDMUIsU0FBUyxDQUFDLE1BQU0sQ0FBQztBQUNuQixDQUFDOyIsCiAgIm5hbWVzIjogW10KfQo=
|
||||||
Reference in New Issue
Block a user