-
Notifications
You must be signed in to change notification settings - Fork 353
Home
import os
folders = { "frontend": ["public", "src/components", "src/pages", "src/assets"], "backend": ["routes", "controllers", "models", "middleware"] }
for base, subfolders in folders.items(): os.makedirs(base, exist_ok=True) for folder in subfolders: path = os.path.join(base, folder) os.makedirs(path, exist_ok=True)
frontend_files = { "frontend/public/index.html": "
<title>Trading App</title>", "frontend/src/index.js": "import React from 'react';\nimport ReactDOM from 'react-dom';\nimport App from './App';\nReactDOM.render(, document.getElementById('root'));", "frontend/src/App.js": "import React from 'react';\nimport LoginPage from './pages/LoginPage';\nimport Dashboard from './pages/Dashboard';\nfunction App() { return (backend_files = { "backend/server.js": "const express = require('express');\nconst app = express();\napp.use(express.json());\napp.get('/', (req, res) => res.send('API Running'));\napp.listen(5000, () => console.log('Server started on port 5000'));", "backend/routes/auth.js": "const express = require('express');\nconst router = express.Router();\nrouter.post('/login', (req, res) => res.send('Login route'));\nmodule.exports = router;", "backend/routes/trade.js": "const express = require('express');\nconst router = express.Router();\nrouter.post('/execute', (req, res) => res.send('Trade executed'));\nmodule.exports = router;", "backend/routes/market.js": "const express = require('express');\nconst router = express.Router();\nrouter.get('/data', (req, res) => res.send('Market data'));\nmodule.exports = router;" }
for filepath, content in {**frontend_files, **backend_files}.items(): with open(filepath, "w") as f: f.write(content)
print("Starter codebase for frontend and backend has been generated.")
import React, { useState } from 'react'; import { Clock, DollarSign, Users, Code, Database, Smartphone, Monitor, Globe } from 'lucide-react';
const StockAppEstimate = () => { const [selectedPlatform, setSelectedPlatform] = useState('web'); const [selectedDataSource, setSelectedDataSource] = useState('free');
const platforms = { web: { name: 'Web App', icon: Globe, multiplier: 1 }, mobile: { name: 'Mobile App', icon: Smartphone, multiplier: 1.3 }, desktop: { name: 'Desktop App', icon: Monitor, multiplier: 1.2 } };
const dataSources = { free: { name: 'Free API (Terbatas)', cost: 0, multiplier: 1 }, basic: { name: 'Basic Financial API', cost: 500000, multiplier: 1.1 }, premium: { name: 'Premium Real-time IDX', cost: 2000000, multiplier: 1.3 } };
const baseFeatures = [ { name: 'Watchlist Management', hours: 16, description: 'CRUD watchlist saham' }, { name: 'Portfolio Dashboard', hours: 24, description: 'Tracking posisi & P&L' }, { name: 'Real-time Monitor', hours: 20, description: 'Live price & volume tracking' }, { name: 'Data Integration', hours: 12, description: 'API integration untuk data saham' }, { name: 'Auto Refresh System', hours: 8, description: 'Background sync setiap 5 menit' }, { name: 'Basic Authentication', hours: 12, description: 'User login & session management' }, { name: 'Responsive UI/UX', hours: 20, description: 'Modern interface design' }, { name: 'Testing & Debugging', hours: 16, description: 'Unit tests & bug fixes' } ];
const platformMultiplier = platforms[selectedPlatform].multiplier; const dataMultiplier = dataSources[selectedDataSource].multiplier;
const totalHours = Math.ceil(baseFeatures.reduce((sum, f) => sum + f.hours, 0) * platformMultiplier * dataMultiplier); const hourlyRate = 150000; // IDR per hour const developmentCost = totalHours * hourlyRate; const dataCost = dataSources[selectedDataSource].cost * 12; // Annual const totalCost = developmentCost + dataCost; const timelineWeeks = Math.ceil(totalHours / 40);
return (
{/* Platform Selection */}
<div className="mb-6 p-4 bg-blue-50 rounded-lg">
<h3 className="text-lg font-semibold mb-3 text-blue-800">Pilih Platform Target</h3>
<div className="grid grid-cols-3 gap-3">
{Object.entries(platforms).map(([key, platform]) => {
const IconComponent = platform.icon;
return (
<button
key={key}
onClick={() => setSelectedPlatform(key)}
className={`p-3 rounded-lg border-2 transition-all ${
selectedPlatform === key
? 'border-blue-500 bg-blue-100'
: 'border-gray-300 hover:border-blue-300'
}`}
>
<IconComponent className="w-6 h-6 mx-auto mb-2" />
<div className="font-medium">{platform.name}</div>
</button>
);
})}
</div>
</div>
{/* Data Source Selection */}
<div className="mb-6 p-4 bg-green-50 rounded-lg">
<h3 className="text-lg font-semibold mb-3 text-green-800">Pilih Sumber Data Saham</h3>
<div className="space-y-2">
{Object.entries(dataSources).map(([key, source]) => (
<button
key={key}
onClick={() => setSelectedDataSource(key)}
className={`w-full p-3 text-left rounded-lg border-2 transition-all ${
selectedDataSource === key
? 'border-green-500 bg-green-100'
: 'border-gray-300 hover:border-green-300'
}`}
>
<div className="font-medium">{source.name}</div>
<div className="text-sm text-gray-600">
Biaya: {source.cost === 0 ? 'Gratis' : `Rp ${source.cost.toLocaleString()}/bulan`}
</div>
</button>
))}
</div>
</div>
{/* Summary Cards */}
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8">
<div className="bg-purple-100 p-4 rounded-lg text-center">
<Clock className="w-8 h-8 mx-auto mb-2 text-purple-600" />
<div className="text-2xl font-bold text-purple-800">{totalHours}h</div>
<div className="text-sm text-purple-600">Total Development</div>
</div>
<div className="bg-blue-100 p-4 rounded-lg text-center">
<Code className="w-8 h-8 mx-auto mb-2 text-blue-600" />
<div className="text-2xl font-bold text-blue-800">{timelineWeeks} minggu</div>
<div className="text-sm text-blue-600">Timeline</div>
</div>
<div className="bg-green-100 p-4 rounded-lg text-center">
<DollarSign className="w-8 h-8 mx-auto mb-2 text-green-600" />
<div className="text-2xl font-bold text-green-800">
Rp {(developmentCost/1000000).toFixed(0)}jt
</div>
<div className="text-sm text-green-600">Development Cost</div>
</div>
<div className="bg-orange-100 p-4 rounded-lg text-center">
<Database className="w-8 h-8 mx-auto mb-2 text-orange-600" />
<div className="text-2xl font-bold text-orange-800">
Rp {(dataCost/1000000).toFixed(0)}jt
</div>
<div className="text-sm text-orange-600">Data Cost (1 tahun)</div>
</div>
</div>
{/* Feature Breakdown */}
<div className="mb-8">
<h3 className="text-xl font-semibold mb-4">Breakdown Fitur & Estimasi</h3>
<div className="bg-gray-50 rounded-lg overflow-hidden">
<div className="grid grid-cols-3 gap-4 p-4 bg-gray-200 font-semibold">
<div>Fitur</div>
<div className="text-center">Estimasi Jam</div>
<div>Deskripsi</div>
</div>
{baseFeatures.map((feature, idx) => (
<div key={idx} className="grid grid-cols-3 gap-4 p-4 border-b border-gray-200">
<div className="font-medium">{feature.name}</div>
<div className="text-center">
{Math.ceil(feature.hours * platformMultiplier * dataMultiplier)}h
</div>
<div className="text-sm text-gray-600">{feature.description}</div>
</div>
))}
</div>
</div>
{/* Timeline */}
<div className="mb-8">
<h3 className="text-xl font-semibold mb-4">Timeline Pengembangan</h3>
<div className="space-y-3">
<div className="flex items-center p-3 bg-blue-50 rounded-lg">
<div className="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center text-white font-bold text-sm mr-4">1</div>
<div>
<div className="font-medium">Setup & Planning (1 minggu)</div>
<div className="text-sm text-gray-600">Architecture, database design, API setup</div>
</div>
</div>
<div className="flex items-center p-3 bg-green-50 rounded-lg">
<div className="w-8 h-8 bg-green-500 rounded-full flex items-center justify-center text-white font-bold text-sm mr-4">2</div>
<div>
<div className="font-medium">Core Development ({Math.ceil(timelineWeeks * 0.6)} minggu)</div>
<div className="text-sm text-gray-600">Watchlist, dashboard, data integration</div>
</div>
</div>
<div className="flex items-center p-3 bg-yellow-50 rounded-lg">
<div className="w-8 h-8 bg-yellow-500 rounded-full flex items-center justify-center text-white font-bold text-sm mr-4">3</div>
<div>
<div className="font-medium">Testing & Polish ({Math.ceil(timelineWeeks * 0.3)} minggu)</div>
<div className="text-sm text-gray-600">Bug fixes, performance optimization, UI polish</div>
</div>
</div>
</div>
</div>
{/* Total Cost */}
<div className="bg-gradient-to-r from-blue-500 to-purple-600 text-white p-6 rounded-lg">
<h3 className="text-2xl font-bold mb-2">Total Estimasi Proyek</h3>
<div className="grid grid-cols-2 gap-4">
<div>
<div className="text-blue-100">Development Cost</div>
<div className="text-2xl font-bold">Rp {(developmentCost/1000000).toFixed(1)}jt</div>
</div>
<div>
<div className="text-blue-100">Operational Cost (1 tahun)</div>
<div className="text-2xl font-bold">Rp {(dataCost/1000000).toFixed(1)}jt</div>
</div>
</div>
<div className="mt-4 pt-4 border-t border-blue-400">
<div className="text-blue-100">Grand Total</div>
<div className="text-3xl font-bold">Rp {(totalCost/1000000).toFixed(1)} juta</div>
</div>
</div>
{/* Notes */}
<div className="mt-6 p-4 bg-yellow-50 rounded-lg">
<h4 className="font-semibold text-yellow-800 mb-2">Catatan Penting:</h4>
<ul className="text-sm text-yellow-700 space-y-1">
<li>• Estimasi berdasarkan developer rate Rp 150k/jam</li>
<li>• Data real-time IDX memerlukan API berbayar untuk akurasi tinggi</li>
<li>• Timeline bisa berubah tergantung kompleksitas requirement tambahan</li>
<li>• Maintenance & support belum termasuk dalam estimasi</li>
</ul>
</div>
</div>
); };
export default StockAppEstimate;