← Mike Higgins Home
Custom Code Experiment β€’

Currency Freaks

Source Code View HTML / CSS / JavaScript
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CurrencyFreaks</title>
    <style>
        * {
            box-sizing: border-box;
            font-family: 'Segoe UI', Roboto, system-ui, sans-serif;
        }
        body {
            background: #0b0f15;
            margin: 0;
            min-height: 100vh;
            display: flex;
            flex-direction: column;
            align-items: center;
            padding: 30px 20px;
            color: #e5e9f0;
        }
        .selector-row {
            display: flex;
            flex-wrap: wrap;
            align-items: center;
            gap: 18px;
            margin-bottom: 30px;
            width: 100%;
            max-width: 1100px;
        }
        .selector-row select {
            background: #1f2632;
            border: 1px solid #384153;
            border-radius: 40px;
            padding: 12px 30px 12px 20px;
            font-size: 1rem;
            color: white;
            appearance: none;
            background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%239aa8bc' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
            background-repeat: no-repeat;
            background-position: right 16px center;
            background-size: 16px;
            cursor: pointer;
            flex: 2 1 300px;
        }
        .selector-row button {
            background: #3e5b8a;
            border: none;
            border-radius: 60px;
            padding: 12px 32px;
            font-size: 1.1rem;
            font-weight: 600;
            color: white;
            cursor: pointer;
            transition: 0.15s;
            border: 1px solid #5f7bb0;
            box-shadow: 0 4px 10px rgba(0,20,50,0.6);
            flex: 0 1 auto;
        }
        .selector-row button:hover {
            background: #5273aa;
            border-color: #8dabeb;
            transform: scale(1.01);
        }
        .selector-row button:active {
            background: #2c4468;
            transform: scale(0.98);
        }
        .params-grid {
            display: flex;
            flex-wrap: wrap;
            gap: 20px;
            margin-top: 5px;
            margin-bottom: 30px;
            width: 100%;
            max-width: 1100px;
        }
        .param-field {
            flex: 1 1 180px;
            min-width: 150px;
        }
        .param-field label {
            display: block;
            font-size: 0.75rem;
            color: #8d9caf;
            margin-bottom: 4px;
            font-weight: 500;
        }
        .param-field input {
            width: 100%;
            background: #1b222d;
            border: 1px solid #314159;
            border-radius: 30px;
            padding: 10px 16px;
            color: white;
            font-size: 0.9rem;
        }
        .param-field input:disabled {
            opacity: 0.5;
            background: #161e28;
            color: #6a768b;
        }
        .helper-note {
            font-size: 0.8rem;
            color: #6d7e98;
            margin: -20px 0 20px 8px;
            width: 100%;
            max-width: 1100px;
        }
        .response-area {
            width: 100%;
            max-width: 1100px;
            margin-top: 10px;
        }
        .response-header {
            display: flex;
            justify-content: space-between;
            color: #b4c2da;
            font-weight: 400;
            margin-bottom: 14px;
            font-size: 0.9rem;
            border-bottom: 1px solid #253040;
            padding-bottom: 10px;
        }
        .response-header .status {
            background: #283040;
            padding: 4px 14px;
            border-radius: 40px;
            color: #b7d0ff;
        }
        pre {
            background: #0b0f15;
            padding: 18px;
            border-radius: 0;
            color: #d6e2f5;
            font-family: 'JetBrains Mono', 'Fira Code', monospace;
            font-size: 0.85rem;
            overflow-x: auto;
            white-space: pre-wrap;
            word-wrap: break-word;
            border: 1px solid #202834;
            max-height: 500px;
            margin: 0;
        }
        .loader {
            display: inline-block;
            width: 20px;
            height: 20px;
            border: 3px solid #384967;
            border-radius: 50%;
            border-top-color: #a2c2ff;
            animation: spin 0.8s linear infinite;
            margin-right: 8px;
        }
        @keyframes spin { to { transform: rotate(360deg); } }
        .flex-row {
            display: flex;
            align-items: center;
            gap: 10px;
        }
    </style>
</head>
<body>
    <div class="selector-row">
        <select id="endpointSelect">
            <option value="latest">πŸ“ˆ Latest rates (all)</option>
            <option value="latest_symbols">🎯 Latest rates (filter by symbols)</option>
            <option value="supported">🌐 Supported currencies (no key needed)</option>
            <option value="symbols">πŸ”£ Currency symbols list</option>
            <option value="hist_limits">πŸ“š Historical data limits</option>
        </select>
        <button id="fetchBtn">▢️ FETCH</button>
    </div>

    <div id="paramsContainer" class="params-grid"></div>
    <div class="response-area">
        <div class="response-header">
            <span class="flex-row"><span class="loader" id="loader" style="display:none;"></span> PROXY RESPONSE</span>
            <span class="status" id="httpStatus">β€”</span>
        </div>
        <pre id="responsePre">πŸ‘ˆ select an endpoint and click FETCH</pre>
    </div>

<script>
    (function() {
        const PROXY_URL = '/.netlify/functions/proxy-request';
        const NOTE_ID = 'a8f3a403-ab5b-4bfc-86d5-04972aa75852';
        const API_BASE_URL = 'https://api.currencyfreaks.com/v2.0';

        const endpointSelect = document.getElementById('endpointSelect');
        const paramsContainer = document.getElementById('paramsContainer');
        const fetchBtn = document.getElementById('fetchBtn');
        const loader = document.getElementById('loader');
        const httpStatusSpan = document.getElementById('httpStatus');
        const responsePre = document.getElementById('responsePre');

        let currentEndpoint = 'latest';

        const paramConfig = {
            latest: [
                { name: 'base', label: 'Base currency (optional)', type: 'text', placeholder: 'USD, EUR, ...', default: '' },
                { name: 'symbols', label: 'Symbols (comma-separated, optional)', type: 'text', placeholder: 'GBP,JPY,CHF', default: '' }
            ],
            latest_symbols: [
                { name: 'symbols', label: 'πŸ”Έ Symbols (required)', type: 'text', placeholder: 'PKR,GBP,EUR', default: 'PKR,GBP,EUR' },
                { name: 'base', label: 'Base (optional)', type: 'text', placeholder: 'USD', default: '' }
            ],
            supported: [],
            symbols: [],
            hist_limits: []
        };

        function renderParams(endpoint) {
            const config = paramConfig[endpoint] || [];
            if (!config.length) {
                paramsContainer.innerHTML = '<div class="helper-note" style="padding: 8px 0;">πŸ“ No parameters needed for this endpoint.</div>';
                return;
            }
            let html = '';
            config.forEach(p => {
                html += `<div class="param-field">
                    <label>${p.label}</label>
                    <input type="${p.type}" id="param_${p.name}" placeholder="${p.placeholder}" value="${p.default}">
                </div>`;
            });
            paramsContainer.innerHTML = html;
        }

        endpointSelect.addEventListener('change', () => {
            currentEndpoint = endpointSelect.value;
            renderParams(currentEndpoint);
        });

        function buildTargetUrl() {
            const baseUrl = API_BASE_URL.replace(/\/$/, '');
            let endpointPath = '';
            const queryParts = [];

            const keyNeeded = ['latest', 'latest_symbols'];

            const addParam = (key, value) => {
                if (value === undefined || value === null || value === '') return;
                if (key === 'apikey' && value === '{{CURRENCY_FREAKS_API_KEY}}') {
                    queryParts.push(`apikey={{CURRENCY_FREAKS_API_KEY}}`);
                } else {
                    queryParts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
                }
            };

            switch (currentEndpoint) {
                case 'latest':
                case 'latest_symbols':
                    endpointPath = '/rates/latest';
                    if (keyNeeded.includes(currentEndpoint)) {
                        addParam('apikey', '{{CURRENCY_FREAKS_API_KEY}}');
                    }
                    const baseLatest = document.getElementById('param_base')?.value;
                    const symLatest = document.getElementById('param_symbols')?.value;
                    addParam('base', baseLatest);
                    addParam('symbols', symLatest);
                    break;
                case 'supported':
                    endpointPath = '/supported-currencies';
                    break;
                case 'symbols':
                    endpointPath = '/currency-symbols';
                    break;
                case 'hist_limits':
                    endpointPath = '/historical-data-limits';
                    break;
                default:
                    endpointPath = '/rates/latest';
                    if (keyNeeded.includes('latest')) {
                        addParam('apikey', '{{CURRENCY_FREAKS_API_KEY}}');
                    }
            }

            const baseFull = baseUrl + endpointPath;
            if (queryParts.length === 0) return baseFull;
            return baseFull + '?' + queryParts.join('&');
        }

        async function fetchData() {
            const targetUrl = buildTargetUrl();

            loader.style.display = 'inline-block';
            httpStatusSpan.innerText = 'proxying...';

            try {
                const response = await fetch(PROXY_URL, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({
                        noteId: NOTE_ID,
                        targetUrl: targetUrl,
                        method: 'GET',
                        options: { headers: {} }
                    })
                });

                httpStatusSpan.innerText = `${response.status} ${response.statusText}`;

                const responseText = await response.text();
                try {
                    const json = JSON.parse(responseText);
                    responsePre.innerText = JSON.stringify(json, null, 2);
                } catch {
                    responsePre.innerText = responseText;
                }
            } catch (error) {
                httpStatusSpan.innerText = 'fetch error';
                responsePre.innerText = `🚨 Proxy request failed:\n${error.message}`;
            } finally {
                loader.style.display = 'none';
            }
        }

        fetchBtn.addEventListener('click', fetchData);

        renderParams('latest');
    })();
</script>
</body>
</html>