diff --git a/.vscode/tasks.json b/.vscode/tasks.json
deleted file mode 100644
index f42e096..0000000
--- a/.vscode/tasks.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "version": "2.0.0",
- "tasks": [
- {
- "label": "Start Arma 3 Server",
- "type": "process",
- "command": "wscript.exe",
- "args": [
- "D:\\SteamLibrary\\steamapps\\common\\Arma 3\\start_serverhub_hidden.vbs"
- ],
- "presentation": {
- "reveal": "silent",
- "panel": "shared",
- "showReuseMessage": false,
- "clear": true
- },
- "problemMatcher": [],
- "group": {
- "kind": "build",
- "isDefault": true
- }
- }
- ]
-}
\ No newline at end of file
diff --git a/addons/admin/ui/_site/index.html b/addons/admin/ui/_site/index.html
new file mode 100644
index 0000000..3235c91
--- /dev/null
+++ b/addons/admin/ui/_site/index.html
@@ -0,0 +1,107 @@
+
+
+
+
+
+ Forge Admin Panel
+
+
+
+
+
+
+
+
+
+
Global Actions
+
+
+
+
+
+
+
+
+
+
Broadcast Message
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/addons/admin/ui/_site/script.js b/addons/admin/ui/_site/script.js
new file mode 100644
index 0000000..9dc0b9c
--- /dev/null
+++ b/addons/admin/ui/_site/script.js
@@ -0,0 +1,228 @@
+// Simulated admin data - this would be replaced with actual game data
+let adminData = {
+ players: [
+ {
+ id: 1,
+ name: "John_Doe",
+ role: "admin",
+ money: 50000,
+ status: "online"
+ },
+ {
+ id: 2,
+ name: "Jane_Smith",
+ role: "mod",
+ money: 25000,
+ status: "online"
+ },
+ {
+ id: 3,
+ name: "Mike_Johnson",
+ role: "player",
+ money: 10000,
+ status: "offline"
+ }
+ ],
+ paydayAmount: 1000
+};
+
+let selectedPlayerId = null;
+
+// Initialize the admin panel
+function initializeAdmin() {
+ updateStats();
+ setupFilterListeners();
+ updatePlayerList();
+}
+
+// Update header statistics
+function updateStats() {
+ const onlinePlayers = adminData.players.filter(p => p.status === "online").length;
+ const onlineStaff = adminData.players.filter(p => p.status === "online" && (p.role === "admin" || p.role === "mod")).length;
+
+ document.getElementById('playerCount').textContent = onlinePlayers;
+ document.getElementById('staffCount').textContent = onlineStaff;
+}
+
+// Set up filter button listeners
+function setupFilterListeners() {
+ const filterButtons = document.querySelectorAll('.filter-btn');
+
+ filterButtons.forEach(button => {
+ button.addEventListener('click', () => {
+ filterButtons.forEach(btn => btn.classList.remove('active'));
+ button.classList.add('active');
+ filterPlayers(button.dataset.filter);
+ });
+ });
+
+ // Set up search functionality
+ const searchInput = document.getElementById('playerSearch');
+ searchInput.addEventListener('input', () => {
+ const activeFilter = document.querySelector('.filter-btn.active').dataset.filter;
+ filterPlayers(activeFilter, searchInput.value);
+ });
+}
+
+// Filter players based on category and search term
+function filterPlayers(filter, searchTerm = '') {
+ let filteredPlayers = adminData.players;
+
+ // Apply category filter
+ if (filter === 'online') {
+ filteredPlayers = filteredPlayers.filter(p => p.status === 'online');
+ } else if (filter === 'staff') {
+ filteredPlayers = filteredPlayers.filter(p => p.role === 'admin' || p.role === 'moderator');
+ }
+
+ // Apply search filter
+ if (searchTerm) {
+ const term = searchTerm.toLowerCase();
+ filteredPlayers = filteredPlayers.filter(p =>
+ p.name.toLowerCase().includes(term)
+ );
+ }
+
+ updatePlayerList(filteredPlayers);
+}
+
+// Update the player list display
+function updatePlayerList(players = adminData.players) {
+ const playerList = document.getElementById('playerList');
+ playerList.innerHTML = players.map(player => `
+
+
+ ${player.name}
+ ${player.role}
+ ${player.status}
+ $${player.money.toLocaleString()}
+
+
+ ${player.role !== 'admin' ? `
+
+ ` : ''}
+ ${player.role !== 'player' ? `
+
+ ` : ''}
+
+
+
+
+ `).join('');
+}
+
+// Role management functions
+function promotePlayer(playerId) {
+ const player = adminData.players.find(p => p.id === playerId);
+ if (player) {
+ if (player.role === 'player') {
+ player.role = 'moderator';
+ } else if (player.role === 'moderator') {
+ player.role = 'admin';
+ }
+ updatePlayerList();
+ }
+}
+
+function demotePlayer(playerId) {
+ const player = adminData.players.find(p => p.id === playerId);
+ if (player) {
+ if (player.role === 'admin') {
+ player.role = 'moderator';
+ } else if (player.role === 'moderator') {
+ player.role = 'player';
+ }
+ updatePlayerList();
+ }
+}
+
+// Money management functions
+function openMoneyModal(playerId) {
+ selectedPlayerId = playerId;
+ const modal = document.getElementById('moneyModal');
+ modal.style.display = 'block';
+}
+
+function closeMoneyModal() {
+ const modal = document.getElementById('moneyModal');
+ modal.style.display = 'none';
+ document.getElementById('moneyAmount').value = '';
+ selectedPlayerId = null;
+}
+
+function giveMoney() {
+ const amount = parseInt(document.getElementById('moneyAmount').value);
+ if (amount && selectedPlayerId) {
+ const player = adminData.players.find(p => p.id === selectedPlayerId);
+ if (player) {
+ player.money += amount;
+ updatePlayerList();
+ closeMoneyModal();
+ }
+ }
+}
+
+function takeMoney() {
+ const amount = parseInt(document.getElementById('moneyAmount').value);
+ if (amount && selectedPlayerId) {
+ const player = adminData.players.find(p => p.id === selectedPlayerId);
+ if (player) {
+ player.money = Math.max(0, player.money - amount);
+ updatePlayerList();
+ closeMoneyModal();
+ }
+ }
+}
+
+// Message system functions
+function openMessageModal(playerId) {
+ selectedPlayerId = playerId;
+ const player = adminData.players.find(p => p.id === playerId);
+ const modal = document.getElementById('messageModal');
+ document.getElementById('messagePlayerName').textContent = player.name;
+ modal.style.display = 'block';
+}
+
+function closeMessageModal() {
+ const modal = document.getElementById('messageModal');
+ modal.style.display = 'none';
+ document.getElementById('messageInput').value = '';
+ selectedPlayerId = null;
+}
+
+function sendPlayerMessage() {
+ const message = document.getElementById('messageInput').value;
+ if (message && selectedPlayerId) {
+ const player = adminData.players.find(p => p.id === selectedPlayerId);
+ if (player) {
+ console.log(`Message sent to ${player.name}: ${message}`);
+ closeMessageModal();
+ }
+ }
+}
+
+function broadcastMessage() {
+ const message = document.getElementById('broadcastMessage').value;
+ if (message) {
+ console.log(`Broadcasting message to all players: ${message}`);
+ document.getElementById('broadcastMessage').value = '';
+ }
+}
+
+// Global actions
+function triggerPayday() {
+ const amount = parseInt(document.getElementById('paydayAmount').value);
+ if (amount) {
+ adminData.players.forEach(player => {
+ player.money += amount;
+ });
+ updatePlayerList();
+ }
+}
+
+// Initialize when DOM is loaded
+document.addEventListener('DOMContentLoaded', initializeAdmin);
diff --git a/addons/admin/ui/_site/styles.css b/addons/admin/ui/_site/styles.css
new file mode 100644
index 0000000..9539122
--- /dev/null
+++ b/addons/admin/ui/_site/styles.css
@@ -0,0 +1,313 @@
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: Arial, sans-serif;
+ line-height: 1.6;
+ background-color: #f4f4f4;
+}
+
+.container {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 20px;
+}
+
+header {
+ background-color: #333;
+ color: white;
+ padding: 1rem 0;
+ margin-bottom: 2rem;
+}
+
+.header-content {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.admin-stats {
+ display: flex;
+ gap: 2rem;
+}
+
+.stat-item {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ background: rgba(255, 255, 255, 0.1);
+ padding: 0.5rem 1rem;
+ border-radius: 4px;
+}
+
+.sections-grid {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 2rem;
+ padding: 1rem;
+}
+
+.action-sections {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 2rem;
+}
+
+.admin-section {
+ background-color: white;
+ border-radius: 8px;
+ overflow: hidden;
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
+ padding: 1.5rem;
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+}
+
+.player-list-section {
+ grid-column: span 2;
+ grid-row: span 2;
+}
+
+.player-list {
+ list-style: none;
+ overflow-y: auto;
+ flex-grow: 1;
+ height: 0;
+ min-height: 400px;
+}
+
+.player-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 1rem;
+ border-bottom: 1px solid #eee;
+ transition: background-color 0.3s;
+}
+
+.player-item:hover {
+ background-color: #f8f9fa;
+}
+
+.player-info {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+ flex: 1;
+}
+
+.player-name {
+ font-weight: 500;
+}
+
+.player-role {
+ font-size: 0.875rem;
+ padding: 0.25rem 0.5rem;
+ border-radius: 4px;
+ font-weight: bold;
+}
+
+.role-admin {
+ background-color: #dc3545;
+ color: white;
+}
+
+.role-mod {
+ background-color: #ffc107;
+ color: black;
+}
+
+.role-player {
+ background-color: #28a745;
+ color: white;
+}
+
+.player-money {
+ font-size: 0.875rem;
+ color: #28a745;
+}
+
+.player-actions {
+ display: flex;
+ gap: 0.5rem;
+}
+
+.action-btn {
+ padding: 0.5rem 1rem;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ font-weight: 500;
+ transition: background-color 0.3s;
+}
+
+.action-btn:disabled {
+ background-color: #6c757d;
+ cursor: not-allowed;
+ opacity: 0.65;
+}
+
+.promote-btn {
+ background-color: #28a745;
+ color: white;
+}
+
+.promote-btn:hover:not(:disabled) {
+ background-color: #218838;
+}
+
+.demote-btn {
+ background-color: #dc3545;
+ color: white;
+}
+
+.demote-btn:hover:not(:disabled) {
+ background-color: #c82333;
+}
+
+.message-btn {
+ background-color: #007bff;
+ color: white;
+}
+
+.message-btn:hover:not(:disabled) {
+ background-color: #0056b3;
+}
+
+.search-bar {
+ display: flex;
+ gap: 1rem;
+ margin-bottom: 1rem;
+}
+
+.search-input {
+ flex: 1;
+ padding: 0.5rem;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ font-size: 1rem;
+}
+
+.form-group {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+ margin-bottom: 1rem;
+}
+
+.form-group label {
+ font-weight: 500;
+ color: #333;
+}
+
+.form-group input {
+ padding: 0.5rem;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ font-size: 1rem;
+}
+
+.submit-btn {
+ background-color: #007bff;
+ color: white;
+ padding: 0.75rem 1rem;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ font-weight: bold;
+ transition: background-color 0.3s;
+}
+
+.submit-btn:hover {
+ background-color: #0056b3;
+}
+
+.modal {
+ display: none;
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: rgba(0, 0, 0, 0.5);
+ z-index: 1000;
+}
+
+.modal-content {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ background-color: white;
+ padding: 2rem;
+ border-radius: 8px;
+ min-width: 400px;
+}
+
+.modal-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 1rem;
+}
+
+.modal-header h2 {
+ margin: 0;
+}
+
+.close-modal {
+ background: none;
+ border: none;
+ font-size: 1.5rem;
+ cursor: pointer;
+ color: #666;
+}
+
+.close-modal:hover {
+ color: #333;
+}
+
+.badge {
+ padding: 0.25rem 0.5rem;
+ border-radius: 4px;
+ font-size: 0.875rem;
+ font-weight: bold;
+}
+
+.badge-online {
+ background-color: #28a745;
+ color: white;
+}
+
+.badge-offline {
+ background-color: #dc3545;
+ color: white;
+}
+
+.filter-bar {
+ display: flex;
+ gap: 1rem;
+ margin-bottom: 1rem;
+}
+
+.filter-btn {
+ padding: 0.5rem 1rem;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ background-color: #444;
+ color: white;
+ transition: background-color 0.3s;
+}
+
+.filter-btn:hover {
+ background-color: #666;
+}
+
+.filter-btn.active {
+ background-color: #007bff;
+}
diff --git a/addons/bank/ui/_site/index.html b/addons/bank/ui/_site/index.html
new file mode 100644
index 0000000..6a6f867
--- /dev/null
+++ b/addons/bank/ui/_site/index.html
@@ -0,0 +1,99 @@
+
+
+
+
+
+ FORGE - FDIC
+
+
+
+
+
+
+
+
+
+
+
+ Transaction History
+
+
+
+
+
diff --git a/addons/bank/ui/_site/public/fdic.png b/addons/bank/ui/_site/public/fdic.png
new file mode 100644
index 0000000..7845bec
Binary files /dev/null and b/addons/bank/ui/_site/public/fdic.png differ
diff --git a/addons/bank/ui/_site/public/fdic_co.paa b/addons/bank/ui/_site/public/fdic_co.paa
new file mode 100644
index 0000000..45fe964
Binary files /dev/null and b/addons/bank/ui/_site/public/fdic_co.paa differ
diff --git a/addons/bank/ui/_site/public/fms.png b/addons/bank/ui/_site/public/fms.png
new file mode 100644
index 0000000..553b09a
Binary files /dev/null and b/addons/bank/ui/_site/public/fms.png differ
diff --git a/addons/bank/ui/_site/public/fms_co.paa b/addons/bank/ui/_site/public/fms_co.paa
new file mode 100644
index 0000000..9d32a24
Binary files /dev/null and b/addons/bank/ui/_site/public/fms_co.paa differ
diff --git a/addons/bank/ui/_site/public/gms.png b/addons/bank/ui/_site/public/gms.png
new file mode 100644
index 0000000..a4717b2
Binary files /dev/null and b/addons/bank/ui/_site/public/gms.png differ
diff --git a/addons/bank/ui/_site/public/gms_co.paa b/addons/bank/ui/_site/public/gms_co.paa
new file mode 100644
index 0000000..ddc6d35
Binary files /dev/null and b/addons/bank/ui/_site/public/gms_co.paa differ
diff --git a/addons/bank/ui/_site/script.js b/addons/bank/ui/_site/script.js
new file mode 100644
index 0000000..21d9dcd
--- /dev/null
+++ b/addons/bank/ui/_site/script.js
@@ -0,0 +1,147 @@
+// Simulated data - this would be replaced with actual game data
+let bankState = {
+ wallet: 1000,
+ account: 5000,
+ players: [
+ { id: 1, name: "Player 1" },
+ { id: 2, name: "Player 2" },
+ { id: 3, name: "Player 3" }
+ ],
+ transactions: []
+};
+
+// Initialize the interface
+function initializeBank() {
+ updateBalanceDisplays();
+ populatePlayerList();
+ setupEventListeners();
+ loadTransactionHistory();
+}
+
+// Update balance displays in the header
+function updateBalanceDisplays() {
+ document.getElementById('walletBalance').textContent = `$${bankState.wallet.toLocaleString()}`;
+ document.getElementById('accountBalance').textContent = `$${bankState.account.toLocaleString()}`;
+}
+
+// Populate the player selection dropdown
+function populatePlayerList() {
+ const playerSelect = document.getElementById('playerSelect');
+ playerSelect.innerHTML = '';
+
+ bankState.players.forEach(player => {
+ const option = document.createElement('option');
+ option.value = player.id;
+ option.textContent = player.name;
+ playerSelect.appendChild(option);
+ });
+}
+
+// Add a new transaction to history
+function addTransaction(type, amount, details = '') {
+ const transaction = {
+ type,
+ amount,
+ details,
+ timestamp: new Date().toISOString()
+ };
+
+ bankState.transactions.unshift(transaction);
+ updateTransactionHistory();
+}
+
+// Update the transaction history display
+function updateTransactionHistory() {
+ const historyList = document.getElementById('transactionHistory');
+ historyList.innerHTML = '';
+
+ bankState.transactions.forEach(transaction => {
+ const li = document.createElement('li');
+ li.className = 'history-item';
+
+ const isNegative = ['transfer_out', 'to_wallet'].includes(transaction.type);
+ const amountClass = isNegative ? 'amount-negative' : 'amount-positive';
+ const amountPrefix = isNegative ? '-' : '+';
+
+ li.innerHTML = `
+ ${formatTransactionType(transaction.type)}
+ ${transaction.details}
+ ${amountPrefix}$${Math.abs(transaction.amount).toLocaleString()}
+ `;
+
+ historyList.appendChild(li);
+ });
+}
+
+// Format transaction type for display
+function formatTransactionType(type) {
+ const types = {
+ 'to_wallet': 'To Wallet',
+ 'to_account': 'To Account',
+ 'transfer_out': 'Transfer Out',
+ 'transfer_in': 'Transfer In',
+ 'timesheet': 'Timesheet'
+ };
+ return types[type] || type;
+}
+
+// Set up all form event listeners
+function setupEventListeners() { // Handle transfers between wallet and account
+ document.getElementById('transferForm').addEventListener('submit', (e) => {
+ e.preventDefault();
+ const amount = parseInt(document.getElementById('transferAmount').value);
+ const transferType = document.getElementById('transferType').value;
+
+ if (transferType === 'to_wallet') {
+ if (amount > bankState.account) {
+ alert('Insufficient funds in account');
+ return;
+ }
+ bankState.account -= amount;
+ bankState.wallet += amount;
+ } else {
+ if (amount > bankState.wallet) {
+ alert('Insufficient funds in wallet');
+ return;
+ }
+ bankState.wallet -= amount;
+ bankState.account += amount;
+ }
+
+ addTransaction(transferType, amount);
+ updateBalanceDisplays();
+ e.target.reset();
+ }); // Transfer to Player
+ document.getElementById('transferPlayerForm').addEventListener('submit', (e) => {
+ e.preventDefault();
+ const amount = parseInt(document.getElementById('playerTransferAmount').value);
+ const playerId = document.getElementById('playerSelect').value;
+ const playerName = bankState.players.find(p => p.id.toString() === playerId)?.name;
+
+ if (amount > bankState.account) {
+ alert('Insufficient funds in account');
+ return;
+ }
+
+ bankState.account -= amount;
+ addTransaction('transfer_out', amount, `To ${playerName}`);
+ updateBalanceDisplays();
+ e.target.reset();
+ });
+
+ // Submit Timesheet
+ document.getElementById('timesheetForm').addEventListener('submit', (e) => {
+ e.preventDefault();
+ const hours = parseFloat(document.getElementById('hoursWorked').value);
+ const rate = parseInt(document.getElementById('hourlyRate').value);
+ const amount = Math.floor(hours * rate);
+
+ bankState.account += amount;
+ addTransaction('timesheet', amount, `${hours} hours @ $${rate}/hr`);
+ updateBalanceDisplays();
+ e.target.reset();
+ });
+}
+
+// Initialize when DOM is loaded
+document.addEventListener('DOMContentLoaded', initializeBank);
diff --git a/addons/bank/ui/_site/styles.css b/addons/bank/ui/_site/styles.css
new file mode 100644
index 0000000..1486173
--- /dev/null
+++ b/addons/bank/ui/_site/styles.css
@@ -0,0 +1,163 @@
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: Arial, sans-serif;
+ line-height: 1.6;
+ background-color: #f4f4f4;
+}
+
+.container {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 20px;
+}
+
+header {
+ background-color: #333;
+ color: white;
+ padding: 1rem 0;
+ margin-bottom: 2rem;
+}
+
+.header-content {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.balance-display {
+ display: flex;
+ gap: 2rem;
+ margin-left: auto;
+}
+
+.balance-item {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ background: rgba(255, 255, 255, 0.1);
+ padding: 0.5rem 1rem;
+ border-radius: 4px;
+}
+
+.balance-item i {
+ font-size: 1.2rem;
+}
+
+.actions-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+ gap: 2rem;
+ padding: 1rem;
+}
+
+.action-tile {
+ background-color: white;
+ border-radius: 8px;
+ overflow: hidden;
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
+ transition: transform 0.3s;
+ aspect-ratio: 1;
+ padding: 2rem;
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+}
+
+.action-tile:hover {
+ transform: translateY(-5px);
+}
+
+.action-tile h2 {
+ font-size: 1.5rem;
+ color: #333;
+ margin-bottom: 1rem;
+}
+
+.form-group {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+ margin-bottom: 1rem;
+}
+
+.form-group label {
+ font-weight: bold;
+ color: #555;
+}
+
+.form-group input, .form-group select {
+ padding: 0.5rem;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ font-size: 1rem;
+}
+
+.submit-btn {
+ background-color: #007bff;
+ color: white;
+ border: none;
+ border-radius: 4px;
+ padding: 0.75rem 1rem;
+ font-size: 1rem;
+ cursor: pointer;
+ transition: background-color 0.3s;
+ margin-top: auto;
+}
+
+.submit-btn:hover {
+ background-color: #0056b3;
+}
+
+.history-section {
+ background: white;
+ border-radius: 8px;
+ padding: 2rem;
+ margin-top: 2rem;
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
+}
+
+.history-list {
+ list-style: none;
+ margin-top: 1rem;
+}
+
+.history-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 1rem;
+ border-bottom: 1px solid #eee;
+}
+
+.history-item:last-child {
+ border-bottom: none;
+}
+
+.transaction-type {
+ font-weight: bold;
+}
+
+.amount-positive {
+ color: #28a745;
+}
+
+.amount-negative {
+ color: #dc3545;
+}
+
+.error-message {
+ color: #dc3545;
+ font-size: 0.875rem;
+ margin-top: 0.25rem;
+}
+
+.success-message {
+ color: #28a745;
+ font-size: 0.875rem;
+ margin-top: 0.25rem;
+}
diff --git a/addons/garage/ui/_site/index.html b/addons/garage/ui/_site/index.html
new file mode 100644
index 0000000..1d73626
--- /dev/null
+++ b/addons/garage/ui/_site/index.html
@@ -0,0 +1,45 @@
+
+
+
+
+
+ FORGE - Garage
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/garage/ui/_site/script.js b/addons/garage/ui/_site/script.js
new file mode 100644
index 0000000..a0d882f
--- /dev/null
+++ b/addons/garage/ui/_site/script.js
@@ -0,0 +1,184 @@
+// Simulated garage data - this would be replaced with actual game data
+let garageData = {
+ vehicles: [
+ {
+ id: 1,
+ name: "Offroad Vehicle",
+ category: "Land",
+ type: "Transport",
+ status: "available",
+ fuel: 100,
+ damage: 0,
+ maintenance: 95,
+ lastUsed: "2025-04-10T15:30:00",
+ image: "placeholder.jpg"
+ },
+ {
+ id: 2,
+ name: "Transport Helicopter",
+ category: "Air",
+ type: "Transport",
+ status: "in-use",
+ fuel: 75,
+ damage: 15,
+ maintenance: 80,
+ lastUsed: "2025-04-12T09:15:00",
+ image: "placeholder.jpg"
+ },
+ {
+ id: 3,
+ name: "Patrol Boat",
+ category: "Sea",
+ type: "Patrol",
+ status: "maintenance",
+ fuel: 50,
+ damage: 35,
+ maintenance: 45,
+ lastUsed: "2025-04-11T18:45:00",
+ image: "placeholder.jpg"
+ },
+ {
+ id: 4,
+ name: "Armed SUV",
+ category: "Land",
+ type: "Combat",
+ status: "available",
+ fuel: 90,
+ damage: 5,
+ maintenance: 88,
+ lastUsed: "2025-04-12T11:20:00",
+ image: "placeholder.jpg"
+ }
+ ]
+};
+
+// Initialize the garage interface
+function initializeGarage() {
+ updateStats();
+ setupCategoryFilters();
+ displayVehicles();
+}
+
+// Update garage statistics
+function updateStats() {
+ const totalVehicles = garageData.vehicles.length;
+ const inMaintenance = garageData.vehicles.filter(v => v.status === 'maintenance').length;
+
+ document.getElementById('vehicleCount').textContent = totalVehicles;
+ document.getElementById('maintenanceCount').textContent = inMaintenance;
+}
+
+// Set up category filters
+function setupCategoryFilters() {
+ const categories = ['All', ...new Set(garageData.vehicles.map(v => v.category))];
+ const filtersContainer = document.getElementById('categoryFilters');
+
+ categories.forEach(category => {
+ const button = document.createElement('button');
+ button.className = 'filter-btn' + (category === 'All' ? ' active' : '');
+ button.textContent = category;
+ button.addEventListener('click', () => filterVehicles(category));
+ filtersContainer.appendChild(button);
+ });
+}
+
+// Filter vehicles by category
+function filterVehicles(category) {
+ // Update active filter button
+ document.querySelectorAll('.filter-btn').forEach(btn => {
+ btn.classList.toggle('active', btn.textContent === category);
+ });
+
+ // Filter and display vehicles
+ const filteredVehicles = category === 'All'
+ ? garageData.vehicles
+ : garageData.vehicles.filter(v => v.category === category);
+
+ displayVehicles(filteredVehicles);
+}
+
+// Display vehicles in the grid
+function displayVehicles(vehicles = garageData.vehicles) {
+ const grid = document.getElementById('vehiclesGrid');
+ grid.innerHTML = '';
+
+ vehicles.forEach(vehicle => {
+ const card = document.createElement('div');
+ card.className = 'vehicle-card';
+
+ const statusText = {
+ 'available': 'Available',
+ 'in-use': 'In Use',
+ 'maintenance': 'Maintenance'
+ }[vehicle.status];
+
+ card.innerHTML = `
+
+
+
+
+
+ Type
+ ${vehicle.type}
+
+
+ Category
+ ${vehicle.category}
+
+
+
Fuel
+
+
+ ${vehicle.fuel}%
+
+
+
+
Condition
+
+
+ ${vehicle.maintenance}%
+
+
+
+
+
+
+
+ `;
+
+ grid.appendChild(card);
+ });
+}
+
+// Get color class for fuel status
+function getFuelStatusColor(fuelLevel) {
+ if (fuelLevel > 66) return 'stat-green';
+ if (fuelLevel > 33) return 'stat-yellow';
+ return 'stat-red';
+}
+
+// Get color class for vehicle condition
+function getConditionStatusColor(condition) {
+ if (condition > 66) return 'stat-green';
+ if (condition > 33) return 'stat-yellow';
+ return 'stat-red';
+}
+
+// Handle vehicle spawn
+function spawnVehicle(vehicleId) {
+ const vehicle = garageData.vehicles.find(v => v.id === vehicleId);
+ if (vehicle && vehicle.status === 'available') {
+ alert(`Spawning vehicle: ${vehicle.name}`);
+ // Here you would typically integrate with your game's vehicle spawning system
+ }
+}
+
+// Initialize when DOM is loaded
+document.addEventListener('DOMContentLoaded', initializeGarage);
diff --git a/addons/garage/ui/_site/styles.css b/addons/garage/ui/_site/styles.css
new file mode 100644
index 0000000..8411181
--- /dev/null
+++ b/addons/garage/ui/_site/styles.css
@@ -0,0 +1,233 @@
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: Arial, sans-serif;
+ line-height: 1.6;
+ background-color: #f4f4f4;
+}
+
+.container {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 20px;
+}
+
+header {
+ background-color: #333;
+ color: white;
+ padding: 1rem 0;
+ margin-bottom: 2rem;
+}
+
+.header-content {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.garage-stats {
+ display: flex;
+ gap: 2rem;
+}
+
+.stat-item {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ background: rgba(255, 255, 255, 0.1);
+ padding: 0.5rem 1rem;
+ border-radius: 4px;
+}
+
+.vehicles-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+ gap: 2rem;
+ padding: 1rem;
+}
+
+.vehicle-card {
+ background-color: white;
+ border-radius: 8px;
+ overflow: hidden;
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
+ transition: transform 0.3s;
+ display: flex;
+ flex-direction: column;
+}
+
+.vehicle-card:hover {
+ transform: translateY(-5px);
+}
+
+.vehicle-image {
+ width: 100%;
+ height: 200px;
+ background-color: #eee;
+ background-size: cover;
+ background-position: center;
+}
+
+.vehicle-info {
+ padding: 1.5rem;
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+}
+
+.vehicle-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+}
+
+.vehicle-name {
+ font-size: 1.2rem;
+ font-weight: bold;
+ color: #333;
+}
+
+.vehicle-status {
+ font-size: 0.875rem;
+ padding: 0.25rem 0.5rem;
+ border-radius: 4px;
+ font-weight: bold;
+}
+
+.status-available {
+ background-color: #28a745;
+ color: white;
+}
+
+.status-in-use {
+ background-color: #dc3545;
+ color: white;
+}
+
+.status-maintenance {
+ background-color: #ffc107;
+ color: black;
+}
+
+.vehicle-details {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 1rem;
+}
+
+.detail-item {
+ display: flex;
+ flex-direction: column;
+ gap: 0.25rem;
+}
+
+.detail-label {
+ font-size: 0.875rem;
+ color: #666;
+}
+
+.detail-value {
+ font-weight: bold;
+ color: #333;
+}
+
+.vehicle-actions {
+ display: flex;
+ gap: 1rem;
+ margin-top: auto;
+ padding-top: 1rem;
+ border-top: 1px solid #eee;
+}
+
+.action-btn {
+ flex: 1;
+ padding: 0.75rem;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ font-weight: bold;
+ transition: background-color 0.3s;
+}
+
+.spawn-btn {
+ background-color: #007bff;
+ color: white;
+}
+
+.spawn-btn:hover {
+ background-color: #0056b3;
+}
+
+.spawn-btn:disabled {
+ background-color: #6c757d;
+ cursor: not-allowed;
+ opacity: 0.65;
+}
+
+.spawn-btn:disabled:hover {
+ background-color: #6c757d;
+}
+
+.maintain-btn {
+ background-color: #ffc107;
+ color: black;
+}
+
+.maintain-btn:hover {
+ background-color: #d39e00;
+}
+
+.category-filters {
+ display: flex;
+ gap: 1rem;
+ margin-bottom: 2rem;
+ flex-wrap: wrap;
+}
+
+.filter-btn {
+ padding: 0.5rem 1rem;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ background-color: #444;
+ color: white;
+ transition: background-color 0.3s;
+}
+
+.filter-btn:hover {
+ background-color: #666;
+}
+
+.filter-btn.active {
+ background-color: #007bff;
+}
+
+.vehicle-stats {
+ display: flex;
+ gap: 0.5rem;
+ align-items: center;
+ color: #666;
+ font-size: 0.875rem;
+}
+
+.stat-dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+}
+
+.stat-green {
+ background-color: #28a745;
+}
+
+.stat-yellow {
+ background-color: #ffc107;
+}
+
+.stat-red {
+ background-color: #dc3545;
+}
diff --git a/addons/locker/ui/_site/index.html b/addons/locker/ui/_site/index.html
new file mode 100644
index 0000000..beeca72
--- /dev/null
+++ b/addons/locker/ui/_site/index.html
@@ -0,0 +1,74 @@
+
+
+
+
+
+ FORGE - Locker
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Current Equipment
+
+
+
+
+
+
+
+
Stored Equipment
+
+
+
+
+
+
+
+
Equipment Details
+
+
+
+ Select an item to view its details
+
+
+
+
+
+
+
diff --git a/addons/locker/ui/_site/script.js b/addons/locker/ui/_site/script.js
new file mode 100644
index 0000000..b2c8ac1
--- /dev/null
+++ b/addons/locker/ui/_site/script.js
@@ -0,0 +1,236 @@
+// Simulated locker data - this would be replaced with actual game data
+let lockerData = {
+ storageSpace: {
+ used: 45,
+ total: 100
+ },
+ playerEquipment: [
+ {
+ id: 1,
+ name: "Combat Uniform",
+ type: "clothing",
+ category: "Uniform",
+ condition: 95
+ },
+ {
+ id: 2,
+ name: "Tactical Vest",
+ type: "clothing",
+ category: "Vest",
+ condition: 88
+ },
+ {
+ id: 3,
+ name: "Combat Backpack",
+ type: "clothing",
+ category: "Backpack",
+ condition: 90
+ },
+ {
+ id: 4,
+ name: "Assault Rifle",
+ type: "weapons",
+ category: "Primary",
+ condition: 92,
+ attachments: ["Scope", "Grip"]
+ }
+ ],
+ storedItems: [
+ {
+ id: 5,
+ name: "Pistol",
+ type: "weapons",
+ category: "Secondary",
+ condition: 100
+ },
+ {
+ id: 6,
+ name: "Medical Kit",
+ type: "equipment",
+ category: "Medical",
+ quantity: 3
+ },
+ {
+ id: 7,
+ name: "Rifle Magazines",
+ type: "magazines",
+ category: "Magazine",
+ quantity: 5
+ },
+ {
+ id: 8,
+ name: "Combat Helmet",
+ type: "clothing",
+ category: "Headgear",
+ condition: 85
+ }
+ ]
+};
+
+// Initialize the locker interface
+function initializeLocker() {
+ updateStats();
+ setupFilterListeners();
+ updateEquipmentLists();
+}
+
+// Update storage statistics
+function updateStats() {
+ document.getElementById('storageSpace').textContent =
+ `${lockerData.storageSpace.used}/${lockerData.storageSpace.total}`;
+ document.getElementById('itemCount').textContent =
+ lockerData.storedItems.length;
+}
+
+// Set up category filter listeners
+function setupFilterListeners() {
+ const filterButtons = document.querySelectorAll('.filter-btn');
+
+ filterButtons.forEach(button => {
+ button.addEventListener('click', (e) => {
+ // Update active state
+ filterButtons.forEach(btn => btn.classList.remove('active'));
+ button.classList.add('active');
+
+ // Filter items
+ const category = button.dataset.category;
+ filterEquipment(category);
+ });
+ });
+}
+
+// Filter equipment by category
+function filterEquipment(category) {
+ const playerItems = category === 'all'
+ ? lockerData.playerEquipment
+ : lockerData.playerEquipment.filter(item => item.type === category);
+
+ const storedItems = category === 'all'
+ ? lockerData.storedItems
+ : lockerData.storedItems.filter(item => item.type === category);
+
+ updateEquipmentLists(playerItems, storedItems);
+}
+
+// Update equipment lists display
+function updateEquipmentLists(playerItems = lockerData.playerEquipment, storedItems = lockerData.storedItems) {
+ const playerList = document.getElementById('playerEquipment');
+ const storedList = document.getElementById('storedEquipment');
+
+ // Update player equipment
+ playerList.innerHTML = playerItems.map(item => `
+
+
+ ${item.name}
+ ${item.category}
+ ${item.condition ? `
+
+ ${item.condition}%
+
+ ` : ''}
+
+
+
+ `).join('');
+
+ // Update stored equipment
+ storedList.innerHTML = storedItems.map(item => `
+
+
+ ${item.name}
+ ${item.category}
+ ${item.quantity ? `
+
+ x${item.quantity}
+
+ ` : item.condition ? `
+
+ ${item.condition}%
+
+ ` : ''}
+
+
+
+ `).join('');
+
+ // Add click listeners for showing details
+ document.querySelectorAll('.equipment-item').forEach(item => {
+ item.addEventListener('click', () => showItemDetails(item.dataset.id));
+ });
+}
+
+// Show item details
+function showItemDetails(itemId) {
+ const item = lockerData.playerEquipment.find(i => i.id === parseInt(itemId)) ||
+ lockerData.storedItems.find(i => i.id === parseInt(itemId));
+
+ if (!item) return;
+
+ const detailsDiv = document.getElementById('equipmentDetails');
+ detailsDiv.innerHTML = `
+ ${item.name}
+
+
+ Category:
+ ${item.category}
+
+
+ Type:
+ ${item.type}
+
+ ${item.condition ? `
+
+ Condition:
+ ${item.condition}%
+
+ ` : ''}
+ ${item.quantity ? `
+
+ Quantity:
+ x${item.quantity}
+
+ ` : ''}
+ ${item.attachments ? `
+
+ Attachments:
+ ${item.attachments.join(', ')}
+
+ ` : ''}
+
+ `;
+}
+
+// Store an item in the locker
+function storeItem(itemId) {
+ const itemIndex = lockerData.playerEquipment.findIndex(item => item.id === itemId);
+ if (itemIndex === -1) return;
+
+ const item = lockerData.playerEquipment[itemIndex];
+ lockerData.playerEquipment.splice(itemIndex, 1);
+ lockerData.storedItems.push(item);
+ lockerData.storageSpace.used += 1;
+
+ updateStats();
+ updateEquipmentLists();
+}
+
+// Equip an item from the locker
+function equipItem(itemId) {
+ const itemIndex = lockerData.storedItems.findIndex(item => item.id === itemId);
+ if (itemIndex === -1) return;
+
+ const item = lockerData.storedItems[itemIndex];
+ lockerData.storedItems.splice(itemIndex, 1);
+ lockerData.playerEquipment.push(item);
+ lockerData.storageSpace.used -= 1;
+
+ updateStats();
+ updateEquipmentLists();
+}
+
+// Initialize when DOM is loaded
+document.addEventListener('DOMContentLoaded', initializeLocker);
diff --git a/addons/locker/ui/_site/styles.css b/addons/locker/ui/_site/styles.css
new file mode 100644
index 0000000..eb5a7ab
--- /dev/null
+++ b/addons/locker/ui/_site/styles.css
@@ -0,0 +1,194 @@
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: Arial, sans-serif;
+ line-height: 1.6;
+ background-color: #f4f4f4;
+}
+
+.container {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 20px;
+}
+
+header {
+ background-color: #333;
+ color: white;
+ padding: 1rem 0;
+ margin-bottom: 2rem;
+}
+
+.header-content {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.storage-stats {
+ display: flex;
+ gap: 2rem;
+}
+
+.stat-item {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ background: rgba(255, 255, 255, 0.1);
+ padding: 0.5rem 1rem;
+ border-radius: 4px;
+}
+
+.sections-grid {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ grid-template-rows: auto auto;
+ gap: 2rem;
+ padding: 1rem;
+ min-height: calc(100vh - 200px); /* Account for header and padding */
+}
+
+.equipment-section {
+ background-color: white;
+ border-radius: 8px;
+ overflow: hidden;
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
+ transition: transform 0.3s;
+ padding: 1.5rem;
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+ max-height: 100%;
+}
+
+.equipment-section h2 {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ font-size: 1.2rem;
+ color: #333;
+}
+
+.equipment-list {
+ list-style: none;
+ overflow-y: auto;
+ flex-grow: 1;
+ height: 0; /* This forces the flex-grow to work properly */
+ min-height: 400px;
+}
+
+.equipment-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0.75rem;
+ border-bottom: 1px solid #eee;
+ transition: background-color 0.3s;
+}
+
+.equipment-item:hover {
+ background-color: #f8f9fa;
+}
+
+.equipment-item:last-child {
+ border-bottom: none;
+}
+
+.item-info {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+}
+
+.item-name {
+ font-weight: 500;
+}
+
+.item-type {
+ font-size: 0.875rem;
+ color: #666;
+}
+
+.item-quantity {
+ font-size: 0.875rem;
+ color: #28a745;
+ background: rgba(40, 167, 69, 0.1);
+ padding: 0.2rem 0.5rem;
+ border-radius: 4px;
+}
+
+.item-actions {
+ display: flex;
+ gap: 0.5rem;
+}
+
+.action-btn {
+ padding: 0.5rem 1rem;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ font-weight: bold;
+ transition: background-color 0.3s;
+}
+
+.store-btn {
+ background-color: #007bff;
+ color: white;
+}
+
+.store-btn:hover {
+ background-color: #0056b3;
+}
+
+.equip-btn {
+ background-color: #28a745;
+ color: white;
+}
+
+.equip-btn:hover {
+ background-color: #218838;
+}
+
+.equip-btn:disabled {
+ background-color: #6c757d;
+ cursor: not-allowed;
+ opacity: 0.65;
+}
+
+.category-filters {
+ display: flex;
+ gap: 1rem;
+ margin-bottom: 2rem;
+ flex-wrap: wrap;
+}
+
+.filter-btn {
+ padding: 0.5rem 1rem;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ background-color: #444;
+ color: white;
+ transition: background-color 0.3s;
+}
+
+.filter-btn:hover {
+ background-color: #666;
+}
+
+.filter-btn.active {
+ background-color: #007bff;
+}
+
+.equipment-section.player-equipment {
+ grid-column: span 2;
+}
+
+.equipment-section.stored-equipment {
+ grid-column: span 1;
+ grid-row: span 2;
+}
diff --git a/addons/org/ui/BaseControl.hpp b/addons/org/ui/BaseControl.hpp
new file mode 100644
index 0000000..e69de29
diff --git a/addons/org/ui/_site/index.html b/addons/org/ui/_site/index.html
new file mode 100644
index 0000000..1be3c64
--- /dev/null
+++ b/addons/org/ui/_site/index.html
@@ -0,0 +1,90 @@
+
+
+
+
+
+ FORGE - ORG
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Memos & Logs
+
+
+
+
+
+
+
+
+
diff --git a/addons/org/ui/_site/public/fdic.png b/addons/org/ui/_site/public/fdic.png
new file mode 100644
index 0000000..7845bec
Binary files /dev/null and b/addons/org/ui/_site/public/fdic.png differ
diff --git a/addons/org/ui/_site/public/fdic_co.paa b/addons/org/ui/_site/public/fdic_co.paa
new file mode 100644
index 0000000..45fe964
Binary files /dev/null and b/addons/org/ui/_site/public/fdic_co.paa differ
diff --git a/addons/org/ui/_site/public/fms.png b/addons/org/ui/_site/public/fms.png
new file mode 100644
index 0000000..553b09a
Binary files /dev/null and b/addons/org/ui/_site/public/fms.png differ
diff --git a/addons/org/ui/_site/public/fms_co.paa b/addons/org/ui/_site/public/fms_co.paa
new file mode 100644
index 0000000..9d32a24
Binary files /dev/null and b/addons/org/ui/_site/public/fms_co.paa differ
diff --git a/addons/org/ui/_site/public/gms.png b/addons/org/ui/_site/public/gms.png
new file mode 100644
index 0000000..a4717b2
Binary files /dev/null and b/addons/org/ui/_site/public/gms.png differ
diff --git a/addons/org/ui/_site/public/gms_co.paa b/addons/org/ui/_site/public/gms_co.paa
new file mode 100644
index 0000000..ddc6d35
Binary files /dev/null and b/addons/org/ui/_site/public/gms_co.paa differ
diff --git a/addons/org/ui/_site/script.js b/addons/org/ui/_site/script.js
new file mode 100644
index 0000000..dbe3198
--- /dev/null
+++ b/addons/org/ui/_site/script.js
@@ -0,0 +1,302 @@
+// Simulated organization data - this would be replaced with actual game data
+let orgData = {
+ name: "Black Rifle Company",
+ reputation: 1250,
+ funds: 75000,
+ transactions: [
+ { id: 1, type: "deposit", amount: 5000, description: "Weekly income", date: "2025-04-12T10:30:00" },
+ { id: 2, type: "withdrawal", amount: -2000, description: "Vehicle maintenance", date: "2025-04-11T15:45:00" },
+ { id: 3, type: "deposit", amount: 3000, description: "Property rent", date: "2025-04-10T09:15:00" },
+ { id: 4, type: "withdrawal", amount: -1500, description: "Equipment purchase", date: "2025-04-09T14:20:00" }
+ ],
+ members: [
+ { id: 1, name: "John Doe", role: "owner", status: "online" },
+ { id: 2, name: "Jane Smith", role: "admin", status: "online" },
+ { id: 3, name: "Mike Johnson", role: "member", status: "offline" },
+ { id: 4, name: "Sarah Wilson", role: "member", status: "online" }
+ ],
+ vehicles: [
+ { id: 1, name: "Transport Truck", type: "Vehicle", value: 25000 },
+ { id: 2, name: "Patrol Car", type: "Vehicle", value: 15000 }
+ ],
+ equipment: [
+ { id: 1, name: "Combat Gear Set", type: "Equipment", value: 5000 },
+ { id: 3, name: "Radio Equipment", type: "Equipment", value: 3000 }
+ ],
+ properties: [
+ { id: 1, name: "Main Base", type: "Property", value: 100000 },
+ { id: 2, name: "Storage Facility", type: "Property", value: 50000 }
+ ],
+ supplies: [
+ { id: 1, name: "Medical Supplies", type: "Supply", value: 2000, quantity: 50 },
+ { id: 2, name: "Ammunition", type: "Supply", value: 5000, quantity: 1000 },
+ { id: 3, name: "Food Rations", type: "Supply", value: 1000, quantity: 100 },
+ { id: 4, name: "Repair Kits", type: "Supply", value: 3000, quantity: 25 }
+ ],
+ memos: [
+ {
+ id: 1,
+ title: "Weekly Mission Update",
+ content: "New assignments available in the northern sector. All teams please check your mission boards.",
+ author: "John Doe",
+ date: "2025-04-12T08:30:00",
+ priority: "high"
+ },
+ {
+ id: 2,
+ title: "Equipment Maintenance",
+ content: "Regular maintenance check required for all vehicles by end of week.",
+ author: "Jane Smith",
+ date: "2025-04-11T14:15:00",
+ priority: "medium"
+ }
+ ]
+};
+
+// Initialize the organization interface
+function initializeOrg() {
+ updateOrgInfo();
+ updateMembers();
+ updateAssets();
+ updateTransactions();
+ updateMemos();
+ setupMemoControls();
+}
+
+// Update organization info in the header
+function updateOrgInfo() {
+ document.getElementById('orgName').textContent = orgData.name;
+ document.getElementById('orgReputation').textContent = orgData.reputation.toLocaleString();
+ document.getElementById('orgFunds').textContent = `$${orgData.funds.toLocaleString()}`;
+}
+
+// Update members list
+function updateMembers() {
+ const membersList = document.getElementById('membersList');
+ membersList.innerHTML = '';
+
+ orgData.members.forEach(member => {
+ const li = document.createElement('li');
+ li.className = 'member-item';
+
+ li.innerHTML = `
+
+
+ ${member.name}
+ ${member.role}
+
+ `;
+
+ membersList.appendChild(li);
+ });
+}
+
+// Update assets lists (vehicles, equipment, properties)
+function updateAssets() {
+ // Update vehicles
+ const vehiclesList = document.getElementById('vehiclesList');
+ vehiclesList.innerHTML = '';
+ orgData.vehicles.forEach(vehicle => {
+ const li = document.createElement('li');
+ li.className = 'asset-item';
+ li.innerHTML = `
+
+ ${vehicle.name}
+ ${vehicle.type}
+
+ $${vehicle.value.toLocaleString()}
+ `;
+ vehiclesList.appendChild(li);
+ });
+
+ // Update equipment
+ const equipmentList = document.getElementById('equipmentList');
+ equipmentList.innerHTML = '';
+ orgData.equipment.forEach(item => {
+ const li = document.createElement('li');
+ li.className = 'asset-item';
+ li.innerHTML = `
+
+ ${item.name}
+ ${item.type}
+
+ $${item.value.toLocaleString()}
+ `;
+ equipmentList.appendChild(li);
+ }); // Update properties
+ const propertiesList = document.getElementById('propertiesList');
+ propertiesList.innerHTML = '';
+ orgData.properties.forEach(property => {
+ const li = document.createElement('li');
+ li.className = 'asset-item';
+ li.innerHTML = `
+
+ ${property.name}
+ ${property.type}
+
+ $${property.value.toLocaleString()}
+ `;
+ propertiesList.appendChild(li);
+ });
+
+ // Update supplies
+ const suppliesList = document.getElementById('suppliesList');
+ suppliesList.innerHTML = '';
+ orgData.supplies.forEach(supply => {
+ const li = document.createElement('li');
+ li.className = 'asset-item';
+ li.innerHTML = `
+
+
${supply.name}
+
+ ${supply.type}
+ Qty: ${supply.quantity}
+
+
+ $${supply.value.toLocaleString()}
+ `;
+ suppliesList.appendChild(li);
+ });
+}
+
+// Update transactions list
+function updateTransactions() {
+ const transactionsList = document.getElementById('transactionsList');
+ transactionsList.innerHTML = '';
+
+ orgData.transactions.forEach(transaction => {
+ const li = document.createElement('li');
+ li.className = 'transaction-item';
+
+ const date = new Date(transaction.date);
+ const formattedDate = date.toLocaleDateString('en-US', {
+ year: 'numeric',
+ month: 'short',
+ day: 'numeric',
+ hour: '2-digit',
+ minute: '2-digit'
+ });
+
+ const amountClass = transaction.amount >= 0 ? 'amount-positive' : 'amount-negative';
+ const prefix = transaction.amount >= 0 ? '+' : '';
+
+ li.innerHTML = `
+
+
${transaction.description}
+
${formattedDate}
+
+ ${prefix}$${Math.abs(transaction.amount).toLocaleString()}
+ `;
+
+ transactionsList.appendChild(li);
+ });
+}
+
+// Update memos list
+function updateMemos() {
+ const memosList = document.getElementById('memosList');
+ memosList.innerHTML = '';
+
+ orgData.memos.forEach(memo => {
+ const li = document.createElement('li');
+ li.className = 'memo-item';
+
+ const date = new Date(memo.date);
+ const formattedDate = date.toLocaleDateString('en-US', {
+ year: 'numeric',
+ month: 'short',
+ day: 'numeric',
+ hour: '2-digit',
+ minute: '2-digit'
+ });
+
+ li.innerHTML = `
+
+ ${memo.content}
+ `;
+
+ memosList.appendChild(li);
+ });
+}
+
+// Set up memo controls and dialog
+function setupMemoControls() {
+ const addMemoBtn = document.getElementById('addMemoBtn');
+
+ addMemoBtn.addEventListener('click', () => {
+ showMemoDialog();
+ });
+}
+
+// Show memo creation dialog
+function showMemoDialog() {
+ const dialog = document.createElement('div');
+ dialog.className = 'memo-dialog';
+
+ dialog.innerHTML = `
+
+ `;
+
+ const overlay = document.createElement('div');
+ overlay.className = 'memo-dialog-overlay';
+
+ document.body.appendChild(overlay);
+ document.body.appendChild(dialog);
+
+ const form = dialog.querySelector('#memoForm');
+ const cancelBtn = dialog.querySelector('#cancelMemo');
+
+ form.addEventListener('submit', (e) => {
+ e.preventDefault();
+
+ const newMemo = {
+ id: orgData.memos.length + 1,
+ title: document.getElementById('memoTitle').value,
+ content: document.getElementById('memoContent').value,
+ author: orgData.members.find(m => m.role === 'owner').name,
+ date: new Date().toISOString(),
+ priority: "normal"
+ };
+
+ orgData.memos.unshift(newMemo);
+ updateMemos();
+
+ closeMemoDialog(dialog, overlay);
+ });
+
+ cancelBtn.addEventListener('click', () => {
+ closeMemoDialog(dialog, overlay);
+ });
+
+ overlay.addEventListener('click', () => {
+ closeMemoDialog(dialog, overlay);
+ });
+}
+
+// Close memo dialog
+function closeMemoDialog(dialog, overlay) {
+ document.body.removeChild(dialog);
+ document.body.removeChild(overlay);
+}
+
+// Initialize when DOM is loaded
+document.addEventListener('DOMContentLoaded', initializeOrg);
diff --git a/addons/org/ui/_site/styles.css b/addons/org/ui/_site/styles.css
new file mode 100644
index 0000000..547a080
--- /dev/null
+++ b/addons/org/ui/_site/styles.css
@@ -0,0 +1,359 @@
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: Arial, sans-serif;
+ line-height: 1.6;
+ background-color: #f4f4f4;
+}
+
+.container {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 20px;
+}
+
+header {
+ background-color: #333;
+ color: white;
+ padding: 1rem 0;
+ margin-bottom: 2rem;
+}
+
+.header-content {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.org-stats {
+ display: flex;
+ gap: 2rem;
+ margin-left: auto;
+}
+
+.stat-item {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ background: rgba(255, 255, 255, 0.1);
+ padding: 0.5rem 1rem;
+ border-radius: 4px;
+}
+
+.stat-item i {
+ font-size: 1.2rem;
+}
+
+.sections-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+ gap: 2rem;
+ padding: 1rem;
+}
+
+/* Make memo section span full width */
+.section-tile.memo-section {
+ grid-column: 1 / -1;
+ aspect-ratio: unset;
+ min-height: 400px;
+}
+
+.section-tile {
+ background-color: white;
+ border-radius: 8px;
+ overflow: hidden;
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
+ transition: transform 0.3s;
+ aspect-ratio: 1;
+ padding: 2rem;
+ display: flex;
+ flex-direction: column;
+}
+
+.section-tile:hover {
+ transform: translateY(-5px);
+}
+
+.section-tile h2 {
+ font-size: 1.5rem;
+ color: #333;
+ margin-bottom: 1rem;
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+}
+
+.member-list, .asset-list {
+ list-style: none;
+ overflow-y: auto;
+ flex-grow: 1;
+}
+
+.member-item, .asset-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0.75rem;
+ border-bottom: 1px solid #eee;
+}
+
+.member-item:last-child, .asset-item:last-child {
+ border-bottom: none;
+}
+
+.member-info, .asset-info {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+}
+
+.member-role {
+ font-size: 0.875rem;
+ padding: 0.25rem 0.5rem;
+ border-radius: 4px;
+ background-color: #e9ecef;
+}
+
+.role-owner {
+ background-color: #ffd700;
+ color: #000;
+}
+
+.role-admin {
+ background-color: #dc3545;
+ color: white;
+}
+
+.role-member {
+ background-color: #28a745;
+ color: white;
+}
+
+.asset-type {
+ font-size: 0.875rem;
+ color: #666;
+}
+
+.supply-details {
+ display: flex;
+ gap: 1rem;
+ align-items: center;
+}
+
+.supply-quantity {
+ font-size: 0.875rem;
+ color: #28a745;
+ background: rgba(40, 167, 69, 0.1);
+ padding: 0.2rem 0.5rem;
+ border-radius: 4px;
+}
+
+.asset-value {
+ color: #007bff;
+ font-weight: bold;
+}
+
+.transaction-list {
+ list-style: none;
+ overflow-y: auto;
+ flex-grow: 1;
+}
+
+.transaction-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0.75rem;
+ border-bottom: 1px solid #eee;
+}
+
+.transaction-item:last-child {
+ border-bottom: none;
+}
+
+.transaction-info {
+ display: flex;
+ flex-direction: column;
+ gap: 0.25rem;
+}
+
+.transaction-description {
+ font-size: 0.9rem;
+ color: #666;
+}
+
+.transaction-date {
+ font-size: 0.8rem;
+ color: #888;
+}
+
+.transaction-amount {
+ font-weight: bold;
+}
+
+.amount-positive {
+ color: #28a745;
+}
+
+.amount-negative {
+ color: #dc3545;
+}
+
+.memo-controls {
+ margin-bottom: 1rem;
+ display: flex;
+ justify-content: flex-end;
+}
+
+.memo-list {
+ list-style: none;
+ overflow-y: auto;
+ flex-grow: 1;
+}
+
+.memo-item {
+ padding: 1rem;
+ border-bottom: 1px solid #eee;
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+}
+
+.memo-item:last-child {
+ border-bottom: none;
+}
+
+.memo-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+}
+
+.memo-title {
+ font-weight: bold;
+ color: #333;
+}
+
+.memo-metadata {
+ display: flex;
+ gap: 1rem;
+ font-size: 0.8rem;
+ color: #666;
+}
+
+.memo-author {
+ color: #007bff;
+}
+
+.memo-content {
+ color: #444;
+ line-height: 1.4;
+}
+
+.memo-dialog {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ background: white;
+ padding: 2rem;
+ border-radius: 8px;
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
+ z-index: 1000;
+ width: 90%;
+ max-width: 500px;
+}
+
+.memo-dialog-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0,0,0,0.5);
+ z-index: 999;
+}
+
+.memo-form {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+}
+
+.memo-form input {
+ width: 100%;
+ padding: 0.5rem;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+}
+
+.memo-form textarea {
+ width: 100%;
+ min-height: 100px;
+ padding: 0.5rem;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ resize: vertical;
+}
+
+.memo-form-buttons {
+ display: flex;
+ justify-content: flex-end;
+ gap: 1rem;
+}
+
+.cancel-btn {
+ width: 100%;
+ background-color: #6c757d;
+ color: white;
+ border: none;
+ border-radius: 4px;
+ padding: 0.75rem 1rem;
+ font-size: 1rem;
+ cursor: pointer;
+ transition: background-color 0.3s;
+}
+
+.add-btn {
+ border: none;
+ border-radius: 4px;
+ padding: 0.75rem 1rem;
+ font-size: 1rem;
+ cursor: pointer;
+ transition: background-color 0.3s;
+}
+
+.submit-btn {
+ width: 100%;
+ background-color: #007bff;
+ color: white;
+ border: none;
+ border-radius: 4px;
+ padding: 0.75rem 1rem;
+ font-size: 1rem;
+ cursor: pointer;
+ transition: background-color 0.3s;
+}
+
+.cancel-btn:hover {
+ background-color: #5a6268;
+}
+
+.member-status {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ margin-right: 0.5rem;
+}
+
+.status-online {
+ background-color: #28a745;
+}
+
+.status-offline {
+ background-color: #dc3545;
+}
diff --git a/addons/store/functions/fnc_buyItem.sqf b/addons/store/functions/fnc_buyItem.sqf
index be265d3..0e258ce 100644
--- a/addons/store/functions/fnc_buyItem.sqf
+++ b/addons/store/functions/fnc_buyItem.sqf
@@ -11,13 +11,13 @@
* 0: Class Name - The classname of the item to purchase
* 1: Price - The price of the item
* 2: Config Type - The type of config ("item", "weapon", "magazine", "backpack")
- * 3: Item Type - The type of item for locker storage
+ * 3: Item Type - The type of item for locker storage ("backpack", "facewear", "headgear", "hmd", "item", "magazine", "uniform", "vest", "weapon")
*
* Return Value:
* None
*
* Example:
- * ["arifle_MX_F", 1000, "weapon", "primary"] call forge_store_fnc_buyItem
+ * ["arifle_MX_F", 1000, "weapon", "weapon"] call forge_store_fnc_buyItem
*
* Public: No
*/
diff --git a/addons/store/functions/fnc_buyVehicle.sqf b/addons/store/functions/fnc_buyVehicle.sqf
index 9237293..f9cbd78 100644
--- a/addons/store/functions/fnc_buyVehicle.sqf
+++ b/addons/store/functions/fnc_buyVehicle.sqf
@@ -10,7 +10,7 @@
* Arguments:
* 0: Class Name - The classname of the vehicle to purchase
* 1: Price - The price of the vehicle
- * 2: Vehicle Type - The type of vehicle ("car", "air", "ship", "tank")
+ * 2: Vehicle Type - The type of vehicle ("car", "armor", "heli", "plane", "naval", "static")
*
* Return Value:
* None
diff --git a/addons/store/functions/fnc_handlePurchase.sqf b/addons/store/functions/fnc_handlePurchase.sqf
index 846936f..6cc1cef 100644
--- a/addons/store/functions/fnc_handlePurchase.sqf
+++ b/addons/store/functions/fnc_handlePurchase.sqf
@@ -23,6 +23,7 @@ params ["_price"];
private _paymentData = GVAR(activePayment);
private _payment = call compile _paymentData;
+private _store = nil;
scopeName "main";
@@ -36,7 +37,7 @@ if (count _payment > 3) then {
};
if (_payment select 0 == "Organization") then {
- private _store = call EFUNC(org,verifyOrgStore);
+ _store = call EFUNC(org,verifyOrgStore);
private _org = _store call ["getOrg", []];
private _ownerUID = _org get "owner";
@@ -49,7 +50,6 @@ if (_payment select 0 == "Organization") then {
private _varType = _payment select 2;
private _balance = switch (_varType) do {
case "organization": {
- private _store = call EFUNC(org,verifyOrgStore);
_store call ["getFunds", []];
};
case "player": { player getVariable [_payment select 1, 0] };
@@ -64,7 +64,6 @@ if (_balance < _price) exitWith {
switch (_varType) do {
case "organization": {
- private _store = call EFUNC(org,verifyOrgStore);
_store call ["updateFunds", -_price];
};
case "player": {
diff --git a/addons/store/ui/_site/data/categories.json b/addons/store/ui/_site/data/categories.json
new file mode 100644
index 0000000..900d10a
--- /dev/null
+++ b/addons/store/ui/_site/data/categories.json
@@ -0,0 +1,150 @@
+{
+ "paymentMethods": [
+ {
+ "id": "cash",
+ "name": "Cash",
+ "icon": "💵"
+ },
+ {
+ "id": "bank",
+ "name": "Bank",
+ "icon": "🏦"
+ }
+ ],
+ "Weapons": {
+ "icon": "",
+ "subcategories": {
+ "Rifles": {
+ "icon": "",
+ "products": [
+ {
+ "id": 1,
+ "name": "Combat Rifle",
+ "price": 1500,
+ "image": "placeholder.jpg"
+ },
+ {
+ "id": 2,
+ "name": "Sniper Rifle",
+ "price": 2500,
+ "image": "placeholder.jpg"
+ }
+ ]
+ },
+ "Pistols": {
+ "icon": "",
+ "products": [
+ {
+ "id": 3,
+ "name": "Tactical Pistol",
+ "price": 800,
+ "image": "placeholder.jpg"
+ }
+ ]
+ }
+ }
+ },
+ "Equipment": {
+ "icon": "",
+ "subcategories": {
+ "Vests": {
+ "icon": "",
+ "products": [
+ {
+ "id": 4,
+ "name": "Tactical Vest",
+ "price": 800,
+ "image": "placeholder.jpg"
+ }
+ ]
+ },
+ "Headwear": {
+ "icon": "",
+ "products": [
+ {
+ "id": 5,
+ "name": "Combat Helmet",
+ "price": 1200,
+ "image": "placeholder.jpg"
+ }
+ ]
+ },
+ "Facewear": {
+ "icon": "",
+ "products": [
+ {
+ "id": 6,
+ "name": "Balaclava",
+ "price": 500,
+ "image": "placeholder.jpg"
+ }
+ ]
+ },
+ "Hmd": {
+ "icon": "",
+ "products": [
+ {
+ "id": 7,
+ "name": "Night Vision Goggles",
+ "price": 2000,
+ "image": "placeholder.jpg"
+ }
+ ]
+ },
+ "Backpacks": {
+ "icon": "",
+ "products": [
+ {
+ "id": 8,
+ "name": "Combat Backpack",
+ "price": 450,
+ "image": "placeholder.jpg"
+ }
+ ]
+ },
+ "Uniforms": {
+ "icon": "",
+ "products": [
+ {
+ "id": 9,
+ "name": "Combat Fatigues",
+ "price": 1000,
+ "image": "placeholder.jpg"
+ }
+ ]
+ }
+ }
+ },
+ "Vehicles": {
+ "icon": "",
+ "subcategories": {
+ "Wheeled": {
+ "icon": "",
+ "products": [
+ {
+ "id": 10,
+ "name": "Transport Vehicle",
+ "price": 25000,
+ "image": "placeholder.jpg"
+ }
+ ]
+ }
+ }
+ },
+ "Supplies": {
+ "icon": "",
+ "subcategories": {
+ "Medical": {
+ "icon": "",
+ "products": [
+ {
+ "id": 11,
+ "name": "First Aid Kit",
+ "price": 300,
+ "image": "placeholder.jpg"
+ }
+ ]
+ }
+ }
+ }
+}
diff --git a/addons/store/ui/_site/index.html b/addons/store/ui/_site/index.html
new file mode 100644
index 0000000..6279940
--- /dev/null
+++ b/addons/store/ui/_site/index.html
@@ -0,0 +1,27 @@
+
+
+
+
+
+ FORGE - GMS
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/addons/store/ui/_site/public/fdic.png b/addons/store/ui/_site/public/fdic.png
new file mode 100644
index 0000000..7845bec
Binary files /dev/null and b/addons/store/ui/_site/public/fdic.png differ
diff --git a/addons/store/ui/_site/public/fdic_co.paa b/addons/store/ui/_site/public/fdic_co.paa
new file mode 100644
index 0000000..45fe964
Binary files /dev/null and b/addons/store/ui/_site/public/fdic_co.paa differ
diff --git a/addons/store/ui/_site/public/fms.png b/addons/store/ui/_site/public/fms.png
new file mode 100644
index 0000000..553b09a
Binary files /dev/null and b/addons/store/ui/_site/public/fms.png differ
diff --git a/addons/store/ui/_site/public/fms_co.paa b/addons/store/ui/_site/public/fms_co.paa
new file mode 100644
index 0000000..9d32a24
Binary files /dev/null and b/addons/store/ui/_site/public/fms_co.paa differ
diff --git a/addons/store/ui/_site/public/gms.png b/addons/store/ui/_site/public/gms.png
new file mode 100644
index 0000000..a4717b2
Binary files /dev/null and b/addons/store/ui/_site/public/gms.png differ
diff --git a/addons/store/ui/_site/public/gms_co.paa b/addons/store/ui/_site/public/gms_co.paa
new file mode 100644
index 0000000..ddc6d35
Binary files /dev/null and b/addons/store/ui/_site/public/gms_co.paa differ
diff --git a/addons/store/ui/_site/script.js b/addons/store/ui/_site/script.js
new file mode 100644
index 0000000..dccc61b
--- /dev/null
+++ b/addons/store/ui/_site/script.js
@@ -0,0 +1,174 @@
+// Store data will be loaded from JSON
+let categories = null;
+let currentCategory = null;
+let currentSubcategory = null;
+let selectedPaymentMethod = null;
+
+function initializePaymentMethods() {
+ const paymentSelect = document.getElementById('paymentMethod');
+ paymentSelect.innerHTML = `
+
+ ${categories.paymentMethods.map(method => `
+
+ `).join('')}
+ `;
+ paymentSelect.addEventListener('change', (e) => {
+ selectedPaymentMethod = e.target.value;
+ updateBuyButtons();
+ });
+}
+
+function updateBuyButtons() {
+ const buyButtons = document.querySelectorAll('.buy-btn');
+ buyButtons.forEach(button => {
+ button.disabled = !selectedPaymentMethod;
+ });
+}
+
+function handleQuantityChange(productId, change, quantityDisplay) {
+ const currentQty = parseInt(quantityDisplay.textContent);
+ const newQty = Math.max(1, currentQty + change);
+ quantityDisplay.textContent = newQty;
+}
+
+function handlePurchase(product, quantity) {
+ if (!selectedPaymentMethod) {
+ alert('Please select a payment method first');
+ return;
+ }
+
+ const total = product.price * quantity;
+ alert(`Purchase Summary:
+Product: ${product.name}
+Quantity: ${quantity}
+Total: $${total.toLocaleString()}
+Payment Method: ${categories.paymentMethods.find(m => m.id === selectedPaymentMethod).name}`);
+ // Here you would typically integrate with your game's purchasing system
+}
+
+function showMainCategories() {
+ const mainContent = document.querySelector('main');
+ currentCategory = null;
+ currentSubcategory = null;
+
+ mainContent.innerHTML = `
+
+ `;
+
+ const categoriesGrid = document.querySelector('.categories-grid');
+ Object.entries(categories)
+ .filter(([key]) => key !== 'paymentMethods')
+ .forEach(([categoryName, categoryData]) => {
+ const categoryTile = document.createElement('button');
+ categoryTile.className = 'category-tile';
+ categoryTile.innerHTML = `
+ ${categoryData.icon}
+ ${categoryName}
+ `;
+ categoryTile.addEventListener('click', () => showSubcategories(categoryName));
+ categoriesGrid.appendChild(categoryTile);
+ });
+}
+
+function showSubcategories(categoryName) {
+ const mainContent = document.querySelector('main');
+ currentCategory = categoryName;
+ currentSubcategory = null;
+
+ mainContent.innerHTML = `
+
+ ${categoryName}
+
+ `;
+
+ const subcategoriesGrid = document.querySelector('.subcategories-grid');
+ const backButton = document.querySelector('.back-button');
+ backButton.addEventListener('click', showMainCategories);
+
+ Object.entries(categories[categoryName].subcategories).forEach(([subCategoryName, subCategoryData]) => {
+ const subCategoryTile = document.createElement('button');
+ subCategoryTile.className = 'subcategory-tile';
+ subCategoryTile.innerHTML = `
+ ${subCategoryData.icon}
+ ${subCategoryName}
+ `;
+ subCategoryTile.addEventListener('click', () => showProducts(categoryName, subCategoryName));
+ subcategoriesGrid.appendChild(subCategoryTile);
+ });
+}
+
+function showProducts(categoryName, subCategoryName) {
+ const mainContent = document.querySelector('main');
+ currentSubcategory = subCategoryName;
+
+ mainContent.innerHTML = `
+
+ ${categoryName} > ${subCategoryName}
+
+ `;
+
+ const productsGrid = document.querySelector('.products-grid');
+ const backButton = document.querySelector('.back-button');
+ backButton.addEventListener('click', () => showSubcategories(categoryName));
+
+ const products = categories[categoryName].subcategories[subCategoryName].products;
+ products.forEach(product => {
+ const productCard = document.createElement('div');
+ productCard.className = 'product-card';
+ productCard.innerHTML = `
+
+
+
${product.name}
+
$${product.price.toLocaleString()}
+
+
+
+ 1
+
+
+
+
+
+ `;
+
+ const quantityDisplay = productCard.querySelector('.quantity-display');
+ const minusBtn = productCard.querySelector('.quantity-btn.minus');
+ const plusBtn = productCard.querySelector('.quantity-btn.plus');
+ const buyBtn = productCard.querySelector('.buy-btn');
+
+ minusBtn.addEventListener('click', () => handleQuantityChange(product.id, -1, quantityDisplay));
+ plusBtn.addEventListener('click', () => handleQuantityChange(product.id, 1, quantityDisplay));
+ buyBtn.addEventListener('click', () => handlePurchase(product, parseInt(quantityDisplay.textContent)));
+
+ productsGrid.appendChild(productCard);
+ });
+}
+
+// Initialize the store
+async function initializeStore() {
+ try {
+ const response = await fetch('http://localhost:8000/data/categories.json');
+ if (!response.ok) {
+ throw new Error('Failed to load store data');
+ }
+ categories = await response.json();
+ initializePaymentMethods();
+ showMainCategories();
+ } catch (error) {
+ console.error('Error loading store data:', error);
+ document.querySelector('main').innerHTML = `
+
+
Error Loading Store Data
+
Please try again later.
+
+ `;
+ }
+}
+
+document.addEventListener('DOMContentLoaded', () => {
+ initializeStore();
+});
diff --git a/addons/store/ui/_site/server.py b/addons/store/ui/_site/server.py
new file mode 100644
index 0000000..3bf590c
--- /dev/null
+++ b/addons/store/ui/_site/server.py
@@ -0,0 +1,16 @@
+import http.server
+import socketserver
+
+PORT = 8000
+DIRECTORY = '.'
+
+class Handler(http.server.SimpleHTTPRequestHandler):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, directory=DIRECTORY, **kwargs)
+
+with socketserver.TCPServer(("", PORT), Handler) as httpd:
+ print(f"Serving at http://localhost:{PORT}")
+ try:
+ httpd.serve_forever()
+ except KeyboardInterrupt:
+ print("\nServer stopped.")
diff --git a/addons/store/ui/_site/styles.css b/addons/store/ui/_site/styles.css
new file mode 100644
index 0000000..caff0c7
--- /dev/null
+++ b/addons/store/ui/_site/styles.css
@@ -0,0 +1,200 @@
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: Arial, sans-serif;
+ line-height: 1.6;
+ background-color: #f4f4f4;
+}
+
+.container {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 20px;
+}
+
+header {
+ background-color: #333;
+ color: white;
+ padding: 1rem 0;
+ margin-bottom: 2rem;
+}
+
+header h1 {
+ text-align: center;
+}
+
+.categories-grid, .subcategories-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
+ gap: 2rem;
+ padding: 1rem;
+ margin-bottom: 2rem;
+}
+
+.category-tile, .subcategory-tile {
+ background-color: #444;
+ color: white;
+ border: none;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: all 0.3s;
+ aspect-ratio: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ text-align: center;
+ padding: 1rem;
+ font-size: 1.3rem;
+}
+
+.category-tile:hover, .subcategory-tile:hover {
+ background-color: #666;
+ transform: translateY(-5px);
+}
+
+.category-tile.active, .subcategory-tile.active {
+ background-color: #007bff;
+}
+
+.category-tile i, .subcategory-tile i {
+ font-size: 2rem;
+ margin-bottom: 1rem;
+}
+
+.back-button {
+ background-color: #444;
+ color: white;
+ border: none;
+ border-radius: 4px;
+ padding: 0.5rem 1rem;
+ cursor: pointer;
+ margin-bottom: 1rem;
+ display: inline-flex;
+ align-items: center;
+ gap: 0.5rem;
+}
+
+.back-button:hover {
+ background-color: #666;
+}
+
+.products-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+ gap: 2rem;
+ padding: 1rem;
+}
+
+.product-card {
+ background-color: white;
+ border-radius: 8px;
+ overflow: hidden;
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
+ transition: transform 0.3s;
+}
+
+.product-card:hover {
+ transform: translateY(-5px);
+}
+
+.product-image {
+ width: 100%;
+ height: 256px;
+ object-fit: cover;
+ background-color: #eee;
+}
+
+.product-info {
+ padding: 1rem;
+}
+
+.product-name {
+ font-size: 1.1rem;
+ margin-bottom: 0.5rem;
+ color: #333;
+}
+
+.product-price {
+ font-size: 1.2rem;
+ color: #007bff;
+ font-weight: bold;
+}
+
+.header-content {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.payment-select {
+ padding: 0.5rem;
+ border-radius: 4px;
+ background-color: white;
+ border: 1px solid #ddd;
+ font-size: 1rem;
+}
+
+.product-controls {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+ margin-top: 1rem;
+ padding-top: 1rem;
+ border-top: 1px solid #eee;
+}
+
+.quantity-controls {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+}
+
+.quantity-btn {
+ background-color: #007bff;
+ color: white;
+ border: none;
+ border-radius: 4px;
+ width: 30px;
+ height: 30px;
+ font-size: 1.2rem;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.quantity-btn:hover {
+ background-color: #0056b3;
+}
+
+.quantity-display {
+ font-size: 1.1rem;
+ min-width: 40px;
+ text-align: center;
+}
+
+.buy-btn {
+ flex: 1;
+ background-color: #28a745;
+ color: white;
+ border: none;
+ border-radius: 4px;
+ padding: 0.5rem 1rem;
+ font-size: 1rem;
+ cursor: pointer;
+ transition: background-color 0.3s;
+}
+
+.buy-btn:hover {
+ background-color: #218838;
+}
+
+.buy-btn:disabled {
+ background-color: #6c757d;
+ cursor: not-allowed;
+}