From 1263bb6f6212a93d3c85541f1476cd23c9a8a12f Mon Sep 17 00:00:00 2001 From: nicksonthc Date: Fri, 26 Sep 2025 14:23:26 +0800 Subject: [PATCH 1/3] feat:coderabbit --- .github/coderabbit.yml | 213 ++++++++++ src/App.js | 2 + .../IncomeTax/IncomeTaxCalculator.css | 376 ++++++++++++++++++ .../IncomeTax/IncomeTaxCalculator.js | 250 ++++++++++++ src/components/Navbar.js | 1 + 5 files changed, 842 insertions(+) create mode 100644 .github/coderabbit.yml create mode 100644 src/components/IncomeTax/IncomeTaxCalculator.css create mode 100644 src/components/IncomeTax/IncomeTaxCalculator.js diff --git a/.github/coderabbit.yml b/.github/coderabbit.yml new file mode 100644 index 0000000..eff3298 --- /dev/null +++ b/.github/coderabbit.yml @@ -0,0 +1,213 @@ +# CodeRabbit Configuration for NickSpace Playground +# https://docs.coderabbit.ai/configuration-file + +# Language settings +language: en-US + +# Review settings +reviews: + # Enable/disable auto reviews + auto_review: true + + # Draft PR reviews + draft_as_pending: true + + # Review scope + review: + # Files to review + include: + - "**/*.js" + - "**/*.jsx" + - "**/*.ts" + - "**/*.tsx" + - "**/*.css" + - "**/*.scss" + - "**/*.json" + - "**/*.md" + - "**/*.yml" + - "**/*.yaml" + + # Files to exclude from review + exclude: + - "node_modules/**" + - "build/**" + - "dist/**" + - "coverage/**" + - "*.min.js" + - "*.bundle.js" + - "package-lock.json" + - "yarn.lock" + + # Auto-approve changes to these files + auto_approve: + - "package.json" # dependency updates + - "README.md" # documentation updates + - "CHANGELOG.md" + + # Require manual approval for these critical files + require_manual_approval: + - "src/App.js" + - ".github/**" + - "public/index.html" + - "CLAUDE.md" + +# Code quality rules +rules: + # JavaScript/React specific rules + javascript: + # Performance + - check_unused_variables: true + - check_console_logs: warn + - check_debugger_statements: error + - suggest_const_over_let: true + + # React specific + - check_react_hooks_dependencies: true + - suggest_react_memo: true + - check_jsx_key_props: true + - suggest_functional_components: true + + # General code quality + - max_function_length: 50 + - max_file_length: 500 + - suggest_early_returns: true + - check_duplicate_code: true + + # CSS specific rules + css: + - check_unused_styles: true + - suggest_css_variables: true + - check_responsive_units: true + - prefer_flexbox_grid: true + + # Security rules + security: + - check_hardcoded_secrets: error + - check_vulnerable_dependencies: warn + - check_unsafe_html: error + +# Comment settings +comments: + # Comment behavior + tone: friendly + max_comments_per_review: 15 + + # Focus areas for comments + focus_areas: + - performance + - security + - maintainability + - accessibility + - best_practices + - code_style + + # Specific comment types + suggest: + - performance_improvements: true + - accessibility_improvements: true + - code_simplification: true + - better_naming: true + - error_handling: true + + # Don't comment on these (auto-fix instead) + auto_fix: + - formatting + - simple_typos + - missing_semicolons + - trailing_whitespace + +# Project-specific settings +project: + # Framework detection + framework: react + + # Build tools + build_tools: + - npm + - create-react-app + + # Testing framework + testing_framework: jest + + # Deployment + deployment_platform: github_pages + +# Integration settings +integrations: + # GitHub settings + github: + # PR title suggestions + suggest_pr_titles: true + + # Auto-assign reviewers based on file changes + auto_assign_reviewers: + - "src/components/GameTheory/**": ["@game-theory-expert"] + - "src/components/Pathfinding/**": ["@algorithms-expert"] + - "src/components/IncomeTax/**": ["@finance-expert"] + - "**/*.css": ["@ui-ux-expert"] + + # PR labels + auto_labels: + - path: "src/components/**/*.js" + labels: ["frontend", "react"] + - path: "**/*.css" + labels: ["styling", "ui"] + - path: "**/*.md" + labels: ["documentation"] + - path: ".github/**" + labels: ["ci/cd", "config"] + - path: "src/components/IncomeTax/**" + labels: ["feature", "calculator"] + +# Custom rules for this project +custom_rules: + # Educational project specific rules + - name: "educational_clarity" + description: "Ensure code is educational and well-documented" + pattern: "src/components/**/*.js" + checks: + - has_component_description: warn + - has_prop_types: suggest + - has_usage_examples: suggest + + # Calculator specific rules + - name: "calculator_accuracy" + description: "Ensure calculator components have proper validation" + pattern: "src/components/*Calculator/**/*.js" + checks: + - has_input_validation: error + - has_error_handling: error + - has_boundary_testing: warn + + # Performance rules for interactive components + - name: "interactive_performance" + description: "Ensure interactive components are optimized" + pattern: "src/components/**/*.js" + checks: + - uses_react_memo_when_needed: suggest + - uses_callback_for_handlers: suggest + - minimal_re_renders: warn + +# Notification settings +notifications: + # When to notify + events: + - review_completed + - high_priority_issues + - security_vulnerabilities + + # Notification channels (customize based on your setup) + channels: + - github_comments + - github_reviews + +# Learning and improvement +learning: + # Track improvement over time + track_metrics: true + + # Suggest learning resources + suggest_resources: true + + # Focus on educational value since this is a playground project + educational_mode: true \ No newline at end of file diff --git a/src/App.js b/src/App.js index d0b0a6a..3418b47 100644 --- a/src/App.js +++ b/src/App.js @@ -4,6 +4,7 @@ import Home from "./Home"; import Pathfinding from "./Pathfinding"; import GameTheory from "./components/GameTheory/GameTheory"; import ProjectCalculator from "./ProjectCalculator"; +import IncomeTaxCalculator from "./components/IncomeTax/IncomeTaxCalculator"; import Algo from './components/AlgoAndDs/AlgoAndDs'; import './App.css'; @@ -15,6 +16,7 @@ function App() { } /> } /> } /> + } /> } /> diff --git a/src/components/IncomeTax/IncomeTaxCalculator.css b/src/components/IncomeTax/IncomeTaxCalculator.css new file mode 100644 index 0000000..2af834c --- /dev/null +++ b/src/components/IncomeTax/IncomeTaxCalculator.css @@ -0,0 +1,376 @@ +.income-tax-container { + max-width: 1400px; + margin: 0 auto; + padding: 2rem; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + min-height: 100vh; +} + +.tax-header { + text-align: center; + margin-bottom: 3rem; + color: white; +} + +.tax-header h1 { + font-size: 2.5rem; + margin-bottom: 0.5rem; + text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3); +} + +.tax-header p { + font-size: 1.1rem; + opacity: 0.9; + margin: 0; +} + +.tax-content { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 2rem; + align-items: start; +} + +@media (max-width: 1024px) { + .tax-content { + grid-template-columns: 1fr; + } +} + +.tax-input-section { + display: flex; + flex-direction: column; + gap: 1.5rem; +} + +.input-card { + background: white; + border-radius: 12px; + padding: 2rem; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.2); +} + +.input-card h2 { + color: #333; + margin-bottom: 1.5rem; + font-size: 1.4rem; + border-bottom: 2px solid #667eea; + padding-bottom: 0.5rem; +} + +.input-group { + margin-bottom: 1rem; +} + +.input-group label { + display: block; + margin-bottom: 0.5rem; + font-weight: 600; + color: #555; + font-size: 0.9rem; +} + +.input-group input { + width: 100%; + padding: 0.75rem; + border: 2px solid #e1e5e9; + border-radius: 8px; + font-size: 1rem; + transition: all 0.3s ease; + background: #f8f9fa; +} + +.input-group input:focus { + outline: none; + border-color: #667eea; + background: white; + box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); +} + +.income-input { + font-size: 1.2rem !important; + font-weight: 600; + padding: 1rem !important; + background: linear-gradient(45deg, #f8f9fa, #e9ecef) !important; +} + +.relief-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 1rem; +} + +.button-group { + display: flex; + gap: 1rem; + justify-content: center; +} + +.calculate-btn, .reset-btn { + padding: 1rem 2rem; + border: none; + border-radius: 8px; + font-size: 1rem; + font-weight: 600; + cursor: pointer; + transition: all 0.3s ease; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.calculate-btn { + background: linear-gradient(45deg, #667eea, #764ba2); + color: white; + box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3); +} + +.calculate-btn:hover { + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4); +} + +.reset-btn { + background: linear-gradient(45deg, #6c757d, #495057); + color: white; + box-shadow: 0 4px 15px rgba(108, 117, 125, 0.3); +} + +.reset-btn:hover { + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(108, 117, 125, 0.4); +} + +.tax-results-section { + display: flex; + flex-direction: column; + gap: 1.5rem; +} + +.results-card { + background: white; + border-radius: 12px; + padding: 2rem; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); + border: 1px solid rgba(255, 255, 255, 0.2); +} + +.results-card h2 { + color: #333; + margin-bottom: 1.5rem; + font-size: 1.4rem; + border-bottom: 2px solid #28a745; + padding-bottom: 0.5rem; +} + +.results-summary { + display: flex; + flex-direction: column; + gap: 0.8rem; + margin-bottom: 2rem; +} + +.summary-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.5rem 0; + border-bottom: 1px solid #f1f3f4; +} + +.summary-item.highlight { + background: linear-gradient(90deg, rgba(40, 167, 69, 0.1), transparent); + padding: 1rem; + border-radius: 8px; + border: 2px solid #28a745; + font-weight: 600; + margin: 0.5rem 0; +} + +.summary-item .label { + font-weight: 600; + color: #666; +} + +.summary-item .value { + font-weight: 700; + font-size: 1.1rem; +} + +.summary-item .value.relief { + color: #28a745; +} + +.summary-item .value.tax { + color: #dc3545; + font-size: 1.2rem; +} + +.summary-item .value.net { + color: #28a745; + font-size: 1.2rem; +} + +.tax-rates { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1rem; + margin-bottom: 2rem; +} + +.rate-item { + background: linear-gradient(45deg, #f8f9fa, #e9ecef); + padding: 1rem; + border-radius: 8px; + text-align: center; + border: 1px solid #dee2e6; +} + +.rate-label { + display: block; + font-size: 0.9rem; + color: #666; + margin-bottom: 0.5rem; +} + +.rate-value { + display: block; + font-size: 1.5rem; + font-weight: 700; + color: #495057; +} + +.tax-breakdown { + border-top: 2px solid #f1f3f4; + padding-top: 1.5rem; +} + +.tax-breakdown h3 { + color: #333; + margin-bottom: 1rem; + font-size: 1.2rem; +} + +.breakdown-table { + display: flex; + flex-direction: column; + border: 1px solid #dee2e6; + border-radius: 8px; + overflow: hidden; +} + +.breakdown-header, .breakdown-row { + display: grid; + grid-template-columns: 2fr 1fr 1.5fr 1.5fr; + gap: 1rem; + padding: 0.75rem; + align-items: center; +} + +.breakdown-header { + background: linear-gradient(45deg, #667eea, #764ba2); + color: white; + font-weight: 600; + font-size: 0.9rem; +} + +.breakdown-row { + border-bottom: 1px solid #f1f3f4; + font-size: 0.9rem; +} + +.breakdown-row:nth-child(even) { + background: #f8f9fa; +} + +.breakdown-row:last-child { + border-bottom: none; +} + +.tax-brackets-info { + background: white; + border-radius: 12px; + padding: 2rem; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); + border: 1px solid rgba(255, 255, 255, 0.2); +} + +.tax-brackets-info h3 { + color: #333; + margin-bottom: 1.5rem; + font-size: 1.4rem; + border-bottom: 2px solid #ffc107; + padding-bottom: 0.5rem; +} + +.brackets-table { + display: flex; + flex-direction: column; + border: 1px solid #dee2e6; + border-radius: 8px; + overflow: hidden; +} + +.brackets-header, .brackets-row { + display: grid; + grid-template-columns: 2fr 1fr; + gap: 1rem; + padding: 0.75rem; + align-items: center; +} + +.brackets-header { + background: linear-gradient(45deg, #ffc107, #fd7e14); + color: white; + font-weight: 600; + font-size: 0.9rem; +} + +.brackets-row { + border-bottom: 1px solid #f1f3f4; + font-size: 0.9rem; +} + +.brackets-row:nth-child(even) { + background: #f8f9fa; +} + +.brackets-row:last-child { + border-bottom: none; +} + +@media (max-width: 768px) { + .income-tax-container { + padding: 1rem; + } + + .tax-header h1 { + font-size: 2rem; + } + + .relief-grid { + grid-template-columns: 1fr; + } + + .button-group { + flex-direction: column; + } + + .tax-rates { + grid-template-columns: 1fr; + } + + .breakdown-header, .breakdown-row { + grid-template-columns: 1fr; + gap: 0.5rem; + text-align: center; + } + + .brackets-header, .brackets-row { + grid-template-columns: 1fr; + gap: 0.5rem; + text-align: center; + } +} \ No newline at end of file diff --git a/src/components/IncomeTax/IncomeTaxCalculator.js b/src/components/IncomeTax/IncomeTaxCalculator.js new file mode 100644 index 0000000..08db887 --- /dev/null +++ b/src/components/IncomeTax/IncomeTaxCalculator.js @@ -0,0 +1,250 @@ +import { useState } from 'react'; +import './IncomeTaxCalculator.css'; + +const IncomeTaxCalculator = () => { + const [income, setIncome] = useState(''); + const [personalRelief, setPersonalRelief] = useState(9000); + const [spouseRelief, setSpouseRelief] = useState(0); + const [childRelief, setChildRelief] = useState(0); + const [educationRelief, setEducationRelief] = useState(0); + const [epfRelief, setEpfRelief] = useState(0); + const [results, setResults] = useState(null); + + const taxBrackets = [ + { min: 0, max: 5000, rate: 0 }, + { min: 5001, max: 20000, rate: 1 }, + { min: 20001, max: 35000, rate: 3 }, + { min: 35001, max: 50000, rate: 6 }, + { min: 50001, max: 70000, rate: 11 }, + { min: 70001, max: 100000, rate: 19 }, + { min: 100001, max: 400000, rate: 25 }, + { min: 400001, max: 600000, rate: 26 }, + { min: 600001, max: 2000000, rate: 28 }, + { min: 2000001, max: Infinity, rate: 30 } + ]; + + const calculateTax = () => { + const grossIncome = parseFloat(income) || 0; + const totalRelief = personalRelief + spouseRelief + (childRelief * 2000) + educationRelief + epfRelief; + const taxableIncome = Math.max(0, grossIncome - totalRelief); + + let tax = 0; + let breakdownData = []; + + for (const bracket of taxBrackets) { + if (taxableIncome > bracket.min - 1) { + const taxableInBracket = Math.min(taxableIncome, bracket.max) - bracket.min + 1; + const taxInBracket = (taxableInBracket * bracket.rate) / 100; + + if (taxInBracket > 0) { + tax += taxInBracket; + breakdownData.push({ + range: bracket.max === Infinity ? `RM ${bracket.min.toLocaleString()}+` : `RM ${bracket.min.toLocaleString()} - RM ${bracket.max.toLocaleString()}`, + rate: bracket.rate, + taxableAmount: taxableInBracket, + taxAmount: taxInBracket + }); + } + } + } + + const netIncome = grossIncome - tax; + const effectiveRate = grossIncome > 0 ? (tax / grossIncome) * 100 : 0; + const marginalRate = taxBrackets.find(bracket => + taxableIncome >= bracket.min - 1 && taxableIncome <= bracket.max + )?.rate || 0; + + setResults({ + grossIncome, + totalRelief, + taxableIncome, + tax, + netIncome, + effectiveRate, + marginalRate, + breakdown: breakdownData + }); + }; + + const handleReset = () => { + setIncome(''); + setPersonalRelief(9000); + setSpouseRelief(0); + setChildRelief(0); + setEducationRelief(0); + setEpfRelief(0); + setResults(null); + }; + + return ( +
+
+

🇲🇾 Malaysia Income Tax Calculator 2025

+

Calculate your Malaysian income tax liability for Assessment Year 2025

+
+ +
+
+
+

Income Information

+
+ + setIncome(e.target.value)} + placeholder="Enter your annual income" + className="income-input" + /> +
+
+ +
+

Tax Relief

+
+
+ + setPersonalRelief(parseFloat(e.target.value) || 0)} + placeholder="9000" + /> +
+
+ + setSpouseRelief(parseFloat(e.target.value) || 0)} + placeholder="4000" + /> +
+
+ + setChildRelief(parseInt(e.target.value) || 0)} + placeholder="0" + /> +
+
+ + setEducationRelief(parseFloat(e.target.value) || 0)} + placeholder="7000" + /> +
+
+ + setEpfRelief(parseFloat(e.target.value) || 0)} + placeholder="4000" + /> +
+
+
+ +
+ + +
+
+ +
+ {results && ( +
+

Tax Calculation Results

+ +
+
+ Gross Income: + RM {results.grossIncome.toLocaleString()} +
+
+ Total Relief: + -RM {results.totalRelief.toLocaleString()} +
+
+ Taxable Income: + RM {results.taxableIncome.toLocaleString()} +
+
+ Tax Payable: + RM {results.tax.toFixed(2)} +
+
+ Net Income: + RM {results.netIncome.toFixed(2)} +
+
+ +
+
+ Effective Tax Rate: + {results.effectiveRate.toFixed(2)}% +
+
+ Marginal Tax Rate: + {results.marginalRate}% +
+
+ + {results.breakdown.length > 0 && ( +
+

Tax Breakdown by Bracket

+
+
+ Income Range + Rate + Taxable Amount + Tax +
+ {results.breakdown.map((bracket, index) => ( +
+ {bracket.range} + {bracket.rate}% + RM {bracket.taxableAmount.toFixed(0)} + RM {bracket.taxAmount.toFixed(2)} +
+ ))} +
+
+ )} +
+ )} + +
+

Malaysia Tax Brackets 2025

+
+
+ Income Range (RM) + Tax Rate +
+ {taxBrackets.map((bracket, index) => ( +
+ + {bracket.min === 0 ? '0' : bracket.min.toLocaleString()} - {' '} + {bracket.max === Infinity ? '2,000,000+' : bracket.max.toLocaleString()} + + {bracket.rate}% +
+ ))} +
+
+
+
+
+ ); +}; + +export default IncomeTaxCalculator; \ No newline at end of file diff --git a/src/components/Navbar.js b/src/components/Navbar.js index 456ecc4..30634f4 100644 --- a/src/components/Navbar.js +++ b/src/components/Navbar.js @@ -18,6 +18,7 @@ function Navbar() { Pathfinding Game Theory Project Calculator + Income Tax AlgoCraft From a094b2e31917df81464d62d6cf90196e5dbc2620 Mon Sep 17 00:00:00 2001 From: nicksonthc Date: Fri, 26 Sep 2025 14:24:49 +0800 Subject: [PATCH 2/3] Update package-lock.json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- package-lock.json | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 775d6f3..63f4f63 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16604,17 +16604,16 @@ } }, "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "license": "Apache-2.0", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=14.17" + "node": ">=4.2.0" } }, "node_modules/unbox-primitive": { From eadb0c7c8f706b6f508d44bdadf56e0bb7312559 Mon Sep 17 00:00:00 2001 From: Nickson Tan Hoo Chuan Date: Fri, 26 Sep 2025 23:51:36 +0800 Subject: [PATCH 3/3] Update .github/coderabbit.yml Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .github/coderabbit.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/coderabbit.yml b/.github/coderabbit.yml index eff3298..46ae701 100644 --- a/.github/coderabbit.yml +++ b/.github/coderabbit.yml @@ -40,7 +40,6 @@ reviews: # Auto-approve changes to these files auto_approve: - - "package.json" # dependency updates - "README.md" # documentation updates - "CHANGELOG.md"