summary history files

forex/index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Currency Converter</title>
  <style>
* {
  box-sizing: border-box;
}

body {
  font-family: Helvetica, Arial, sans-serif;
  max-width: 600px;
  margin: 50px auto;
  padding: 20px;
  background: #f5f5f5;
}

.container {
  background: white;
  padding: 30px;
  border-radius: 8px;
  box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}

h1 {
  margin: 0 0 30px 0;
  font-size: 24px;
  color: #333;
  text-align: center;
}

.input-group {
  margin-bottom: 20px;
}

label {
  display: block;
  margin-bottom: 5px;
  color: #555;
  font-size: 14px;
  font-weight: 600;
}

input, select {
  font-size: 16px;
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 4px;
  width: 100%;
  font-family: Helvetica, Arial, sans-serif;
  background: white;
}

.row {
  display: flex;
  gap: 10px;
  align-items: flex-end;
  margin-bottom: 20px;
}

.col {
  flex: 1;
}

.col-swap {
  flex: 0 0 auto;
}

.swap-btn {
  padding: 10px 15px;
  background: #f0f0f0;
  border: 1px solid #ddd;
  border-radius: 4px;
  cursor: pointer;
  font-size: 20px;
  line-height: 1;
  margin-bottom: 0;
}

.swap-btn:hover {
  background: #e0e0e0;
}

.convert-btn, .share-btn {
  width: 100%;
  padding: 12px;
  background: #007bff;
  color: white;
  border: none;
  border-radius: 4px;
  font-size: 16px;
  cursor: pointer;
  font-family: Helvetica, Arial, sans-serif;
  margin-bottom: 10px;
}

.convert-btn:hover, .share-btn:hover {
  background: #0056b3;
}

.share-btn {
  background: #28a745;
}

.share-btn:hover {
  background: #218838;
}

.result {
  margin-top: 30px;
  padding: 20px;
  background: #f8f9fa;
  border-radius: 4px;
  text-align: center;
  display: none;
}

.result-amount {
  font-size: 32px;
  font-weight: bold;
  color: #333;
  margin-bottom: 10px;
}

.result-rate {
  font-size: 14px;
  color: #666;
}

.meta {
  margin-top: 20px;
  font-size: 12px;
  color: #999;
  text-align: center;
}

.error {
  color: #dc3545;
  padding: 10px;
  background: #f8d7da;
  border-radius: 4px;
  margin-top: 15px;
  display: none;
  font-size: 14px;
}

.success {
  color: #155724;
  padding: 10px;
  background: #d4edda;
  border-radius: 4px;
  margin-top: 15px;
  display: none;
  font-size: 14px;
}
  </style>
</head>
<body>
  <div class="container">
    <h1>Currency Converter</h1>

    <div class="input-group">
      <label for="amount">Amount</label>
      <input type="number" id="amount" value="100" min="0" step="any">
    </div>

    <div class="row">
      <div class="col">
        <label for="from">From</label>
        <select id="from"></select>
      </div>
      <div class="col-swap">
        <button id="swap" class="swap-btn">⇄</button>
      </div>
      <div class="col">
        <label for="to">To</label>
        <select id="to"></select>
      </div>
    </div>

    <button id="share" class="share-btn">Copy Share Link</button>

    <div id="success" class="success"></div>
    <div id="error" class="error"></div>

    <div id="result" class="result">
      <div class="result-amount" id="result-amount"></div>
      <div class="result-rate" id="result-rate"></div>
    </div>

    <div class="meta">
      <span id="last-updated">Loading rates...</span>
    </div>
  </div>

  <script type="module">
const currencies = [
  { code: 'AUD', name: 'Australian Dollar', country: 'AU' },
  { code: 'USD', name: 'US Dollar', country: 'US' },
  { code: 'CAD', name: 'Canadian Dollar', country: 'CA' },
  { code: 'GBP', name: 'British Pound', country: 'GB' },
  { code: 'EUR', name: 'Euro', country: 'EU' },
  { code: 'JPY', name: 'Japanese Yen', country: 'JP' },
  { code: 'CNY', name: 'Chinese Yuan', country: 'CN' }
];

const STORAGE_KEY = 'currencyConverterPrefs';
const CACHE_KEY = 'exchangeRates';
const CACHE_TIME_KEY = 'exchangeRatesTime';
const CACHE_DURATION = 60 * 60 * 1000;

const amountInput = document.getElementById('amount');
const fromSelect = document.getElementById('from');
const toSelect = document.getElementById('to');
const swapBtn = document.getElementById('swap');
const shareBtn = document.getElementById('share');
const resultDiv = document.getElementById('result');
const resultAmount = document.getElementById('result-amount');
const resultRate = document.getElementById('result-rate');
const errorDiv = document.getElementById('error');
const successDiv = document.getElementById('success');
const lastUpdatedSpan = document.getElementById('last-updated');

function populateSelects() {
  currencies.forEach(curr => {
    const optionText = `${curr.code} - ${curr.name}`;
    fromSelect.add(new Option(optionText, curr.code));
    toSelect.add(new Option(optionText, curr.code));
  });
}

function formatAmount(amount, currency) {
  if (currency === 'JPY') {
    return amount.toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: 0 });
  }
  return amount.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}

async function detectUserCountry() {
  try {
    const response = await fetch('https://ipapi.co/json/', { timeout: 5000 });
    if (response.ok) {
      const data = await response.json();
      return data.country_code;
    }
  } catch (e) {
    console.log('IP detection failed, using timezone fallback');
  }

  try {
    const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
    const country = timeZone.split('/')[0];
    if (country === 'Australia') return 'AU';
    if (country === 'America' || country === 'US') return 'US';
    if (country === 'Canada') return 'CA';
    if (country === 'Europe' || country === 'London') return 'GB';
    if (country === 'Japan' || country === 'Tokyo') return 'JP';
    if (country === 'China' || country === 'Shanghai') return 'CN';
  } catch (e) {
    console.log('Timezone detection failed');
  }

  return null;
}

function getCurrencyFromCountry(countryCode) {
  const mapping = {
    'AU': 'AUD', 'US': 'USD', 'CA': 'CAD', 'GB': 'GBP',
    'EU': 'EUR', 'JP': 'JPY', 'CN': 'CNY'
  };
  return mapping[countryCode] || null;
}

function loadFromURL() {
  const params = new URLSearchParams(window.location.search);
  const amount = params.get('amount');
  const from = params.get('from');
  const to = params.get('to');

  if (amount && !isNaN(parseFloat(amount))) {
    amountInput.value = amount;
  }
  if (from && currencies.find(c => c.code === from)) {
    fromSelect.value = from;
  }
  if (to && currencies.find(c => c.code === to)) {
    toSelect.value = to;
  }

  return !!(amount && from && to);
}

function loadFromStorage() {
  try {
    const stored = localStorage.getItem(STORAGE_KEY);
    if (stored) {
      const data = JSON.parse(stored);
      if (data.amount) amountInput.value = data.amount;
      if (data.from && currencies.find(c => c.code === data.from)) fromSelect.value = data.from;
      if (data.to && currencies.find(c => c.code === data.to)) toSelect.value = data.to;
      return true;
    }
  } catch (e) {
    console.log('Error loading from storage');
  }
  return false;
}

function saveToStorage() {
  try {
    const data = {
      amount: amountInput.value,
      from: fromSelect.value,
      to: toSelect.value,
      timestamp: Date.now()
    };
    localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
  } catch (e) {
    console.log('Error saving to storage');
  }
}

async function initializeDefaults() {
  populateSelects();

  const loadedFromURL = loadFromURL();

  if (!loadedFromURL) {
    const loadedFromStorage = loadFromStorage();

    if (!loadedFromStorage) {
      const country = await detectUserCountry();
      const defaultCurrency = getCurrencyFromCountry(country);

      if (defaultCurrency && currencies.find(c => c.code === defaultCurrency)) {
        fromSelect.value = defaultCurrency;
        toSelect.value = defaultCurrency === 'USD' ? 'EUR' : 'USD';
      } else {
        fromSelect.value = 'USD';
        toSelect.value = 'EUR';
      }
    }
  }
}

async function fetchRates(base) {
  const cache = localStorage.getItem(CACHE_KEY);
  const cacheTime = localStorage.getItem(CACHE_TIME_KEY);
  const now = Date.now();

  if (cache && cacheTime && (now - parseInt(cacheTime)) < CACHE_DURATION) {
    const parsed = JSON.parse(cache);
    if (parsed.base === base) {
      return parsed;
    }
  }

  const response = await fetch(`https://api.exchangerate-api.com/v4/latest/${base}`);
  if (!response.ok) throw new Error('Failed to fetch exchange rates');
  const data = await response.json();

  localStorage.setItem(CACHE_KEY, JSON.stringify(data));
  localStorage.setItem(CACHE_TIME_KEY, now.toString());

  return data;
}

function showError(message) {
  errorDiv.textContent = message;
  errorDiv.style.display = 'block';
  resultDiv.style.display = 'none';
  setTimeout(() => { errorDiv.style.display = 'none'; }, 5000);
}

function showSuccess(message) {
  successDiv.textContent = message;
  successDiv.style.display = 'block';
  setTimeout(() => { successDiv.style.display = 'none'; }, 3000);
}

function hideError() {
  errorDiv.style.display = 'none';
}

function updateTimestamp(dateStr) {
  if (!dateStr) {
    lastUpdatedSpan.textContent = 'Rates loaded';
    return;
  }
  const date = new Date(dateStr);
  lastUpdatedSpan.textContent = `Last updated: ${date.toLocaleString()}`;
}

function displayResult(amount, from, converted, to, rate) {
  resultAmount.textContent = `${formatAmount(converted, to)} ${to}`;
  resultRate.innerHTML = `${formatAmount(amount, from)} ${from} = ${formatAmount(converted, to)} ${to}<br>1 ${from} = ${formatAmount(rate, to)} ${to}`;
  resultDiv.style.display = 'block';
}

async function convert() {
  const amount = parseFloat(amountInput.value);
  const from = fromSelect.value;
  const to = toSelect.value;

  if (isNaN(amount) || amount < 0) {
    showError('Please enter a valid amount');
    return;
  }

  hideError();

  if (from === to) {
    displayResult(amount, from, amount, to, 1);
    updateTimestamp(new Date());
    saveToStorage();
    return;
  }

  try {
    const data = await fetchRates(from);

    if (!data.rates || !(to in data.rates)) {
      throw new Error('Exchange rate not available for selected currency pair');
    }

    const rate = data.rates[to];
    const converted = amount * rate;

    displayResult(amount, from, converted, to, rate);
    updateTimestamp(data.date);
    saveToStorage();
  } catch (error) {
    showError('Unable to fetch exchange rates. Please check your connection and try again.');
  }
}

function swapCurrencies() {
  const temp = fromSelect.value;
  fromSelect.value = toSelect.value;
  toSelect.value = temp;
  convert();
}

function generateShareLink() {
  const params = new URLSearchParams({
    amount: amountInput.value,
    from: fromSelect.value,
    to: toSelect.value
  });

  const url = `${window.location.origin}${window.location.pathname}?${params.toString()}`;

  navigator.clipboard.writeText(url).then(() => {
    showSuccess('Link copied to clipboard!');
  }).catch(() => {
    showError('Failed to copy link. URL is: ' + url);
  });
}

amountInput.addEventListener('input', convert);
fromSelect.addEventListener('change', convert);
toSelect.addEventListener('change', convert);
swapBtn.addEventListener('click', swapCurrencies);
shareBtn.addEventListener('click', generateShareLink);

document.addEventListener('DOMContentLoaded', async () => {
  await initializeDefaults();
  convert();
});
  </script>
</body>
</html>