feat: implement new branding and layout system with full test suite scaffolding
This commit is contained in:
parent
0907e407a5
commit
72ba3feb90
9 changed files with 3916 additions and 204 deletions
86
guide.md
Normal file
86
guide.md
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
# Supplier Portal - View & Navigation Guide
|
||||
|
||||
This guide explains how to start the development server, navigate the screens, and view the onboarding flows and simulated dashboard preview.
|
||||
|
||||
## 1. Running the Portal Locally
|
||||
|
||||
To start the Vite development server, run the following command in the project root:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
By default, the server runs at **[http://localhost:5173/](http://localhost:5173/)** (or the port specified in your console).
|
||||
|
||||
---
|
||||
|
||||
## 2. Page Routing & State Mapping
|
||||
|
||||
The portal uses a client-side state router mapping component views to logical routes:
|
||||
|
||||
| Route State | Component/View Description | Navigable Via |
|
||||
| :--- | :--- | :--- |
|
||||
| `home` | **Landing Page**: Brand value proposition, hero banner, primary action CTAs. | Logo click, "Platform" header link. |
|
||||
| `about` | **About Us**: Organization mission details and team profiles. | "Success Stories" header link. |
|
||||
| `contact` | **Contact Support**: "Get in Touch" inquiry form and help desk coordinates. | "Support" header link. |
|
||||
| `login` | **Supplier Login**: Account login via password credentials or OTP mock. | "Login" header button. |
|
||||
| `signup` | **Supplier Registration**: Fields for business email, password, and GSTIN. | "Get Started" header button. |
|
||||
| `profile-completion` | **Profile Wizard (Step 2)**: Logo picker, bio, and interactive GSTIN validation. | Auto-redirect on Signup form submit. |
|
||||
| `confirmation` | **Dashboard Preview (Step 3)**: Payout status notifications and mock graphs. | Auto-redirect on Profile Completion save. |
|
||||
| `dashboard` | **Seller Dashboard & Tools**: Overview performance metrics, single/bulk product uploads, orders tracking log, returns approvals, wallet payouts, and settings configuration. | Click "Go to Dashboard" button in confirmation, or submit Login form. |
|
||||
|
||||
---
|
||||
|
||||
## 3. Navigating the Phase 1 Onboarding Flow
|
||||
|
||||
You can step through the interactive onboarding flow sequentially to experience the full walkthrough:
|
||||
|
||||
### Step A: The Landing Page
|
||||
- Open the application. You will see the **Global Artisans Hub** landing page with the primary theme (Deep Navy Blue headers/footers, Amber Gold accents, and Warm Cream backgrounds).
|
||||
- Click **Platform**, **Success Stories** (About Us), or **Support** (Contact Us) in the header navigation to view the respective information layouts.
|
||||
|
||||
### Step B: Account Registration
|
||||
1. Click **Get Started** in the header.
|
||||
2. The view will switch to the **Register** tab.
|
||||
3. Fill in the required fields:
|
||||
- **Business Email**
|
||||
- **Mobile Number**
|
||||
- **Create Password**
|
||||
- **GSTIN / Tax ID**
|
||||
4. Click **Register Business**.
|
||||
|
||||
### Step C: Profile Completion (Step 2 of 3)
|
||||
1. You will be taken to the **Complete Your Supplier Profile** wizard.
|
||||
2. Enter a **Store Display Name**.
|
||||
3. (Optional) Choose an image file to upload. It will immediately show an avatar logo preview.
|
||||
4. Enter an **About Your Craft / Business** description.
|
||||
5. Fill in the **Business Address for Pickup** fields (Street Address, City, State, Pincode).
|
||||
6. Enter a GSTIN, and click **Verify GSTIN**. The button will show a loading spinner, then update to **Verified ✓** status.
|
||||
7. Click **Save & Continue**.
|
||||
|
||||
### Step D: Onboarding Confirmation (Step 3 of 3)
|
||||
- You will arrive at the **Welcome to Global Artisans Hub!** confirmation view.
|
||||
- Click **Go to Dashboard (Preview Only)** to go to the live seller portal dashboard page.
|
||||
- Alternatively, you can directly access the dashboard page by going to **Login** -> enter credentials -> click **Secure Login**.
|
||||
|
||||
### Step E: Navigating the Seller Dashboard & Tools
|
||||
Once inside the active dashboard (`/dashboard`), use the left sidebar menu to navigate the tools:
|
||||
1. **Overview & Analytics**: View Sales, net margins, stock, and returns. Toggle the date filters (Year, Week, Day, Custom Date) to see the metrics update dynamically.
|
||||
2. **Manage Products**:
|
||||
- View your active inventory listing.
|
||||
- Fill out the **Upload Product** form on the right to add a product or click **Edit** on a row to modify it.
|
||||
- Click the bulk upload zone to simulate importing products via CSV files.
|
||||
3. **Orders & Transit**: Track active orders, status (Ready to Ship, Shipped, Delivered), carriers, and tracking coordinates.
|
||||
4. **Returns Management**: View buyer return requests. Click **Approve** or **Reject** to update their logistics status in the warehouse transit log.
|
||||
5. **Wallet & Payouts**: Enter an amount to process a withdrawal from your outstanding ready balance.
|
||||
6. **Store Settings**: Update your store name, logo file, bio, and pickup coordinates.
|
||||
|
||||
---
|
||||
|
||||
## 4. Run Automated Component Tests
|
||||
|
||||
To run the Vitest unit tests verifying these navigation flows and tools, execute:
|
||||
|
||||
```bash
|
||||
npm run test
|
||||
```
|
||||
1198
package-lock.json
generated
1198
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -7,7 +7,7 @@
|
|||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"lint": "oxlint",
|
||||
"test": "echo 'No tests specified' && exit 0",
|
||||
"test": "vitest run",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
@ -15,12 +15,17 @@
|
|||
"react-dom": "^19.2.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/dom": "^10.4.1",
|
||||
"@testing-library/jest-dom": "^7.0.0",
|
||||
"@testing-library/react": "^16.3.2",
|
||||
"@types/node": "^24.13.3",
|
||||
"@types/react": "^19.2.17",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@vitejs/plugin-react": "^6.0.4",
|
||||
"jsdom": "^30.0.1",
|
||||
"oxlint": "^1.75.0",
|
||||
"typescript": "~6.0.2",
|
||||
"vite": "^8.2.0"
|
||||
"vite": "^8.2.0",
|
||||
"vitest": "^4.1.10"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
133
src/App.test.tsx
Normal file
133
src/App.test.tsx
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
import { render, screen, fireEvent } from '@testing-library/react'
|
||||
import { describe, it, expect, vi, beforeAll } from 'vitest'
|
||||
import App from './App'
|
||||
|
||||
// Mock window.scrollTo since jsdom does not implement it
|
||||
beforeAll(() => {
|
||||
window.scrollTo = vi.fn()
|
||||
})
|
||||
|
||||
describe('Supplier Portal Onboarding & Active Dashboard Tests', () => {
|
||||
it('renders landing page with correct primary titles', () => {
|
||||
render(<App />)
|
||||
|
||||
expect(screen.getByText(/Sell Globally\./i)).toBeInTheDocument()
|
||||
expect(screen.getByText(/Celebrate Craft\./i)).toBeInTheDocument()
|
||||
|
||||
expect(screen.getByRole('heading', { name: /^Expand Your Reach$/i })).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('navigates to Support page and handles contact form submission', () => {
|
||||
render(<App />)
|
||||
|
||||
const supportBtn = screen.getByText('Support')
|
||||
fireEvent.click(supportBtn)
|
||||
|
||||
const nameInput = screen.getByLabelText(/Full Name \*/i)
|
||||
const emailInput = screen.getByLabelText(/Email Address \*/i)
|
||||
const msgInput = screen.getByLabelText(/Message \*/i)
|
||||
|
||||
fireEvent.change(nameInput, { target: { value: 'John Doe' } })
|
||||
fireEvent.change(emailInput, { target: { value: 'john@example.com' } })
|
||||
fireEvent.change(msgInput, { target: { value: 'Hello support!' } })
|
||||
|
||||
const alertMock = vi.spyOn(window, 'alert').mockImplementation(() => {})
|
||||
|
||||
const submitBtn = screen.getByText('Submit Inquiry')
|
||||
fireEvent.click(submitBtn)
|
||||
|
||||
expect(alertMock).toHaveBeenCalled()
|
||||
alertMock.mockRestore()
|
||||
})
|
||||
|
||||
it('signs up successfully and completes profile to active dashboard', async () => {
|
||||
render(<App />)
|
||||
|
||||
const getStartedBtn = screen.getByRole('button', { name: /^Get Started$/i })
|
||||
fireEvent.click(getStartedBtn)
|
||||
|
||||
const emailInput = screen.getByLabelText(/Business Email \*/i)
|
||||
const phoneInput = screen.getByLabelText(/Mobile Number \*/i)
|
||||
const passInput = screen.getByLabelText(/Create Password \*/i)
|
||||
const taxInput = screen.getByLabelText(/GSTIN \/ Tax ID \*/i)
|
||||
|
||||
fireEvent.change(emailInput, { target: { value: 'seller@example.com' } })
|
||||
fireEvent.change(phoneInput, { target: { value: '9876543210' } })
|
||||
fireEvent.change(passInput, { target: { value: 'password123' } })
|
||||
fireEvent.change(taxInput, { target: { value: '29AAAAA1111A1Z1' } })
|
||||
|
||||
const registerBtn = screen.getByRole('button', { name: /Register Business/i })
|
||||
fireEvent.click(registerBtn)
|
||||
|
||||
expect(screen.getByText('Step 2 of 3: Business Details')).toBeInTheDocument()
|
||||
|
||||
const storeNameInput = screen.getByLabelText(/Store Display Name \*/i)
|
||||
fireEvent.change(storeNameInput, { target: { value: 'Crafty Store' } })
|
||||
|
||||
const bioText = screen.getByLabelText(/About Your Craft \/ Business \*/i)
|
||||
fireEvent.change(bioText, { target: { value: 'Handmade pottery.' } })
|
||||
|
||||
const streetInput = screen.getByPlaceholderText('Street Address')
|
||||
const cityInput = screen.getByPlaceholderText('City')
|
||||
const stateInput = screen.getByPlaceholderText('State')
|
||||
const pincodeInput = screen.getByPlaceholderText('Pincode')
|
||||
|
||||
fireEvent.change(streetInput, { target: { value: '123 Loom Lane' } })
|
||||
fireEvent.change(cityInput, { target: { value: 'Handloom City' } })
|
||||
fireEvent.change(stateInput, { target: { value: 'Karnataka' } })
|
||||
fireEvent.change(pincodeInput, { target: { value: '560001' } })
|
||||
|
||||
const saveBtn = screen.getByRole('button', { name: /Save & Continue/i })
|
||||
fireEvent.click(saveBtn)
|
||||
|
||||
// Redirects directly to Dashboard Page
|
||||
expect(screen.getByText('Performance Analytics')).toBeInTheDocument()
|
||||
expect(screen.getByText(/Total Sales Volume/i)).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('logs in successfully and navigates dashboard tools', () => {
|
||||
render(<App />)
|
||||
|
||||
const loginBtn = screen.getByRole('button', { name: /^Login$/i })
|
||||
fireEvent.click(loginBtn)
|
||||
|
||||
const emailInput = screen.getByLabelText(/Business Email/i)
|
||||
const passInput = screen.getByLabelText(/Password/i)
|
||||
|
||||
fireEvent.change(emailInput, { target: { value: 'seller@example.com' } })
|
||||
fireEvent.change(passInput, { target: { value: 'password123' } })
|
||||
|
||||
const submitBtn = screen.getByRole('button', { name: /Secure Login/i })
|
||||
fireEvent.click(submitBtn)
|
||||
|
||||
// Check dashboard rendering
|
||||
expect(screen.getByText('Performance Analytics')).toBeInTheDocument()
|
||||
|
||||
// Check Sidebar Tab click: Manage Products
|
||||
const productsTabBtn = screen.getByText(/Manage Products/i)
|
||||
fireEvent.click(productsTabBtn)
|
||||
|
||||
expect(screen.getByText(/Active Inventory/i)).toBeInTheDocument()
|
||||
expect(screen.getByText(/Upload New Product/i)).toBeInTheDocument()
|
||||
|
||||
// Check Sidebar Tab click: Orders & Transit
|
||||
const ordersTabBtn = screen.getByText(/Orders & Transit/i)
|
||||
fireEvent.click(ordersTabBtn)
|
||||
|
||||
expect(screen.getByText(/Active Orders & Payout status/i)).toBeInTheDocument()
|
||||
|
||||
// Check Sidebar Tab click: Returns Management
|
||||
const returnsTabBtn = screen.getByText(/Returns Management/i)
|
||||
fireEvent.click(returnsTabBtn)
|
||||
|
||||
expect(screen.getByText(/Returns & Quality Assurance Center/i)).toBeInTheDocument()
|
||||
expect(screen.getByText(/Items Requested for Return/i)).toBeInTheDocument()
|
||||
|
||||
// Check Sidebar Tab click: Wallet & Payouts
|
||||
const walletTabBtn = screen.getByText(/Wallet & Payouts/i)
|
||||
fireEvent.click(walletTabBtn)
|
||||
|
||||
expect(screen.getByText(/Wallet & Payout Portal/i)).toBeInTheDocument()
|
||||
expect(screen.getByText(/Outstanding Ready Balance/i)).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
1426
src/App.tsx
1426
src/App.tsx
File diff suppressed because it is too large
Load diff
116
src/config.ts
Normal file
116
src/config.ts
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
export const CONFIG = {
|
||||
companyName: 'Global Artisans Hub',
|
||||
logoLetter: 'A',
|
||||
supportEmail: 'support@globalartisanshub.com',
|
||||
supportPhone: '+1 (800) 555-0199',
|
||||
address: '1775 Artisan Ridge Rd, Craftsville, CA 90210',
|
||||
|
||||
hero: {
|
||||
title: 'Sell Globally. Celebrate Craft.',
|
||||
subtitle: 'Join the premier marketplace for artisans and reach millions of conscious customers worldwide.',
|
||||
image: 'https://images.unsplash.com/photo-1513519245088-0e12902e5a38?auto=format&fit=crop&q=80&w=800'
|
||||
},
|
||||
|
||||
features: [
|
||||
{
|
||||
icon: '🌍',
|
||||
title: 'Expand Your Reach',
|
||||
description: 'Globalize your marketplace for artisans and reach millions of conscious customers.'
|
||||
},
|
||||
{
|
||||
icon: '🛠️',
|
||||
title: 'Easy Inventory Tools',
|
||||
description: 'Dashboard marketplace to manage and update listings with ease.'
|
||||
},
|
||||
{
|
||||
icon: '🛡️',
|
||||
title: 'Secure Payments',
|
||||
description: 'Secure payments and reliable payout processing directly to your bank account.'
|
||||
}
|
||||
],
|
||||
|
||||
featuredArtisan: {
|
||||
name: 'Naomi K.',
|
||||
role: 'Master Weaver',
|
||||
quote: '"Joining the Global Artisans Hub has transformed our community. We went from selling at local weekend fairs to serving buyers worldwide, preserving our traditional craft."',
|
||||
avatar: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?auto=format&fit=crop&q=80&w=300'
|
||||
},
|
||||
|
||||
about: {
|
||||
missionTitle: 'Our Mission',
|
||||
missionDescription: 'We aim to empower artisan communities worldwide, providing a premium, transparent, and direct channel to conscious consumers globally.',
|
||||
team: [
|
||||
{
|
||||
name: 'Sunder R.',
|
||||
role: 'Founder & CEO',
|
||||
avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?auto=format&fit=crop&q=80&w=150'
|
||||
},
|
||||
{
|
||||
name: 'Sarayu N.',
|
||||
role: 'Head of Artisan Partnerships',
|
||||
avatar: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?auto=format&fit=crop&q=80&w=150'
|
||||
},
|
||||
{
|
||||
name: 'John C.',
|
||||
role: 'Operations Lead',
|
||||
avatar: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?auto=format&fit=crop&q=80&w=150'
|
||||
},
|
||||
{
|
||||
name: 'Elena P.',
|
||||
role: 'Tech Lead',
|
||||
avatar: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?auto=format&fit=crop&q=80&w=150'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// --- Mock Dashboard Data ---
|
||||
dashboardData: {
|
||||
filters: {
|
||||
year: {
|
||||
totalSales: 45280.00,
|
||||
totalEarned: 38488.00,
|
||||
stockDetails: 342,
|
||||
returnedItems: 12,
|
||||
chartValues: [30, 45, 35, 60, 75, 55, 80, 65, 90, 70, 85, 95] // Monthly values
|
||||
},
|
||||
week: {
|
||||
totalSales: 1240.00,
|
||||
totalEarned: 1054.00,
|
||||
stockDetails: 342,
|
||||
returnedItems: 1,
|
||||
chartValues: [40, 60, 50, 70, 45, 80, 90] // Daily values
|
||||
},
|
||||
day: {
|
||||
totalSales: 210.00,
|
||||
totalEarned: 178.50,
|
||||
stockDetails: 342,
|
||||
returnedItems: 0,
|
||||
chartValues: [10, 15, 20, 25, 35, 45, 30, 20, 10] // Hourly intervals
|
||||
},
|
||||
custom: {
|
||||
totalSales: 5820.00,
|
||||
totalEarned: 4947.00,
|
||||
stockDetails: 342,
|
||||
returnedItems: 3,
|
||||
chartValues: [50, 40, 65, 55, 75, 85, 60, 70] // Range intervals
|
||||
}
|
||||
},
|
||||
|
||||
initialProducts: [
|
||||
{ id: '1', title: 'Handwoven Indigo Shawl', category: 'Apparel', price: 85.00, stock: 45, sku: 'SHAWL-IND-01', image: 'https://images.unsplash.com/photo-1544022613-e87ca75a784a?auto=format&fit=crop&q=80&w=100' },
|
||||
{ id: '2', title: 'Terracotta Clay Pot Set', category: 'Home Decor', price: 42.00, stock: 24, sku: 'POT-TER-02', image: 'https://images.unsplash.com/photo-1612196808214-b8e1d6145a8c?auto=format&fit=crop&q=80&w=100' },
|
||||
{ id: '3', title: 'Brass Elephant Figurine', category: 'Sculptures', price: 120.00, stock: 12, sku: 'FIG-ELE-03', image: 'https://images.unsplash.com/photo-1590736969955-71cc94801759?auto=format&fit=crop&q=80&w=100' }
|
||||
],
|
||||
|
||||
initialOrders: [
|
||||
{ id: 'ORD-8492', date: '2026-08-07', item: 'Handwoven Indigo Shawl', quantity: 1, customer: 'Alice Vance', total: 85.00, status: 'Ready to Ship', carrier: 'DHL Express', tracking: 'DHL-9284102', eta: '2026-08-10' },
|
||||
{ id: 'ORD-8488', date: '2026-08-06', item: 'Terracotta Clay Pot Set', quantity: 2, customer: 'David Miller', total: 84.00, status: 'Shipped', carrier: 'FedEx Ground', tracking: 'FDX-5829104', eta: '2026-08-09' },
|
||||
{ id: 'ORD-8471', date: '2026-08-05', item: 'Brass Elephant Figurine', quantity: 1, customer: 'Sarah Connor', total: 120.00, status: 'Delivered', carrier: 'Local Express', tracking: 'LOC-1294801', eta: '2026-08-07' }
|
||||
],
|
||||
|
||||
initialReturns: [
|
||||
{ id: 'RET-019', orderId: 'ORD-8411', customer: 'Lillian G.', item: 'Handwoven Indigo Shawl', reason: 'Slightly different shade than pictured', status: 'Pending Approval', image: 'https://images.unsplash.com/photo-1544022613-e87ca75a784a?auto=format&fit=crop&q=80&w=100', returningTracking: 'DHL-RET-9031' },
|
||||
{ id: 'RET-018', orderId: 'ORD-8390', customer: 'Robert H.', item: 'Terracotta Clay Pot Set', reason: 'Damaged in transit', status: 'In Transit', image: 'https://images.unsplash.com/photo-1612196808214-b8e1d6145a8c?auto=format&fit=crop&q=80&w=100', returningTracking: 'FDX-RET-2940' }
|
||||
]
|
||||
}
|
||||
}
|
||||
1112
src/index.css
1112
src/index.css
File diff suppressed because it is too large
Load diff
1
src/setupTests.ts
Normal file
1
src/setupTests.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
import '@testing-library/jest-dom'
|
||||
13
vitest.config.ts
Normal file
13
vitest.config.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import { defineConfig, mergeConfig } from 'vitest/config'
|
||||
import viteConfig from './vite.config'
|
||||
|
||||
export default mergeConfig(
|
||||
viteConfig,
|
||||
defineConfig({
|
||||
test: {
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
setupFiles: './src/setupTests.ts',
|
||||
},
|
||||
})
|
||||
)
|
||||
Loading…
Reference in a new issue