Skip to content

Commit 4068e90

Browse files
updating readme
1 parent 87164cb commit 4068e90

1 file changed

File tree

README.md

-35.1 KB

<title>SmartBudgetManager — README</title> <style> :root { --bg: #07070f; --surface: #0f0f1e; --surface2: #161628; --purple: #6c63ff; --pink: #ff6584; --green: #43e97b; --gold: #f7c948; --blue: #4facfe; --text: #e8e8f0; --muted: #6b6b8a; --border: #1e1e38; }

  • { margin: 0; padding: 0; box-sizing: border-box; }

body { background: var(--bg); color: var(--text); font-family: 'Syne', sans-serif; overflow-x: hidden; min-height: 100vh; }

/* Animated background */ body::before { content: ''; position: fixed; inset: 0; background: radial-gradient(ellipse 80% 50% at 20% 20%, rgba(108,99,255,0.08) 0%, transparent 60%), radial-gradient(ellipse 60% 40% at 80% 80%, rgba(255,101,132,0.06) 0%, transparent 60%), radial-gradient(ellipse 50% 50% at 50% 50%, rgba(67,233,123,0.03) 0%, transparent 70%); pointer-events: none; z-index: 0; }

.container { max-width: 900px; margin: 0 auto; padding: 40px 24px 80px; position: relative; z-index: 1; }

/* Hero */ .hero { text-align: center; padding: 60px 0 40px; animation: fadeUp 0.8s ease both; }

.hero-emoji { font-size: 80px; display: block; animation: float 3s ease-in-out infinite; filter: drop-shadow(0 0 30px rgba(108,99,255,0.5)); }

@keyframes float { 0%, 100% { transform: translateY(0px); } 50% { transform: translateY(-12px); } }

.hero h1 { font-size: clamp(2.5rem, 6vw, 4rem); font-weight: 800; margin-top: 20px; background: linear-gradient(135deg, #fff 0%, var(--purple) 50%, var(--pink) 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; line-height: 1.1; }

.hero-sub { font-size: 1.1rem; color: var(--muted); margin-top: 16px; font-weight: 400; letter-spacing: 0.02em; }

/* Animated typewriter */ .typewriter { display: inline-block; color: var(--green); font-family: 'JetBrains Mono', monospace; font-size: 0.95rem; margin-top: 12px; border-right: 2px solid var(--green); white-space: nowrap; overflow: hidden; animation: typing 3.5s steps(50) 1s both, blink 0.8s step-end infinite; max-width: 100%; }

@keyframes typing { from { width: 0; } to { width: 100%; } }

@keyframes blink { 50% { border-color: transparent; } }

/* Badges */ .badges { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-top: 24px; }

.badge { padding: 6px 16px; border-radius: 100px; font-size: 0.75rem; font-weight: 600; font-family: 'JetBrains Mono', monospace; letter-spacing: 0.05em; animation: fadeUp 0.6s ease both; }

.badge-purple { background: rgba(108,99,255,0.15); color: var(--purple); border: 1px solid rgba(108,99,255,0.3); } .badge-green { background: rgba(67,233,123,0.12); color: var(--green); border: 1px solid rgba(67,233,123,0.3); } .badge-pink { background: rgba(255,101,132,0.12); color: var(--pink); border: 1px solid rgba(255,101,132,0.3); } .badge-gold { background: rgba(247,201,72,0.12); color: var(--gold); border: 1px solid rgba(247,201,72,0.3); } .badge-blue { background: rgba(79,172,254,0.12); color: var(--blue); border: 1px solid rgba(79,172,254,0.3); }

/* Section */ .section { margin-top: 56px; animation: fadeUp 0.6s ease both; }

.section-label { font-family: 'JetBrains Mono', monospace; font-size: 0.7rem; color: var(--purple); letter-spacing: 0.2em; text-transform: uppercase; margin-bottom: 12px; }

.section h2 { font-size: 1.8rem; font-weight: 800; color: #fff; margin-bottom: 24px; }

/* Feature cards */ .features { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 16px; }

.feature-card { background: var(--surface); border: 1px solid var(--border); border-radius: 20px; padding: 24px; transition: all 0.3s ease; position: relative; overflow: hidden; animation: fadeUp 0.6s ease both; }

.feature-card::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 2px; border-radius: 20px 20px 0 0; opacity: 0; transition: opacity 0.3s; }

.feature-card:hover { transform: translateY(-4px); border-color: rgba(108,99,255,0.3); } .feature-card:hover::before { opacity: 1; } .feature-card.purple::before { background: linear-gradient(90deg, var(--purple), var(--blue)); } .feature-card.pink::before { background: linear-gradient(90deg, var(--pink), var(--gold)); } .feature-card.green::before { background: linear-gradient(90deg, var(--green), var(--blue)); }

.feature-icon { font-size: 2rem; margin-bottom: 14px; display: block; }

.feature-card h3 { font-size: 1rem; font-weight: 700; color: #fff; margin-bottom: 8px; }

.feature-card p { font-size: 0.875rem; color: var(--muted); line-height: 1.6; font-weight: 400; }

/* Tech stack */ .tech-grid { display: flex; flex-wrap: wrap; gap: 10px; }

.tech-pill { display: flex; align-items: center; gap: 8px; background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 10px 16px; font-size: 0.85rem; font-weight: 600; transition: all 0.2s; cursor: default; }

.tech-pill:hover { border-color: var(--purple); background: rgba(108,99,255,0.08); transform: scale(1.03); } .tech-pill span { font-size: 1.2rem; }

/* Steps */ .steps { display: flex; flex-direction: column; gap: 0; }

.step { display: flex; gap: 20px; position: relative; animation: fadeUp 0.5s ease both; }

.step:not(:last-child)::after { content: ''; position: absolute; left: 19px; top: 44px; bottom: -16px; width: 2px; background: linear-gradient(180deg, var(--purple), transparent); }

.step-num { width: 40px; height: 40px; min-width: 40px; border-radius: 12px; background: linear-gradient(135deg, var(--purple), var(--blue)); display: flex; align-items: center; justify-content: center; font-weight: 800; font-size: 0.9rem; margin-top: 4px; box-shadow: 0 4px 20px rgba(108,99,255,0.3); }

.step-content { background: var(--surface); border: 1px solid var(--border); border-radius: 16px; padding: 20px 24px; flex: 1; margin-bottom: 16px; transition: border-color 0.2s; }

.step-content:hover { border-color: rgba(108,99,255,0.25); }

.step-content h3 { font-size: 1rem; font-weight: 700; color: #fff; margin-bottom: 10px; }

.step-content p { font-size: 0.875rem; color: var(--muted); margin-bottom: 12px; line-height: 1.6; }

/* Code blocks */ pre { background: #0a0a16; border: 1px solid var(--border); border-radius: 12px; padding: 16px; overflow-x: auto; position: relative; }

code { font-family: 'JetBrains Mono', monospace; font-size: 0.8rem; color: var(--green); line-height: 1.7; }

.code-comment { color: #3d3d5c; } .code-key { color: var(--blue); } .code-val { color: var(--gold); }

/* Copy button */ .code-wrapper { position: relative; }

.copy-btn { position: absolute; top: 10px; right: 10px; background: rgba(108,99,255,0.2); border: 1px solid rgba(108,99,255,0.3); color: var(--purple); border-radius: 8px; padding: 4px 10px; font-size: 0.7rem; font-family: 'JetBrains Mono', monospace; cursor: pointer; transition: all 0.2s; }

.copy-btn:hover { background: rgba(108,99,255,0.35); }

/* Buttons */ .btn-row { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 32px; justify-content: center; }

.btn { display: inline-flex; align-items: center; gap: 8px; padding: 14px 28px; border-radius: 14px; font-weight: 700; font-size: 0.9rem; font-family: 'Syne', sans-serif; cursor: pointer; border: none; text-decoration: none; transition: all 0.25s cubic-bezier(0.34, 1.56, 0.64, 1); position: relative; overflow: hidden; }

.btn::after { content: ''; position: absolute; inset: 0; background: rgba(255,255,255,0.1); opacity: 0; transition: opacity 0.2s; }

.btn:hover { transform: translateY(-3px) scale(1.03); } .btn:hover::after { opacity: 1; } .btn:active { transform: scale(0.97); }

.btn-purple { background: linear-gradient(135deg, var(--purple), #8b5cf6); color: #fff; box-shadow: 0 8px 24px rgba(108,99,255,0.35); }

.btn-green { background: linear-gradient(135deg, #1a3a2a, #1e4a30); color: var(--green); border: 1px solid rgba(67,233,123,0.3); box-shadow: 0 8px 24px rgba(67,233,123,0.1); }

.btn-pink { background: linear-gradient(135deg, #3a1a22, #4a1a28); color: var(--pink); border: 1px solid rgba(255,101,132,0.3); box-shadow: 0 8px 24px rgba(255,101,132,0.1); }

.btn-gold { background: linear-gradient(135deg, #3a2e0a, #4a3a0a); color: var(--gold); border: 1px solid rgba(247,201,72,0.3); box-shadow: 0 8px 24px rgba(247,201,72,0.1); }

/* Env table */ .env-table { width: 100%; border-collapse: collapse; font-family: 'JetBrains Mono', monospace; font-size: 0.8rem; }

.env-table th { text-align: left; padding: 10px 14px; color: var(--muted); border-bottom: 1px solid var(--border); font-weight: 600; }

.env-table td { padding: 10px 14px; border-bottom: 1px solid rgba(30,30,56,0.5); color: var(--text); }

.env-table tr:hover td { background: rgba(108,99,255,0.05); } .env-key { color: var(--blue); } .env-desc { color: var(--muted); font-size: 0.75rem; }

/* Divider */ .divider { height: 1px; background: linear-gradient(90deg, transparent, var(--border), transparent); margin: 48px 0; }

/* Footer */ .footer { text-align: center; padding: 40px 0 20px; color: var(--muted); font-size: 0.85rem; }

.footer strong { color: var(--purple); }

/* Animations */ @keyframes fadeUp { from { opacity: 0; transform: translateY(24px); } to { opacity: 1; transform: translateY(0); } }

.section:nth-child(2) { animation-delay: 0.1s; } .section:nth-child(3) { animation-delay: 0.2s; } .section:nth-child(4) { animation-delay: 0.3s; }

.feature-card:nth-child(1) { animation-delay: 0.1s; } .feature-card:nth-child(2) { animation-delay: 0.2s; } .feature-card:nth-child(3) { animation-delay: 0.3s; }

.step:nth-child(1) { animation-delay: 0.05s; } .step:nth-child(2) { animation-delay: 0.1s; } .step:nth-child(3) { animation-delay: 0.15s; } .step:nth-child(4) { animation-delay: 0.2s; } .step:nth-child(5) { animation-delay: 0.25s; } .step:nth-child(6) { animation-delay: 0.3s; } .step:nth-child(7) { animation-delay: 0.35s; }

/* Scrollbar */ ::-webkit-scrollbar { width: 6px; } ::-webkit-scrollbar-track { background: var(--bg); } ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }

/* Highlight */ .highlight { color: var(--purple); } .highlight-green { color: var(--green); } .highlight-pink { color: var(--pink); }

.warning-box { background: rgba(247,201,72,0.07); border: 1px solid rgba(247,201,72,0.2); border-radius: 12px; padding: 14px 18px; font-size: 0.85rem; color: var(--gold); margin-top: 12px; display: flex; gap: 10px; align-items: flex-start; } </style>

💳

💳 Smart Budget Manager

Zero-effort expense tracking by securely parsing your bank transaction SMS

Zero-effort expense tracking by securely parsing your bank transaction SMS

React Native Expo Firebase Android


React NativeExpo SDK 52FirebaseTypeScript ReadyAndroidFree & Open Source

✨ Features

🔍

Smart SMS Parsing

Automatically extracts amount, merchant, and date from any Indian bank SMS — HDFC, SBI, ICICI, Axis, Kotak and more.

📊

Real-time Dashboard

Beautiful pie charts and category breakdowns give you instant visual clarity on where your money goes.

🔐

Secure Cloud Sync

Firebase Auth + Firestore keeps your data encrypted, private, and synced across devices. Only you can see it.

🏷️

Auto Categorization

Transactions are instantly sorted into Food, Transport, Shopping, Entertainment, Health, Utilities and more.

📱

Monthly Insights

Track this month's spending vs total spend. Pull-to-refresh keeps everything up to date in real time.

🌐

Offline-first Design

Parse and preview transactions without internet. Syncs to cloud when connection is restored.

Feature Description
🔍 Smart SMS Parsing Auto-extracts amount, merchant and date from Indian bank SMS
📊 Real-time Dashboard Pie charts and category breakdowns at a glance
🔐 Secure Cloud Sync Firebase Auth + Firestore — only you can see your data
🏷️ Auto Categorization Food, Transport, Shopping, Health, Utilities and more
📱 Monthly Insights This month vs total spend with pull-to-refresh
🏦 Multi-bank Support HDFC, SBI, ICICI, Axis, Kotak and most Indian banks

🛠 Tech Stack

⚛️
  • ⚛️ React Native — Mobile UI
  • 📦 Expo SDK 52 — Build and dev tooling
  • 🔥 Firebase Auth — Secure authentication
  • 🗄️ Firestore — Encrypted cloud database
  • 🧭 React Navigation — Tab-based routing
  • 📈 React Native Chart Kit — Pie charts
  • 🌿 dotenv — Environment variable management
  • 📅 date-fns — Date formatting

📦 🔥 🗄️ 🧭 📈 🔑 🌿 📅 🎨

📋 Prerequisites

🟢

Node.js 18+

Download from nodejs.org. Run node --version to verify.

📱

Expo Go App

Install Expo Go from Play Store on your Android device for instant preview.

🔥

Firebase Account

Free account at console.firebase.google.com. Enable Auth + Firestore.

Before you begin, make sure you have:


🚀 Installation Guide

  <div class="step">
    <div class="step-num">1</div>
    <div class="step-content">
      <h3>📥 Clone the Repository</h3>
      <p>Clone or download the project to your local machine.</p>
      <div class="code-wrapper">
        <pre><code>git clone https://github.com/yourusername/SmartBudgetManager.git

cd SmartBudgetManager copy

  <div class="step">
    <div class="step-num">2</div>
    <div class="step-content">
      <h3>📦 Install Dependencies</h3>
      <p>Install all required packages with npm.</p>
      <div class="code-wrapper">
        <pre><code>npm install

npx expo install expo-asset expo-font expo-secure-store npx expo install @react-native-async-storage/async-storage npx expo install expo-constants copy

  <div class="step">
    <div class="step-num">3</div>
    <div class="step-content">
      <h3>🔥 Setup Firebase</h3>
      <p>Create a project at <strong style="color:var(--gold)">console.firebase.google.com</strong>, then enable:</p>
      <p>→ <span class="highlight">Authentication</span> → Email/Password<br>
      → <span class="highlight-green">Firestore Database</span> → Start in test mode<br>
      → Get your config from Project Settings → Your Apps</p>
    </div>
  </div>

  <div class="step">
    <div class="step-num">4</div>
    <div class="step-content">
      <h3>🔑 Configure Environment</h3>
      <p>Create a <code style="color:var(--green);font-family:monospace">.env</code> file in the project root:</p>
      <div class="code-wrapper">
        <pre><code><span class="code-comment"># .env — never commit this file!</span>

FIREBASE_API_KEY=AIzaSyB_your_key_here FIREBASE_AUTH_DOMAIN=yourproject.firebaseapp.com FIREBASE_PROJECT_ID=yourproject-id FIREBASE_STORAGE_BUCKET=yourproject.firebasestorage.app FIREBASE_MESSAGING_SENDER_ID=123456789012 FIREBASE_APP_ID=1:123456789012:android:abcdef copy

⚠️

Step 1 — Clone the Repository

git clone https://github.com/YOUR_USERNAME/SmartBudgetManager.git
cd SmartBudgetManager

Step 2 — Install Dependencies

npm install
npx expo install expo-asset expo-font expo-secure-store
npx expo install @react-native-async-storage/async-storage
npx expo install expo-constants
npm install dotenv

Step 3 — Setup Firebase

  1. Go to console.firebase.google.com
  2. Click Add project and name it SmartBudgetManager
  3. Left sidebar → AuthenticationGet started → Enable Email/Password
  4. Left sidebar → Firestore DatabaseCreate databaseStart in test mode
  5. Choose asia-south1 (Mumbai) as your region
  6. Go to Project SettingsYour apps → copy your config values

Step 4 — Configure Environment Variables

Create a .env file in the project root:

FIREBASE_API_KEY=******
FIREBASE_AUTH_DOMAIN=******
FIREBASE_PROJECT_ID=******
FIREBASE_STORAGE_BUCKET=******
FIREBASE_MESSAGING_SENDER_ID=******
FIREBASE_APP_ID=******

⚠️ Never commit .env to git. It is already listed in .gitignore.

Copy your real values from Firebase Project Settings into each field.

Step 5 — Set Firestore Security Rules

In Firebase Console → FirestoreRules tab, paste:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /transactions/{docId} {
      allow read, write: if request.auth != null
        && request.auth.uid == resource.data.userId;
      allow create: if request.auth != null
        && request.auth.uid == request.resource.data.userId;
    }
  }
}

Click Publish.

Step 6 — Start the App

npx expo start --clear

Scan the QR code with Expo Go on your Android phone.


📦 Build APK

To build a standalone APK using EAS:

npm install -g eas-cli
npx eas-cli login
npx eas-cli build --platform android --profile preview
.env .gitignore

  <div class="step">
    <div class="step-num">5</div>
    <div class="step-content">
      <h3>🛡️ Set Firestore Security Rules</h3>
      <p>In Firebase Console → Firestore → Rules tab, paste:</p>
      <div class="code-wrapper">
        <pre><code>rules_version = '2';

service cloud.firestore { match /databases/{database}/documents { match /transactions/{docId} { allow read, write: if request.auth != null && request.auth.uid == resource.data.userId; allow create: if request.auth != null && request.auth.uid == request.resource.data.userId; } } } copy

  <div class="step">
    <div class="step-num">6</div>
    <div class="step-content">
      <h3>▶️ Start the App</h3>
      <p>Launch the Expo development server.</p>
      <div class="code-wrapper">
        <pre><code>npx expo start --clear</code></pre>
        <button class="copy-btn" onclick="copyCode(this)">copy</button>
      </div>
      <p style="margin-top:10px">Scan the QR code with <strong style="color:var(--purple)">Expo Go</strong> on your Android phone.</p>
    </div>
  </div>

  <div class="step">
    <div class="step-num">7</div>
    <div class="step-content">
      <h3>📦 Build APK (Optional)</h3>
      <p>To build a standalone APK using EAS Build:</p>
      <div class="code-wrapper">
        <pre><code>npm install -g eas-cli

npx eas-cli login npx eas-cli build --platform android --profile preview copy

Download the APK from your expo.dev dashboard and install on your phone.

</div>

🔐 Environment Variables Reference

Variable Description Where to find
FIREBASE_API_KEY Firebase Web API Key Project Settings → General
FIREBASE_AUTH_DOMAIN Authentication domain Project Settings → Your Apps
FIREBASE_PROJECT_ID Unique project identifier Project Settings → General
FIREBASE_STORAGE_BUCKET Cloud Storage bucket URL Project Settings → Your Apps
FIREBASE_MESSAGING_SENDER_ID FCM Sender ID Project Settings → Cloud Messaging
FIREBASE_APP_ID Firebase App ID Project Settings → Your Apps
Variable Description Where to find
FIREBASE_API_KEY Firebase Web API Key Project Settings → General
FIREBASE_AUTH_DOMAIN Auth domain Project Settings → Your Apps
FIREBASE_PROJECT_ID Project identifier Project Settings → General
FIREBASE_STORAGE_BUCKET Storage bucket URL Project Settings → Your Apps
FIREBASE_MESSAGING_SENDER_ID FCM Sender ID Project Settings → Cloud Messaging
FIREBASE_APP_ID Firebase App ID Project Settings → Your Apps

📁 Project Structure

SmartBudgetManager/
├── App.js
├── app.config.js              # Expo config with env vars
├── babel.config.js
├── .env                       # Secret keys (never commit)
├── .env.example               # Safe template for teammates
├── scripts/
│   ├── mask_keys.py           # Pre-commit secret masking
│   ├── compile_check.py       # Multi-language syntax checker
│   └── check_requirements.py
├── src/
│   ├── config/
│   │   └── firebase.js
│   ├── context/
│   │   └── AuthContext.js
│   ├── navigation/
│   │   └── AppNavigator.js
│   ├── screens/
│   │   ├── LoginScreen.js
│   │   ├── DashboardScreen.js
│   │   ├── ImportScreen.js
│   │   └── SettingsScreen.js
│   └── utils/
│       ├── smsParser.js
│       └── transactionStore.js
└── .github/
    └── workflows/
        └── ci.yml             # GitHub Actions CI

🔒 Security

  • 🔑 All API keys stored in .env — never committed to git
  • 🛡️ Pre-commit hooks automatically mask any exposed secrets
  • 🔐 Firebase Auth ensures only authenticated users access data
  • 📋 Firestore rules restrict each user to their own transactions only
  • 🤖 GitHub Actions CI scans for raw secrets on every push

🤖 Pre-commit Hooks

This project uses pre-commit hooks that run on every git commit:

Hook What it does
trailing-whitespace Removes trailing spaces
end-of-file-fixer Ensures files end with newline
detect-private-key Blocks raw PEM/RSA keys
check-merge-conflict Blocks unresolved merge conflicts
flake8 Python code linting
mask-secrets Masks API keys with ******
compile-check Syntax check for 15+ languages

Supported Languages for Compile Check

Python JavaScript TypeScript Java Kotlin Go Rust Ruby PHP Dart Swift C C++


🏦 Supported Banks (SMS Parsing)

Bank Status
HDFC Bank ✅ Supported
SBI ✅ Supported
ICICI Bank ✅ Supported
Axis Bank ✅ Supported
Kotak Mahindra ✅ Supported
Yes Bank ✅ Supported
IndusInd Bank ✅ Supported
Other banks ✅ Generic pattern fallback

🏷️ Expense Categories

Category Keywords Detected
🍕 Food Zomato, Swiggy, restaurant, cafe, dining
🚗 Transport Uber, Ola, metro, fuel, IRCTC, Rapido
🛍️ Shopping Amazon, Flipkart, Myntra, Ajio, Meesho
🎬 Entertainment Netflix, Prime, Spotify, BookMyShow, PVR
🏥 Health Apollo, pharmacy, hospital, MedPlus
⚡ Utilities Jio, Airtel, electricity, broadband
💰 Finance EMI, loan, insurance, SIP, mutual fund
🛒 Groceries BigBasket, Blinkit, Zepto, Grofers

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/my-feature
  3. Commit your changes: git commit -m "Add my feature"
  4. Push to the branch: git push origin feature/my-feature
  5. Open a Pull Request

Note: Direct commits to production branch are blocked by pre-commit hooks.


📄 License

MIT License — feel free to use, modify and distribute.


🔗 Quick Links

🔥 Firebase Console📦 Expo Dashboard📚 Expo Docs📱 Get Expo Go

Built with ❤️ using React Native + Firebase

Smart Budget Manager — Track every rupee, effortlessly 💳

<script> function copyCode(btn) { const pre = btn.previousElementSibling; const text = pre.innerText; navigator.clipboard.writeText(text).then(() => { btn.textContent = 'copied!'; btn.style.color = 'var(--green)'; btn.style.borderColor = 'rgba(67,233,123,0.4)'; setTimeout(() => { btn.textContent = 'copy'; btn.style.color = ''; btn.style.borderColor = ''; }, 2000); }); } // Staggered scroll animations const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.style.opacity = '1'; entry.target.style.transform = 'translateY(0)'; } }); }, { threshold: 0.1 }); document.querySelectorAll('.feature-card, .step, .tech-pill').forEach(el => { el.style.opacity = '0'; el.style.transform = 'translateY(20px)'; el.style.transition = 'opacity 0.5s ease, transform 0.5s ease'; observer.observe(el); }); </script> �

💳 Track every rupee, effortlessly

0 commit comments

Comments
 (0)