diff --git a/index.html b/index.html index b369cec..68f1e28 100644 --- a/index.html +++ b/index.html @@ -20,7 +20,7 @@ --border: #333333; --text: #ffffff; --text-muted: #aaaaaa; - --success: #ff0099; + --success: #11cc77; --danger: #ff0099; --card-bg: rgba(0, 0, 0, 0.8); } @@ -436,8 +436,194 @@ margin-top: 15px; font-family: 'Roboto Mono', monospace; } + + /* Petition Modal Styles */ + .modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.85); + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; + backdrop-filter: blur(4px); + } + + .modal-container { + background: var(--card-bg); + width: 90%; + max-width: 500px; + border: 1px solid var(--primary); + box-shadow: 0 0 30px rgba(255, 0, 153, 0.4); + padding: 0; + position: relative; + } + + .modal-header { + padding: 20px; + border-bottom: 1px solid var(--border); + display: flex; + justify-content: space-between; + align-items: center; + } + + .modal-header h3 { + margin: 0; + color: var(--primary); + font-size: 1.2rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 1px; + } + + .modal-close { + background: none; + border: none; + color: var(--text-muted); + font-size: 20px; + cursor: pointer; + padding: 0; + margin: 0; + } + + .modal-close:hover { + color: var(--primary); + } + + .modal-body { + padding: 20px; + } + + .modal-body p { + margin-bottom: 20px; + font-size: 0.9rem; + line-height: 1.6; + } + + .petition-form { + display: flex; + flex-direction: column; + gap: 15px; + } + + .form-group { + display: flex; + flex-direction: column; + gap: 5px; + } + + .form-group label { + font-size: 0.8rem; + color: var(--text-muted); + font-weight: 500; + } + + .form-input { + padding: 12px; + background-color: var(--darker); + border: 1px solid var(--border); + color: var(--text); + font-size: 14px; + font-family: 'Roboto Mono', monospace; + } + + .form-input:focus { + outline: none; + border-color: var(--primary); + box-shadow: 0 0 0 2px rgba(255, 0, 153, 0.2); + } + + .form-error { + color: var(--primary); + font-size: 0.8rem; + margin-top: 5px; + } + + .consent-checkbox { + display: flex; + align-items: flex-start; + gap: 10px; + margin-top: 10px; + } + + .consent-checkbox input { + margin-top: 2px; + } + + .consent-checkbox label { + font-size: 0.8rem; + line-height: 1.4; + color: var(--text-muted); + } + + .petition-submit-btn { + background-color: var(--primary); + color: var(--dark); + border: none; + padding: 12px; + font-size: 14px; + font-weight: 600; + cursor: pointer; + text-transform: uppercase; + letter-spacing: 1px; + margin-top: 10px; + transition: all 0.3s ease; + font-family: 'Roboto Mono', monospace; + } + + .petition-submit-btn:hover:not([disabled]) { + background-color: var(--primary-light); + transform: translateY(-2px); + } + + .petition-submit-btn:disabled { + background-color: #333; + color: #555; + cursor: not-allowed; + transform: none; + } + + .petition-badge { + background-color: #11cc77; + color: var(--dark); + } + + .petition-info { + padding: 10px 20px; + margin-top: -10px; + margin-bottom: 15px; + font-size: 0.8rem; + display: flex; + align-items: center; + gap: 8px; + background-color: rgba(17, 204, 119, 0.1); + border-top: 1px solid rgba(17, 204, 119, 0.3); + border-bottom: 1px solid rgba(17, 204, 119, 0.3); + } + + .petition-icon { + color: #11cc77; + font-weight: bold; + } + + .petition-count { + font-weight: 600; + color: #11cc77; + } + + .info-text { + font-style: italic; + font-size: 0.7rem; + color: var(--text-muted); + line-height: 1.4; + margin-top: 5px; + border-left: 2px solid var(--border); + padding-left: 10px; + } - /* Responsive design */ @media (max-width: 600px) { .container { padding: 20px; @@ -456,6 +642,12 @@ gap: 15px; align-items: flex-start; } + + .modal-container { + width: 95%; + max-height: 90vh; + overflow-y: auto; + } } @@ -481,6 +673,7 @@ + @@ -507,151 +700,227 @@ // Configuration const API_BASE_URL = 'https://radical.omar-c29.workers.dev/api'; // Replace with your actual worker URL - // DOM elements - const proposalInput = document.getElementById('proposal-input'); - const charCounter = document.getElementById('char-counter'); - const postButton = document.getElementById('post-button'); - const proposalsContainer = document.getElementById('proposals-container'); - const sortSelect = document.getElementById('sort-select'); - // Character limit const CHAR_LIMIT = 280; - // User information (in a real app, this would come from authentication) - let currentUser = JSON.parse(localStorage.getItem('radical_user')); - - // If no user exists, create a new one - if (!currentUser) { - currentUser = { - id: 'citizen_' + Math.random().toString(36).substr(2, 9), - name: 'Citizen ' + Math.floor(Math.random() * 9000 + 1000) - }; - - // Save user to local storage - localStorage.setItem('radical_user', JSON.stringify(currentUser)); - - // Create user in database - createUser(currentUser); - } - - // Update the createUser function in your frontend JavaScript -async function createUser(user) { - try { - console.log("Creating user:", user); // Log the user data - - const response = await fetch(`${API_BASE_URL}/users`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - id: user.id, - name: user.name - }) - }); - - if (!response.ok) { - const errorData = await response.json(); - console.error("User creation API error:", errorData); - throw new Error('Failed to create user'); - } - - const data = await response.json(); - console.log("User creation successful:", data); - return data; - } catch (error) { - console.error('Error creating user:', error); - throw error; - } -} - -// Ensure user is created before posting proposals -window.addEventListener('DOMContentLoaded', async () => { - // Try to create the user immediately when page loads - try { - await createUser(currentUser); - console.log("User registration complete"); - } catch (error) { - console.error("Failed to register user on page load"); - } -}); + // Global variables + let currentUser; + let verifiedUsers = {}; + let currentProposalId = null; + let currentVoteType = null; // Catchy political slogans for placeholder text const placeholders = [ - "Your idea to revolutionize Australia? (280 character limit)", + "Your petition idea for Australia? (280 character limit)", "Got a hot take on Australian politics? (280 character limit)", "Parliament failed the vibe check. Your solution? (280 character limit)", "What would make Australia actually slap? (280 character limit)", "Drop your 🔥 policy idea here (280 character limit)" ]; - // Rotate placeholder text every 5 seconds - let placeholderIndex = 0; - setInterval(() => { - placeholderIndex = (placeholderIndex + 1) % placeholders.length; - proposalInput.placeholder = placeholders[placeholderIndex]; - }, 5000); - - // Update character counter - proposalInput.addEventListener('input', () => { - const remaining = CHAR_LIMIT - proposalInput.value.length; - charCounter.textContent = `${remaining} characters remaining`; - - if (remaining < 20) { - charCounter.classList.add('limit'); - } else { - charCounter.classList.remove('limit'); - } - - // Enable/disable post button - postButton.disabled = proposalInput.value.trim().length === 0; + // Document Ready Handler + document.addEventListener('DOMContentLoaded', async () => { + // Initialize variables for DOM elements + initializeApp(); }); - // Post new proposal - postButton.addEventListener('click', async () => { - const text = proposalInput.value.trim(); + // Initialize application + async function initializeApp() { + // Update UI elements + updateUIText(); - if (text) { - try { - const trending = Math.random() > 0.8; // Randomly mark some as trending for effect + // Load user information + await loadUserData(); + + // Load verified users data + loadVerifiedUsers(); + + // Setup event listeners + setupEventListeners(); + + // Initial load of proposals + fetchProposals(); + } + + // Update UI text for petition system + function updateUIText() { + const tagline = document.querySelector('.tagline'); + if (tagline) tagline.textContent = 'Petition, Vote, Veto'; + + const headerDesc = document.querySelector('header p'); + if (headerDesc) { + headerDesc.textContent = 'Australia\'s direct democracy movement. Create and sign official petitions, vote on proposals, take back control.'; + } + + const filterTitle = document.querySelector('.filter-title'); + if (filterTitle) filterTitle.textContent = 'Active Petitions'; + + const sortSelect = document.getElementById('sort-select'); + if (sortSelect) { + const options = sortSelect.querySelectorAll('option'); + if (options[0]) options[0].textContent = 'Latest Petitions'; + if (options[1]) options[1].textContent = 'Most Signatures'; + if (options[2]) options[2].textContent = 'Most Debated'; + } + + const proposalInput = document.getElementById('proposal-input'); + if (proposalInput) { + proposalInput.placeholder = 'Your petition idea for Australia? (280 character limit)'; + } + + const postButton = document.getElementById('post-button'); + if (postButton) { + postButton.textContent = 'Create Petition'; + } + } + + // Load user data + async function loadUserData() { + // User information (in a real app, this would come from authentication) + currentUser = JSON.parse(localStorage.getItem('radical_user')); + + // If no user exists, create a new one + if (!currentUser) { + currentUser = { + id: 'citizen_' + Math.random().toString(36).substr(2, 9), + name: 'Citizen ' + Math.floor(Math.random() * 9000 + 1000) + }; + + // Save user to local storage + localStorage.setItem('radical_user', JSON.stringify(currentUser)); + } + + // Create or get user in the database + try { + await createUser(currentUser); + console.log("User registration complete"); + } catch (error) { + console.error("Failed to register user on page load"); + } + } + + // Load verified users data + function loadVerifiedUsers() { + verifiedUsers = JSON.parse(localStorage.getItem('radical_verified_users')) || {}; + } + + // Setup event listeners + function setupEventListeners() { + const proposalInput = document.getElementById('proposal-input'); + const charCounter = document.getElementById('char-counter'); + const postButton = document.getElementById('post-button'); + const sortSelect = document.getElementById('sort-select'); + + // Rotate placeholder text every 5 seconds + let placeholderIndex = 0; + setInterval(() => { + if (proposalInput) { + placeholderIndex = (placeholderIndex + 1) % placeholders.length; + proposalInput.placeholder = placeholders[placeholderIndex]; + } + }, 5000); + + // Update character counter + if (proposalInput) { + proposalInput.addEventListener('input', () => { + const remaining = CHAR_LIMIT - proposalInput.value.length; + charCounter.textContent = `${remaining} characters remaining`; - const response = await fetch(`${API_BASE_URL}/proposals`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - authorId: currentUser.id, - text: text, - trending: trending - }) - }); - - if (!response.ok) { - throw new Error('Failed to create proposal'); + if (remaining < 20) { + charCounter.classList.add('limit'); + } else { + charCounter.classList.remove('limit'); } - // Clear input - proposalInput.value = ''; - charCounter.textContent = `${CHAR_LIMIT} characters remaining`; - postButton.disabled = true; - - // Show success animation - animatePostSuccess(); - - // Refresh proposals - fetchProposals(); - } catch (error) { - console.error('Error creating proposal:', error); - showErrorMessage('Failed to post your proposal. Parliament might be onto us!'); - } + // Enable/disable post button + postButton.disabled = proposalInput.value.trim().length === 0; + }); } - }); + + // Post new proposal + if (postButton) { + postButton.addEventListener('click', async () => { + const text = proposalInput.value.trim(); + + if (text) { + try { + const trending = Math.random() > 0.8; // Randomly mark some as trending for effect + + const response = await fetch(`${API_BASE_URL}/proposals`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + authorId: currentUser.id, + text: text, + trending: trending + }) + }); + + if (!response.ok) { + throw new Error('Failed to create proposal'); + } + + // Clear input + proposalInput.value = ''; + charCounter.textContent = `${CHAR_LIMIT} characters remaining`; + postButton.disabled = true; + + // Show success animation + animatePostSuccess(); + + // Refresh proposals + fetchProposals(); + } catch (error) { + console.error('Error creating proposal:', error); + showErrorMessage('Failed to post your petition. Parliament might be onto us!'); + } + } + }); + } + + // Sort proposals when sort option changes + if (sortSelect) { + sortSelect.addEventListener('change', fetchProposals); + } + } + + // Create or get user in DB + async function createUser(user) { + try { + console.log("Creating user:", user); // Log the user data + + const response = await fetch(`${API_BASE_URL}/users`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + id: user.id, + name: user.name + }) + }); + + if (!response.ok) { + const errorData = await response.json(); + console.error("User creation API error:", errorData); + throw new Error('Failed to create user'); + } + + const data = await response.json(); + console.log("User creation successful:", data); + return data; + } catch (error) { + console.error('Error creating user:', error); + throw error; + } + } // Animation for successful post function animatePostSuccess() { const successEl = document.createElement('div'); - successEl.textContent = '🔥 Take that, Parliament!'; + successEl.textContent = '🔥 Petition created!'; successEl.style.position = 'fixed'; successEl.style.bottom = '20px'; successEl.style.left = '50%'; @@ -686,26 +955,65 @@ window.addEventListener('DOMContentLoaded', async () => { // Show loading indicator function showLoadingIndicator() { - proposalsContainer.innerHTML = ` -
Loading democracy...
-Loading petitions...
+${message}
- ${isRetryable ? '' : ''} -${message}
+ ${isRetryable ? '' : ''} +No proposals yet. Be the first to drop your hot take!
+No petitions yet. Be the first to create one!