version 2 initial commit
Some checks failed
Beta CI/CD Pipeline / build-and-test (push) Failing after 47s
Beta CI/CD Pipeline / deploy-beta (push) Has been skipped

This commit is contained in:
vickytechkey 2026-08-21 10:43:19 +05:30
parent 89ceb23c5a
commit 125b1f9d26
24 changed files with 2715 additions and 5302 deletions

View file

@ -1,11 +1,16 @@
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en" data-theme="light">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>sellercentral</title> <title>sellercentral</title>
</head>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Libre+Caslon+Text:ital,wght@0,400;0,700;1,400&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0" />
</head>
<body> <body>
<div id="root"></div> <div id="root"></div>
<script type="module" src="/src/main.tsx"></script> <script type="module" src="/src/main.tsx"></script>

389
package-lock.json generated
View file

@ -10,7 +10,8 @@
"dependencies": { "dependencies": {
"bulma": "^1.0.4", "bulma": "^1.0.4",
"react": "^19.2.8", "react": "^19.2.8",
"react-dom": "^19.2.8" "react-dom": "^19.2.8",
"recharts": "^3.10.1"
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.49.0", "@playwright/test": "^1.49.0",
@ -779,6 +780,32 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@reduxjs/toolkit": {
"version": "2.12.0",
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.12.0.tgz",
"integrity": "sha512-KiT+RzZbp6mQET+Mg+h2c97+9j1sNflUxQkIHI7Yuzf6Peu+OYpmkn6nbHWmLLWj+1ZODUJFwGZ7gx3L9R9EOw==",
"license": "MIT",
"dependencies": {
"@standard-schema/spec": "^1.0.0",
"@standard-schema/utils": "^0.3.0",
"immer": "^11.0.0",
"redux": "^5.0.1",
"redux-thunk": "^3.1.0",
"reselect": "^5.1.0"
},
"peerDependencies": {
"react": "^16.9.0 || ^17.0.0 || ^18 || ^19",
"react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"react-redux": {
"optional": true
}
}
},
"node_modules/@rolldown/binding-android-arm64": { "node_modules/@rolldown/binding-android-arm64": {
"version": "1.2.3", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.3.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.3.tgz",
@ -1028,7 +1055,12 @@
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
"integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
"dev": true, "license": "MIT"
},
"node_modules/@standard-schema/utils": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz",
"integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@tailwindcss/node": { "node_modules/@tailwindcss/node": {
@ -1659,6 +1691,69 @@
"assertion-error": "^2.0.1" "assertion-error": "^2.0.1"
} }
}, },
"node_modules/@types/d3-array": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz",
"integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==",
"license": "MIT"
},
"node_modules/@types/d3-color": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
"integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
"license": "MIT"
},
"node_modules/@types/d3-ease": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
"integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==",
"license": "MIT"
},
"node_modules/@types/d3-interpolate": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
"integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
"license": "MIT",
"dependencies": {
"@types/d3-color": "*"
}
},
"node_modules/@types/d3-path": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
"integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==",
"license": "MIT"
},
"node_modules/@types/d3-scale": {
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
"integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
"license": "MIT",
"dependencies": {
"@types/d3-time": "*"
}
},
"node_modules/@types/d3-shape": {
"version": "3.1.8",
"resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz",
"integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==",
"license": "MIT",
"dependencies": {
"@types/d3-path": "*"
}
},
"node_modules/@types/d3-time": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
"integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==",
"license": "MIT"
},
"node_modules/@types/d3-timer": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
"integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",
"license": "MIT"
},
"node_modules/@types/deep-eql": { "node_modules/@types/deep-eql": {
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
@ -1687,7 +1782,7 @@
"version": "19.2.18", "version": "19.2.18",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz",
"integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==", "integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==",
"dev": true, "devOptional": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"csstype": "^3.2.2" "csstype": "^3.2.2"
@ -1724,6 +1819,12 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/use-sync-external-store": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
"integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==",
"license": "MIT"
},
"node_modules/@vitejs/plugin-react": { "node_modules/@vitejs/plugin-react": {
"version": "6.0.5", "version": "6.0.5",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.5.tgz", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.5.tgz",
@ -2291,6 +2392,15 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/color-convert": { "node_modules/color-convert": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@ -2416,7 +2526,7 @@
"version": "3.2.3", "version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
"dev": true, "devOptional": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/cypress": { "node_modules/cypress": {
@ -2473,6 +2583,127 @@
"node": "^20.1.0 || ^22.0.0 || >=24.0.0" "node": "^20.1.0 || ^22.0.0 || >=24.0.0"
} }
}, },
"node_modules/d3-array": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
"license": "ISC",
"dependencies": {
"internmap": "1 - 2"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-color": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-ease": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-format": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz",
"integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-interpolate": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
"license": "ISC",
"dependencies": {
"d3-color": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-path": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
"integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-scale": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
"license": "ISC",
"dependencies": {
"d3-array": "2.10.0 - 3",
"d3-format": "1 - 3",
"d3-interpolate": "1.2.0 - 3",
"d3-time": "2.1.1 - 3",
"d3-time-format": "2 - 4"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-shape": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
"integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
"license": "ISC",
"dependencies": {
"d3-path": "^3.1.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-time": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
"integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
"license": "ISC",
"dependencies": {
"d3-array": "2 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-time-format": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
"license": "ISC",
"dependencies": {
"d3-time": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-timer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/dashdash": { "node_modules/dashdash": {
"version": "1.14.1", "version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
@ -2547,6 +2778,12 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/decimal.js-light": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
"integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==",
"license": "MIT"
},
"node_modules/delayed-stream": { "node_modules/delayed-stream": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@ -2723,6 +2960,18 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/es-toolkit": {
"version": "1.51.0",
"resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.51.0.tgz",
"integrity": "sha512-zC2lQGkM7QX+Gm6iM3+WIdZJzthsEd14LvRNJneSO2hzyz/zNBENR8+YXWo1cKxgPBtV6ksPYHELbcwBRzmdCw==",
"license": "MIT",
"workspaces": [
"docs",
"benchmarks",
"tests/types",
"tests/browser-compat"
]
},
"node_modules/estree-walker": { "node_modules/estree-walker": {
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
@ -2744,7 +2993,6 @@
"version": "5.0.4", "version": "5.0.4",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
"integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/execa": { "node_modules/execa": {
@ -3153,6 +3401,16 @@
], ],
"license": "BSD-3-Clause" "license": "BSD-3-Clause"
}, },
"node_modules/immer": {
"version": "11.1.18",
"resolved": "https://registry.npmjs.org/immer/-/immer-11.1.18.tgz",
"integrity": "sha512-EQyQtLiYW029lyoczMl/Hh4Xu7cDecSc58JRYpHyL4tIAu3eqd1yJzQX04d2BZHDkzFFvm6qJEJWOtfDSWAXbQ==",
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
}
},
"node_modules/indent-string": { "node_modules/indent-string": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
@ -3173,6 +3431,15 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/internmap": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/is-fullwidth-code-point": { "node_modules/is-fullwidth-code-point": {
"version": "5.1.0", "version": "5.1.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz",
@ -4274,9 +4541,61 @@
"version": "17.0.2", "version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/react-redux": {
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.3.0.tgz",
"integrity": "sha512-KQopgqFo/p/fgmAs5qz6p5RWaNAzq40WAu7fJIXnQpYxFPbJYtsJPWvGeF2rOBaY/kEuV77AVsX8TsQzKm+A/g==",
"license": "MIT",
"dependencies": {
"@types/use-sync-external-store": "^0.0.6",
"use-sync-external-store": "^1.4.0"
},
"peerDependencies": {
"@types/react": "^18.2.25 || ^19",
"react": "^18.0 || ^19",
"redux": "^5.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"redux": {
"optional": true
}
}
},
"node_modules/recharts": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/recharts/-/recharts-3.10.1.tgz",
"integrity": "sha512-QXFrvt6IVcw7eeZCoyXTwkIJAX3Dv1nyVhMicXJ47GsGDDpcN8z6o644DibE9XjpBTThtsomLKnTV6lc+cVFUA==",
"license": "MIT",
"workspaces": [
"www"
],
"dependencies": {
"@reduxjs/toolkit": "^1.9.0 || 2.x.x",
"clsx": "^2.1.1",
"decimal.js-light": "^2.5.1",
"es-toolkit": "^1.39.3",
"eventemitter3": "^5.0.1",
"immer": "^11.1.8",
"react-redux": "8.x.x || 9.x.x",
"reselect": "5.2.0",
"tiny-invariant": "^1.3.3",
"use-sync-external-store": "^1.2.2",
"victory-vendor": "^37.0.2"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
"react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/redent": { "node_modules/redent": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz",
@ -4291,6 +4610,21 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/redux": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
"license": "MIT"
},
"node_modules/redux-thunk": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz",
"integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==",
"license": "MIT",
"peerDependencies": {
"redux": "^5.0.0"
}
},
"node_modules/request-progress": { "node_modules/request-progress": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz",
@ -4311,6 +4645,12 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/reselect": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-5.2.0.tgz",
"integrity": "sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==",
"license": "MIT"
},
"node_modules/restore-cursor": { "node_modules/restore-cursor": {
"version": "5.1.0", "version": "5.1.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
@ -4823,6 +5163,12 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/tiny-invariant": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
"license": "MIT"
},
"node_modules/tinybench": { "node_modules/tinybench": {
"version": "2.9.0", "version": "2.9.0",
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
@ -5024,6 +5370,15 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/use-sync-external-store": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
"integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
"license": "MIT",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/verror": { "node_modules/verror": {
"version": "1.10.0", "version": "1.10.0",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
@ -5039,6 +5394,28 @@
"extsprintf": "^1.2.0" "extsprintf": "^1.2.0"
} }
}, },
"node_modules/victory-vendor": {
"version": "37.3.6",
"resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz",
"integrity": "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==",
"license": "MIT AND ISC",
"dependencies": {
"@types/d3-array": "^3.0.3",
"@types/d3-ease": "^3.0.0",
"@types/d3-interpolate": "^3.0.1",
"@types/d3-scale": "^4.0.2",
"@types/d3-shape": "^3.1.0",
"@types/d3-time": "^3.0.0",
"@types/d3-timer": "^3.0.0",
"d3-array": "^3.1.6",
"d3-ease": "^3.0.1",
"d3-interpolate": "^3.0.1",
"d3-scale": "^4.0.2",
"d3-shape": "^3.1.0",
"d3-time": "^3.0.0",
"d3-timer": "^3.0.1"
}
},
"node_modules/vite": { "node_modules/vite": {
"version": "8.2.1", "version": "8.2.1",
"resolved": "https://registry.npmjs.org/vite/-/vite-8.2.1.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.1.tgz",

View file

@ -16,7 +16,8 @@
"dependencies": { "dependencies": {
"bulma": "^1.0.4", "bulma": "^1.0.4",
"react": "^19.2.8", "react": "^19.2.8",
"react-dom": "^19.2.8" "react-dom": "^19.2.8",
"recharts": "^3.10.1"
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.49.0", "@playwright/test": "^1.49.0",

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,42 @@
import React from 'react';
import { useSeller } from '../../context/SellerContext';
import Sidebar from '../layout/Sidebar';
import DashboardHeader from '../layout/DashboardHeader';
import OverviewTab from './tabs/OverviewTab';
import OrdersTab from './tabs/OrdersTab';
import ProductsTab from './tabs/ProductsTab';
import AddProductTab from './tabs/AddProductTab';
import PaymentsTab from './tabs/PaymentsTab';
import SettingsTab from './tabs/SettingsTab';
export default function DashboardLayout() {
const { dashTab } = useSeller();
const renderContent = () => {
switch (dashTab) {
case 'overview': return <OverviewTab />;
case 'orders': return <OrdersTab />;
case 'products': return <ProductsTab />;
case 'add-product': return <AddProductTab />;
case 'payments': return <PaymentsTab />;
case 'settings': return <SettingsTab />;
default: return <OverviewTab />;
}
};
return (
<div className="has-background-white-ter" style={{ minHeight: '100vh', display: 'flex', flexDirection: 'column' }}>
<DashboardHeader />
<div className="columns is-gapless is-flex-grow-1 is-marginless">
<div className="column is-2 is-hidden-mobile has-background-white" style={{ borderRight: '1px solid #e2dfdb' }}>
<Sidebar />
</div>
<div className="column is-10 is-12-mobile">
<main className="p-6" style={{ height: '100%' }}>
{renderContent()}
</main>
</div>
</div>
</div>
);
}

View file

@ -0,0 +1,113 @@
import React from 'react';
import { useSeller } from '../../../context/SellerContext';
export default function AddProductTab() {
const { setDashTab } = useSeller();
return (
<div>
<div className="is-flex is-align-items-center mb-5">
<button
className="button is-ghost has-text-grey pl-0 mr-3"
onClick={() => setDashTab('products')}
>
<span className="icon"><span className="material-symbols-outlined">arrow_back</span></span>
<span>Back</span>
</button>
<h1 className="title is-3 has-text-dark mb-0" style={{ fontFamily: 'Libre Caslon Text, serif' }}>Add New Product</h1>
</div>
<div className="box" style={{ borderTop: '4px solid var(--bulma-primary)' }}>
<form onSubmit={(e) => { e.preventDefault(); setDashTab('products'); }}>
<div className="columns is-multiline">
{/* Left Column: Basic Info */}
<div className="column is-8">
<div className="field mb-4">
<label className="label has-text-grey-dark">Product Title</label>
<div className="control">
<input className="input" type="text" placeholder="e.g. Handwoven Silk Scarf" required />
</div>
</div>
<div className="field mb-4">
<label className="label has-text-grey-dark">Description</label>
<div className="control">
<textarea className="textarea" placeholder="Describe the artisan craft, material, and story..." rows={4} required></textarea>
</div>
</div>
<div className="columns">
<div className="column is-6">
<div className="field mb-4">
<label className="label has-text-grey-dark">Price (USD)</label>
<div className="control has-icons-left">
<input className="input" type="number" placeholder="0.00" step="0.01" required />
<span className="icon is-small is-left">
<span className="material-symbols-outlined">attach_money</span>
</span>
</div>
</div>
</div>
<div className="column is-6">
<div className="field mb-4">
<label className="label has-text-grey-dark">Stock Quantity</label>
<div className="control has-icons-left">
<input className="input" type="number" placeholder="10" min="0" required />
<span className="icon is-small is-left">
<span className="material-symbols-outlined">inventory_2</span>
</span>
</div>
</div>
</div>
</div>
</div>
{/* Right Column: Imagery & Category */}
<div className="column is-4">
<div className="field mb-4">
<label className="label has-text-grey-dark">Product Image</label>
<div className="control">
<div className="file is-boxed is-fullwidth">
<label className="file-label is-flex-direction-column is-align-items-center">
<input className="file-input" type="file" name="resume" accept="image/*" />
<span className="file-cta is-flex is-flex-direction-column is-align-items-center py-5 has-background-white" style={{ border: '1px solid var(--bulma-primary)' }}>
<span className="file-icon mb-2 has-text-primary">
<span className="material-symbols-outlined is-size-3">upload</span>
</span>
<span className="file-label has-text-primary">
Choose an image
</span>
</span>
</label>
</div>
</div>
</div>
<div className="field mb-4">
<label className="label has-text-grey-dark">Category</label>
<div className="control">
<div className="select is-fullwidth">
<select required>
<option value="">Select a category</option>
<option value="textiles">Apparel & Textiles</option>
<option value="jewelry">Jewelry</option>
<option value="pottery">Pottery</option>
<option value="woodwork">Woodwork</option>
</select>
</div>
</div>
</div>
</div>
</div>
<div className="is-flex is-justify-content-flex-end mt-5">
<button type="button" className="button is-light mr-3" onClick={() => setDashTab('products')}>Cancel</button>
<button type="submit" className="button is-primary is-outlined">Publish Product</button>
</div>
</form>
</div>
</div>
);
}

View file

@ -0,0 +1,56 @@
import React from 'react';
export default function OrdersTab() {
return (
<div>
<div className="is-flex is-justify-content-space-between is-align-items-center mb-5">
<h1 className="title is-3 has-text-dark mb-0" style={{ fontFamily: 'Libre Caslon Text, serif' }}>Orders</h1>
<button className="button is-primary is-outlined">
<span className="icon"><span className="material-symbols-outlined">download</span></span>
<span>Export</span>
</button>
</div>
<div className="box">
<div className="tabs">
<ul>
<li className="is-active"><a>All Orders</a></li>
<li><a>Pending</a></li>
<li><a>Shipped</a></li>
<li><a>Completed</a></li>
</ul>
</div>
<table className="table is-fullwidth is-hoverable is-striped">
<thead>
<tr>
<th>Order ID</th>
<th>Date</th>
<th>Customer</th>
<th>Status</th>
<th>Total</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{[
{ id: '#1045', date: 'Oct 24, 2023', customer: 'Jane Doe', status: 'Processing', type: 'info', total: '$120.00' },
{ id: '#1044', date: 'Oct 22, 2023', customer: 'Alex Johnson', status: 'Shipped', type: 'warning', total: '$45.50' },
{ id: '#1043', date: 'Oct 20, 2023', customer: 'Sarah Williams', status: 'Completed', type: 'success', total: '$210.00' }
].map(order => (
<tr key={order.id}>
<td><strong>{order.id}</strong></td>
<td>{order.date}</td>
<td>{order.customer}</td>
<td><span className={`tag is-${order.type} is-light`}>{order.status}</span></td>
<td>{order.total}</td>
<td>
<button className="button is-small is-light">View</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
}

View file

@ -0,0 +1,116 @@
import React from 'react';
import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
const data = [
{ name: 'Jan', sales: 4000 },
{ name: 'Feb', sales: 3000 },
{ name: 'Mar', sales: 5000 },
{ name: 'Apr', sales: 2780 },
{ name: 'May', sales: 8890 },
{ name: 'Jun', sales: 14500 },
];
export default function OverviewTab() {
const handleDownloadCsv = () => {
// Generate CSV string
const header = "Month,Sales\n";
const rows = data.map(row => `${row.name},${row.sales}`).join("\n");
const csvContent = header + rows;
// Create Blob and trigger download
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.setAttribute("href", url);
link.setAttribute("download", "sales_report.csv");
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
return (
<div>
<div className="is-flex is-justify-content-space-between is-align-items-center mb-5">
<h1 className="title is-3 has-text-dark mb-0" style={{ fontFamily: 'Libre Caslon Text, serif' }}>Overview</h1>
<button className="button is-primary is-outlined" onClick={handleDownloadCsv}>
<span className="icon"><span className="material-symbols-outlined">download</span></span>
<span>Download Report</span>
</button>
</div>
<div className="columns is-multiline">
<div className="column is-4">
<div className="box" style={{ borderTop: '4px solid var(--bulma-primary)' }}>
<p className="heading has-text-grey-dark">Total Sales</p>
<p className="title is-2 has-text-dark">$14,500</p>
</div>
</div>
<div className="column is-4">
<div className="box" style={{ borderTop: '4px solid var(--bulma-primary)' }}>
<p className="heading has-text-grey-dark">Active Orders</p>
<p className="title is-2 has-text-dark">23</p>
</div>
</div>
<div className="column is-4">
<div className="box" style={{ borderTop: '4px solid var(--bulma-primary)' }}>
<p className="heading has-text-grey-dark">Page Views</p>
<p className="title is-2 has-text-dark">1,204</p>
</div>
</div>
</div>
{/* Sales Chart */}
<div className="box mt-4">
<h2 className="title is-5 has-text-dark mb-4">Sales Performance (YTD)</h2>
<div style={{ width: '100%', height: 300 }}>
<ResponsiveContainer>
<AreaChart
data={data}
margin={{ top: 10, right: 30, left: 0, bottom: 0 }}
>
<defs>
<linearGradient id="colorSales" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="var(--bulma-primary)" stopOpacity={0.8}/>
<stop offset="95%" stopColor="var(--bulma-primary)" stopOpacity={0}/>
</linearGradient>
</defs>
<CartesianGrid strokeDasharray="3 3" vertical={false} />
<XAxis dataKey="name" tick={{fill: '#5f5e5b'}} axisLine={false} tickLine={false} />
<YAxis tick={{fill: '#5f5e5b'}} axisLine={false} tickLine={false} tickFormatter={(value) => `$${value}`} />
<Tooltip formatter={(value) => [`$${value}`, 'Sales']} />
<Area type="monotone" dataKey="sales" stroke="var(--bulma-primary)" fillOpacity={1} fill="url(#colorSales)" />
</AreaChart>
</ResponsiveContainer>
</div>
</div>
<div className="box mt-5">
<h2 className="title is-5 has-text-dark">Recent Activity</h2>
<table className="table is-fullwidth is-hoverable">
<thead>
<tr>
<th>Date</th>
<th>Customer</th>
<th>Action</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr>
<td>Oct 24, 2023</td>
<td>Jane Doe</td>
<td>Placed Order #1045</td>
<td><span className="tag is-info is-light">Processing</span></td>
</tr>
<tr>
<td>Oct 23, 2023</td>
<td>John Smith</td>
<td>Left a 5-star review</td>
<td><span className="tag is-success is-light">Completed</span></td>
</tr>
</tbody>
</table>
</div>
</div>
);
}

View file

@ -0,0 +1,51 @@
import React from 'react';
export default function PaymentsTab() {
return (
<div>
<h1 className="title is-3 has-text-dark mb-5" style={{ fontFamily: 'Libre Caslon Text, serif' }}>Payments & Earnings</h1>
<div className="columns is-multiline mb-5">
<div className="column is-6">
<div className="box has-background-primary has-text-white">
<p className="heading has-text-white-ter mb-1">Available for Payout</p>
<p className="title is-1 has-text-white mb-4">$1,240.00</p>
<button className="button is-white is-outlined is-fullwidth">Withdraw Funds</button>
</div>
</div>
<div className="column is-6">
<div className="box" style={{ height: '100%' }}>
<p className="heading has-text-grey-dark mb-1">Pending Clearance</p>
<p className="title is-3 has-text-dark mb-4">$320.00</p>
<p className="is-size-7 has-text-grey">Funds will be available in 3-5 business days after order delivery.</p>
</div>
</div>
</div>
<div className="box">
<h2 className="title is-5 has-text-dark">Recent Transactions</h2>
<table className="table is-fullwidth">
<thead>
<tr>
<th>Date</th>
<th>Description</th>
<th>Amount</th>
</tr>
</thead>
<tbody>
<tr>
<td>Oct 20, 2023</td>
<td>Payout to Bank Account (...1234)</td>
<td className="has-text-danger">-$500.00</td>
</tr>
<tr>
<td>Oct 19, 2023</td>
<td>Order #1042 Revenue</td>
<td className="has-text-success">+$85.00</td>
</tr>
</tbody>
</table>
</div>
</div>
);
}

View file

@ -0,0 +1,51 @@
import React from 'react';
import { useSeller } from '../../../context/SellerContext';
export default function ProductsTab() {
const { setDashTab } = useSeller();
return (
<div>
<div className="is-flex is-justify-content-space-between is-align-items-center mb-5">
<h1 className="title is-3 has-text-dark mb-0" style={{ fontFamily: 'Libre Caslon Text, serif' }}>Products</h1>
<div className="buttons">
<button className="button is-light" onClick={() => alert('Bulk upload coming soon!')}>
<span className="icon"><span className="material-symbols-outlined">upload_file</span></span>
<span>Bulk Upload</span>
</button>
<button className="button is-primary is-outlined" onClick={() => setDashTab('add-product')}>
<span className="icon"><span className="material-symbols-outlined">add</span></span>
<span>Add Product</span>
</button>
</div>
</div>
<div className="columns is-multiline">
{[1, 2, 3, 4].map(item => (
<div className="column is-3" key={item}>
<div className="card" style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
<div className="card-image">
<figure className="image is-4by3 has-background-light">
<div className="is-flex is-align-items-center is-justify-content-center" style={{ height: '100%' }}>
<span className="material-symbols-outlined has-text-grey-light is-size-1">image</span>
</div>
</figure>
</div>
<div className="card-content is-flex-grow-1">
<p className="title is-6 mb-2">Handwoven Silk Scarf</p>
<p className="subtitle is-6 has-text-primary has-text-weight-bold">$85.00</p>
<div className="is-flex is-align-items-center is-size-7 has-text-grey">
<span className="icon is-small mr-1"><span className="material-symbols-outlined is-size-7">inventory</span></span>
<span>12 in stock</span>
</div>
</div>
<footer className="card-footer">
<a href="#" className="card-footer-item has-text-grey">Edit</a>
<a href="#" className="card-footer-item has-text-danger">Delete</a>
</footer>
</div>
</div>
))}
</div>
</div>
);
}

View file

@ -0,0 +1,91 @@
import React from 'react';
import { useSeller } from '../../../context/SellerContext';
export default function SettingsTab() {
const { storeSlug, supportEmail, supportPhone } = useSeller();
return (
<div className="columns is-centered">
<div className="column is-8">
<h1 className="title is-3 has-text-dark mb-5" style={{ fontFamily: 'Libre Caslon Text, serif' }}>Settings</h1>
<div className="box mb-5">
<h2 className="title is-5 has-text-primary border-bottom pb-2" style={{ borderBottom: '1px solid #eee' }}>Store Profile</h2>
<div className="field">
<label className="label">Store Name</label>
<div className="control">
<input className="input" type="text" defaultValue="My Artisan Store" />
</div>
</div>
<div className="field">
<label className="label">Store URL Slug</label>
<div className="control">
<input className="input" type="text" value={storeSlug} readOnly />
</div>
<p className="help">Contact support to change your store URL.</p>
</div>
<div className="field">
<label className="label">Store Description</label>
<div className="control">
<textarea className="textarea" placeholder="Tell buyers about your craft..."></textarea>
</div>
</div>
</div>
<div className="box mb-5">
<h2 className="title is-5 has-text-primary border-bottom pb-2" style={{ borderBottom: '1px solid #eee' }}>Contact Information</h2>
<div className="columns">
<div className="column is-half">
<div className="field">
<label className="label">Support Email</label>
<div className="control">
<input className="input" type="email" defaultValue={supportEmail} />
</div>
</div>
</div>
<div className="column is-half">
<div className="field">
<label className="label">Support Phone</label>
<div className="control">
<input className="input" type="tel" defaultValue={supportPhone} />
</div>
</div>
</div>
</div>
</div>
<div className="box mb-5">
<h2 className="title is-5 has-text-primary border-bottom pb-2" style={{ borderBottom: '1px solid #eee' }}>Business Documents</h2>
<p className="has-text-grey-dark mb-4">Upload any additional documents (like business licenses, identity proof, or craft certifications).</p>
<div className="field">
<div className="control">
<div className="file is-boxed">
<label className="file-label is-flex-direction-column is-align-items-center">
<input className="file-input" type="file" name="business_doc" />
<span className="file-cta is-flex is-flex-direction-column is-align-items-center py-4 px-6 has-background-white" style={{ border: '1px solid var(--bulma-primary)' }}>
<span className="file-icon mb-2 has-text-primary">
<span className="material-symbols-outlined is-size-3">upload_file</span>
</span>
<span className="file-label has-text-primary">
Choose a document
</span>
</span>
</label>
</div>
</div>
</div>
</div>
<div className="is-flex is-justify-content-flex-end">
<button className="button is-primary is-outlined">Save Changes</button>
</div>
</div>
</div>
);
}

View file

@ -0,0 +1,51 @@
import React from 'react';
import { useSeller } from '../../context/SellerContext';
export default function DashboardHeader() {
const { handleLogout, storeSlug } = useSeller();
return (
<nav className="navbar has-background-white" style={{ borderBottom: '1px solid #e2dfdb', position: 'sticky', top: 0, zIndex: 30 }}>
<div className="navbar-brand">
<a className="navbar-item" href="#">
<h1 className="title is-4 has-text-primary" style={{ fontFamily: 'Libre Caslon Text, serif' }}>
Tradhox
</h1>
</a>
<a role="button" className="navbar-burger" aria-label="menu" aria-expanded="false">
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div className="navbar-menu">
<div className="navbar-end pr-5">
<div className="navbar-item has-dropdown is-hoverable">
<a className="navbar-link is-arrowless is-flex is-align-items-center">
<span className="icon has-text-grey mr-3">
<span className="material-symbols-outlined">notifications</span>
</span>
<div className="is-flex is-align-items-center">
<div style={{ width: '32px', height: '32px', borderRadius: '50%', backgroundColor: 'var(--bulma-primary)', color: 'white', display: 'flex', alignItems: 'center', justifyContent: 'center', fontWeight: 'bold' }}>
{storeSlug ? storeSlug.charAt(0).toUpperCase() : 'S'}
</div>
</div>
</a>
<div className="navbar-dropdown is-right">
<a className="navbar-item">
<span className="icon mr-2"><span className="material-symbols-outlined">account_circle</span></span>
Profile
</a>
<hr className="navbar-divider" />
<a className="navbar-item has-text-danger" onClick={handleLogout}>
<span className="icon mr-2"><span className="material-symbols-outlined">logout</span></span>
Log Out
</a>
</div>
</div>
</div>
</div>
</nav>
);
}

View file

@ -0,0 +1,38 @@
import React from 'react';
export default function Footer() {
return (
<footer className="footer has-background-white-bis" style={{ borderTop: '1px solid #dac0c2' }}>
<div className="container" style={{ maxWidth: '1120px' }}>
<div className="columns">
{/* Brand & Copyright */}
<div className="column is-4">
<h2 className="title is-4 has-text-primary mb-3" style={{ fontFamily: 'Libre Caslon Text, serif' }}>Tradhox</h2>
<p className="has-text-grey-dark">
© 2024 Tradhox. Honoring Human Craftsmanship.
</p>
</div>
{/* Links 1 */}
<div className="column is-4">
<ul style={{ listStyle: 'none', marginLeft: 0 }}>
<li className="mb-2"><a href="#" className="has-text-grey-dark hover-text-primary" style={{ transition: 'color 0.2s' }}>Privacy Policy</a></li>
<li><a href="#" className="has-text-grey-dark hover-text-primary" style={{ transition: 'color 0.2s' }}>Terms of Service</a></li>
</ul>
</div>
{/* Links 2 */}
<div className="column is-4">
<ul style={{ listStyle: 'none', marginLeft: 0 }}>
<li className="mb-2"><a href="#" className="has-text-grey-dark hover-text-primary" style={{ transition: 'color 0.2s' }}>Sustainability Report</a></li>
<li><a href="#" className="has-text-grey-dark hover-text-primary" style={{ transition: 'color 0.2s' }}>Shipping Info</a></li>
</ul>
</div>
</div>
</div>
<style>{`
.hover-text-primary:hover { color: var(--bulma-primary) !important; }
`}</style>
</footer>
);
}

View file

@ -0,0 +1,88 @@
import React from 'react';
import { useSeller } from '../../context/SellerContext';
export default function Header() {
const { currentPage, setCurrentPage, isMobileMenuOpen, setIsMobileMenuOpen } = useSeller();
return (
<nav className="navbar has-background-white-bis" style={{ borderBottom: '1px solid #dac0c2' }} role="navigation" aria-label="main navigation">
<div className="container" style={{ maxWidth: '1120px' }}>
<div className="navbar-brand">
<a className="navbar-item is-size-4 has-text-primary" style={{ fontFamily: 'Libre Caslon Text, serif', letterSpacing: '-0.02em', fontWeight: 700 }} onClick={() => setCurrentPage('home')}>
Tradhox
</a>
<a role="button" className={`navbar-burger ${isMobileMenuOpen ? 'is-active' : ''}`} aria-label="menu" aria-expanded="false" onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)} style={{ color: 'var(--bulma-primary)' }}>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div className={`navbar-menu ${isMobileMenuOpen ? 'is-active' : ''}`} style={{ backgroundColor: 'transparent' }}>
<div className="navbar-start" style={{ margin: '0 auto', gap: '2rem' }}>
<a
className="navbar-item has-text-weight-medium custom-nav-link"
onClick={() => setCurrentPage('home')}
style={{ color: currentPage === 'home' ? 'var(--bulma-primary)' : '#5f5e5b' }}
>
Shop
</a>
<a
className="navbar-item has-text-weight-medium custom-nav-link"
onClick={() => setCurrentPage('about')}
style={{ color: currentPage === 'about' ? 'var(--bulma-primary)' : '#5f5e5b' }}
>
Our Story
</a>
<a
className="navbar-item has-text-weight-medium custom-nav-link"
onClick={() => setCurrentPage('contact')}
style={{ color: currentPage === 'contact' ? 'var(--bulma-primary)' : '#5f5e5b' }}
>
Contact Us
</a>
</div>
<div className="navbar-end">
<div className="navbar-item">
<div className="buttons">
<a className="button is-ghost has-text-weight-semibold" style={{ color: '#5f5e5b' }} onClick={() => setCurrentPage('login')}>
User Login
</a>
<a className="button is-primary is-outlined has-text-weight-semibold" onClick={() => setCurrentPage('login')}>
Seller Login
</a>
</div>
</div>
</div>
</div>
</div>
<style>{`
.custom-nav-link {
position: relative;
background-color: transparent !important;
}
.custom-nav-link:hover {
color: var(--bulma-primary) !important;
}
.custom-nav-link::after {
content: '';
position: absolute;
bottom: 0px;
left: 50%;
transform: translateX(-50%);
width: 0;
height: 2px;
background-color: var(--bulma-primary);
transition: width 0.3s ease;
}
.custom-nav-link:hover::after {
width: 80%;
}
`}</style>
</nav>
);
}

View file

@ -0,0 +1,38 @@
import React from 'react';
import { useSeller } from '../../context/SellerContext';
export default function Sidebar() {
const { dashTab, setDashTab } = useSeller();
const menuItems = [
{ id: 'overview', icon: 'grid_view', label: 'Overview' },
{ id: 'orders', icon: 'local_shipping', label: 'Orders' },
{ id: 'products', icon: 'inventory_2', label: 'Products' },
{ id: 'payments', icon: 'account_balance_wallet', label: 'Payments & Earnings' },
{ id: 'settings', icon: 'settings', label: 'Settings' }
];
return (
<aside className="menu p-5" style={{ position: 'sticky', top: '4rem' }}>
<p className="menu-label has-text-weight-bold tracking-wide" style={{ color: 'var(--bulma-grey)', letterSpacing: '0.1em' }}>
Seller Central
</p>
<ul className="menu-list">
{menuItems.map(item => (
<li key={item.id} className="mb-2">
<a
className={`is-flex is-align-items-center py-3 px-4 ${dashTab === item.id ? 'has-background-primary-light has-text-primary has-text-weight-bold' : 'has-text-grey-dark'}`}
style={{ borderRadius: '8px', transition: 'all 0.2s ease' }}
onClick={() => setDashTab(item.id)}
>
<span className="icon mr-3">
<span className="material-symbols-outlined is-size-5">{item.icon}</span>
</span>
<span>{item.label}</span>
</a>
</li>
))}
</ul>
</aside>
);
}

View file

@ -0,0 +1,98 @@
import React from 'react';
import { useSeller } from '../../context/SellerContext';
export default function AboutPage() {
const { setCurrentPage } = useSeller();
return (
<div>
{/* Hero Section */}
<section className="hero is-large" style={{ position: 'relative', overflow: 'hidden', minHeight: '800px' }}>
<div style={{
position: 'absolute', top: 0, left: 0, right: 0, bottom: 0,
backgroundImage: "url('https://lh3.googleusercontent.com/aida-public/AB6AXuA5mjm6T9CY7j2AtbFE_jEFFkhOzCmc_eKwvgj0hHVKV6AoVKeAf8ZfBnnz4sSYkdzX5gyzlk-pGufTtwpJN5MhYgDAIezV1yaDN8ANw66nZ5kk40-GGDiVWp3eGiPJZ4mIuL_aJp6O1TqJsQihFvPZyfj5VgjH4IQivXEltdrAAUfKSMdbWgNHLi3SP2X5uLF6Xc_gDyYs-tOepA8IuU6_1Q9wcaSebXAQOQhAQAPW7wVcUFGY7crerw')",
backgroundSize: 'cover',
backgroundPosition: 'center',
zIndex: 0
}}>
<div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(to right, rgba(251, 249, 248, 0.9), rgba(251, 249, 248, 0.4))' }}></div>
</div>
<div className="hero-body" style={{ position: 'relative', zIndex: 10 }}>
<div className="container" style={{ maxWidth: '1120px' }}>
<div className="columns is-vcentered">
<div className="column is-6">
<p className="subtitle is-6 has-text-primary is-uppercase has-text-weight-bold tracking-widest mb-4" style={{ letterSpacing: '0.2em' }}>
Honoring Human Craftsmanship
</p>
<h1 className="title has-text-dark mb-5" style={{ fontSize: '3.5rem', fontFamily: 'Libre Caslon Text, serif' }}>
Preserving Heritage, Inspiring the Modern World.
</h1>
<p className="subtitle is-5 has-text-grey-dark mb-6" style={{ lineHeight: '1.6' }}>
Discover authentic, handcrafted masterworks from generations of skilled artisans. Every piece tells a story of culture, resilience, and unparalleled skill.
</p>
<button className="button is-primary is-medium px-6 is-outlined" onClick={() => setCurrentPage('home')}>
<span className="has-text-weight-bold">Explore the Marketplace</span>
<span className="icon ml-2"><span className="material-symbols-outlined">arrow_forward</span></span>
</button>
</div>
</div>
</div>
</div>
</section>
{/* Why Tradhox Section */}
<section className="section py-6 has-background-white-bis">
<div className="container" style={{ maxWidth: '1120px' }}>
<div className="has-text-centered mb-6" style={{ maxWidth: '800px', margin: '0 auto' }}>
<h2 className="title is-2 has-text-primary mb-4" style={{ fontFamily: 'Libre Caslon Text, serif' }}>Why Choose Tradhox</h2>
<p className="subtitle is-5 has-text-grey-dark">
We bridge the gap between discerning collectors and master artisans, ensuring fairness, authenticity, and enduring quality.
</p>
</div>
<div className="columns is-multiline mt-5">
{/* Feature 1 */}
<div className="column is-4-tablet">
<div className="box has-text-centered h-100 feature-card" style={{ padding: '2rem', height: '100%', border: '1px solid #dac0c2', backgroundColor: '#ffffff', boxShadow: 'none', transition: 'box-shadow 0.3s' }}>
<span className="icon is-large has-text-primary mb-4" style={{ backgroundColor: '#f5f3f3', borderRadius: '50%', width: '64px', height: '64px' }}>
<span className="material-symbols-outlined is-size-3" style={{ fontVariationSettings: "'FILL' 1" }}>verified</span>
</span>
<h3 className="title is-4 mb-3" style={{ fontFamily: 'Libre Caslon Text, serif' }}>Authenticity Guaranteed</h3>
<p className="has-text-grey-dark">Direct sourcing from verified artisanal clusters ensures every piece is genuine and ethically procured.</p>
</div>
</div>
{/* Feature 2 */}
<div className="column is-4-tablet">
<div className="box has-text-centered h-100 feature-card" style={{ padding: '2rem', height: '100%', border: '1px solid #dac0c2', backgroundColor: '#ffffff', boxShadow: 'none', transition: 'box-shadow 0.3s' }}>
<span className="icon is-large has-text-primary mb-4" style={{ backgroundColor: '#f5f3f3', borderRadius: '50%', width: '64px', height: '64px' }}>
<span className="material-symbols-outlined is-size-3" style={{ fontVariationSettings: "'FILL' 1" }}>handshake</span>
</span>
<h3 className="title is-4 mb-3" style={{ fontFamily: 'Libre Caslon Text, serif' }}>Fair Trade Promise</h3>
<p className="has-text-grey-dark">We empower makers by providing direct market access, ensuring they receive equitable compensation for their craft.</p>
</div>
</div>
{/* Feature 3 */}
<div className="column is-4-tablet">
<div className="box has-text-centered h-100 feature-card" style={{ padding: '2rem', height: '100%', border: '1px solid #dac0c2', backgroundColor: '#ffffff', boxShadow: 'none', transition: 'box-shadow 0.3s' }}>
<span className="icon is-large has-text-primary mb-4" style={{ backgroundColor: '#f5f3f3', borderRadius: '50%', width: '64px', height: '64px' }}>
<span className="material-symbols-outlined is-size-3" style={{ fontVariationSettings: "'FILL' 1" }}>eco</span>
</span>
<h3 className="title is-4 mb-3" style={{ fontFamily: 'Libre Caslon Text, serif' }}>Sustainable Heritage</h3>
<p className="has-text-grey-dark">Supporting traditional techniques that inherently respect natural materials and sustainable production cycles.</p>
</div>
</div>
</div>
</div>
</section>
<style>{`
.feature-card:hover {
box-shadow: 0 4px 20px rgba(107, 26, 44, 0.05) !important;
}
`}</style>
</div>
);
}

View file

@ -0,0 +1,86 @@
import React from 'react';
import { useSeller } from '../../context/SellerContext';
export default function AuthForms() {
const { activeTab, setActiveTab, email, setEmail, password, setPassword, confirmPassword, setConfirmPassword, phone, setPhone, policyAccepted, setPolicyAccepted, handleLoginSubmit, handleRegisterSubmit } = useSeller();
return (
<section className="section">
<div className="container">
<div className="columns is-centered">
<div className="column is-half">
<div className="box">
<h1 className="title has-text-centered">Seller Central Login</h1>
<div className="tabs is-centered">
<ul>
<li className={activeTab === 'login' ? 'is-active' : ''}>
<a onClick={() => setActiveTab('login')}>Log In</a>
</li>
<li className={activeTab === 'register' ? 'is-active' : ''}>
<a onClick={() => setActiveTab('register')}>Register</a>
</li>
</ul>
</div>
{activeTab === 'login' && (
<form onSubmit={handleLoginSubmit}>
<div className="field">
<label className="label">Email or Phone</label>
<div className="control">
<input className="input" type="text" placeholder="e.g. alex@example.com" value={email} onChange={e => setEmail(e.target.value)} />
</div>
</div>
<div className="field">
<label className="label">Password</label>
<div className="control">
<input className="input" type="password" placeholder="********" value={password} onChange={e => setPassword(e.target.value)} />
</div>
</div>
<button type="submit" className="button is-primary is-fullwidth mt-4 is-outlined">Log in</button>
</form>
)}
{activeTab === 'register' && (
<form onSubmit={handleRegisterSubmit}>
<div className="field">
<label className="label">Email</label>
<div className="control">
<input className="input" type="email" placeholder="e.g. alex@example.com" value={email} onChange={e => setEmail(e.target.value)} />
</div>
</div>
<div className="field">
<label className="label">Phone Number</label>
<div className="control">
<input className="input" type="tel" placeholder="e.g. 9876543210" value={phone} onChange={e => setPhone(e.target.value)} />
</div>
</div>
<div className="field">
<label className="label">Password</label>
<div className="control">
<input className="input" type="password" placeholder="********" value={password} onChange={e => setPassword(e.target.value)} />
</div>
</div>
<div className="field">
<label className="label">Confirm Password</label>
<div className="control">
<input className="input" type="password" placeholder="********" value={confirmPassword} onChange={e => setConfirmPassword(e.target.value)} />
</div>
</div>
<div className="field">
<div className="control">
<label className="checkbox">
<input type="checkbox" checked={policyAccepted} onChange={e => setPolicyAccepted(e.target.checked)} />
{' '}I agree to the <a href="#">Terms of Service</a> and <a href="#">Privacy Policy</a>
</label>
</div>
</div>
<button type="submit" className="button is-primary is-fullwidth mt-4 is-outlined">Register</button>
</form>
)}
</div>
</div>
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,164 @@
import React from 'react';
export default function ContactPage() {
return (
<div className="container" style={{ maxWidth: '1120px', padding: '4rem 1.5rem' }}>
{/* Header Section */}
<div className="mb-6" style={{ maxWidth: '800px' }}>
<h1 className="title is-1 has-text-primary mb-4" style={{ fontFamily: 'Libre Caslon Text, serif' }}>Get in Touch</h1>
<p className="subtitle is-4 has-text-grey-dark" style={{ lineHeight: '1.6' }}>
Whether you have a question about an artisanal piece, a partnership inquiry, or need support with your order, we are here to help. Reach out to the Tradhox team.
</p>
</div>
{/* Contact Section */}
<div className="columns is-multiline mb-6">
{/* Left Side: Contact Details */}
<div className="column is-4">
<div className="box mb-5" style={{ border: '1px solid #dac0c2', boxShadow: 'none' }}>
<h2 className="title is-4 has-text-primary mb-5" style={{ fontFamily: 'Libre Caslon Text, serif' }}>Contact Information</h2>
<div className="is-flex mb-4">
<span className="icon has-text-primary mr-3 mt-1"><span className="material-symbols-outlined">location_on</span></span>
<div>
<h3 className="is-size-7 has-text-grey has-text-weight-bold is-uppercase mb-1" style={{ letterSpacing: '0.1em' }}>Headquarters</h3>
<p className="has-text-dark">12 Craft Heritage Lane,<br/>Dashashwamedh Ghat Area,<br/>Varanasi, UP 221001, India</p>
</div>
</div>
<div className="is-flex mb-4">
<span className="icon has-text-primary mr-3 mt-1"><span className="material-symbols-outlined">mail</span></span>
<div>
<h3 className="is-size-7 has-text-grey has-text-weight-bold is-uppercase mb-1" style={{ letterSpacing: '0.1em' }}>Email Us</h3>
<a href="mailto:namaste@tradhox.com" className="has-text-primary has-text-weight-semibold">namaste@tradhox.com</a>
<p className="is-size-7 has-text-grey mt-1">We aim to reply within 24 hours.</p>
</div>
</div>
<div className="is-flex">
<span className="icon has-text-primary mr-3 mt-1"><span className="material-symbols-outlined">call</span></span>
<div>
<h3 className="is-size-7 has-text-grey has-text-weight-bold is-uppercase mb-1" style={{ letterSpacing: '0.1em' }}>Call Us</h3>
<a href="tel:+918005550199" className="has-text-dark has-text-weight-semibold hover-text-primary">+91 800 555 0199</a>
<p className="is-size-7 has-text-grey mt-1">Mon-Fri, 9am - 6pm IST</p>
</div>
</div>
</div>
{/* Social Links Card */}
<div className="box" style={{ border: '1px solid #dac0c2', boxShadow: 'none' }}>
<h3 className="is-size-7 has-text-grey has-text-weight-bold is-uppercase mb-3" style={{ letterSpacing: '0.1em' }}>Connect With Us</h3>
<div className="is-flex gap-2">
<a href="#" className="button is-rounded is-outlined border-grey has-text-dark hover-primary-border mr-2">
<span className="icon"><span className="material-symbols-outlined">public</span></span>
</a>
<a href="#" className="button is-rounded is-outlined border-grey has-text-dark hover-primary-border mr-2">
<span className="icon"><span className="material-symbols-outlined">share</span></span>
</a>
<a href="#" className="button is-rounded is-outlined border-grey has-text-dark hover-primary-border">
<span className="icon"><span className="material-symbols-outlined">groups</span></span>
</a>
</div>
</div>
</div>
{/* Right Side: Inquiry Form */}
<div className="column is-8">
<div className="box h-100" style={{ border: '1px solid #dac0c2', boxShadow: 'none', padding: '2.5rem' }}>
<h2 className="title is-4 has-text-primary mb-5" style={{ fontFamily: 'Libre Caslon Text, serif' }}>Send an Inquiry</h2>
<form onSubmit={(e) => e.preventDefault()}>
<div className="columns is-multiline">
<div className="column is-6 pb-0">
<div className="field">
<label className="label is-size-7 has-text-grey has-text-weight-bold is-uppercase" style={{ letterSpacing: '0.1em' }}>Full Name</label>
<div className="control">
<input className="input" type="text" placeholder="e.g. Ananya Sharma" required style={{ borderColor: '#dac0c2', boxShadow: 'none' }} />
</div>
</div>
</div>
<div className="column is-6 pb-0">
<div className="field">
<label className="label is-size-7 has-text-grey has-text-weight-bold is-uppercase" style={{ letterSpacing: '0.1em' }}>Email Address</label>
<div className="control">
<input className="input" type="email" placeholder="e.g. ananya@example.com" required style={{ borderColor: '#dac0c2', boxShadow: 'none' }} />
</div>
</div>
</div>
<div className="column is-12 pb-0">
<div className="field">
<label className="label is-size-7 has-text-grey has-text-weight-bold is-uppercase" style={{ letterSpacing: '0.1em' }}>Subject of Inquiry</label>
<div className="control">
<div className="select is-fullwidth">
<select style={{ borderColor: '#dac0c2', boxShadow: 'none' }}>
<option value="customer_support">Customer Support (Order Issues, Returns)</option>
<option value="seller_inquiry">Seller/Artisan Inquiry (Join Tradhox)</option>
<option value="partnership">Business Partnership / B2B Bulk Orders</option>
<option value="general">General Question</option>
</select>
</div>
</div>
</div>
</div>
<div className="column is-12 pb-0">
<div className="field">
<label className="label is-size-7 has-text-grey has-text-weight-bold is-uppercase" style={{ letterSpacing: '0.1em' }}>Message</label>
<div className="control">
<textarea className="textarea has-fixed-size" rows={5} placeholder="How can we assist you today?" required style={{ borderColor: '#dac0c2', boxShadow: 'none' }}></textarea>
</div>
</div>
</div>
<div className="column is-12 mt-3">
<button type="submit" className="button is-primary is-medium px-6 is-outlined">
<span className="has-text-weight-bold">Send Message</span>
<span className="icon ml-2"><span className="material-symbols-outlined is-size-5">arrow_forward</span></span>
</button>
</div>
</div>
</form>
</div>
</div>
</div>
{/* Visit Our Studio Section */}
<div className="box p-0 overflow-hidden" style={{ border: '1px solid #dac0c2', boxShadow: 'none', borderRadius: '12px' }}>
<div className="columns is-gapless mb-0">
<div className="column is-6 is-flex is-flex-direction-column is-justify-content-center p-6 has-background-white-bis">
<h2 className="title is-3 has-text-primary mb-4" style={{ fontFamily: 'Libre Caslon Text, serif' }}>Visit Our Studio</h2>
<p className="has-text-grey-dark mb-5" style={{ lineHeight: '1.6' }}>
Experience the craftsmanship firsthand. Our Varanasi studio is open to visitors who wish to learn about our sourcing process and see exclusive GI-tagged products before they launch online.
</p>
<div className="box" style={{ border: '1px solid #dac0c2', boxShadow: 'none', alignSelf: 'flex-start' }}>
<h3 className="is-size-7 has-text-grey has-text-weight-bold is-uppercase mb-3" style={{ letterSpacing: '0.1em' }}>Studio Hours</h3>
<ul className="has-text-dark">
<li className="is-flex is-justify-content-space-between mb-2" style={{ width: '220px' }}><span>Monday - Friday:</span> <span className="has-text-weight-semibold">10:00 - 18:00</span></li>
<li className="is-flex is-justify-content-space-between mb-2" style={{ width: '220px' }}><span>Saturday:</span> <span className="has-text-weight-semibold">10:00 - 14:00</span></li>
<li className="is-flex is-justify-content-space-between" style={{ width: '220px' }}><span className="has-text-grey">Sunday:</span> <span className="has-text-grey has-text-weight-semibold">Closed</span></li>
</ul>
</div>
</div>
<div className="column is-6 is-flex is-align-items-center is-justify-content-center" style={{ backgroundColor: '#e4e2e2', borderLeft: '1px solid #dac0c2', minHeight: '350px', position: 'relative' }}>
<div className="has-text-centered p-5" style={{ zIndex: 1 }}>
<span className="icon is-large has-text-grey mb-2"><span className="material-symbols-outlined" style={{ fontSize: '48px' }}>map</span></span>
<p className="is-size-7 has-text-grey has-text-weight-bold is-uppercase mt-3" style={{ letterSpacing: '0.1em' }}>Interactive Map view of Varanasi Studio</p>
</div>
{/* Dot Pattern Overlay */}
<div style={{ position: 'absolute', inset: 0, opacity: 0.1, backgroundImage: 'radial-gradient(#887274 1.5px, transparent 1.5px)', backgroundSize: '24px 24px' }}></div>
</div>
</div>
</div>
<style>{`
.hover-text-primary:hover { color: var(--bulma-primary) !important; }
.hover-primary-border:hover { border-color: var(--bulma-primary) !important; color: var(--bulma-primary) !important; }
.border-grey { border-color: #dac0c2; }
`}</style>
</div>
);
}

View file

@ -0,0 +1,113 @@
import React from 'react';
import { useSeller } from '../../context/SellerContext';
export default function LandingPage() {
const { setCurrentPage } = useSeller();
const handleSellerLogin = () => {
setCurrentPage('login');
};
return (
<div>
{/* Hero Section */}
<section className="hero is-large has-background-dark" style={{
position: 'relative',
overflow: 'hidden',
minHeight: '600px'
}}>
<div style={{
position: 'absolute',
top: 0, left: 0, right: 0, bottom: 0,
backgroundImage: "url('https://lh3.googleusercontent.com/aida-public/AB6AXuCWM3QRK9NLvhVDgb1yr3ZPMxI4YJ2zEvrECk8pbXQ8kTCi9EQFYsJchNz2sfzWd8Gb5vdmZPyg83MZtWRG3ZzVG-xVBVALPGm_LAPbgxH4YJTprM-xhBDoG_dlzo7T9v5nOXnRF_MibxuJ9ct3JBgbQNzN9Fa57uY0gfjGHEk_qSZLWBiZQxxTgtjEZtGqnCXCZ1kno2Gn9gnYQHgmfY3_PVnjWnUKk-F2ZLmIIl7F3AlCv3B7U6lXLg')",
backgroundSize: 'cover',
backgroundPosition: 'center',
zIndex: 0
}}>
<div style={{ position: 'absolute', inset: 0, backgroundColor: 'rgba(0,0,0,0.4)' }}></div>
</div>
<div className="hero-body is-flex is-align-items-center is-justify-content-center" style={{ position: 'relative', zIndex: 10 }}>
<div className="container has-text-centered" style={{ maxWidth: '800px' }}>
<p className="subtitle is-6 has-text-grey-light is-uppercase has-text-weight-bold tracking-widest mb-4" style={{ letterSpacing: '0.2em' }}>
Seasonal Collection
</p>
<h1 className="title has-text-white mb-5" style={{ fontSize: '3.5rem', fontFamily: 'Libre Caslon Text, serif', textShadow: '0 2px 4px rgba(0,0,0,0.3)' }}>
The Winter Weaves
</h1>
<p className="subtitle is-5 has-text-white-ter mb-6" style={{ lineHeight: '1.6', textShadow: '0 1px 2px rgba(0,0,0,0.3)' }}>
Discover the warmth of tradition. Hand-loomed pashminas and thick cotton weaves, crafted by generational artisans from the highlands.
</p>
<button className="button is-primary is-medium is-rounded px-6 is-outlined" onClick={handleSellerLogin}>
<span className="has-text-weight-bold">Explore Collection</span>
<span className="icon ml-2"><span className="material-symbols-outlined">arrow_forward</span></span>
</button>
</div>
</div>
</section>
{/* Categories Bento Grid */}
<section className="section py-6">
<div className="container" style={{ maxWidth: '1120px' }}>
<div className="has-text-centered mb-6">
<h2 className="title is-2 has-text-dark" style={{ fontFamily: 'Libre Caslon Text, serif' }}>Explore by Craft</h2>
<div style={{ width: '64px', height: '4px', backgroundColor: 'var(--bulma-primary)', margin: '16px auto', borderRadius: '4px' }}></div>
</div>
<div className="columns is-multiline">
{/* Handloom (Large span - 8 columns) */}
<div className="column is-8-tablet">
<a className="box p-0 is-clickable is-clipped" style={{ position: 'relative', height: '400px', display: 'block', border: '1px solid #dac0c2' }}>
<div style={{
position: 'absolute', inset: 0,
backgroundImage: "url('https://lh3.googleusercontent.com/aida-public/AB6AXuAS9r3NI9-FM8tgHGT2hWRg-M8knvFHZx5atRJ4rhhNLbZMRDwLJMhzK2ON7vpXvZoecKox8x5DPSYrmVTTYT62g_Es8tHv4R9YNHG8GdacoUglwQOOx2CWtc_y244pgcUKTskwMF2so3MkqXUH9puqF9Un0FGplE_BERtUsY3V_lhdLgt72RRhTKc3XvooH19hMuCYN2ljDzPrUWY4917V8qekx0cKfBYQsrPI1GiJP1o4li2_j2VHjg')",
backgroundSize: 'cover', backgroundPosition: 'center',
transition: 'transform 0.5s ease'
}} className="hover-zoom"></div>
<div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(to top, rgba(0,0,0,0.8), transparent 60%)' }}></div>
<div style={{ position: 'absolute', bottom: 0, left: 0, padding: '2rem' }}>
<span className="tag is-primary is-medium mb-3 has-text-weight-bold" style={{ backgroundColor: 'rgba(77, 2, 24, 0.9)' }}>Apparel & Textiles</span>
<h3 className="title is-3 has-text-white" style={{ fontFamily: 'Libre Caslon Text, serif' }}>Handloom Heritage</h3>
</div>
</a>
</div>
{/* Right side items (4 columns) */}
<div className="column is-4-tablet is-flex is-flex-direction-column">
{/* Pottery */}
<a className="box p-0 is-clickable is-clipped mb-4 is-flex-grow-1" style={{ position: 'relative', height: '192px', display: 'block', border: '1px solid #dac0c2' }}>
<div style={{
position: 'absolute', inset: 0,
backgroundImage: "url('https://lh3.googleusercontent.com/aida-public/AB6AXuBQ4-Xs5qziux8_GlEJ0-ufRlJrIRx8C1oQCnRqE4DszCnYl_I30-ckRS3gFJChcgsaRXqmlQKB6rd1yajOCgURWi61y_-KiW5TtK_UqMjT3r4UrZHwyHoD091oxgN0ehJb-nKbkD5H2IyKr5MigS_W0M-n2ouvGlbmBELRwvaDfbEEUGlF488_X5DsuZE2Sgtu42Qqz3oQdxWBUNstUHgTgov_mzEv06FuJxLdPA5c7hVGFHWV7rQp5Q')",
backgroundSize: 'cover', backgroundPosition: 'center'
}} className="hover-zoom"></div>
<div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(to top, rgba(0,0,0,0.7), transparent)' }}></div>
<div style={{ position: 'absolute', bottom: 0, left: 0, padding: '1.25rem' }}>
<h3 className="title is-4 has-text-white" style={{ fontFamily: 'Libre Caslon Text, serif' }}>Studio Pottery</h3>
</div>
</a>
{/* Woodwork */}
<a className="box p-0 is-clickable is-clipped is-flex-grow-1" style={{ position: 'relative', height: '192px', display: 'block', border: '1px solid #dac0c2' }}>
<div style={{
position: 'absolute', inset: 0,
backgroundImage: "url('https://lh3.googleusercontent.com/aida-public/AB6AXuDFzLzVAg22uWncCePb-VxR7tx9BnFu8jMLulD2SFXg5Gt2qjgpJoMQmGMcv2m99GA1oAoUG2dLPFTuWPhjxk7TqyUbNlBA9uCVSwPScN6wa_T8BKnLv88zT7glVF81p00Qe2YUNAsH1lzFn_jxGADhHvMv_o9Wior2Uk373C1m2F3z_TwXMfRNDPmJc7WOuHC67Pt0K036WLz5lFSKaf3MwxwtKSE1HO1vmm2U5rnoyrES8Z_iEnglNA')",
backgroundSize: 'cover', backgroundPosition: 'center'
}} className="hover-zoom"></div>
<div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(to top, rgba(0,0,0,0.7), transparent)' }}></div>
<div style={{ position: 'absolute', bottom: 0, left: 0, padding: '1.25rem' }}>
<h3 className="title is-4 has-text-white" style={{ fontFamily: 'Libre Caslon Text, serif' }}>Carved Woodwork</h3>
</div>
</a>
</div>
</div>
</div>
</section>
{/* Add hover zoom style */}
<style>{`
.hover-zoom:hover { transform: scale(1.05); }
`}</style>
</div>
);
}

View file

@ -0,0 +1,255 @@
import React from 'react';
import { useSeller } from '../../context/SellerContext';
export default function OnboardingWizard() {
const {
profileStep, setProfileStep,
setIsProfileComplete, setCurrentPage,
businessType, setBusinessType,
gstin, setGstin,
storeSlug, setStoreSlug,
supportEmail, setSupportEmail,
supportPhone, setSupportPhone,
selectedCategories, setSelectedCategories
} = useSeller();
const handleNext = () => {
if (profileStep < 3) {
setProfileStep(profileStep + 1);
} else {
setIsProfileComplete(true);
setCurrentPage('dashboard');
}
};
const handleBack = () => {
if (profileStep > 1) {
setProfileStep(profileStep - 1);
}
};
const toggleCategory = (cat: string) => {
if (selectedCategories.includes(cat)) {
setSelectedCategories(selectedCategories.filter((c: string) => c !== cat));
} else {
setSelectedCategories([...selectedCategories, cat]);
}
};
const renderStep1 = () => (
<div className="columns is-vcentered" style={{ minHeight: 'calc(100vh - 4rem)' }}>
{/* Left Column (Text) */}
<div className="column is-5 is-flex is-flex-direction-column is-justify-content-center p-6">
<p className="has-text-primary mb-4 is-size-7 has-text-weight-bold is-uppercase tracking-wide" style={{ letterSpacing: '0.1em' }}>
Welcome to Tradhox &mdash; Step 1 of 3
</p>
<h1 className="title is-1 has-text-dark mb-5" style={{ fontFamily: 'Libre Caslon Text, serif', lineHeight: '1.2' }}>
Every Piece You List Keeps a Craft Alive
</h1>
<p className="subtitle is-5 has-text-grey-dark mb-6">
Join a global marketplace dedicated to cultural preservation. Your craftsmanship tells a story. Let's build your store to share it with the world.
</p>
<div className="mb-6">
<div className="is-flex is-align-items-flex-start mb-5">
<span className="icon is-large has-text-primary mr-4" style={{ backgroundColor: 'rgba(226, 223, 219, 0.2)', borderRadius: '50%', padding: '24px', border: '1px solid #d7c3b0' }}>
<span className="material-symbols-outlined is-size-4">handshake</span>
</span>
<div>
<h3 className="title is-5 mb-2 has-text-dark">Fair Trade Certified</h3>
<p className="has-text-grey-dark is-size-6">Directly connect with buyers seeking authentic, ethically sourced goods.</p>
</div>
</div>
<div className="is-flex is-align-items-flex-start mb-5">
<span className="icon is-large has-text-primary mr-4" style={{ backgroundColor: 'rgba(226, 223, 219, 0.2)', borderRadius: '50%', padding: '24px', border: '1px solid #d7c3b0' }}>
<span className="material-symbols-outlined is-size-4">storefront</span>
</span>
<div>
<h3 className="title is-5 mb-2 has-text-dark">Your Brand, Your Way</h3>
<p className="has-text-grey-dark is-size-6">Customize your storefront to reflect your unique heritage and style.</p>
</div>
</div>
</div>
<div>
<button className="button is-primary is-medium is-rounded is-outlined" onClick={handleNext}>
<span>Continue to Store Setup</span>
<span className="icon is-small ml-2"><span className="material-symbols-outlined">arrow_forward</span></span>
</button>
</div>
</div>
{/* Right Column (Image) */}
<div className="column is-7 is-hidden-mobile" style={{ height: 'calc(100vh - 4rem)' }}>
<div style={{ position: 'relative', height: '100%', padding: '2rem' }}>
<div style={{
position: 'relative',
width: '100%',
height: '100%',
borderRadius: '1rem',
overflow: 'hidden',
boxShadow: '0 4px 20px rgba(107,26,44,0.05)',
border: '1px solid #d7c3b0'
}}>
<img
src="https://lh3.googleusercontent.com/aida-public/AB6AXuB4W-NVA2QTeIF_XSyCokGOQf4RVLTiMmxLi69rYdK7uVQ5yINgHSBzGIeC1WJSEtTjXGBp3hx_OrKdiiV7_hcwN9oQdLnuoABA1XYYFf-RrQbgv9khd1zVujiaAjlPvBaj8WKCD83RjZqHUmuGM_-vKmmKWiijoqaOdoPAuWJdZu-QkASeowWzfwCljpwZTeVD2VaLxc5y1PzIKEmnTfy6O8jqTsZhOo-k6HrhZggm1lMF9kondr_oQg"
alt="Artisan at work"
style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' }}
/>
{/* Global Reach Badge */}
<div style={{
position: 'absolute',
bottom: '24px',
left: '24px',
backgroundColor: 'rgba(255, 255, 255, 0.9)',
backdropFilter: 'blur(4px)',
border: '1px solid #d7c3b0',
borderRadius: '8px',
padding: '16px',
display: 'flex',
alignItems: 'center',
gap: '12px',
boxShadow: '0 2px 8px rgba(0,0,0,0.05)'
}}>
<span className="material-symbols-outlined has-text-primary is-size-3">public</span>
<div>
<p className="has-text-weight-bold has-text-dark is-size-6 mb-0">Global Reach</p>
<p className="has-text-grey-dark is-size-7 mb-0">Buyers in 50+ countries</p>
</div>
</div>
</div>
</div>
</div>
</div>
);
const renderStep2 = () => (
<div className="columns is-centered mt-6">
<div className="column is-8-desktop is-10-tablet">
<div className="box p-6">
<p className="has-text-grey mb-2 is-size-7 has-text-weight-bold is-uppercase tracking-wide has-text-centered">
Step 2 of 3
</p>
<h1 className="title is-3 has-text-primary has-text-centered mb-6" style={{ fontFamily: 'Libre Caslon Text, serif' }}>
Business Details
</h1>
<div className="field">
<label className="label">Business Type</label>
<div className="control">
<div className="select is-fullwidth">
<select value={businessType} onChange={e => setBusinessType(e.target.value as any)}>
<option value="registered_company">Registered Company</option>
<option value="self_help_group">Self-Help Group (SHG)</option>
<option value="individual_maker">Individual Maker / Artisan</option>
</select>
</div>
</div>
</div>
<div className="field">
<label className="label">GSTIN (Optional for Individuals)</label>
<div className="control has-icons-left">
<input className="input" type="text" placeholder="29AAAAA1111A1Z1" value={gstin} onChange={e => setGstin(e.target.value)} />
<span className="icon is-small is-left"><span className="material-symbols-outlined">receipt_long</span></span>
</div>
</div>
<div className="columns mt-4">
<div className="column is-half">
<div className="field">
<label className="label">Support Email</label>
<div className="control has-icons-left">
<input className="input" type="email" placeholder="support@brand.com" value={supportEmail} onChange={e => setSupportEmail(e.target.value)} />
<span className="icon is-small is-left"><span className="material-symbols-outlined">mail</span></span>
</div>
</div>
</div>
<div className="column is-half">
<div className="field">
<label className="label">Support Phone</label>
<div className="control has-icons-left">
<input className="input" type="tel" placeholder="+91 9876543210" value={supportPhone} onChange={e => setSupportPhone(e.target.value)} />
<span className="icon is-small is-left"><span className="material-symbols-outlined">phone</span></span>
</div>
</div>
</div>
</div>
<div className="field mt-4">
<label className="label">Store URL Slug</label>
<div className="field has-addons">
<p className="control">
<a className="button is-static">tradhox.com/store/</a>
</p>
<p className="control is-expanded">
<input className="input" type="text" placeholder="my-artisan-store" value={storeSlug} onChange={e => setStoreSlug(e.target.value)} />
</p>
</div>
</div>
<div className="is-flex is-justify-content-space-between mt-6">
<button className="button is-light" onClick={handleBack}>Back</button>
<button className="button is-primary is-outlined" onClick={handleNext}>Next: Categories</button>
</div>
</div>
</div>
</div>
);
const renderStep3 = () => {
const availableCategories = ['Textiles & Apparel', 'Home Decor', 'Jewelry', 'Art & Collectibles', 'Beauty & Wellness', 'Food & Beverages'];
return (
<div className="columns is-centered mt-6">
<div className="column is-8-desktop is-10-tablet">
<div className="box p-6">
<p className="has-text-grey mb-2 is-size-7 has-text-weight-bold is-uppercase tracking-wide has-text-centered">
Step 3 of 3
</p>
<h1 className="title is-3 has-text-primary has-text-centered mb-2" style={{ fontFamily: 'Libre Caslon Text, serif' }}>
What do you sell?
</h1>
<p className="has-text-centered has-text-grey mb-6">Select all categories that apply to your handcrafted products.</p>
<div className="columns is-multiline">
{availableCategories.map(cat => (
<div className="column is-6" key={cat}>
<div
className={`box is-clickable is-flex is-align-items-center transition-all duration-200 ${selectedCategories.includes(cat) ? 'has-background-primary-light border-primary' : 'has-background-white-ter'}`}
onClick={() => toggleCategory(cat)}
style={{ border: selectedCategories.includes(cat) ? '2px solid var(--bulma-primary)' : '2px solid transparent' }}
>
<span className={`icon is-large mr-3 ${selectedCategories.includes(cat) ? 'has-text-primary' : 'has-text-grey'}`}>
<span className="material-symbols-outlined">
{selectedCategories.includes(cat) ? 'check_circle' : 'circle'}
</span>
</span>
<span className={`has-text-weight-medium ${selectedCategories.includes(cat) ? 'has-text-primary' : 'has-text-grey-dark'}`}>
{cat}
</span>
</div>
</div>
))}
</div>
<div className="is-flex is-justify-content-space-between mt-6">
<button className="button is-light" onClick={handleBack}>Back</button>
<button className="button is-primary is-outlined" onClick={handleNext}>Complete Onboarding</button>
</div>
</div>
</div>
</div>
);
};
return (
<section className="has-background-white" style={{ minHeight: 'calc(100vh - 4rem)' }}>
<div className="container is-fluid px-0">
{profileStep === 1 && renderStep1()}
{profileStep === 2 && renderStep2()}
{profileStep === 3 && renderStep3()}
</div>
</section>
);
}

View file

@ -121,100 +121,6 @@ export const CONFIG = {
} }
export function apiFetch(url: string, options: RequestInit = {}): Promise<Response> { export function apiFetch(url: string, options: RequestInit = {}): Promise<Response> {
if ((window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') && !navigator.userAgent.includes('jsdom')) {
const path = url.replace(CONFIG.apiBaseUrl, '');
let status = 200;
let data: any = {};
if (path.startsWith('/api/auth/login/')) {
data = {
access_token: 'mock-access-token',
refresh_token: 'mock-refresh-token',
user: {
id: 1,
username: 'tradhox_artisan',
email: 'artisan@tradhox.com',
profile: {
onboarding_step: 4,
phone_verified: true,
email_verified: true,
is_gstin_verified: true,
aadhar_s3_key: 'mock_aadhar',
pan_s3_key: 'mock_pan',
store_name: 'Tradhox Artisan Co.',
logo_s3_key: 'mock_logo',
street: '123 Loom Lane',
status: 'pending_approval',
phone: '9876543210',
gstin: '29AAAAA1111A1Z1',
business_bio: 'Preserving centuries-old handloom techniques, our family has been creating pure silk and cotton textiles for three generations.',
city: 'Textile Town',
state: 'Karnataka',
pincode: '560001',
business_type: 'individual_maker',
store_slug: 'tradhox-artisan',
support_email: 'hello@tradhox.com',
support_phone: '+91 98765 43210',
selected_categories: ['Textiles & Apparel', 'Sustainable Products']
}
}
};
} else if (path.startsWith('/api/auth/register/')) {
data = { message: 'Registration successful' };
} else if (path.startsWith('/api/auth/verify-otp/')) {
data = { verified: true, message: 'OTP verified successfully' };
} else if (path.startsWith('/api/profile/submit-gstin/')) {
data = { message: 'GSTIN submitted', is_gstin_verified: true, verified: true };
} else if (path.startsWith('/api/profile/presigned-url/')) {
data = { url: 'https://mock-s3-upload-url.com', key: 'mock-s3-key' };
} else if (path.startsWith('/api/profile/')) {
data = {
phone: '9876543210',
phone_verified: true,
email_verified: true,
gstin: '29AAAAA1111A1Z1',
is_gstin_verified: true,
store_name: 'Heritage Weaves & Co.',
business_bio: 'Preserving centuries-old handloom techniques, our family has been creating pure silk and cotton textiles for three generations. Every piece is a testament to the dedication of our master weavers in Varanasi.',
street: '123 Handloom Lane',
city: 'Textile Town',
state: 'Karnataka',
pincode: '560001',
store_logo: '',
business_type: 'individual_maker',
store_slug: 'heritage-weaves',
support_email: 'hello@heritageweaves.in',
support_phone: '+91 98765 43210',
selected_categories: ['Textiles & Apparel', 'Sustainable Products']
};
} else if (path.startsWith('/api/products/')) {
data = CONFIG.dashboardData.initialProducts;
} else if (path.startsWith('/api/orders/')) {
if (path.includes('/accept/') || path.includes('/reject/')) {
data = { message: 'Order status updated successfully' };
} else {
data = CONFIG.dashboardData.initialOrders;
}
} else if (path.startsWith('/api/returns/')) {
if (path.includes('/action/')) {
data = { message: 'Return status updated successfully' };
} else {
data = CONFIG.dashboardData.initialReturns;
}
} else if (path.startsWith('/api/wallet/withdraw/')) {
data = { message: 'Withdrawal processed successfully' };
} else if (path.startsWith('/api/wallet/')) {
data = { outstanding: 38488.00, withdrawn: 6792.00, transactions: [] };
} else if (path.startsWith('/api/auth/logout/')) {
data = { message: 'Logout successful' };
}
return Promise.resolve(new Response(JSON.stringify(data), {
status,
headers: { 'Content-Type': 'application/json' }
}));
}
const token = localStorage.getItem('access_token'); const token = localStorage.getItem('access_token');
const headers = { const headers = {
...options.headers, ...options.headers,

View file

@ -0,0 +1,822 @@
import React, { createContext, useContext, useState, useEffect } from 'react';
import { CONFIG, apiFetch } from '../config';
type Page = 'home' | 'about' | 'contact' | 'login' | 'signup' | 'profile-completion' | 'confirmation' | 'dashboard' | 'forgot-password' | 'login-otp' | 'welcome-tour';
type DashboardTab = 'overview' | 'products' | 'orders' | 'returns' | 'wallet' | 'settings' | 'barcode-generator' | 'analytics';
type DateFilter = 'year' | 'week' | 'day' | 'custom';
interface Product {
id: string;
title: string;
category: string;
price: number;
stock: number;
sku: string;
image: string;
}
interface Order {
id: string;
date: string;
item: string;
quantity: number;
customer: string;
total: number;
status: 'Ready to Ship test' | 'Ready to Ship' | 'Shipped' | 'Delivered' | 'Cancelled' | 'Pending Acceptance' | 'Rejected';
carrier: string;
tracking: string;
eta: string;
}
interface ReturnRequest {
id: string;
orderId: string;
customer: string;
item: string;
reason: string;
status: 'Pending Approval' | 'Approved' | 'Rejected' | 'In Transit';
image: string;
returningTracking: string;
}
export const SellerContext = createContext<any>(null);
export const useSeller = () => useContext(SellerContext);
export const SellerProvider: React.FC<{children: React.ReactNode}> = ({ children }) => {
const [currentPage, setCurrentPage] = useState<Page>('home')
const [isSidebarOpen, setIsSidebarOpen] = useState(false)
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false)
const [activeTab, setActiveTab] = useState<'login' | 'register'>('login')
// Onboarding Status
const [isProfileComplete, setIsProfileComplete] = useState(false)
const [profileStep, setProfileStep] = useState(1)
// Registration / Onboarding Form States
const [email, setEmail] = useState('')
const [phone, setPhone] = useState('')
const [password, setPassword] = useState('')
// Step 1: Verification
const [phoneVerified, setPhoneVerified] = useState(false)
const [emailVerified, setEmailVerified] = useState(false)
const [phoneOtpSent, setPhoneOtpSent] = useState(false)
const [emailOtpSent, setEmailOtpSent] = useState(false)
const [enteredPhoneOtp, setEnteredPhoneOtp] = useState('')
const [enteredEmailOtp, setEnteredEmailOtp] = useState('')
// Step 2: Business details (moved GSTIN here)
const [gstin, setGstin] = useState('29AAAAA1111A1Z1')
const [isGstinVerified, setIsGstinVerified] = useState(false)
const [aadharFile, setAadharFile] = useState<string | null>(null)
const [panFile, setPanFile] = useState<string | null>(null)
const [aadharS3Key, setAadharS3Key] = useState<string | null>(null)
const [panS3Key, setPanS3Key] = useState<string | null>(null)
// New onboarding customization states
const [businessType, setBusinessType] = useState<'registered_company' | 'self_help_group' | 'individual_maker'>('registered_company')
const [storeSlug, setStoreSlug] = useState('')
const [supportEmail, setSupportEmail] = useState('')
const [supportPhone, setSupportPhone] = useState('')
const [selectedCategories, setSelectedCategories] = useState<string[]>([])
// Step 3: Store and Location details
const [storeName, setStoreName] = useState('My Artisan Handloom')
const [storeLogo, setStoreLogo] = useState<string | null>(null)
const [logoS3Key, setLogoS3Key] = useState<string | null>(null)
const [businessBio, setBusinessBio] = useState('Traditional weaving and local sustainable designs.')
const [address, setAddress] = useState({
street: '123 Handloom Lane',
city: 'Textile Town',
state: 'Karnataka',
pincode: '560001'
})
const [mapCoordinates, setMapCoordinates] = useState({ lat: 12.9716, lng: 77.5946 })
// --- Password visibility, confirm password and policy states ---
const [showLoginPass, setShowLoginPass] = useState(false)
const [showSignupPass, setShowSignupPass] = useState(false)
const [showSignupConfirmPass, setShowSignupConfirmPass] = useState(false)
const [confirmPassword, setConfirmPassword] = useState('')
const [policyAccepted, setPolicyAccepted] = useState(false)
// --- Password Reset Page States ---
const [resetEmail, setResetEmail] = useState('')
const [resetOtpSent, setResetOtpSent] = useState(false)
const [resetOtp, setResetOtp] = useState('')
const [resetPassword, setResetPassword] = useState('')
// --- OTP Login States ---
const [otpLoginPhone, setOtpLoginPhone] = useState('')
const [otpLoginSent, setOtpLoginSent] = useState(false)
const [otpLoginCode, setOtpLoginCode] = useState('')
// --- Barcode Generator States ---
const [barcodeProductSku, setBarcodeProductSku] = useState('')
const [barcodeGenerated, setBarcodeGenerated] = useState(false)
// --- Active Dashboard States ---
const [dashTab, setDashTab] = useState<DashboardTab>('overview')
const [dateFilter, setDateFilter] = useState<DateFilter>('year')
const [customDates, setCustomDates] = useState({ start: '2026-06-01', end: '2026-06-15' })
// Lists
const [products, setProducts] = useState<Product[]>([])
const [orders, setOrders] = useState<Order[]>([])
const [returns, setReturns] = useState<ReturnRequest[]>([])
// Forms & Editing
const [productForm, setProductForm] = useState({
id: '',
title: '',
category: 'Apparel',
price: 0,
stock: 0,
sku: '',
image: 'https://images.unsplash.com/photo-1544022613-e87ca75a784a?auto=format&fit=crop&q=80&w=100'
})
const [isEditingProduct, setIsEditingProduct] = useState(false)
// New Heritage UI wizard states
const [productWizardStep, setProductWizardStep] = useState<number>(0) // 0: Catalog list, 1-5: Product upload wizard steps
const [productFormDetails, setProductFormDetails] = useState({
id: '',
name: '',
category: 'Textiles & Apparel',
description: '',
isGiTagged: false,
primaryImage: 'https://images.unsplash.com/photo-1544022613-e87ca75a784a?auto=format&fit=crop&q=80&w=300',
additionalViews: [] as string[],
videoUrl: '',
view360Url: '',
basePrice: '',
compareAtPrice: '',
trackInventory: true,
sku: '',
initialStock: '',
shippingProfile: 'Standard Fragile',
processingDays: 3,
packageWeight: 1.0
})
const [selectedOrderDetail, setSelectedOrderDetail] = useState<any | null>(null)
const [orderNotes, setOrderNotes] = useState<string>('')
// Payout outstanding states
const [wallet, setWallet] = useState({
outstanding: 850.00,
withdrawn: 1250.00,
history: [
{ id: 'TX-9031', date: '2026-08-01', amount: 500.00, status: 'Transferred' },
{ id: 'TX-9022', date: '2026-07-15', amount: 750.00, status: 'Transferred' }
]
})
const [withdrawAmount, setWithdrawAmount] = useState('')
// Load data from backend on mount or when profile is complete / logged in
useEffect(() => {
const token = localStorage.getItem('access_token');
if (token) {
apiFetch(`${CONFIG.apiBaseUrl}/api/profile/`)
.then(res => {
if (res.ok) return res.json();
throw new Error('Session expired');
})
.then(user => {
if (user && user.profile) {
const profile = user.profile;
setPhone(profile.phone || '');
setPhoneVerified(profile.phone_verified || false);
setEmail(user.email || '');
setEmailVerified(profile.email_verified || false);
setGstin(profile.gstin || '29AAAAA1111A1Z1');
setIsGstinVerified(profile.is_gstin_verified || false);
setAadharFile(profile.aadhar_file || null);
setPanFile(profile.pan_file || null);
setAadharS3Key(profile.aadhar_s3_key || null);
setPanS3Key(profile.pan_s3_key || null);
setBusinessType(profile.business_type || 'registered_company');
setStoreSlug(profile.store_slug || '');
setSupportEmail(profile.support_email || '');
setSupportPhone(profile.support_phone || '');
setSelectedCategories(profile.categories || []);
setStoreName(profile.store_name || 'My Artisan Handloom');
setStoreLogo(profile.store_logo || null);
setLogoS3Key(profile.logo_s3_key || null);
setBusinessBio(profile.business_bio || 'Traditional weaving and local sustainable designs.');
setAddress({
street: profile.street || '123 Handloom Lane',
city: profile.city || 'Textile Town',
state: profile.state || 'Karnataka',
pincode: profile.pincode || '560001'
});
if (profile.latitude && profile.longitude) {
setMapCoordinates({ lat: Number(profile.latitude), lng: Number(profile.longitude) });
}
const step = profile.onboarding_step;
if (step >= 7) {
setIsProfileComplete(true);
setCurrentPage('dashboard');
} else {
setProfileStep(step);
setIsProfileComplete(false);
setCurrentPage('profile-completion');
}
}
})
.catch(err => {
console.error('Session restore failed:', err);
localStorage.removeItem('access_token');
});
}
}, []);
useEffect(() => {
if (isProfileComplete || currentPage === 'dashboard') {
// Fetch Products
apiFetch(`${CONFIG.apiBaseUrl}/api/products/`)
.then(res => res.json())
.then(data => {
if (Array.isArray(data)) setProducts(data);
else if (data && Array.isArray(data.results)) setProducts(data.results);
})
.catch(err => console.error('Error fetching products:', err));
// Fetch Orders
apiFetch(`${CONFIG.apiBaseUrl}/api/orders/`)
.then(res => res.json())
.then(data => {
if (Array.isArray(data)) setOrders(data);
else if (data && Array.isArray(data.results)) setOrders(data.results);
})
.catch(err => console.error('Error fetching orders:', err));
// Fetch Returns
apiFetch(`${CONFIG.apiBaseUrl}/api/returns/`)
.then(res => res.json())
.then(data => {
if (Array.isArray(data)) setReturns(data);
else if (data && Array.isArray(data.results)) setReturns(data.results);
})
.catch(err => console.error('Error fetching returns:', err));
// Fetch Wallet
apiFetch(`${CONFIG.apiBaseUrl}/api/wallet/`)
.then(res => res.json())
.then(data => {
if (data) setWallet(data);
})
.catch(err => console.error('Error fetching wallet:', err));
}
}, [isProfileComplete, currentPage]);
const uploadDocument = async (file: File, fileType: string) => {
try {
const response = await apiFetch(`${CONFIG.apiBaseUrl}/api/profile/presigned-url/`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ file_type: fileType, content_type: file.type })
});
if (!response.ok) throw new Error('Failed to get presigned URL');
const data = await response.json();
// Simulate file upload PUT to mock presigned_url
await fetch(data.presigned_url, {
method: 'PUT',
body: file,
headers: { 'Content-Type': file.type }
}).catch(err => console.log('Mock S3 upload:', err));
return data.s3_key;
} catch (err) {
console.error(err);
return `suppliers/default/${fileType}.jpg`;
}
};
const handleAcceptOrder = (id: string) => {
apiFetch(`${CONFIG.apiBaseUrl}/api/orders/${id}/accept/`, {
method: 'POST'
})
.then(res => res.json())
.then(updatedOrder => {
setOrders(orders.map(o => o.id === updatedOrder.id ? updatedOrder : o))
alert(`Order accepted successfully!`)
})
.catch(err => console.error(err))
}
const handleRejectOrder = (id: string) => {
apiFetch(`${CONFIG.apiBaseUrl}/api/orders/${id}/reject/`, {
method: 'POST'
})
.then(res => res.json())
.then(updatedOrder => {
setOrders(orders.map(o => o.id === updatedOrder.id ? updatedOrder : o))
alert(`Order rejected.`)
})
.catch(err => console.error(err))
}
// Bulk Upload
const [bulkLog, setBulkLog] = useState<string[]>([])
const [isParsingBulk, setIsParsingBulk] = useState(false)
const [bulkCsvFile, setBulkCsvFile] = useState<File | null>(null)
const [bulkZipFile, setBulkZipFile] = useState<File | null>(null)
// GSTIN verification simulator
const handleVerifyGstin = () => {
if (!gstin.trim() || gstin.length !== 15) {
alert('Invalid GSTIN length. Must be 15 chars.');
return;
}
apiFetch(`${CONFIG.apiBaseUrl}/api/profile/submit-gstin/`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ gstin })
})
.then(res => {
if (!res.ok) throw new Error('Failed to verify GSTIN.');
return res.json();
})
.then(data => {
if (data.verified) {
setIsGstinVerified(true);
}
})
.catch(err => {
alert(err.message);
});
}
// Handle Logo Upload simulation
const handleLogoChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0]
if (file) {
const reader = new FileReader()
reader.onloadend = () => {
setStoreLogo(reader.result as string)
}
reader.readAsDataURL(file)
const s3Key = await uploadDocument(file, 'logo');
setLogoS3Key(s3Key);
}
}
// Route/Navigation Guard: If profile is not complete, redirect to profile-completion
const navigateTo = (page: Page, forceComplete: boolean = false) => {
const publicPages: Page[] = ['home', 'about', 'contact', 'login', 'signup', 'forgot-password', 'login-otp']
const complete = isProfileComplete || forceComplete
if (!complete && !publicPages.includes(page) && page !== 'profile-completion') {
alert('Access Denied: Please complete your supplier profile first!')
setCurrentPage('profile-completion')
} else {
setCurrentPage(page)
}
window.scrollTo({ top: 0, behavior: 'smooth' })
}
const handleLogout = () => {
apiFetch(`${CONFIG.apiBaseUrl}/api/auth/logout/`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
})
.catch(err => console.error('Error logging out:', err))
.finally(() => {
localStorage.removeItem('access_token')
// Reset all onboarding & profile states to their defaults
setIsProfileComplete(false)
setProfileStep(1)
setEmail('')
setPhone('')
setPassword('')
setConfirmPassword('')
setPolicyAccepted(false)
setPhoneVerified(false)
setEmailVerified(false)
setPhoneOtpSent(false)
setEmailOtpSent(false)
setEnteredPhoneOtp('')
setEnteredEmailOtp('')
setGstin('29AAAAA1111A1Z1')
setIsGstinVerified(false)
setAadharFile(null)
setPanFile(null)
setAadharS3Key(null)
setPanS3Key(null)
setStoreName('My Artisan Handloom')
setStoreLogo(null)
setLogoS3Key(null)
setBusinessBio('Traditional weaving and local sustainable designs.')
setAddress({
street: '123 Handloom Lane',
city: 'Textile Town',
state: 'Karnataka',
pincode: '560001'
})
setMapCoordinates({ lat: 12.9716, lng: 77.5946 })
navigateTo('home', true)
})
}
const handleRegisterSubmit = (e: React.FormEvent) => {
e.preventDefault()
// Email Validation
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
alert('Please enter a valid email address.');
return;
}
// Phone Validation
const phoneRegex = /^[6-9]\d{9}$/;
if (!phoneRegex.test(phone)) {
alert('Please enter a valid 10-digit mobile number starting with 6, 7, 8, or 9.');
return;
}
// Password Validation
if (password.length < 8) {
alert('Password must be at least 8 characters long.');
return;
}
if (password !== confirmPassword) {
alert('Passwords do not match.');
return;
}
// Policy Validation
if (!policyAccepted) {
alert('You must accept the Terms of Service and Privacy Policy.');
return;
}
apiFetch(`${CONFIG.apiBaseUrl}/api/auth/register/`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: email,
email: email,
phone: phone,
password: password,
confirm_password: confirmPassword,
policy_accepted: policyAccepted
})
})
.then(res => {
if (!res.ok) throw new Error('Registration failed. Username/email might already be taken.');
return res.json();
})
.then(() => {
// Auto login after signup
apiFetch(`${CONFIG.apiBaseUrl}/api/auth/login/`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: email, password: password })
})
.then(res => {
if (!res.ok) throw new Error('Auto-login failed.');
return res.json();
})
.then(data => {
localStorage.setItem('access_token', data.access_token);
navigateTo('profile-completion')
})
})
.catch(err => {
alert(err.message);
});
}
const handleLoginSubmit = (e: React.FormEvent) => {
e.preventDefault()
if (!email.trim()) {
alert('Please enter your email or phone number.');
return;
}
apiFetch(`${CONFIG.apiBaseUrl}/api/auth/login/`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: email, password: password })
})
.then(res => {
if (!res.ok) throw new Error('Invalid credentials.');
return res.json();
})
.then(data => {
localStorage.setItem('access_token', data.access_token);
if (data.user && data.user.profile) {
const profile = data.user.profile;
setPhone(profile.phone || '');
setPhoneVerified(profile.phone_verified || false);
setEmail(data.user.email || '');
setEmailVerified(profile.email_verified || false);
setGstin(profile.gstin || '29AAAAA1111A1Z1');
setIsGstinVerified(profile.is_gstin_verified || false);
setAadharFile(profile.aadhar_file || null);
setPanFile(profile.pan_file || null);
setAadharS3Key(profile.aadhar_s3_key || null);
setPanS3Key(profile.pan_s3_key || null);
setStoreName(profile.store_name || 'My Artisan Handloom');
setStoreLogo(profile.store_logo || null);
setLogoS3Key(profile.logo_s3_key || null);
setBusinessBio(profile.business_bio || 'Traditional weaving and local sustainable designs.');
setAddress({
street: profile.street || '123 Handloom Lane',
city: profile.city || 'Textile Town',
state: profile.state || 'Karnataka',
pincode: profile.pincode || '560001'
});
if (profile.latitude && profile.longitude) {
setMapCoordinates({ lat: Number(profile.latitude), lng: Number(profile.longitude) });
}
const step = profile.onboarding_step;
if (step >= 4) {
setIsProfileComplete(true);
navigateTo('dashboard', true);
} else {
setProfileStep(step);
setIsProfileComplete(false);
navigateTo('profile-completion');
}
} else {
setIsProfileComplete(false);
navigateTo('profile-completion');
}
})
.catch(err => {
alert(err.message);
});
}
const saveProfileBackend = (isComplete: boolean) => {
return apiFetch(`${CONFIG.apiBaseUrl}/api/profile/`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
phone: phone,
phone_verified: phoneVerified,
email_verified: emailVerified,
gstin: gstin,
is_gstin_verified: isGstinVerified,
business_type: businessType,
store_slug: storeSlug,
support_email: supportEmail,
support_phone: supportPhone,
categories: selectedCategories,
aadhar_file: aadharFile,
pan_file: panFile,
aadhar_s3_key: aadharS3Key || 'suppliers/default/aadhar.pdf',
pan_s3_key: panS3Key || 'suppliers/default/pan.pdf',
store_name: storeName,
store_logo: storeLogo || 'https://images.unsplash.com/photo-1513519245088-0e12902e5a38?auto=format&fit=crop&q=80&w=800',
logo_s3_key: logoS3Key || 'suppliers/default/logo.jpg',
business_bio: businessBio,
street: address.street,
city: address.city,
state: address.state,
pincode: address.pincode,
latitude: mapCoordinates.lat,
longitude: mapCoordinates.lng,
is_profile_complete: isComplete
})
})
.then(res => {
if (!res.ok) throw new Error('Failed to update profile.');
return res.json();
});
};
const handleProfileSubmit = (e: React.FormEvent) => {
e.preventDefault()
if (profileStep === 2) {
setIsGstinVerified(true)
}
const isLastStep = profileStep === 6;
saveProfileBackend(isLastStep)
.then(() => {
if (!isLastStep) {
setProfileStep(prev => prev + 1)
} else {
setProfileStep(7);
}
})
.catch(err => {
alert(err.message);
});
}
// --- Dashboard Logic Actions ---
// Product creation/modification
const handleSaveProduct = (e: React.FormEvent) => {
e.preventDefault()
const url = isEditingProduct
? `${CONFIG.apiBaseUrl}/api/products/${productForm.id}/`
: `${CONFIG.apiBaseUrl}/api/products/`
const method = isEditingProduct ? 'PUT' : 'POST'
apiFetch(url, {
method: method,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
title: productForm.title,
category: productForm.category,
price: String(productForm.price),
stock: Number(productForm.stock),
sku: productForm.sku || `PROD-${Date.now().toString().slice(-6)}`,
image: productForm.image
})
})
.then(res => res.json())
.then(() => {
// Refresh products from backend
apiFetch(`${CONFIG.apiBaseUrl}/api/products/`)
.then(r => r.json())
.then(prods => {
if (Array.isArray(prods)) setProducts(prods);
else if (prods && Array.isArray(prods.results)) setProducts(prods.results);
})
setIsEditingProduct(false)
alert(isEditingProduct ? 'Product modified successfully!' : 'Product uploaded successfully!')
})
.catch(err => console.error(err))
// reset form
setProductForm({ id: '', title: '', category: 'Apparel', price: 0, stock: 0, sku: '', image: 'https://images.unsplash.com/photo-1544022613-e87ca75a784a?auto=format&fit=crop&q=80&w=100' })
}
const handleEditClick = (p: Product) => {
setProductForm(p)
setIsEditingProduct(true)
}
const handleDeleteProduct = (id: string) => {
if (confirm('Are you sure you want to delete this listing?')) {
apiFetch(`${CONFIG.apiBaseUrl}/api/products/${id}/`, {
method: 'DELETE'
})
.then(() => {
setProducts(products.filter(p => p.id !== id))
})
.catch(err => console.error(err))
}
}
const handleBulkUploadSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!bulkCsvFile) {
alert("Please select a CSV file to upload.");
return;
}
setIsParsingBulk(true);
setBulkLog(['Uploading files to server...', 'Parsing CSV data and extracting ZIP images...']);
const formData = new FormData();
formData.append('csv_file', bulkCsvFile);
if (bulkZipFile) {
formData.append('zip_file', bulkZipFile);
}
try {
const token = localStorage.getItem('access_token');
const response = await fetch(`${CONFIG.apiBaseUrl}/api/products/bulk-upload/`, {
method: 'POST',
headers: {
...(token ? { 'Authorization': `Bearer ${token}` } : {})
},
body: formData
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || 'Failed to complete bulk upload.');
}
setBulkLog([
'CSV file parsed successfully.',
`Extracted and matched images for SKUs from ZIP file.`,
`Successfully added ${data.products?.length || 0} product listings!`,
]);
// Refresh product list
const prodRes = await apiFetch(`${CONFIG.apiBaseUrl}/api/products/`);
const prods = await prodRes.json();
if (Array.isArray(prods)) {
setProducts(prods);
} else if (prods && Array.isArray(prods.results)) {
setProducts(prods.results);
}
setBulkCsvFile(null);
setBulkZipFile(null);
} catch (err: any) {
setBulkLog(prev => [...prev, `Error: ${err.message}`]);
} finally {
setIsParsingBulk(false);
}
};
// Wallet outstanding requests
const handleWithdrawRequest = (e: React.FormEvent) => {
e.preventDefault()
const amount = Number(withdrawAmount)
if (isNaN(amount) || amount <= 0) {
alert('Please enter a valid amount')
return
}
apiFetch(`${CONFIG.apiBaseUrl}/api/wallet/withdraw/`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ amount: String(amount) })
})
.then(res => {
if (!res.ok) throw new Error('Insufficient funds or invalid request');
return res.json();
})
.then(updatedWallet => {
setWallet(updatedWallet)
setWithdrawAmount('')
alert(`Payout of $${amount} successfully transferred!`)
})
.catch(err => {
alert(err.message)
})
}
// Returns actions
const handleReturnAction = (id: string, action: 'Approved' | 'Rejected') => {
apiFetch(`${CONFIG.apiBaseUrl}/api/returns/${id}/action/`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: action })
})
.then(res => res.json())
.then(updatedReturn => {
setReturns(returns.map(ret => ret.id === id ? updatedReturn : ret))
alert(`Return request ${action.toLowerCase()}!`)
})
.catch(err => console.error(err))
}
// Compute realtime metrics based on state loaded from backend
const computeRealtimeMetrics = () => {
const totalSales = orders
.filter(o => o.status !== 'Cancelled' && o.status !== 'Rejected')
.reduce((sum, o) => sum + Number(o.total), 0)
const totalEarned = Number(wallet.outstanding) + Number(wallet.withdrawn)
const totalStock = products.reduce((sum, p) => sum + Number(p.stock), 0)
const totalReturns = returns.length
const isLoggedIn = currentPage === 'dashboard' || currentPage === 'profile-completion' || currentPage === 'welcome-tour' || isProfileComplete
if (isLoggedIn) {
return {
totalSales,
totalEarned,
stockDetails: totalStock,
returnedItems: totalReturns,
chartValues: orders.length > 0
? orders.map(o => Number(o.total))
: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
}
}
return {
totalSales: 45280.00,
totalEarned: 38488.00,
stockDetails: 342,
returnedItems: 12,
chartValues: CONFIG.dashboardData.filters[dateFilter]?.chartValues || [30, 45, 35, 60, 50, 75, 65, 80, 70, 95, 90, 110]
}
}
const selectedMetrics = computeRealtimeMetrics()
const contextValue = {
setBusinessBio, email, resetPassword, setLogoS3Key, setStoreName, setShowLoginPass, storeName, otpLoginSent, setOtpLoginSent, aadharS3Key, logoS3Key, customDates, setProductFormDetails, handleBulkUploadSubmit, setMapCoordinates, setAadharFile, gstin, mapCoordinates, setIsGstinVerified, enteredPhoneOtp, address, setEmailVerified, dashTab, selectedOrderDetail, setShowSignupConfirmPass, handleRegisterSubmit, setBulkCsvFile, confirmPassword, navigateTo, setResetPassword, orderNotes, setOrders, handleRejectOrder, barcodeProductSku, setEnteredEmailOtp, setIsEditingProduct, setBulkLog, isMobileMenuOpen, resetOtp, productWizardStep, setCustomDates, setConfirmPassword, isGstinVerified, bulkCsvFile, setAddress, storeLogo, handleProfileSubmit, setPhoneOtpSent, setOrderNotes, handleDeleteProduct, setWallet, policyAccepted, emailOtpSent, setResetOtpSent, setGstin, phoneOtpSent, showLoginPass, showSignupConfirmPass, setPhoneVerified, aadharFile, setDateFilter, activeTab, computeRealtimeMetrics, setIsParsingBulk, handleLogout, panS3Key, setCurrentPage, selectedCategories, setEmailOtpSent, showSignupPass, resetEmail, handleLogoChange, dateFilter, setShowSignupPass, businessBio, setWithdrawAmount, phoneVerified, currentPage, setResetOtp, enteredEmailOtp, setEnteredPhoneOtp, setPanFile, setIsSidebarOpen, setSelectedOrderDetail, handleAcceptOrder, saveProfileBackend, supportPhone, handleEditClick, setStoreLogo, setEmail, password, setOtpLoginPhone, bulkLog, businessType, setPanS3Key, setProducts, setSupportEmail, setOtpLoginCode, withdrawAmount, handleSaveProduct, setStoreSlug, setBarcodeProductSku, setProductForm, setBulkZipFile, productFormDetails, setIsProfileComplete, otpLoginPhone, orders, setActiveTab, bulkZipFile, isSidebarOpen, setBarcodeGenerated, setBusinessType, handleReturnAction, setSupportPhone, barcodeGenerated, products, productForm, setProductWizardStep, resetOtpSent, emailVerified, panFile, isParsingBulk, setDashTab, setReturns, setPassword, handleWithdrawRequest, uploadDocument, setPolicyAccepted, isEditingProduct, setIsMobileMenuOpen, profileStep, isProfileComplete, setSelectedCategories, storeSlug, phone, setPhone, setAadharS3Key, handleVerifyGstin, setProfileStep, returns, handleLoginSubmit, setResetEmail, wallet, otpLoginCode, supportEmail
};
return (
<SellerContext.Provider value={contextValue}>
{children}
</SellerContext.Provider>
);
};

File diff suppressed because it is too large Load diff

View file

@ -6,4 +6,12 @@ export default defineConfig({
plugins: [ plugins: [
react(), react(),
], ],
server: {
proxy: {
'/api': {
target: 'http://127.0.0.1:8000',
changeOrigin: true,
},
},
},
}) })