+64 27 7250 007
// ============================================================================ // FOCUS360 FULL-STACK MONOLITHIC FILE SETUP // Save this file as setup.js and run: node setup.js // It will generate the complete folder structure and all code files automatically. // ============================================================================ const fs = require('fs'); const path = require('path'); const files = { // -------------------------------------------------------------------------- // BACKEND FILES // -------------------------------------------------------------------------- 'backend/package.json': JSON.stringify({ "name": "focus360-backend", "version": "1.0.0", "main": "server.js", "scripts": { "start": "node server.js", "dev": "nodemon server.js" }, "dependencies": { "bcryptjs": "^2.4.3", "cors": "^2.8.5", "dotenv": "^16.4.5", "express": "^4.19.2", "jsonwebtoken": "^9.0.2", "mongoose": "^8.2.0" } }, null, 2), 'backend/.env': `PORT=5000\nMONGO_URI=mongodb://localhost:27017/focus360\nJWT_SECRET=focus360_super_secret_jwt_key\n`, 'backend/models/User.js': `const mongoose = require('mongoose'); const userSchema = new mongoose.Schema({ email: { type: String, required: true, unique: true }, password: { type: String, required: true }, createdAt: { type: Date, default: Date.now } }); module.exports = mongoose.model('User', userSchema); `, 'backend/models/Trade.js': `const mongoose = require('mongoose'); const tradeSchema = new mongoose.Schema({ userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true }, symbol: { type: String, required: true }, type: { type: String, enum: ['BUY', 'SELL'], required: true }, entryPrice: { type: Number, required: true }, exitPrice: { type: Number, default: null }, pnl: { type: Number, default: 0 }, status: { type: String, enum: ['OPEN', 'CLOSED'], default: 'OPEN' }, createdAt: { type: Date, default: Date.now } }); module.exports = mongoose.model('Trade', tradeSchema); `, 'backend/middleware/auth.js': `const jwt = require('jsonwebtoken'); module.exports = function (req, res, next) { const token = req.header('Authorization')?.replace('Bearer ', ''); if (!token) return res.status(401).json({ message: 'Access denied. No token provided.' }); try { const verified = jwt.verify(token, process.env.JWT_SECRET); req.user = verified; next(); } catch (err) { res.status(400).json({ message: 'Invalid token.' }); } }; `, 'backend/server.js': `const express = require('express'); const mongoose = require('mongoose'); const cors = require('cors'); const bcrypt = require('bcryptjs'); const jwt = require('jsonwebtoken'); require('dotenv').config(); const User = require('./models/User'); const Trade = require('./models/Trade'); const auth = require('./middleware/auth'); const app = express(); app.use(express.json()); app.use(cors()); mongoose.connect(process.env.MONGO_URI) .then(() => console.log('MongoDB Connected')) .catch(err => console.error('DB Error:', err)); app.post('/api/auth/register', async (req, res) => { try { const { email, password } = req.body; let user = await User.findOne({ email }); if (user) return res.status(400).json({ message: 'User already exists' }); const salt = await bcrypt.genSalt(10); const hashedPassword = await bcrypt.hash(password, salt); user = new User({ email, password: hashedPassword }); await user.save(); const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: '7d' }); res.json({ token, user: { id: user._id, email: user.email } }); } catch (err) { res.status(500).json({ message: 'Server error' }); } }); app.post('/api/auth/login', async (req, res) => { try { const { email, password } = req.body; const user = await User.findOne({ email }); if (!user) return res.status(400).json({ message: 'Invalid credentials' }); const isMatch = await bcrypt.compare(password, user.password); if (!isMatch) return res.status(400).json({ message: 'Invalid credentials' }); const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: '7d' }); res.json({ token, user: { id: user._id, email: user.email } }); } catch (err) { res.status(500).json({ message: 'Server error' }); } }); app.get('/api/trades', auth, async (req, res) => { try { const trades = await Trade.find({ userId: req.user.id }).sort({ createdAt: -1 }); res.json(trades); } catch (err) { res.status(500).json({ message: 'Error fetching trades' }); } }); app.post('/api/trades', auth, async (req, res) => { try { const { symbol, type, entryPrice, exitPrice, pnl, status } = req.body; const newTrade = new Trade({ userId: req.user.id, symbol, type, entryPrice, exitPrice, pnl, status }); await newTrade.save(); res.json(newTrade); } catch (err) { res.status(500).json({ message: 'Error saving trade' }); } }); const PORT = process.env.PORT || 5000; app.listen(PORT, () => console.log(\`Focus360 Server running on port \${PORT}\`)); `, // -------------------------------------------------------------------------- // FRONTEND FILES // -------------------------------------------------------------------------- 'frontend/package.json': JSON.stringify({ "name": "focus360-frontend", "version": "1.0.0", "main": "node_modules/expo/AppEntry.js", "scripts": { "start": "expo start", "android": "expo start --android", "ios": "expo start --ios", "web": "expo start --web" }, "dependencies": { "@react-native-async-storage/async-storage": "~1.21.0", "expo": "~50.0.0", "react": "18.2.0", "react-native": "0.73.4", "react-native-svg": "14.1.0" } }, null, 2), 'frontend/context/AuthContext.js': `import React, { createContext, useState, useEffect } from 'react'; import AsyncStorage from '@react-native-async-storage/async-storage'; export const AuthContext = createContext(); export const AuthProvider = ({ children }) => { const [userToken, setUserToken] = useState(null); const [loading, setLoading] = useState(true); const API_URL = 'http://localhost:5000/api/auth'; useEffect(() => { const loadToken = async () => { const token = await AsyncStorage.getItem('userToken'); setUserToken(token); setLoading(false); }; loadToken(); }, []); const login = async (email, password) => { const res = await fetch(\`\${API_URL}/login\`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }), }); const data = await res.json(); if (data.token) { await AsyncStorage.setItem('userToken', data.token); setUserToken(data.token); } else { throw new Error(data.message || 'Login failed'); } }; const logout = async () => { await AsyncStorage.removeItem('userToken'); setUserToken(null); }; return ( {children} ); }; `, 'frontend/components/PnlChart.js': `import React from 'react'; import { View, Text, StyleSheet } from 'react-native'; import Svg, { Polyline, Line } from 'react-native-svg'; export default function PnlChart({ trades }) { if (!trades || trades.length === 0) { return No trade data available for chart; } let cumulativePnl = 0; const pnlPoints = trades.map((t, idx) => { cumulativePnl += t.pnl || 0; const x = (idx / Math.max(trades.length - 1, 1)) * 300; const y = 100 - Math.min(Math.max(cumulativePnl, -100), 100); return \`\${x},\${y}\`; }).join(' '); return ( Cumulative Performance ); } const styles = StyleSheet.create({ container: { backgroundColor: '#1A1D24', padding: 16, borderRadius: 8, alignItems: 'center', marginVertical: 10 }, title: { color: '#FFF', fontWeight: 'bold', marginBottom: 10 }, emptyText: { color: '#888', textAlign: 'center', marginVertical: 20 } }); `, 'frontend/screens/LoginScreen.js': `import React, { useState, useContext } from 'react'; import { View, Text, TextInput, TouchableOpacity, StyleSheet, Alert } from 'react-native'; import { AuthContext } from '../context/AuthContext'; export default function LoginScreen() { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const { login } = useContext(AuthContext); const handleLogin = async () => { try { await login(email, password); } catch (err) { Alert.alert('Error', err.message); } }; return ( FOCUS 360 SIGN IN ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#0D0F12', justifyContent: 'center', padding: 24 }, logo: { fontSize: 32, fontWeight: '900', color: '#00E676', textAlign: 'center', marginBottom: 40, letterSpacing: 2 }, input: { backgroundColor: '#1A1D24', color: '#FFF', padding: 16, borderRadius: 8, marginBottom: 16 }, button: { backgroundColor: '#00E676', padding: 16, borderRadius: 8, alignItems: 'center', marginTop: 10 }, buttonText: { color: '#000', fontWeight: 'bold', fontSize: 16 } }); `, 'frontend/screens/DashboardScreen.js': `import React, { useState, useEffect, useContext } from 'react'; import { View, Text, FlatList, TouchableOpacity, StyleSheet } from 'react-native'; import { AuthContext } from '../context/AuthContext'; import AsyncStorage from '@react-native-async-storage/async-storage'; import PnlChart from '../components/PnlChart'; export default function DashboardScreen({ onNavigate }) { const { logout } = useContext(AuthContext); const [trades, setTrades] = useState([]); useEffect(() => { fetchTrades(); }, []); const fetchTrades = async () => { const token = await AsyncStorage.getItem('userToken'); const res = await fetch('http://localhost:5000/api/trades', { headers: { Authorization: \`Bearer \${token}\` } }); const data = await res.json(); if (Array.isArray(data)) setTrades(data); }; return ( Focus360 Dashboard Logout onNavigate('TradeEntry')}> + Log New Trade item._id} renderItem={({ item }) => ( {item.symbol} ({item.type}) Entry: \${item.entryPrice} = 0 ? '#00E676' : '#FF5252' }]}> {item.pnl >= 0 ? \`+\$\${item.pnl}\` : \`-\$\${Math.abs(item.pnl)}\`} )} /> ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#0D0F12', padding: 20, paddingTop: 50 }, header: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 }, headerTitle: { color: '#FFF', fontSize: 20, fontWeight: 'bold' }, logout: { color: '#FF5252' }, addBtn: { backgroundColor: '#00E676', padding: 12, borderRadius: 6, alignItems: 'center', marginVertical: 15 }, addBtnText: { color: '#000', fontWeight: 'bold' }, tradeCard: { backgroundColor: '#1A1D24', padding: 16, borderRadius: 8, flexDirection: 'row', justifyContent: 'space-between', marginBottom: 10 }, symbol: { color: '#FFF', fontWeight: 'bold', fontSize: 16 }, details: { color: '#888', fontSize: 12, marginTop: 4 }, pnl: { fontWeight: 'bold', fontSize: 16 } }); `, 'frontend/screens/TradeEntryScreen.js': `import React, { useState } from 'react'; import { View, Text, TextInput, TouchableOpacity, StyleSheet, Alert } from 'react-native'; import AsyncStorage from '@react-native-async-storage/async-storage'; export default function TradeEntryScreen({ onBack }) { const [symbol, setSymbol] = useState(''); const [type, setType] = useState('BUY'); const [entryPrice, setEntryPrice] = useState(''); const [pnl, setPnl] = useState(''); const handleSave = async () => { if (!symbol || !entryPrice) { Alert.alert('Missing Info', 'Please enter a symbol and entry price'); return; } const token = await AsyncStorage.getItem('userToken'); const res = await fetch('http://localhost:5000/api/trades', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: \`Bearer \${token}\` }, body: JSON.stringify({ symbol: symbol.toUpperCase(), type, entryPrice: parseFloat(entryPrice), pnl: parseFloat(pnl) || 0, status: 'CLOSED' }) }); if (res.ok) { onBack(); } else { Alert.alert('Error', 'Failed to save trade'); } }; return ( ← Back Log Trade setType('BUY')}> BUY setType('SELL')}> SELL Save Trade ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#0D0F12', padding: 20, paddingTop: 50 }, back: { color: '#00E676', fontSize: 16, marginBottom: 20 }, title: { color: '#FFF', fontSize: 24, fontWeight: 'bold', marginBottom: 20 }, input: { backgroundColor: '#1A1D24', color: '#FFF', padding: 16, borderRadius: 8, marginBottom: 12 }, typeContainer: { flexDirection: 'row', gap: 10, marginVertical: 12 }, typeBtn: { flex: 1, padding: 14, backgroundColor: '#1A1D24', borderRadius: 8, alignItems: 'center' }, selectedBuy: { backgroundColor: '#00E676' }, selectedSell: { backgroundColor: '#FF5252' }, typeText: { color: '#FFF', fontWeight: 'bold' }, saveBtn: { backgroundColor: '#00E676', padding: 16, borderRadius: 8, alignItems: 'center', marginTop: 20 }, saveText: { color: '#000', fontWeight: 'bold', fontSize: 16 } }); `, 'frontend/App.js': `import React, { useContext, useState } from 'react'; import { AuthProvider, AuthContext } from './context/AuthContext'; import LoginScreen from './screens/LoginScreen'; import DashboardScreen from './screens/DashboardScreen'; import TradeEntryScreen from './screens/TradeEntryScreen'; function MainNavigator() { const { userToken } = useContext(AuthContext); const [screen, setScreen] = useState('Dashboard'); if (!userToken) { return ; } if (screen === 'TradeEntry') { return setScreen('Dashboard')} />; } return ; } export default function App() { return ( ); } ` }; // Automatic extraction execution block Object.entries(files).forEach(([filePath, content]) => { const dir = path.dirname(filePath); if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } fs.writeFileSync(filePath, content, 'utf8'); console.log(`Created: ${filePath}`); }); console.log('\nFocus360 Codebase Setup Complete!');