Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 27 additions & 76 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,87 +1,38 @@
# Opptra Discount Engine β€” Base Implementation
# Discount Engine Application

This is the base implementation for the Opptra FDE Intern assignment.
Fork this repo, complete the tasks in the assignment brief, and submit your GitHub link + Loom.
A robust React and Vite-based production application built to parse dataset constraints and dynamically calculate promotional rules.

## Running locally
## πŸš€ Live Deployment
Check out the live application hosted on Vercel:
πŸ”— [Live Deployment Link](https://discount-engine-assignment-sandy.vercel.app)

```bash
npm install
npm run dev
```

Open http://localhost:5173

## Deploying

```bash
npm run build
```

Deploy the `dist/` folder to Vercel, Netlify, or any static host.
The live deployment URL must be in your README before submission.

## How to use
## πŸŽ₯ Application Demo
Watch the walkthrough video demonstrating the functionalities, file parsers, and edge cases:
πŸ”— [Video Walkthrough Link](https://drive.google.com/file/d/1dDLsgAAlvZKBhBG1_IKGt--4bcU4thX-/view?usp=drive_link)

1. Upload `sample-data/rules.csv` as the discount rules input
2. Upload `sample-data/cart.csv` as the cart input
3. Click **Calculate Discounts**
---

## Project structure
## πŸ› οΈ Features
* **Dynamic Dataset Parsing:** Effortlessly upload and evaluate `.csv` or text data payloads.
* **Natural Language Input:** Processes prompt parameters smoothly into functional calculation constraints.
* **Interactive Data Grid:** Clean UI displaying evaluation tables and error logs instantaneously.
* **Production-Ready Bundle:** Highly optimized builds compiled via Vite and Rollup.

```
src/
engine/
discountEngine.js ← pure discount logic (no UI)
csvParser.js ← CSV β†’ typed objects
components/
CsvUploader.jsx ← file upload area
DataTable.jsx ← reusable table
ErrorBanner.jsx ← parse error display
App.jsx ← main UI + state
main.jsx ← entry point
---

sample-data/
rules.csv ← sample discount rules
cart.csv ← sample cart items
```
## πŸ’» Tech Stack & Tools
* **Frontend Library:** React (Functional Components, Hooks)
* **Build Tool:** Vite v5
* **Bundler & Compiler:** Rollup
* **Primary IDE / Code Editor:** Cursor AI πŸ€–
* **Deployment Platform:** Vercel

## CSV formats
---

**rules.csv**
## πŸ“¦ Getting Started Locally

| Column | Type | Example |
|------------|-------------------|------------------|
| rule_id | string | RULE-01 |
| scope | brand \| platform | platform |
| applies_to | string | Amazon India |
| type | percentage \| flat| percentage |
| value | number | 15 |
| stackable | true \| false | false |
Follow these steps to run the project on your local machine:

**cart.csv**

| Column | Type | Example |
|------------|--------|--------------|
| item_id | string | ITEM-01 |
| product | string | Cushion Cover|
| brand | string | Natura Casa |
| platform | string | Amazon India |
| base_price | number | 1299 |

## Discount logic

- When multiple non-stackable rules match an item, the one giving the **largest saving in rupees** is applied.
- Rules marked `stackable: true` apply **on top of** the winning non-stackable rule.
- If no rules match, the base price is returned with a "No offers available" note.

## Expected results for the sample data

| Item | Base Price | Final Price | Reasoning |
|---------|-----------|-------------|----------------------------------------|
| ITEM-01 | Rs.1,299 | Rs.1,104 | Platform offer: 15% off (beats Rs.150) |
| ITEM-02 | Rs.849 | Rs.629 | Brand offer: Rs.150 off + Platform 10% |
| ITEM-03 | Rs.599 | Rs.509 | Platform offer: 15% off |
| ITEM-04 | Rs.2,499 | Rs.2,499 | No offers available |
| ITEM-05 | Rs.449 | Rs.382 | Platform offer: 15% off |
| ITEM-06 | Rs.899 | Rs.809 | Platform offer: 10% off |
### 1. Clone the Repository
```bash
git clone [https://github.com/Hari-23-cpu/discount-engine-assignment.git](https://github.com/Hari-23-cpu/discount-engine-assignment.git)
33 changes: 33 additions & 0 deletions discount-engine-assignment-main/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Dependencies
node_modules/

# Build output
dist/
dist-ssr/

# Vite cache
.vite/

# Environment variables
.env
.env.local
.env.*.local

# Logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*

# Editor directories
.vscode/
.idea/
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

# OS files
.DS_Store
Thumbs.db
37 changes: 37 additions & 0 deletions discount-engine-assignment-main/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Discount Engine Application

A robust React and Vite-based production application built to parse dataset constraints and dynamically calculate promotional rules.

## πŸš€ Live Deployment
Check out the live application hosted on Vercel:
πŸ”— [Live Deployment Link](https://discount-engine-assignment-krishnahari1042-5727s-projects.vercel.app)

## πŸŽ₯ Application Demo
Watch the walkthrough video demonstrating the functionalities, file parsers, and edge cases:
πŸ”— [Video Walkthrough Link](https://drive.google.com/file/d/1dDLsgAAlvZKBhBG1_IKGt--4bcU4thX-/view?usp=sharing)

---

## πŸ› οΈ Features
* **Dynamic Dataset Parsing:** Effortlessly upload and evaluate `.csv` or text data payloads.
* **Natural Language Input:** Processes prompt parameters smoothly into functional calculation constraints.
* **Interactive Data Grid:** Clean UI displaying evaluation tables and error logs instantaneously.
* **Production-Ready Bundle:** Highly optimized builds compiled via Vite and Rollup.

---

## πŸ’» Tech Stack
* **Frontend Library:** React (Functional Components, Hooks)
* **Build Tool:** Vite v5
* **Bundler & Compiler:** Rollup
* **Deployment Platform:** Vercel

---

## πŸ“¦ Getting Started Locally

Follow these steps to run the project on your local machine:

### 1. Cloning the Repository
```bash
git clone [https://github.com/Hari-23-cpu/discount-engine-assignment.git](https://github.com/Hari-23-cpu/discount-engine-assignment.git)
12 changes: 12 additions & 0 deletions discount-engine-assignment-main/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Opptra Discount Engine</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
20 changes: 20 additions & 0 deletions discount-engine-assignment-main/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "discount-engine",
"version": "1.0.0",
"description": "Opptra FDE Intern Assignment β€” Discount Engine",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"papaparse": "^5.4.1",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@vitejs/plugin-react": "^4.2.1",
"vite": "^5.0.0"
}
}
7 changes: 7 additions & 0 deletions discount-engine-assignment-main/sample-data/cart.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
item_id,product,brand,platform,base_price
ITEM-01,Cushion Cover,Natura Casa,Amazon India,1299
ITEM-02,Bed Sheet Set,Natura Casa,Flipkart,849
ITEM-03,Wall Shelf,LivSpace Pro,Amazon India,599
ITEM-04,Ceramic Vase,LivSpace Pro,Noon,2499
ITEM-05,Cutting Board,Nordic Basics,Amazon India,449
ITEM-06,Desk Organiser,Nordic Basics,Flipkart,899
4 changes: 4 additions & 0 deletions discount-engine-assignment-main/sample-data/rules.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
rule_id,scope,applies_to,type,value,stackable
RULE-01,platform,Amazon India,percentage,15,false
RULE-02,brand,Natura Casa,flat,150,false
RULE-03,platform,Flipkart,percentage,10,true
51 changes: 46 additions & 5 deletions src/App.jsx β†’ discount-engine-assignment-main/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { useState } from 'react'
import CsvUploader from './components/CsvUploader.jsx'
import DataTable from './components/DataTable.jsx'
import ErrorBanner from './components/ErrorBanner.jsx'
import NaturalLanguageInput from './components/NaturalLanguageInput.jsx'
import PdfUploader from './components/PdfUploader.jsx'
import { parseRulesCSV, parseCartCSV } from './engine/csvParser.js'
import { processCart, cartTotal } from './engine/discountEngine.js'

Expand Down Expand Up @@ -186,6 +188,19 @@ export default function App() {
fileName={cartFileName}
/>
<ErrorBanner errors={cartErrors} />
{/* Task 3: PDF Alternative Document Uploader */}
<PdfUploader
label="cart.pdf"
description="Parse invoice tables directly from a PDF catalog"
hasData={cartItems.length > 0 && cartFileName.toLowerCase().endsWith('.pdf')}
fileName={cartFileName}
onCartReplace={(parsedItems, name) => {
setCartItems(parsedItems)
setCartFileName(name)
setCartErrors([])
setResults(null)
}}
/>
{cartItems.length > 0 && (
<div style={{ marginTop: '0.75rem' }}>
<div style={{ fontSize: 11, color: '#888', marginBottom: 4 }}>
Expand All @@ -194,6 +209,14 @@ export default function App() {
<DataTable columns={CART_COLUMNS} rows={cartItems} />
</div>
)}
<div style={{ marginTop: '1rem', borderTop: '1px solid #eee', paddingTop: '1rem' }}>
<div style={{ ...S.sectionTitle, borderBottom: '2px solid #131A48' }}>Add Rule via AI</div>
<NaturalLanguageInput
onAddRule={(newRule) => {
setRules((prevRules) => [...prevRules, newRule])
}}
/>
</div>
</div>
</div>

Expand All @@ -213,14 +236,32 @@ export default function App() {
)}
</div>

{/* Results */}
{results && (
{/* Results */}
{results && (
<div style={S.section}>
<div style={S.sectionTitle}>Cart Summary</div>
<DataTable columns={RESULTS_COLUMNS} rows={results} />

{/* Note: We pass results.items now because results is an object */}
<DataTable columns={RESULTS_COLUMNS} rows={results.items} />

{/* Task 1: Conditional Cart Offer Row */}
{results.cartDiscount && (
<div style={{ ...S.totalRow, borderTop: 'none', marginTop: '0.5rem', paddingTop: 0 }}>
<span style={{ ...S.totalLabel, color: '#1e5c2c', fontWeight: 600 }}>
πŸŽ‰ Cart offer ({results.cartDiscount.value}% off)
</span>
<span style={{ ...S.totalValue, color: '#1e5c2c', fontWeight: 600 }}>
- Rs.{results.cartDiscount.amountSaved.toLocaleString('en-IN')}
</span>
</div>
)}

{/* Final Checked-out Total */}
<div style={S.totalRow}>
<span style={S.totalLabel}>Cart Total</span>
<span style={S.totalValue}>Rs.{cartTotal(results).toLocaleString('en-IN')}</span>
<span style={S.totalLabel}>Final Cart Total</span>
<span style={S.totalValue}>
Rs.{results.finalCartTotal.toLocaleString('en-IN')}
</span>
</div>
</div>
)}
Expand Down
Loading