From f42148ac9b9bb9ba86efedb418300f5cd6000ddd Mon Sep 17 00:00:00 2001 From: rohit Date: Sun, 3 Aug 2025 18:49:28 +0530 Subject: [PATCH] initial commit --- README.md | 289 ++++++- package-lock.json | 930 +++++++++++++++++++++- package.json | 16 + postcss.config.js | 6 + public/index.html | 4 +- public/manifest.json | 4 +- src/App.tsx | 122 ++- src/components/DetailView.tsx | 286 +++++++ src/components/DualCurrencyDisplay.tsx | 34 + src/components/Layout/Layout.tsx | 19 + src/components/Layout/Sidebar.tsx | 189 +++++ src/components/Modal.tsx | 76 ++ src/components/MoreOptionsDropdown.tsx | 118 +++ src/components/PlaceholderPage.tsx | 115 +++ src/components/forms/AddCustomerForm.tsx | 253 ++++++ src/components/forms/AddInstanceForm.tsx | 196 +++++ src/components/forms/AddInvoiceForm.tsx | 121 +++ src/components/forms/AddTicketForm.tsx | 147 ++++ src/components/forms/EditCustomerForm.tsx | 270 +++++++ src/components/forms/EditInstanceForm.tsx | 211 +++++ src/components/forms/MailComposeForm.tsx | 205 +++++ src/data/mockData.ts | 331 ++++++++ src/index.css | 244 +++++- src/pages/Billing/index.tsx | 345 ++++++++ src/pages/Customers/index.tsx | 354 ++++++++ src/pages/Dashboard.tsx | 354 ++++++++ src/pages/Instances/index.tsx | 350 ++++++++ src/pages/Reports/index.tsx | 302 +++++++ src/pages/Support/index.tsx | 362 +++++++++ src/store/hooks.ts | 5 + src/store/index.ts | 15 + src/store/slices/authSlice.ts | 70 ++ src/store/slices/dashboardSlice.ts | 81 ++ src/store/slices/themeSlice.ts | 52 ++ src/utils/cn.ts | 6 + src/utils/format.ts | 131 +++ tailwind.config.js | 123 +++ 37 files changed, 6637 insertions(+), 99 deletions(-) create mode 100644 postcss.config.js create mode 100644 src/components/DetailView.tsx create mode 100644 src/components/DualCurrencyDisplay.tsx create mode 100644 src/components/Layout/Layout.tsx create mode 100644 src/components/Layout/Sidebar.tsx create mode 100644 src/components/Modal.tsx create mode 100644 src/components/MoreOptionsDropdown.tsx create mode 100644 src/components/PlaceholderPage.tsx create mode 100644 src/components/forms/AddCustomerForm.tsx create mode 100644 src/components/forms/AddInstanceForm.tsx create mode 100644 src/components/forms/AddInvoiceForm.tsx create mode 100644 src/components/forms/AddTicketForm.tsx create mode 100644 src/components/forms/EditCustomerForm.tsx create mode 100644 src/components/forms/EditInstanceForm.tsx create mode 100644 src/components/forms/MailComposeForm.tsx create mode 100644 src/data/mockData.ts create mode 100644 src/pages/Billing/index.tsx create mode 100644 src/pages/Customers/index.tsx create mode 100644 src/pages/Dashboard.tsx create mode 100644 src/pages/Instances/index.tsx create mode 100644 src/pages/Reports/index.tsx create mode 100644 src/pages/Support/index.tsx create mode 100644 src/store/hooks.ts create mode 100644 src/store/index.ts create mode 100644 src/store/slices/authSlice.ts create mode 100644 src/store/slices/dashboardSlice.ts create mode 100644 src/store/slices/themeSlice.ts create mode 100644 src/utils/cn.ts create mode 100644 src/utils/format.ts create mode 100644 tailwind.config.js diff --git a/README.md b/README.md index b87cb00..b5e9c90 100644 --- a/README.md +++ b/README.md @@ -1,46 +1,283 @@ -# Getting Started with Create React App +# Cloudtopiaa Reseller Dashboard -This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). +A comprehensive dashboard for resellers to manage their customers, track commissions, and monitor business performance in the Cloudtopiaa ecosystem. -## Available Scripts +## 🏒 **Project Overview** -In the project directory, you can run: +The Reseller Dashboard is designed for individual reseller companies to manage their customer relationships, track their performance, and monitor their commissions earned through the Cloudtopiaa partnership program. -### `npm start` +## 🎯 **Key Features** -Runs the app in the development mode.\ -Open [http://localhost:3000](http://localhost:3000) to view it in the browser. +### **Dashboard Metrics & Analytics** +- **Real-time Performance Tracking** +- **Dual Currency Support** (INR & USD) +- **Customer Management** +- **Revenue Analytics** -The page will reload if you make edits.\ -You will also see any lint errors in the console. +### **Customer Management** +- **Customer Onboarding** +- **Instance Management** +- **Payment Tracking** +- **Support Ticket System** -### `npm test` +### **Business Operations** +- **Revenue Tracking** +- **Commission Monitoring** +- **Invoice Management** +- **Performance Analytics** -Launches the test runner in the interactive watch mode.\ -See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. +### **User Experience** +- **Dark/Light Theme Support** +- **Responsive Design** +- **Smooth Animations & Transitions** +- **Cookie Consent Management** -### `npm run build` +## πŸ“Š **Key Metrics Explained** -Builds the app for production to the `build` folder.\ -It correctly bundles React in production mode and optimizes the build for the best performance. +### **Total Revenue (β‚Ή28,47,500)** +**What it means:** The total revenue generated from all customer subscriptions and services. -The build is minified and the filenames include the hashes.\ -Your app is ready to be deployed! +**What it tells you:** Your overall business performance and growth trajectory. -See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. +### **Total Customers (156)** +**What it means:** The number of active customers you're currently serving. -### `npm run eject` +**What it tells you:** Your customer base size and market reach. -**Note: this is a one-way operation. Once you `eject`, you can’t go back!** +### **Active Instances (89)** +**What it means:** The number of active cloud service instances currently running for your customers. -If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. +**What it tells you:** Your service utilization and customer engagement levels. -Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. +### **Pending Invoices (12)** +**What it means:** The number of invoices that are awaiting payment from customers. -You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. +**What it tells you:** Your accounts receivable status and cash flow situation. -## Learn More +## πŸš€ **Business Intelligence Insights** -You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). +### **Revenue Growth (23.5%)** +- **Positive growth** indicates expanding business +- **Monthly comparison** shows performance trends +- **Helps identify** successful strategies -To learn React, check out the [React documentation](https://reactjs.org/). +### **Customer Acquisition** +- **156 total customers** shows market penetration +- **89 active instances** indicates service utilization +- **12 pending invoices** requires attention for cash flow + +### **Performance Indicators** +- **High customer count** with good revenue suggests strong market position +- **Active instances** show customer engagement +- **Pending invoices** need follow-up for timely payments + +## πŸ›  **Technical Stack** + +### **Frontend** +- **React 18** with TypeScript +- **Redux Toolkit** for state management +- **React Router DOM** for routing +- **Tailwind CSS** for styling +- **Lucide React** for icons +- **Recharts** for data visualization + +### **UI/UX Features** +- **Responsive Design** (Mobile-first approach) +- **Dark/Light Theme** with system preference detection +- **Smooth Animations** and transitions +- **Cookie Consent** management +- **Dual Currency** display (INR & USD) + +### **Development Tools** +- **PostCSS** with Autoprefixer +- **TypeScript** for type safety +- **ESLint** for code quality +- **Create React App** setup + +## πŸ“ **Project Structure** + +``` +reseller-dashboard/ +β”œβ”€β”€ public/ +β”œβ”€β”€ src/ +β”‚ β”œβ”€β”€ components/ +β”‚ β”‚ β”œβ”€β”€ Layout/ +β”‚ β”‚ β”‚ β”œβ”€β”€ Layout.tsx +β”‚ β”‚ β”‚ └── Sidebar.tsx +β”‚ β”‚ └── CookieConsent.tsx +β”‚ β”œβ”€β”€ pages/ +β”‚ β”‚ β”œβ”€β”€ Dashboard.tsx +β”‚ β”‚ └── PlaceholderPage.tsx +β”‚ β”œβ”€β”€ store/ +β”‚ β”‚ β”œβ”€β”€ slices/ +β”‚ β”‚ β”‚ β”œβ”€β”€ authSlice.ts +β”‚ β”‚ β”‚ β”œβ”€β”€ dashboardSlice.ts +β”‚ β”‚ β”‚ └── themeSlice.ts +β”‚ β”‚ β”œβ”€β”€ hooks.ts +β”‚ β”‚ └── index.ts +β”‚ β”œβ”€β”€ utils/ +β”‚ β”‚ β”œβ”€β”€ cn.ts +β”‚ β”‚ └── format.ts +β”‚ β”œβ”€β”€ data/ +β”‚ β”‚ └── mockData.ts +β”‚ β”œβ”€β”€ App.tsx +β”‚ └── index.tsx +β”œβ”€β”€ package.json +β”œβ”€β”€ tailwind.config.js +└── README.md +``` + +## πŸš€ **Getting Started** + +### **Prerequisites** +- Node.js (v16 or higher) +- npm or yarn + +### **Installation** + +1. **Clone the repository** + ```bash + git clone + cd reseller-dashboard + ``` + +2. **Install dependencies** + ```bash + npm install + ``` + +3. **Start the development server** + ```bash + npm start + ``` + +4. **Open your browser** + Navigate to `http://localhost:3000` + +### **Available Scripts** + +- `npm start` - Start development server +- `npm build` - Build for production +- `npm test` - Run tests +- `npm eject` - Eject from Create React App + +## 🎨 **Theme Configuration** + +The dashboard supports both light and dark themes with automatic system preference detection. + +### **Theme Features** +- **Automatic Detection** of system theme preference +- **Manual Toggle** via sidebar button +- **Persistent Storage** of user preference +- **Smooth Transitions** between themes + +### **Color Scheme** +- **Primary:** Blue gradient (#0EA5E9 to #0284C7) +- **Success:** Green (#10B981) +- **Warning:** Yellow (#F59E0B) +- **Danger:** Red (#EF4444) +- **Secondary:** Gray scale + +## πŸ” **Authentication & Security** + +### **Features** +- **JWT Token** management +- **Refresh Token** support +- **Session Management** +- **Secure Logout** + +### **User Roles** +- **Reseller Admin** (Current implementation) +- **Reseller Staff** (Future implementation) +- **Customer Support** (Future implementation) + +## 🌐 **Internationalization** + +### **Currency Support** +- **INR (β‚Ή)** - Indian Rupees +- **USD ($)** - US Dollars +- **Automatic Conversion** (1 USD β‰ˆ 83 INR) +- **Dual Display** with primary currency prominent + +### **Formatting** +- **Number formatting** with locale support +- **Date formatting** with relative time +- **Currency formatting** with proper symbols + +## πŸ“± **Responsive Design** + +### **Breakpoints** +- **Mobile:** < 640px +- **Tablet:** 640px - 1024px +- **Desktop:** > 1024px + +### **Features** +- **Mobile-first** approach +- **Touch-friendly** interface +- **Collapsible sidebar** for mobile +- **Optimized layouts** for all screen sizes + +## πŸ”§ **Development Guidelines** + +### **Code Style** +- **TypeScript** for type safety +- **ESLint** configuration +- **Prettier** formatting +- **Component-based** architecture + +### **State Management** +- **Redux Toolkit** for global state +- **Local state** for component-specific data +- **Persistent storage** for user preferences + +### **Performance** +- **Lazy loading** for routes +- **Optimized images** and assets +- **Efficient re-renders** with React.memo +- **Bundle optimization** + +## πŸš€ **Deployment** + +### **Build for Production** +```bash +npm run build +``` + +### **Deployment Options** +- **Netlify** - Static site hosting +- **Vercel** - React app deployment +- **AWS S3** - Static website hosting +- **Docker** - Containerized deployment + +## πŸ“ž **Support & Documentation** + +### **Additional Resources** +- **API Documentation** (Coming soon) +- **User Guide** (Coming soon) +- **Developer Documentation** (Coming soon) + +### **Contact** +For support and questions, please contact the development team. + +--- + +## 🎯 **Business Impact** + +This Reseller Dashboard provides: + +1. **Customer Management** - Manage all customer relationships efficiently +2. **Revenue Tracking** - Monitor income and growth patterns +3. **Commission Monitoring** - Track earnings from Cloudtopiaa partnership +4. **Performance Analytics** - Data-driven business decisions +5. **Operational Efficiency** - Streamlined customer service processes + +--- + +## πŸ”— **Related Projects** + +- **[Channel Partner Dashboard](../channel-partner-dashboard/)** - Admin panel for managing resellers +- **[Backend API](../backend/)** - Server-side implementation (Coming soon) + +--- + +**Built with ❀️ for Cloudtopiaa's Reseller Program** diff --git a/package-lock.json b/package-lock.json index 00a7054..2274508 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,10 @@ "name": "reseller-dashboard", "version": "0.1.0", "dependencies": { + "@headlessui/react": "^2.2.7", + "@reduxjs/toolkit": "^2.8.2", + "@tailwindcss/forms": "^0.5.10", + "@tailwindcss/typography": "^0.5.16", "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.6.4", "@testing-library/react": "^16.3.0", @@ -16,11 +20,23 @@ "@types/node": "^16.18.126", "@types/react": "^19.1.9", "@types/react-dom": "^19.1.7", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^0.536.0", "react": "^19.1.1", "react-dom": "^19.1.1", + "react-redux": "^9.2.0", + "react-router-dom": "^6.30.1", "react-scripts": "5.0.1", + "recharts": "^3.1.0", + "tailwind-merge": "^3.3.1", "typescript": "^4.9.5", "web-vitals": "^2.1.4" + }, + "devDependencies": { + "autoprefixer": "^10.4.16", + "postcss": "^8.4.31", + "tailwindcss": "^3.4.0" } }, "node_modules/@adobe/css-tools": { @@ -2461,6 +2477,79 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", + "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.3.tgz", + "integrity": "sha512-uZA413QEpNuhtb3/iIKoYMSK07keHPYeXF02Zhd6e213j+d1NamLix/mCLxBUDW/Gx52sPH2m+chlUsyaBs/Ag==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.3", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/react": { + "version": "0.26.28", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.28.tgz", + "integrity": "sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.1.2", + "@floating-ui/utils": "^0.2.8", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.5.tgz", + "integrity": "sha512-HDO/1/1oH9fjj4eLgegrlH3dklZpHtUYYFiVwMUwfGvk9jWDRWqkklA2/NFScknrcNSspbV868WjXORvreDX+Q==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.3" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "license": "MIT" + }, + "node_modules/@headlessui/react": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.2.7.tgz", + "integrity": "sha512-WKdTymY8Y49H8/gUc/lIyYK1M+/6dq0Iywh4zTZVAaiTDprRfioxSgD0wnXTQTBpjpGJuTL1NO/mqEvc//5SSg==", + "license": "MIT", + "dependencies": { + "@floating-ui/react": "^0.26.16", + "@react-aria/focus": "^3.20.2", + "@react-aria/interactions": "^3.25.0", + "@tanstack/react-virtual": "^3.13.9", + "use-sync-external-store": "^1.5.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "react-dom": "^18 || ^19 || ^19.0.0-rc" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", @@ -3080,6 +3169,148 @@ } } }, + "node_modules/@react-aria/focus": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.21.0.tgz", + "integrity": "sha512-7NEGtTPsBy52EZ/ToVKCu0HSelE3kq9qeis+2eEq90XSuJOMaDHUQrA7RC2Y89tlEwQB31bud/kKRi9Qme1dkA==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/interactions": "^3.25.4", + "@react-aria/utils": "^3.30.0", + "@react-types/shared": "^3.31.0", + "@swc/helpers": "^0.5.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/interactions": { + "version": "3.25.4", + "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.25.4.tgz", + "integrity": "sha512-HBQMxgUPHrW8V63u9uGgBymkMfj6vdWbB0GgUJY49K9mBKMsypcHeWkWM6+bF7kxRO728/IK8bWDV6whDbqjHg==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/ssr": "^3.9.10", + "@react-aria/utils": "^3.30.0", + "@react-stately/flags": "^3.1.2", + "@react-types/shared": "^3.31.0", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/ssr": { + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.10.tgz", + "integrity": "sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/utils": { + "version": "3.30.0", + "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.30.0.tgz", + "integrity": "sha512-ydA6y5G1+gbem3Va2nczj/0G0W7/jUVo/cbN10WA5IizzWIwMP5qhFr7macgbKfHMkZ+YZC3oXnt2NNre5odKw==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/ssr": "^3.9.10", + "@react-stately/flags": "^3.1.2", + "@react-stately/utils": "^3.10.8", + "@react-types/shared": "^3.31.0", + "@swc/helpers": "^0.5.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-stately/flags": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@react-stately/flags/-/flags-3.1.2.tgz", + "integrity": "sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, + "node_modules/@react-stately/utils": { + "version": "3.10.8", + "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.8.tgz", + "integrity": "sha512-SN3/h7SzRsusVQjQ4v10LaVsDc81jyyR0DD5HnsQitm/I5WDpaSr2nRHtyloPFU48jlql1XX/S04T2DLQM7Y3g==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/shared": { + "version": "3.31.0", + "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.31.0.tgz", + "integrity": "sha512-ua5U6V66gDcbLZe4P2QeyNgPp4YWD1ymGA6j3n+s8CGExtrCPe64v+g4mvpT8Bnb985R96e4zFT61+m0YCwqMg==", + "license": "Apache-2.0", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@reduxjs/toolkit": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.8.2.tgz", + "integrity": "sha512-MYlOhQ0sLdw4ud48FoC5w0dH9VfWQjtCjreKwYTT3l+r427qYC5Y8PihNutepr8XrNaBUDQo9khWUwQxZaqt5A==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@standard-schema/utils": "^0.3.0", + "immer": "^10.0.3", + "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/@reduxjs/toolkit/node_modules/immer": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz", + "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/@remix-run/router": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz", + "integrity": "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -3195,6 +3426,18 @@ "@sinonjs/commons": "^1.7.0" } }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "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" + }, "node_modules/@surma/rollup-plugin-off-main-thread": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", @@ -3428,6 +3671,82 @@ "url": "https://github.com/sponsors/gregberge" } }, + "node_modules/@swc/helpers": { + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", + "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@tailwindcss/forms": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.10.tgz", + "integrity": "sha512-utI1ONF6uf/pPNO68kmN1b8rEwNXv3czukalo8VtJH8ksIkZXr3Q3VYudZLkCsDd4Wku120uF02hYK25XGPorw==", + "license": "MIT", + "dependencies": { + "mini-svg-data-uri": "^1.2.3" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1" + } + }, + "node_modules/@tailwindcss/typography": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.16.tgz", + "integrity": "sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA==", + "license": "MIT", + "dependencies": { + "lodash.castarray": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "postcss-selector-parser": "6.0.10" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" + } + }, + "node_modules/@tailwindcss/typography/node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@tanstack/react-virtual": { + "version": "3.13.12", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.12.tgz", + "integrity": "sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA==", + "license": "MIT", + "dependencies": { + "@tanstack/virtual-core": "3.13.12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@tanstack/virtual-core": { + "version": "3.13.12", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.12.tgz", + "integrity": "sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@testing-library/dom": { "version": "10.4.1", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", @@ -3628,6 +3947,69 @@ "@types/node": "*" } }, + "node_modules/@types/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==", + "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.7", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", + "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", + "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/eslint": { "version": "8.56.12", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz", @@ -3907,6 +4289,12 @@ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", "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/@types/ws": { "version": "8.18.1", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", @@ -4865,9 +5253,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.21", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", - "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "version": "10.4.16", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.16.tgz", + "integrity": "sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==", "funding": [ { "type": "opencollective", @@ -4884,11 +5272,11 @@ ], "license": "MIT", "dependencies": { - "browserslist": "^4.24.4", - "caniuse-lite": "^1.0.30001702", - "fraction.js": "^4.3.7", + "browserslist": "^4.21.10", + "caniuse-lite": "^1.0.30001538", + "fraction.js": "^4.3.6", "normalize-range": "^0.1.2", - "picocolors": "^1.1.1", + "picocolors": "^1.0.0", "postcss-value-parser": "^4.2.0" }, "bin": { @@ -5631,6 +6019,18 @@ "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", "license": "MIT" }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, "node_modules/clean-css": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", @@ -5663,6 +6063,15 @@ "wrap-ansi": "^7.0.0" } }, + "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/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -6101,6 +6510,34 @@ } } }, + "node_modules/css-loader/node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/css-minimizer-webpack-plugin": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz", @@ -6399,6 +6836,127 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "license": "MIT" }, + "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.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "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/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -6493,6 +7051,12 @@ "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", "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/dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -7137,6 +7701,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-toolkit": { + "version": "1.39.8", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.39.8.tgz", + "integrity": "sha512-A8QO9TfF+rltS8BXpdu8OS+rpGgEdnRhqIVxO/ZmNvnXBYgOdSsxukT55ELyP94gZIntWJ+Li9QRrT2u1Kitpg==", + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks" + ] + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -9286,6 +9860,15 @@ "node": ">= 0.4" } }, + "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/ipaddr.js": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", @@ -11189,12 +11772,24 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "license": "MIT" }, + "node_modules/lodash.castarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", + "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==", + "license": "MIT" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "license": "MIT" }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -11249,6 +11844,15 @@ "yallist": "^3.0.2" } }, + "node_modules/lucide-react": { + "version": "0.536.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.536.0.tgz", + "integrity": "sha512-2PgvNa9v+qz4Jt/ni8vPLt4jwoFybXHuubQT8fv4iCW5TjDxkbZjNZZHa485ad73NSEn/jdsEtU57eE1g+ma8A==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/lz-string": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", @@ -11453,6 +12057,15 @@ "webpack": "^5.0.0" } }, + "node_modules/mini-svg-data-uri": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", + "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", + "license": "MIT", + "bin": { + "mini-svg-data-uri": "cli.js" + } + }, "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -12226,9 +12839,9 @@ } }, "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "funding": [ { "type": "opencollective", @@ -12245,9 +12858,9 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" }, "engines": { "node": "^10 || ^12 || >=14" @@ -13940,6 +14553,29 @@ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "license": "MIT" }, + "node_modules/react-redux": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", + "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+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/react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -13949,6 +14585,38 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "6.30.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.1.tgz", + "integrity": "sha512-X1m21aEmxGXqENEPG3T6u0Th7g0aS4ZmoNynhbs+Cn+q+QGTLt+d5IQ2bHAXKzKcxGJjxACpVbnYQSCRcfxHlQ==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.30.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.1.tgz", + "integrity": "sha512-llKsgOkZdbPU1Eg3zK8lCn+sjD9wMRZZPuzmdWWX5SUs8OFkN5HnFVC0u5KMeMaC9aoancFI/KoLuKPqN+hxHw==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.0", + "react-router": "6.30.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/react-scripts": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", @@ -14022,6 +14690,83 @@ } } }, + "node_modules/react-scripts/node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/react-scripts/node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react-scripts/node_modules/tailwindcss": { + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -14057,6 +14802,49 @@ "node": ">=8.10.0" } }, + "node_modules/recharts": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.1.0.tgz", + "integrity": "sha512-NqAqQcGBmLrfDs2mHX/bz8jJCQtG2FeXfE0GqpZmIuXIjkpIwj8sd9ad0WyvKiBKPd8ZgNG0hL85c8sFDwascw==", + "license": "MIT", + "dependencies": { + "@reduxjs/toolkit": "1.x.x || 2.x.x", + "clsx": "^2.1.1", + "decimal.js-light": "^2.5.1", + "es-toolkit": "^1.39.3", + "eventemitter3": "^5.0.1", + "immer": "^10.1.1", + "react-redux": "8.x.x || 9.x.x", + "reselect": "5.1.1", + "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/recharts/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, + "node_modules/recharts/node_modules/immer": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz", + "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, "node_modules/recursive-readdir": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", @@ -14082,6 +14870,21 @@ "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/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -14247,6 +15050,12 @@ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "license": "MIT" }, + "node_modules/reselect": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", + "license": "MIT" + }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -15887,34 +16696,50 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "license": "MIT" }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", + "license": "MIT" + }, + "node_modules/tailwind-merge": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz", + "integrity": "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, "node_modules/tailwindcss": { - "version": "3.4.17", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", - "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.0.tgz", + "integrity": "sha512-VigzymniH77knD1dryXbyxR+ePHihHociZbXnLZHUyzf2MMs2ZVqlUrZ3FvpXP8pno9JzmILt1sZPD19M3IxtA==", "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", - "chokidar": "^3.6.0", + "chokidar": "^3.5.3", "didyoumean": "^1.2.2", "dlv": "^1.1.3", - "fast-glob": "^3.3.2", + "fast-glob": "^3.3.0", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", - "jiti": "^1.21.6", - "lilconfig": "^3.1.3", - "micromatch": "^4.0.8", + "jiti": "^1.19.1", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", - "picocolors": "^1.1.1", - "postcss": "^8.4.47", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.2", - "postcss-nested": "^6.2.0", - "postcss-selector-parser": "^6.1.2", - "resolve": "^1.22.8", - "sucrase": "^3.35.0" + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" }, "bin": { "tailwind": "lib/cli.js", @@ -15924,18 +16749,6 @@ "node": ">=14.0.0" } }, - "node_modules/tailwindcss/node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, "node_modules/tapable": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", @@ -16111,6 +16924,12 @@ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "license": "MIT" }, + "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/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -16547,6 +17366,15 @@ "requires-port": "^1.0.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", + "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -16621,6 +17449,28 @@ "node": ">= 0.8" } }, + "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/w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", diff --git a/package.json b/package.json index 5c3daaa..f0ea3d6 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,10 @@ "version": "0.1.0", "private": true, "dependencies": { + "@headlessui/react": "^2.2.7", + "@reduxjs/toolkit": "^2.8.2", + "@tailwindcss/forms": "^0.5.10", + "@tailwindcss/typography": "^0.5.16", "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.6.4", "@testing-library/react": "^16.3.0", @@ -11,9 +15,16 @@ "@types/node": "^16.18.126", "@types/react": "^19.1.9", "@types/react-dom": "^19.1.7", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^0.536.0", "react": "^19.1.1", "react-dom": "^19.1.1", + "react-redux": "^9.2.0", + "react-router-dom": "^6.30.1", "react-scripts": "5.0.1", + "recharts": "^3.1.0", + "tailwind-merge": "^3.3.1", "typescript": "^4.9.5", "web-vitals": "^2.1.4" }, @@ -40,5 +51,10 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "devDependencies": { + "autoprefixer": "^10.4.16", + "postcss": "^8.4.31", + "tailwindcss": "^3.4.0" } } diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..0cc9a9d --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} \ No newline at end of file diff --git a/public/index.html b/public/index.html index aa069f2..3d547a8 100644 --- a/public/index.html +++ b/public/index.html @@ -7,7 +7,7 @@ - React App + Reseller diff --git a/public/manifest.json b/public/manifest.json index 080d6c7..e1abc8a 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -1,6 +1,6 @@ { - "short_name": "React App", - "name": "Create React App Sample", + "short_name": "Reseller", + "name": "Cloudtopiaa Reseller Portal", "icons": [ { "src": "favicon.ico", diff --git a/src/App.tsx b/src/App.tsx index a53698a..71b8799 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,25 +1,109 @@ -import React from 'react'; -import logo from './logo.svg'; -import './App.css'; +import React, { useEffect } from 'react'; +import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; +import { Provider } from 'react-redux'; +import { store } from './store'; +import { setTheme } from './store/slices/themeSlice'; +import Layout from './components/Layout/Layout'; +import Dashboard from './pages/Dashboard'; +import Customers from './pages/Customers'; +import Instances from './pages/Instances'; +import Billing from './pages/Billing'; +import Support from './pages/Support'; +import Reports from './pages/Reports'; +import PlaceholderPage from './components/PlaceholderPage'; +import { + Wallet, + BookOpen, + ShoppingBag, + Award, + HelpCircle, + Settings +} from 'lucide-react'; +import './index.css'; function App() { + useEffect(() => { + // Initialize theme from localStorage + const savedTheme = localStorage.getItem('theme'); + if (savedTheme) { + store.dispatch(setTheme(savedTheme as 'light' | 'dark')); + } else { + // Check system preference + const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; + store.dispatch(setTheme(systemTheme)); + } + + // Listen for system theme changes + const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); + const handleChange = (e: MediaQueryListEvent) => { + const newTheme = e.matches ? 'dark' : 'light'; + store.dispatch(setTheme(newTheme)); + }; + + mediaQuery.addEventListener('change', handleChange); + return () => mediaQuery.removeEventListener('change', handleChange); + }, []); + return ( -
-
- logo -

- Edit src/App.tsx and save to reload. -

- - Learn React - -
-
+ + +
+ + + } /> + } /> + } /> + } /> + } /> + } /> + + } /> + + } /> + + } /> + + } /> + + } /> + + } /> + } /> + + +
+
+
); } diff --git a/src/components/DetailView.tsx b/src/components/DetailView.tsx new file mode 100644 index 0000000..22af103 --- /dev/null +++ b/src/components/DetailView.tsx @@ -0,0 +1,286 @@ +import React from 'react'; +import { User, Mail, Phone, Building, MapPin, DollarSign, Award, Calendar, TrendingUp, Users, Server, Headphones, Clock, AlertCircle } from 'lucide-react'; +import { formatDate } from '../utils/format'; +import DualCurrencyDisplay from './DualCurrencyDisplay'; + +interface DetailViewProps { + type: 'customer' | 'instance' | 'invoice' | 'ticket'; + data: any; +} + +const DetailView: React.FC = ({ type, data }) => { + if (!data) { + return ( +
+

No data available

+
+ ); + } + + const renderCustomerDetails = () => ( +
+ {/* Enhanced Header */} +
+
+
+ {data.name} { + const target = e.target as HTMLImageElement; + target.style.display = 'none'; + target.nextElementSibling?.classList.remove('hidden'); + }} + /> +
+ +
+
+
+
+

{data.name}

+

{data.email}

+
+ + {data.tier.charAt(0).toUpperCase() + data.tier.slice(1)} Tier + + + {data.status.charAt(0).toUpperCase() + data.status.slice(1)} + +
+
+
+
+ + {/* Contact Information */} +
+
+
+
+ +
+
+

Email

+

{data.email}

+
+
+
+ +
+
+
+ +
+
+

Phone

+

{data.phone}

+
+
+
+
+ + {/* Performance Stats */} +
+
+
+
+ +
+
+

Total Spent

+ +
+
+
+ +
+
+
+ +
+
+

Active Instances

+

{data.activeInstances || 0}

+
+
+
+ +
+
+
+ +
+
+

Last Activity

+

{formatDate(data.lastActivity || new Date())}

+
+
+
+
+
+ ); + + const renderTicketDetails = () => ( +
+ {/* Enhanced Header */} +
+
+
+
+ +
+
+
+

{data.id}

+

{data.subject}

+
+ + {data.priority.charAt(0).toUpperCase() + data.priority.slice(1)} Priority + + + {data.status.replace('_', ' ').charAt(0).toUpperCase() + data.status.replace('_', ' ').slice(1)} + +
+
+
+
+ + {/* Ticket Information */} +
+
+
+
+ +
+
+

Customer

+

{data.customer}

+
+
+
+ +
+
+
+ +
+
+

Assigned To

+

{data.assignedTo}

+
+
+
+
+ + {/* Description */} +
+

Description

+

{data.description}

+
+ + {/* Ticket Stats */} +
+
+
+
+ +
+
+

Created

+

{formatDate(data.createdAt)}

+
+
+
+ +
+
+
+ +
+
+

Response Time

+

{data.responseTime || '2 hours'}

+
+
+
+ +
+
+
+ +
+
+

Category

+

{data.category || 'Technical'}

+
+
+
+
+
+ ); + + const renderContent = () => { + switch (type) { + case 'customer': + return renderCustomerDetails(); + case 'instance': + return ( +
+
+ +

Instance Details

+

Instance details will be displayed here

+
+
+ ); + case 'invoice': + return ( +
+
+ +

Invoice Details

+

Invoice details will be displayed here

+
+
+ ); + case 'ticket': + return renderTicketDetails(); + default: + return ( +
+

Unknown type

+
+ ); + } + }; + + return ( +
+ {renderContent()} +
+ ); +}; + +export default DetailView; \ No newline at end of file diff --git a/src/components/DualCurrencyDisplay.tsx b/src/components/DualCurrencyDisplay.tsx new file mode 100644 index 0000000..13532dd --- /dev/null +++ b/src/components/DualCurrencyDisplay.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import { formatCurrencyDualDisplay } from '../utils/format'; + +interface DualCurrencyDisplayProps { + amount: number; + currency?: 'USD' | 'INR'; + className?: string; + showSecondary?: boolean; +} + +const DualCurrencyDisplay: React.FC = ({ + amount, + currency = 'INR', + className = '', + showSecondary = true +}) => { + const formatted = formatCurrencyDualDisplay(amount, currency); + + return ( +
+ + {formatted.primary} + + {showSecondary && ( + + )} +
+ ); +}; + +export default DualCurrencyDisplay; \ No newline at end of file diff --git a/src/components/Layout/Layout.tsx b/src/components/Layout/Layout.tsx new file mode 100644 index 0000000..b857ba1 --- /dev/null +++ b/src/components/Layout/Layout.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import Sidebar from './Sidebar'; + +interface LayoutProps { + children: React.ReactNode; +} + +const Layout: React.FC = ({ children }) => { + return ( +
+ +
+ {children} +
+
+ ); +}; + +export default Layout; \ No newline at end of file diff --git a/src/components/Layout/Sidebar.tsx b/src/components/Layout/Sidebar.tsx new file mode 100644 index 0000000..1d2047c --- /dev/null +++ b/src/components/Layout/Sidebar.tsx @@ -0,0 +1,189 @@ +import React, { useState } from 'react'; +import { Link, useLocation } from 'react-router-dom'; +import { useAppSelector, useAppDispatch } from '../../store/hooks'; +import { + Home, + Users, + Server, + FileText, + Headphones, + BarChart3, + Settings, + Wallet, + BookOpen, + ShoppingBag, + Award, + HelpCircle, + Menu, + X, + Sun, + Moon, + LogOut, + Building +} from 'lucide-react'; +import { RootState } from '../../store'; +import { toggleTheme } from '../../store/slices/themeSlice'; +import { logout } from '../../store/slices/authSlice'; +import { cn } from '../../utils/cn'; + +const navigation = [ + { name: 'Dashboard', href: '/', icon: Home }, + { name: 'Customers', href: '/customers', icon: Users }, + { name: 'Instances', href: '/instances', icon: Server }, + { name: 'Billing', href: '/billing', icon: FileText }, + { name: 'Support', href: '/support', icon: Headphones }, + { name: 'Reports', href: '/reports', icon: BarChart3 }, + { name: 'Wallet', href: '/wallet', icon: Wallet }, + { name: 'Training', href: '/training', icon: BookOpen }, + { name: 'Marketplace', href: '/marketplace', icon: ShoppingBag }, + { name: 'Certifications', href: '/certifications', icon: Award }, + { name: 'Knowledge Base', href: '/knowledge-base', icon: HelpCircle }, + { name: 'Settings', href: '/settings', icon: Settings }, +]; + +const Sidebar: React.FC = () => { + const [isCollapsed, setIsCollapsed] = useState(false); + const location = useLocation(); + const dispatch = useAppDispatch(); + const { theme } = useAppSelector((state: RootState) => state.theme); + const { user } = useAppSelector((state: RootState) => state.auth); + + const handleLogout = () => { + dispatch(logout()); + }; + + return ( +
+ {/* Header */} +
+ {!isCollapsed && ( +
+
+ +
+ + Reseller + +
+ )} + +
+ + {/* Navigation */} + + + {/* User Profile & Actions */} +
+ {/* Theme Toggle */} +
+ {!isCollapsed && ( + + Theme + + )} + +
+ + {/* User Profile */} + {user && ( +
+
+ {user.name} { + const target = e.target as HTMLImageElement; + target.style.display = 'none'; + target.nextElementSibling?.classList.remove('hidden'); + }} + /> +
+ + {user.name.split(' ').map(n => n[0]).join('')} + +
+
+ {!isCollapsed && ( +
+

+ {user.name} +

+

+ {user.company} +

+
+ + {user.tier.charAt(0).toUpperCase() + user.tier.slice(1)} + +
+
+ )} + +
+ )} +
+
+ ); +}; + +export default Sidebar; \ No newline at end of file diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx new file mode 100644 index 0000000..833caaf --- /dev/null +++ b/src/components/Modal.tsx @@ -0,0 +1,76 @@ +import React, { useEffect } from 'react'; +import { X } from 'lucide-react'; +import { cn } from '../utils/cn'; + +interface ModalProps { + isOpen: boolean; + onClose: () => void; + title: string; + children: React.ReactNode; + size?: 'sm' | 'md' | 'lg' | 'xl'; +} + +const Modal: React.FC = ({ isOpen, onClose, title, children, size = 'md' }) => { + useEffect(() => { + const handleEscape = (e: KeyboardEvent) => { + if (e.key === 'Escape') { + onClose(); + } + }; + + if (isOpen) { + document.addEventListener('keydown', handleEscape); + document.body.style.overflow = 'hidden'; + } + + return () => { + document.removeEventListener('keydown', handleEscape); + document.body.style.overflow = 'unset'; + }; + }, [isOpen, onClose]); + + if (!isOpen) return null; + + const sizeClasses = { + sm: 'max-w-md', + md: 'max-w-lg', + lg: 'max-w-2xl', + xl: 'max-w-4xl', + }; + + return ( +
+ {/* Backdrop */} +
+ + {/* Modal */} +
+ {/* Header */} +
+

+ {title} +

+ +
+ + {/* Content */} +
+ {children} +
+
+
+ ); +}; + +export default Modal; \ No newline at end of file diff --git a/src/components/MoreOptionsDropdown.tsx b/src/components/MoreOptionsDropdown.tsx new file mode 100644 index 0000000..ec28dc3 --- /dev/null +++ b/src/components/MoreOptionsDropdown.tsx @@ -0,0 +1,118 @@ +import React, { useEffect, useRef } from 'react'; +import { + Eye, + Download, + Send, + UserCheck, + UserX, + Trash2, + Mail, + TrendingUp, + Bell, + Award +} from 'lucide-react'; + +interface MoreOptionsDropdownProps { + itemType: 'customer' | 'instance' | 'invoice' | 'ticket'; + onViewPerformance: () => void; + onDownloadReport: () => void; + onSendNotification: () => void; + onChangeTier: () => void; + onDeactivate: () => void; + onDelete: () => void; + onSendMail: () => void; + onClose: () => void; +} + +const MoreOptionsDropdown: React.FC = ({ + itemType, + onViewPerformance, + onDownloadReport, + onSendNotification, + onChangeTier, + onDeactivate, + onDelete, + onSendMail, + onClose +}) => { + const dropdownRef = useRef(null); + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { + onClose(); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, [onClose]); + + const getOptions = () => { + switch (itemType) { + case 'customer': + return [ + { icon: Eye, label: 'View Performance', onClick: onViewPerformance }, + { icon: Download, label: 'Download Report', onClick: onDownloadReport }, + { icon: Bell, label: 'Send Notification', onClick: onSendNotification }, + { icon: Award, label: 'Change Tier', onClick: onChangeTier }, + { icon: Mail, label: 'Send Email', onClick: onSendMail }, + { icon: UserX, label: 'Deactivate', onClick: onDeactivate }, + { icon: Trash2, label: 'Delete', onClick: onDelete }, + ]; + case 'instance': + return [ + { icon: Eye, label: 'View Details', onClick: onViewPerformance }, + { icon: Download, label: 'Download Logs', onClick: onDownloadReport }, + { icon: Bell, label: 'Send Alert', onClick: onSendNotification }, + { icon: Trash2, label: 'Delete', onClick: onDelete }, + ]; + case 'invoice': + return [ + { icon: Eye, label: 'View Details', onClick: onViewPerformance }, + { icon: Download, label: 'Download PDF', onClick: onDownloadReport }, + { icon: Mail, label: 'Send Reminder', onClick: onSendMail }, + { icon: Trash2, label: 'Delete', onClick: onDelete }, + ]; + case 'ticket': + return [ + { icon: Eye, label: 'View Details', onClick: onViewPerformance }, + { icon: Download, label: 'Download Logs', onClick: onDownloadReport }, + { icon: Bell, label: 'Send Notification', onClick: onSendNotification }, + { icon: Award, label: 'Change Priority', onClick: onChangeTier }, + { icon: UserX, label: 'Close Ticket', onClick: onDeactivate }, + { icon: Mail, label: 'Send Email', onClick: onSendMail }, + { icon: Trash2, label: 'Delete', onClick: onDelete }, + ]; + default: + return []; + } + }; + + return ( +
+
+ {getOptions().map((option, index) => ( + + ))} +
+
+ ); +}; + +export default MoreOptionsDropdown; \ No newline at end of file diff --git a/src/components/PlaceholderPage.tsx b/src/components/PlaceholderPage.tsx new file mode 100644 index 0000000..27add1d --- /dev/null +++ b/src/components/PlaceholderPage.tsx @@ -0,0 +1,115 @@ +import React from 'react'; +import { + Construction, + ArrowLeft, + Home +} from 'lucide-react'; +import { Link } from 'react-router-dom'; + +interface PlaceholderPageProps { + title: string; + description: string; + icon?: React.ComponentType; +} + +const PlaceholderPage: React.FC = ({ title, description, icon: Icon = Construction }) => { + return ( +
+
+
+ {/* Icon */} +
+ +
+ + {/* Title */} +

+ {title} +

+ + {/* Description */} +

+ {description} +

+ + {/* Status Badge */} +
+ + Coming Soon +
+ + {/* Features Preview */} +
+
+
+ 1 +
+

+ Advanced Features +

+

+ Powerful tools and functionality designed for resellers +

+
+ +
+
+ 2 +
+

+ Real-time Analytics +

+

+ Live data and insights to optimize your business +

+
+ +
+
+ 3 +
+

+ Seamless Integration +

+

+ Connect with your existing tools and workflows +

+
+
+ + {/* Action Buttons */} +
+ + + Back to Dashboard + + +
+ + {/* Progress Indicator */} +
+
+
+
+
+
+

+ Our team is working hard to bring you this feature +

+
+
+
+
+ ); +}; + +export default PlaceholderPage; \ No newline at end of file diff --git a/src/components/forms/AddCustomerForm.tsx b/src/components/forms/AddCustomerForm.tsx new file mode 100644 index 0000000..a61522e --- /dev/null +++ b/src/components/forms/AddCustomerForm.tsx @@ -0,0 +1,253 @@ +import React, { useState } from 'react'; +import { X } from 'lucide-react'; +import { cn } from '../../utils/cn'; + +interface AddCustomerFormProps { + onSubmit: (data: any) => void; + onCancel: () => void; +} + +const AddCustomerForm: React.FC = ({ onSubmit, onCancel }) => { + const [formData, setFormData] = useState({ + name: '', + email: '', + phone: '', + company: '', + tier: 'silver', + address: '', + city: '', + state: '', + country: 'India', + postalCode: '' + }); + + const [errors, setErrors] = useState>({}); + + const validateForm = () => { + const newErrors: Record = {}; + + if (!formData.name.trim()) { + newErrors.name = 'Company name is required'; + } + + if (!formData.email.trim()) { + newErrors.email = 'Email is required'; + } else if (!/\S+@\S+\.\S+/.test(formData.email)) { + newErrors.email = 'Email is invalid'; + } + + if (!formData.phone.trim()) { + newErrors.phone = 'Phone number is required'; + } + + if (!formData.company.trim()) { + newErrors.company = 'Company name is required'; + } + + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }; + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + if (validateForm()) { + onSubmit(formData); + } + }; + + const handleChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; + setFormData(prev => ({ ...prev, [name]: value })); + if (errors[name]) { + setErrors(prev => ({ ...prev, [name]: '' })); + } + }; + + return ( +
+
+
+ + + {errors.name &&

{errors.name}

} +
+ +
+ + + {errors.company &&

{errors.company}

} +
+ +
+ + + {errors.email &&

{errors.email}

} +
+ +
+ + + {errors.phone &&

{errors.phone}

} +
+ +
+ + +
+ +
+ + +
+
+ +
+ +