diff --git a/eslint.config.js b/eslint.config.js
index f24d543..6808ce1 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -26,14 +26,11 @@ export default tseslint.config(
},
rules: {
...reactHooks.configs.recommended.rules,
- "react-refresh/only-export-components": [
- "warn",
- { allowConstantExport: true },
- ],
+ "react-refresh/only-export-components": ["warn", { allowConstantExport: true }],
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-unused-vars": [
"error",
- { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }
+ { argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
],
},
},
diff --git a/playwright.config.ts b/playwright.config.ts
index c8da832..c6fea3b 100644
--- a/playwright.config.ts
+++ b/playwright.config.ts
@@ -22,7 +22,7 @@ export default defineConfig({
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: "on-first-retry",
-
+
/* Disable video/screenshots by default for speed, can be enabled on retry */
screenshot: "only-on-failure",
},
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e25753c..399d20e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -87,6 +87,9 @@ importers:
'@eslint/js':
specifier: ^9.39.4
version: 9.39.4
+ '@playwright/test':
+ specifier: ^1.60.0
+ version: 1.60.0
'@tailwindcss/typography':
specifier: ^0.5.19
version: 0.5.19(tailwindcss@4.3.0)
@@ -732,6 +735,11 @@ packages:
'@open-draft/until@2.1.0':
resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==}
+ '@playwright/test@1.60.0':
+ resolution: {integrity: sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag==}
+ engines: {node: '>=18'}
+ hasBin: true
+
'@radix-ui/number@1.1.1':
resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==}
@@ -2671,6 +2679,11 @@ packages:
resolution: {integrity: sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==}
engines: {node: '>=14.14'}
+ fsevents@2.3.2:
+ resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -3358,6 +3371,16 @@ packages:
resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==}
engines: {node: '>=16.20.0'}
+ playwright-core@1.60.0:
+ resolution: {integrity: sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ playwright@1.60.0:
+ resolution: {integrity: sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==}
+ engines: {node: '>=18'}
+ hasBin: true
+
postcss-selector-parser@6.0.10:
resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==}
engines: {node: '>=4'}
@@ -4667,6 +4690,10 @@ snapshots:
'@open-draft/until@2.1.0': {}
+ '@playwright/test@1.60.0':
+ dependencies:
+ playwright: 1.60.0
+
'@radix-ui/number@1.1.1': {}
'@radix-ui/primitive@1.1.3': {}
@@ -6618,6 +6645,9 @@ snapshots:
jsonfile: 6.2.1
universalify: 2.0.1
+ fsevents@2.3.2:
+ optional: true
+
fsevents@2.3.3:
optional: true
@@ -7180,6 +7210,14 @@ snapshots:
pkce-challenge@5.0.1: {}
+ playwright-core@1.60.0: {}
+
+ playwright@1.60.0:
+ dependencies:
+ playwright-core: 1.60.0
+ optionalDependencies:
+ fsevents: 2.3.2
+
postcss-selector-parser@6.0.10:
dependencies:
cssesc: 3.0.0
diff --git a/src/config/sidebar.config.ts b/src/config/sidebar.config.ts
index 5bd94f2..6cc836d 100644
--- a/src/config/sidebar.config.ts
+++ b/src/config/sidebar.config.ts
@@ -34,7 +34,7 @@ export const sidebarItems = [
},
{
title: "Teams",
- path: "/org/teams",
+ path: "/member/teams",
icon: Users,
},
{
diff --git a/src/features/Auth/v1/Pages/LoginPage.tsx b/src/features/Auth/v1/Pages/LoginPage.tsx
index 360e1c3..236b270 100644
--- a/src/features/Auth/v1/Pages/LoginPage.tsx
+++ b/src/features/Auth/v1/Pages/LoginPage.tsx
@@ -51,7 +51,9 @@ const LoginPage = () => {
password,
});
- const Role = response.data.role;
+ console.log("Login successful: come from login page --->", response);
+
+ const Role = response.data.FindUser.role;
if (Role === "organization") {
navigate("/org/dashboard");
diff --git a/src/features/Auth/v1/Store/Organization.Store.ts b/src/features/Auth/v1/Store/Organization.Store.ts
index 2da8ef9..447362b 100644
--- a/src/features/Auth/v1/Store/Organization.Store.ts
+++ b/src/features/Auth/v1/Store/Organization.Store.ts
@@ -2,7 +2,6 @@ import { create } from "zustand";
import { persist } from "zustand/middleware";
import { CommunitySchema } from "../Types/Organization.Type";
-
const useOrganizationStore = create<{
organization: CommunitySchema | null;
setOrganization: (organization: CommunitySchema) => void;
diff --git a/src/features/Auth/v1/hooks/useAuth.ts b/src/features/Auth/v1/hooks/useAuth.ts
index ed568c6..f42900a 100644
--- a/src/features/Auth/v1/hooks/useAuth.ts
+++ b/src/features/Auth/v1/hooks/useAuth.ts
@@ -5,8 +5,9 @@ import AUTH_ENDPOINTS from "../Constant/Auth.Endpoint.Constant";
import useAuthStore from "../Store/Auth.Store";
import useOrganizationStore from "../Store/Organization.Store";
-const baseUrl =
- import.meta.env.VITE_API_BASE_URL || "http://localhost:8000/api/v1";
+import usePermissionStore from "@/features/Permission/Store/Permission.Store";
+
+const baseUrl = import.meta.env.VITE_API_BASE_URL || "http://localhost:8000/api/v1";
// =========================
// GET ORGANIZATION
@@ -18,13 +19,14 @@ const useGetOrganizationMutation = () => {
mutationFn: async (_id: string) => {
const response = await api.get(
- `${baseUrl}${AUTH_ENDPOINTS.GET_ORGANIZATION_BY_ID}?ownerId=${_id}`
+ `${baseUrl}${AUTH_ENDPOINTS.GET_ORGANIZATION_BY_ID}?ownerId=${_id}`,
);
return response.data;
},
onSuccess: (response) => {
+ console.log("Organization fetched successfully:", response.data);
useOrganizationStore.getState().setOrganization(response.data);
},
@@ -44,23 +46,17 @@ const useLoginMutation = () => {
return useMutation({
mutationKey: ["login"],
- mutationFn: async (credentials: {
- email: string;
- password: string;
- }) => {
- const response = await api.post(
- `${baseUrl}${AUTH_ENDPOINTS.LOGIN}`,
- credentials
- );
+ mutationFn: async (credentials: { email: string; password: string }) => {
+ const response = await api.post(`${baseUrl}${AUTH_ENDPOINTS.LOGIN}`, credentials);
return response.data;
},
onSuccess: async (response) => {
- const user = response.data;
+ const user = response.data.FindUser;
const token = response.token;
- console.log("Login successful:", user);
+ usePermissionStore.getState().setPermissions(response.data.perms);
// Save auth first
useAuthStore.getState().setAuthData(user, token);
@@ -87,4 +83,4 @@ export const useAuth = () => {
return {
loginMutation,
};
-};
\ No newline at end of file
+};
diff --git a/src/features/Billing/v1/components/analytics/UsageCharts.tsx b/src/features/Billing/v1/components/analytics/UsageCharts.tsx
index 853b609..3d15702 100644
--- a/src/features/Billing/v1/components/analytics/UsageCharts.tsx
+++ b/src/features/Billing/v1/components/analytics/UsageCharts.tsx
@@ -32,7 +32,10 @@ export default function UsageCharts() {
))}
diff --git a/src/features/Billing/v1/components/layout/AddFundsModal.tsx b/src/features/Billing/v1/components/layout/AddFundsModal.tsx
index 4fe2792..d791b6d 100644
--- a/src/features/Billing/v1/components/layout/AddFundsModal.tsx
+++ b/src/features/Billing/v1/components/layout/AddFundsModal.tsx
@@ -12,7 +12,12 @@ import {
} from "lucide-react";
import { MIN_ADD_RUPEES } from "../../constants/billing.constants";
import { useAddFunds } from "../../hooks/useWallet";
-import { buildAddFundsPreview, formatCredits, formatRupees, validateMinAddFunds } from "../../utils/credits";
+import {
+ buildAddFundsPreview,
+ formatCredits,
+ formatRupees,
+ validateMinAddFunds,
+} from "../../utils/credits";
import type { AddFundsPayload, PaymentState } from "../../Billing.types";
import Input from "@/Component/ui/Input";
@@ -118,10 +123,17 @@ export default function AddFundsModal({ isOpen, onClose, onSuccess, onError }: P
}}
>
-
+
Community wallet
-
+
Add Funds
@@ -141,7 +153,7 @@ export default function AddFundsModal({ isOpen, onClose, onSuccess, onError }: P
setAmountStr(digitsOnly.replace(/^0+(?=\d)/, ""));
}}
leftIcon={}
- error={validation.valid ? undefined : validation.error ?? undefined}
+ error={validation.valid ? undefined : (validation.error ?? undefined)}
className="w-full !mb-0"
inputClassName="!text-lg !font-bold"
/>
@@ -151,14 +163,26 @@ export default function AddFundsModal({ isOpen, onClose, onSuccess, onError }: P
-
+
0 ? `+${formatCredits(preview.bonusCredits)}` : "0"}
+ value={
+ preview.bonusCredits > 0 ? `+${formatCredits(preview.bonusCredits)}` : "0"
+ }
+ />
+
-
@@ -195,23 +219,42 @@ export default function AddFundsModal({ isOpen, onClose, onSuccess, onError }: P
-
+
{preview.bonusCredits > 0 ? (
-
+
) : null}
-
-
+
+
@@ -243,7 +286,10 @@ export default function AddFundsModal({ isOpen, onClose, onSuccess, onError }: P
)}
-
+
Encrypted payment simulation
@@ -257,7 +303,10 @@ export default function AddFundsModal({ isOpen, onClose, onSuccess, onError }: P
function SectionTitle({ title }: { title: string }) {
return (
-
+
{title}
);
@@ -280,10 +329,16 @@ function GeneratedCreditTile({
borderColor: accent ? "var(--cd-primary)" : "var(--cd-border-subtle)",
}}
>
-
+
{label}
-
+
{value}
@@ -328,7 +383,11 @@ function SuccessContent({ credits, onClose }: { credits: number; onClose: () =>
{formatCredits(credits)} credits have been added to your wallet.
-
diff --git a/src/features/Billing/v1/components/layout/LowBalanceModal.tsx b/src/features/Billing/v1/components/layout/LowBalanceModal.tsx
index 312b13a..415fd10 100644
--- a/src/features/Billing/v1/components/layout/LowBalanceModal.tsx
+++ b/src/features/Billing/v1/components/layout/LowBalanceModal.tsx
@@ -11,7 +11,13 @@ interface Props {
onAddFunds?: () => void;
}
-export default function LowBalanceModal({ isOpen, availableCredits, threshold, onDismiss, onAddFunds }: Props) {
+export default function LowBalanceModal({
+ isOpen,
+ availableCredits,
+ threshold,
+ onDismiss,
+ onAddFunds,
+}: Props) {
const navigate = useNavigate();
const ref = useRef(null);
diff --git a/src/features/Billing/v1/components/layout/PayoutAccountDetails.tsx b/src/features/Billing/v1/components/layout/PayoutAccountDetails.tsx
index 711312a..9d642f0 100644
--- a/src/features/Billing/v1/components/layout/PayoutAccountDetails.tsx
+++ b/src/features/Billing/v1/components/layout/PayoutAccountDetails.tsx
@@ -12,7 +12,7 @@ export default function PayoutAccountDetails() {
accountHolder: "",
bankName: "",
accountNumber: "",
- ifsc: ""
+ ifsc: "",
});
const handleSave = () => {
@@ -40,10 +40,17 @@ export default function PayoutAccountDetails() {
>
-
Payout Account Details
-
Receive funds and revenue securely to your bank account.
+
+ Payout Account Details
+
+
+ Receive funds and revenue securely to your bank account.
+
-
@@ -93,8 +100,13 @@ export default function PayoutAccountDetails() {
disabled={loading || saved}
className="cd-btn cd-btn-secondary px-6 py-2.5 rounded-xl text-sm font-bold flex items-center gap-2"
>
- {loading ?
:
- saved ?
:
}
+ {loading ? (
+
+ ) : saved ? (
+
+ ) : (
+
+ )}
{saved ? "Saved" : "Save Details"}
diff --git a/src/features/Billing/v1/components/layout/QuickRecharge.tsx b/src/features/Billing/v1/components/layout/QuickRecharge.tsx
index a6b4046..c460434 100644
--- a/src/features/Billing/v1/components/layout/QuickRecharge.tsx
+++ b/src/features/Billing/v1/components/layout/QuickRecharge.tsx
@@ -22,7 +22,7 @@ export default function QuickRecharge() {
await addFunds.mutateAsync({
amountRupees: amt,
paymentMethod: "upi",
- idempotencyKey: `quick-${crypto.randomUUID()}`
+ idempotencyKey: `quick-${crypto.randomUUID()}`,
});
addToast("success", "Recharge Successful", `Added ${formatCredits(amt * 10)} credits.`);
} catch {
@@ -43,10 +43,17 @@ export default function QuickRecharge() {
>
-
Quick Recharge
-
Top up instantly with UPI.
+
+ Quick Recharge
+
+
+ Top up instantly with UPI.
+
-
@@ -92,11 +99,15 @@ export default function QuickRecharge() {
>
You get
- {formatCredits(preview.totalCredits)} cr
+
+ {formatCredits(preview.totalCredits)} cr
+
Payable
- {formatRupees(preview.totalPayableRupees)}
+
+ {formatRupees(preview.totalPayableRupees)}
+
@@ -105,7 +116,11 @@ export default function QuickRecharge() {
disabled={addFunds.isPending}
className="cd-btn cd-btn-primary w-full rounded-xl py-2.5 font-bold flex justify-center items-center gap-2"
>
- {addFunds.isPending ? : }
+ {addFunds.isPending ? (
+
+ ) : (
+
+ )}
{addFunds.isPending ? "Processing..." : `Pay ${formatRupees(preview.totalPayableRupees)}`}
diff --git a/src/features/Billing/v1/components/layout/WalletHeader.tsx b/src/features/Billing/v1/components/layout/WalletHeader.tsx
index 7eacf64..ed10375 100644
--- a/src/features/Billing/v1/components/layout/WalletHeader.tsx
+++ b/src/features/Billing/v1/components/layout/WalletHeader.tsx
@@ -79,7 +79,10 @@ export default function WalletHeader({ wallet, activeTab, onTabChange, onAddFund
color: isActive ? "var(--cd-text)" : "var(--cd-text-muted)",
}}
>
-
+
{tab.label}
);
@@ -114,7 +117,10 @@ function WalletTitle({ wallet }: { wallet: WalletType | undefined }) {
>
Community Wallet
-
+
{wallet ? (
<>
diff --git a/src/features/Billing/v1/components/layout/WalletStatsGrid.tsx b/src/features/Billing/v1/components/layout/WalletStatsGrid.tsx
index ab9cd52..e354c26 100644
--- a/src/features/Billing/v1/components/layout/WalletStatsGrid.tsx
+++ b/src/features/Billing/v1/components/layout/WalletStatsGrid.tsx
@@ -54,7 +54,10 @@ export default function WalletStatsGrid({ wallet, burnRatePerDay = 52 }: Props)
{stat.label}
-
+
@@ -66,11 +69,12 @@ export default function WalletStatsGrid({ wallet, burnRatePerDay = 52 }: Props)
>
{stat.val}
-
credits
+
+ credits
+
))}
);
}
-
diff --git a/src/features/Billing/v1/components/table/TeamUsageTable.tsx b/src/features/Billing/v1/components/table/TeamUsageTable.tsx
index e5b8f5c..0ba1f76 100644
--- a/src/features/Billing/v1/components/table/TeamUsageTable.tsx
+++ b/src/features/Billing/v1/components/table/TeamUsageTable.tsx
@@ -49,14 +49,20 @@ export default function TeamUsageTable() {
}
return (
-
+
|
Member
|
-
+ |
Credits Used
|
@@ -71,7 +77,10 @@ export default function TeamUsageTable() {
className="border-t hover:bg-[var(--cd-hover)]"
style={{ borderColor: "var(--cd-border-subtle)" }}
>
- |
+ |
diff --git a/src/features/Billing/v1/components/table/TransactionTable.tsx b/src/features/Billing/v1/components/table/TransactionTable.tsx
index 4308e04..6fd7314 100644
--- a/src/features/Billing/v1/components/table/TransactionTable.tsx
+++ b/src/features/Billing/v1/components/table/TransactionTable.tsx
@@ -36,10 +36,16 @@ export default function TransactionTable({ transactions, isLoading }: Props) {
|
Source
|
-
+ |
Credits
|
-
+ |
Balance
|
diff --git a/src/features/Billing/v1/hooks/useAddFunds.ts b/src/features/Billing/v1/hooks/useAddFunds.ts
index f6c5915..d307f3e 100644
--- a/src/features/Billing/v1/hooks/useAddFunds.ts
+++ b/src/features/Billing/v1/hooks/useAddFunds.ts
@@ -20,7 +20,7 @@ export function useAddFunds() {
const result = walletStore.addFunds(amountRupees, idempotencyKey);
const preview = buildAddFundsPreview(amountRupees);
-
+
return {
preview,
transaction: result.transaction,
@@ -34,4 +34,3 @@ export function useAddFunds() {
},
});
}
-
diff --git a/src/features/Billing/v1/hooks/useBillingGate.ts b/src/features/Billing/v1/hooks/useBillingGate.ts
index 6ad3165..aa81b00 100644
--- a/src/features/Billing/v1/hooks/useBillingGate.ts
+++ b/src/features/Billing/v1/hooks/useBillingGate.ts
@@ -4,7 +4,6 @@ import { isLowBalance } from "../utils/credits";
const DEFAULT_LOW_BALANCE_THRESHOLD = 200;
-
export function useBillingGate() {
const { data: wallet } = useWallet();
diff --git a/src/features/Billing/v1/mock/walletStore.ts b/src/features/Billing/v1/mock/walletStore.ts
index c0cae51..e43e150 100644
--- a/src/features/Billing/v1/mock/walletStore.ts
+++ b/src/features/Billing/v1/mock/walletStore.ts
@@ -220,8 +220,7 @@ function enforceDailyLimits(feature: string) {
const todayStr = new Date().toDateString();
const txsToday = transactions.filter(
(t) =>
- t.transactionType === "USAGE_DEDUCTION" &&
- new Date(t.createdAt).toDateString() === todayStr,
+ t.transactionType === "USAGE_DEDUCTION" && new Date(t.createdAt).toDateString() === todayStr,
);
if (feature.startsWith("AI_")) {
@@ -256,9 +255,7 @@ function enforceDailyLimits(feature: string) {
function checkSuspiciousActivity(credits: number) {
const oneDayAgo = Date.now() - 24 * 60 * 60 * 1000;
const recentDeductions = transactions.filter(
- (t) =>
- t.transactionType === "USAGE_DEDUCTION" &&
- new Date(t.createdAt).getTime() > oneDayAgo,
+ (t) => t.transactionType === "USAGE_DEDUCTION" && new Date(t.createdAt).getTime() > oneDayAgo,
);
const totalBurn = Math.abs(recentDeductions.reduce((sum, t) => sum + t.credits, 0)) + credits;
@@ -350,7 +347,8 @@ export const walletStore = {
if (idempotencyKeys.has(idempotencyKey)) {
const existing = getExistingTransaction("CREDIT_PURCHASE", idempotencyKey);
- if (existing) return { wallet: cloneWallet(communityWallet), transaction: cloneTransaction(existing) };
+ if (existing)
+ return { wallet: cloneWallet(communityWallet), transaction: cloneTransaction(existing) };
throw new Error("IDEMPOTENCY_KEY_REUSED");
}
@@ -388,7 +386,8 @@ export const walletStore = {
if (idempotencyKeys.has(idempotencyKey)) {
const existing = getExistingTransaction("USAGE_DEDUCTION", idempotencyKey);
- if (existing) return { wallet: cloneWallet(communityWallet), transaction: cloneTransaction(existing) };
+ if (existing)
+ return { wallet: cloneWallet(communityWallet), transaction: cloneTransaction(existing) };
throw new Error("IDEMPOTENCY_KEY_REUSED");
}
@@ -433,26 +432,25 @@ export const walletStore = {
if (idempotencyKeys.has(idempotencyKey)) {
const existing = getExistingTransaction("REFUND", idempotencyKey);
- if (existing) return { wallet: cloneWallet(communityWallet), transaction: cloneTransaction(existing) };
+ if (existing)
+ return { wallet: cloneWallet(communityWallet), transaction: cloneTransaction(existing) };
throw new Error("IDEMPOTENCY_KEY_REUSED");
}
idempotencyKeys.add(idempotencyKey);
- const tx = appendTransaction(
- communityWallet.id,
- "REFUND",
- credits,
- "refund",
- sourceId,
- { idempotencyKey },
- );
+ const tx = appendTransaction(communityWallet.id, "REFUND", credits, "refund", sourceId, {
+ idempotencyKey,
+ });
return { wallet: cloneWallet(communityWallet), transaction: cloneTransaction(tx) };
},
setAutoRecharge: (enabled: boolean, thresholdCredits?: number, amountRupees?: number) => {
- if (thresholdCredits !== undefined && (!Number.isInteger(thresholdCredits) || thresholdCredits < 0)) {
+ if (
+ thresholdCredits !== undefined &&
+ (!Number.isInteger(thresholdCredits) || thresholdCredits < 0)
+ ) {
throw new Error("INVALID_AUTO_RECHARGE_THRESHOLD");
}
if (
diff --git a/src/features/Billing/v1/pages/AddFundsPage.tsx b/src/features/Billing/v1/pages/AddFundsPage.tsx
index ed9474e..9074e52 100644
--- a/src/features/Billing/v1/pages/AddFundsPage.tsx
+++ b/src/features/Billing/v1/pages/AddFundsPage.tsx
@@ -13,7 +13,12 @@ import {
Wallet,
} from "lucide-react";
import { RECHARGE_PACKS, MIN_ADD_RUPEES } from "../constants/billing.constants";
-import { buildAddFundsPreview, formatCredits, formatRupees, validateMinAddFunds } from "../utils/credits";
+import {
+ buildAddFundsPreview,
+ formatCredits,
+ formatRupees,
+ validateMinAddFunds,
+} from "../utils/credits";
import { useAddFunds } from "../hooks/useWallet";
import { ToastContainer, useToast } from "@/features/Tasks/v1/components/common/ToastNotification";
import type { PaymentState } from "../Billing.types";
@@ -55,7 +60,11 @@ export default function AddFundsPage() {
idempotencyKey: `pay-${crypto.randomUUID()}${forceFail ? "-fail" : ""}`,
});
setPaymentState("success");
- addToast("success", "Payment successful", `${formatCredits(preview.totalCredits)} credits added.`);
+ addToast(
+ "success",
+ "Payment successful",
+ `${formatCredits(preview.totalCredits)} credits added.`,
+ );
} catch {
setPaymentState("failed");
addToast("error", "Payment failed", "Please try again or use a different method.");
@@ -86,7 +95,10 @@ export default function AddFundsPage() {
>
-
+
@@ -110,7 +122,10 @@ export default function AddFundsPage() {
-
+
{RECHARGE_PACKS.map((pack) => {
const isSelected = amount === pack.amountRupees;
@@ -120,27 +135,40 @@ export default function AddFundsPage() {
onClick={() => setAmountStr(pack.amountRupees.toString())}
className="group min-h-[142px] rounded-2xl border p-4 text-left transition-all hover:-translate-y-1 hover:shadow-lg"
style={{
- backgroundColor: isSelected ? "var(--cd-primary-subtle)" : "var(--cd-surface)",
+ backgroundColor: isSelected
+ ? "var(--cd-primary-subtle)"
+ : "var(--cd-surface)",
borderColor: isSelected ? "var(--cd-primary)" : "var(--cd-border-subtle)",
boxShadow: isSelected ? "0 14px 28px var(--cd-shadow)" : "none",
}}
>
-
+
{pack.label}
- {isSelected ? : null}
+ {isSelected ? (
+
+ ) : null}
{formatRupees(pack.amountRupees)}
-
+
{formatCredits(pack.baseCredits + pack.bonusCredits)} cr
{pack.bonusCredits > 0 && (
+{formatCredits(pack.bonusCredits)} bonus
@@ -153,7 +181,10 @@ export default function AddFundsPage() {
-
+
{PAYMENT_METHODS.map((m) => {
const isSelected = paymentMethod === m.id;
@@ -183,7 +217,9 @@ export default function AddFundsPage() {
onClick={() => setPaymentMethod(m.id)}
className="rounded-xl border p-4 text-left transition-all hover:-translate-y-0.5"
style={{
- backgroundColor: isSelected ? "var(--cd-primary-subtle)" : "var(--cd-surface)",
+ backgroundColor: isSelected
+ ? "var(--cd-primary-subtle)"
+ : "var(--cd-surface)",
borderColor: isSelected ? "var(--cd-primary)" : "var(--cd-border-subtle)",
color: isSelected ? "var(--cd-primary-text)" : "var(--cd-text)",
boxShadow: isSelected ? "0 10px 22px var(--cd-shadow)" : "none",
@@ -237,7 +273,10 @@ export default function AddFundsPage() {
<>Pay {formatRupees(preview.totalPayableRupees)}>
)}
-
+
Encrypted payment simulation
@@ -254,7 +293,10 @@ export default function AddFundsPage() {
function SectionLabel({ title, description }: { title: string; description: string }) {
return (
-
+
{title}
@@ -293,7 +335,10 @@ function PreviewCard({ preview }: { preview: ReturnType
-
@@ -301,7 +346,9 @@ function PreviewCard({ preview }: { preview: ReturnType (
{row.label}
- {row.value}
+
+ {row.value}
+
))}
Credits command center
-
+
Billing & Credits
-
+
Manage your community wallet, add funds, and track usage. Rs. 10 = 100 credits.
@@ -78,7 +84,10 @@ export default function BillingHubPage() {
borderColor: "var(--cd-border-subtle)",
}}
>
-
+
Secure wallet
@@ -101,7 +110,10 @@ export default function BillingHubPage() {
>
-
+
Available balance
@@ -112,8 +124,15 @@ export default function BillingHubPage() {
["Reserved", wallet.reservedCredits],
["Locked", wallet.lockedCredits],
].map(([label, value]) => (
-
-
+
+
{label}
@@ -140,7 +159,10 @@ export default function BillingHubPage() {
diff --git a/src/features/Billing/v1/pages/CommunityWalletPage.tsx b/src/features/Billing/v1/pages/CommunityWalletPage.tsx
index 0cb6178..f885db8 100644
--- a/src/features/Billing/v1/pages/CommunityWalletPage.tsx
+++ b/src/features/Billing/v1/pages/CommunityWalletPage.tsx
@@ -47,9 +47,7 @@ export default function CommunityWalletPage() {
void allTxQuery.refetch();
} catch (error: unknown) {
const message =
- error instanceof Error
- ? error.message
- : "Insufficient balance or daily limit reached.";
+ error instanceof Error ? error.message : "Insufficient balance or daily limit reached.";
addToast("error", "Failed to consume credits", message);
}
};
@@ -142,8 +140,12 @@ export default function CommunityWalletPage() {
onClick={() => setShowAIFeatures(!showAIFeatures)}
className="rounded-xl px-4 py-2.5 text-sm font-semibold border transition-all hover:scale-[1.02]"
style={{
- backgroundColor: showAIFeatures ? "var(--cd-primary-subtle)" : "var(--cd-surface)",
- borderColor: showAIFeatures ? "var(--cd-primary)" : "var(--cd-border-subtle)",
+ backgroundColor: showAIFeatures
+ ? "var(--cd-primary-subtle)"
+ : "var(--cd-surface)",
+ borderColor: showAIFeatures
+ ? "var(--cd-primary)"
+ : "var(--cd-border-subtle)",
color: showAIFeatures ? "var(--cd-primary-text)" : "var(--cd-text)",
}}
>
@@ -159,7 +161,10 @@ export default function CommunityWalletPage() {
borderColor: "var(--cd-border-subtle)",
}}
>
-
+
Available AI Services
@@ -174,7 +179,9 @@ export default function CommunityWalletPage() {
color: "var(--cd-text)",
}}
>
- {consumeCredits.isPending ? "Generating..." : "Generate AI Summary (15 credits)"}
+ {consumeCredits.isPending
+ ? "Generating..."
+ : "Generate AI Summary (15 credits)"}
diff --git a/src/features/Billing/v1/pages/UsageDashboardPage.tsx b/src/features/Billing/v1/pages/UsageDashboardPage.tsx
index fac340a..4ff8c07 100644
--- a/src/features/Billing/v1/pages/UsageDashboardPage.tsx
+++ b/src/features/Billing/v1/pages/UsageDashboardPage.tsx
@@ -64,7 +64,10 @@ export default function UsageDashboardPage() {
>
-
+
{stat.label}
@@ -85,8 +88,7 @@ export default function UsageDashboardPage() {
>
- AI usage spike risk:{" "}
- {forecast.aiSpikeRisk}
+ AI usage spike risk: {forecast.aiSpikeRisk}
Storage growth estimate: {formatCredits(forecast.storageGrowthCredits)} credits ·
@@ -101,4 +103,3 @@ export default function UsageDashboardPage() {
);
}
-
diff --git a/src/features/Billing/v1/services/billingSecurity.test.ts b/src/features/Billing/v1/services/billingSecurity.test.ts
index 3991be1..856acad 100644
--- a/src/features/Billing/v1/services/billingSecurity.test.ts
+++ b/src/features/Billing/v1/services/billingSecurity.test.ts
@@ -51,7 +51,7 @@ describe("Billing Security & Cost Protection", () => {
const result = walletStore.refundCredits(500, "pay-123", "idem-ref-1");
expect(result.wallet.availableCredits).toBe(initial + 500);
-
+
const transactions = walletStore.getTransactions();
const lastTx = transactions[0];
expect(lastTx.transactionType).toBe("REFUND");
@@ -73,7 +73,7 @@ describe("Billing Security & Cost Protection", () => {
describe("Daily Limits & Caps", () => {
it("allows deductions within daily limit", async () => {
const wallet = walletStore.getWallet();
-
+
const res = await BillingService.consumeCredits({
walletId: wallet.id,
feature: "AI_SUMMARY",
diff --git a/src/features/Billing/v1/utils/credits.ts b/src/features/Billing/v1/utils/credits.ts
index e74f04d..ce69e6c 100644
--- a/src/features/Billing/v1/utils/credits.ts
+++ b/src/features/Billing/v1/utils/credits.ts
@@ -87,4 +87,3 @@ export function applyTransactionFilters(
return true;
});
}
-
diff --git a/src/features/Billing/v1/utils/security.ts b/src/features/Billing/v1/utils/security.ts
index c9d5f36..c167422 100644
--- a/src/features/Billing/v1/utils/security.ts
+++ b/src/features/Billing/v1/utils/security.ts
@@ -9,27 +9,27 @@
export async function verifyWebhookSignature(
payload: string,
signature: string,
- secret: string
+ secret: string,
): Promise {
if (!payload || !signature || !secret) return false;
try {
const encoder = new TextEncoder();
const keyData = encoder.encode(secret);
-
+
// Import HMAC Key
const key = await window.crypto.subtle.importKey(
"raw",
keyData,
{ name: "HMAC", hash: "SHA-256" },
false,
- ["sign", "verify"]
+ ["sign", "verify"],
);
// Convert hex signature string back to a Uint8Array
const hexParts = signature.match(/.{1,2}/g);
if (!hexParts) return false;
const signatureBytes = new Uint8Array(hexParts.map((byte) => parseInt(byte, 16)));
-
+
const payloadData = encoder.encode(payload);
// Cryptographically verify signature
diff --git a/src/features/Dashboard/Member/v1/Page/DashboardPage.tsx b/src/features/Dashboard/Member/v1/Page/DashboardPage.tsx
index 7f528d3..5b77fe6 100644
--- a/src/features/Dashboard/Member/v1/Page/DashboardPage.tsx
+++ b/src/features/Dashboard/Member/v1/Page/DashboardPage.tsx
@@ -13,6 +13,7 @@ import SummaryCard from "@/features/Dashboard/components/SummaryCard";
import TaskOverview from "@/features/Dashboard/components/TaskOverview";
import UpcomingUrgentTasks from "@/features/Dashboard/components/UpcomingUrgentTasks";
import { useDashboardData } from "@/features/Member/v1/hooks/useDashboardData";
+import Header from "@/layouts/MemberLayout/Components/Header";
export default function DashboardPage() {
const { data, isLoading, error } = useDashboardData();
@@ -49,21 +50,23 @@ export default function DashboardPage() {
}
return (
-
- {/* Header */}
-
+
+
+
+ {/* Header */}
+
-
-
- Welcome back, {data.user.name.split(" ")[0]} 👋
-
+ >
+ Welcome back, {data.user.name.split(" ")[0]} 👋
+
-
- Here’s what’s happening today
-
+ >
+ Here’s what’s happening today
+
+
-
- {/* Summary Cards */}
-
-
+ >
+
-
+
-
+
-
+
-
-
+
+
- {/* Main Grid */}
-
- {/* LEFT */}
-
- {/* Top Section */}
-
-
+ >
+
-
-
+
+
-
+
-
+
-
+
-
+
-
-
+
+
- {/* RIGHT */}
-
diff --git a/src/features/Member/v1/Components/MemberHeader.tsx b/src/features/Member/v1/Components/MemberHeader.tsx
index 37e9e70..c025452 100644
--- a/src/features/Member/v1/Components/MemberHeader.tsx
+++ b/src/features/Member/v1/Components/MemberHeader.tsx
@@ -1,4 +1,6 @@
import Button from "@/Component/ui/Button";
+import PermissionWrapper from "@/features/Permission/Component/PermissionWrapper";
+import { MemberPermissionConstant } from "@/features/Permission/Constant/Permission.Constant";
import { IoPersonAdd } from "react-icons/io5";
import { useNavigate } from "react-router";
@@ -31,11 +33,13 @@ const MemberHeader = () => {
- navigate("/org/add-member")}
- icon={}
- />
+
+ navigate("/org/add-member")}
+ icon={}
+ />
+
{
+ const permissions = usePermissionStore((state) => state.permissions);
+
+ const hasPermission = permissions.some(
+ (permission) => permission.name === requiredPermission && permission.action === action,
+ );
+
+ if (!hasPermission) {
+ return null; // or you can return a fallback UI
+ }
+
+ return {children} ;
+};
+
+export default PermissionWrapper;
diff --git a/src/features/Permission/Constant/Permission.Constant.ts b/src/features/Permission/Constant/Permission.Constant.ts
new file mode 100644
index 0000000..10ce32a
--- /dev/null
+++ b/src/features/Permission/Constant/Permission.Constant.ts
@@ -0,0 +1,17 @@
+export type PermissionConstantItem = {
+ name: string;
+ action: "create" | "read" | "update" | "delete";
+ resource: string;
+ description?: string;
+ level: number;
+ _id?: string;
+ createdAt?: string;
+ updatedAt?: string;
+};
+
+export const MemberPermissionConstant = {
+ create: "member:create",
+ read: "member:read",
+ update: "member:update",
+ delete: "member:delete",
+};
diff --git a/src/features/Permission/Store/Permission.Store.ts b/src/features/Permission/Store/Permission.Store.ts
new file mode 100644
index 0000000..c429aca
--- /dev/null
+++ b/src/features/Permission/Store/Permission.Store.ts
@@ -0,0 +1,26 @@
+import { create } from "zustand";
+import { persist } from "zustand/middleware";
+import { PermissionState } from "../Types/Permission.Type";
+
+const usePermissionStore = create()(
+ persist(
+ (set) => ({
+ permissions: [],
+
+ setNewPermission: (permission) =>
+ set((state) => ({
+ permissions: [...state.permissions, permission],
+ })),
+
+ setPermissions: (permissions) =>
+ set(() => ({
+ permissions: permissions,
+ })),
+ }),
+ {
+ name: "permission-store",
+ },
+ ),
+);
+
+export default usePermissionStore;
diff --git a/src/features/Permission/Types/Permission.Type.ts b/src/features/Permission/Types/Permission.Type.ts
new file mode 100644
index 0000000..99d57fe
--- /dev/null
+++ b/src/features/Permission/Types/Permission.Type.ts
@@ -0,0 +1,13 @@
+export type PermissionState = {
+ permissions: PermissionType[] | [];
+ setNewPermission: (permission: PermissionType) => void;
+ setPermissions: (permissions: PermissionType[]) => void;
+};
+
+export type PermissionType = {
+ name: string;
+ action: "create" | "read" | "update" | "delete"; // create, read, update, delete
+ resource: string; // create event , read event, update event, delete event
+ description?: string;
+ userId: string | null; // Reference to the user who created the permission
+};
diff --git a/src/features/SideBar/v1/Section/SideBar.tsx b/src/features/SideBar/v1/Section/SideBar.tsx
index 582b995..81357c5 100644
--- a/src/features/SideBar/v1/Section/SideBar.tsx
+++ b/src/features/SideBar/v1/Section/SideBar.tsx
@@ -18,14 +18,18 @@ import SideBarLink from "../Components/SideBarLink";
import useAuthStore from "@/features/Auth/v1/Store/Auth.Store";
import useOrganizationStore from "@/features/Auth/v1/Store/Organization.Store";
+import usePermissionStore from "@/features/Permission/Store/Permission.Store";
+import PermissionWrapper from "@/features/Permission/Component/PermissionWrapper";
const SideBar = () => {
const user = useAuthStore((state) => state.user);
const organization = useOrganizationStore((state) => state.organization);
+ const permissions = usePermissionStore((state) => state.permissions);
const [profileImageFailed, setProfileImageFailed] = useState(false);
console.log("User in SideBar:", user);
console.log("Organization in SideBar:", organization);
+ console.log("Permissions in SideBar:", permissions);
const { theme } = useTheme();
const communityName = organization?.CommunityName || "CommDesk";
@@ -79,7 +83,11 @@ const SideBar = () => {
} text="Dashboard" link="/org/dashboard" />
} text="Projects" link="/org/projects" />
- } text="Teams" link="/org/member" />
+
+
+ } text="Teams" link="/org/member" />
+
+
} text="Events" link="/org/events" />
} text="Tasks" link="/org/tasks" />
} text="Webhooks" link="/org/dashboard/webhooks" />
diff --git a/src/features/Tasks/v1/hooks/useTasks.ts b/src/features/Tasks/v1/hooks/useTasks.ts
index 8a952eb..e7b76d6 100644
--- a/src/features/Tasks/v1/hooks/useTasks.ts
+++ b/src/features/Tasks/v1/hooks/useTasks.ts
@@ -119,7 +119,7 @@ export function useUpdateTask() {
payload: UpdateTaskPayload;
}): Promise => {
await new Promise((r) => setTimeout(r, 400));
-
+
const { assignedTo: _ids, ...rest } = payload;
const patch: Partial = { ...rest };
if (payload.assignedTo) {
diff --git a/src/features/Tasks/v1/pages/TaskDetailPage.tsx b/src/features/Tasks/v1/pages/TaskDetailPage.tsx
index 2240250..29bd780 100644
--- a/src/features/Tasks/v1/pages/TaskDetailPage.tsx
+++ b/src/features/Tasks/v1/pages/TaskDetailPage.tsx
@@ -458,7 +458,6 @@ export default function TaskDetailPage() {
onConfirm={() => void handleDelete()}
onCancel={() => setShowDeleteModal(false)}
isLoading={deleteTask.isPending}
-
/>
diff --git a/src/features/Tasks/v1/pages/TaskManagementPage.tsx b/src/features/Tasks/v1/pages/TaskManagementPage.tsx
index f149688..2cb2b4e 100644
--- a/src/features/Tasks/v1/pages/TaskManagementPage.tsx
+++ b/src/features/Tasks/v1/pages/TaskManagementPage.tsx
@@ -261,7 +261,6 @@ export default function TaskManagementPage() {
onConfirm={() => void handleDelete()}
onCancel={() => setTaskToDelete(null)}
isLoading={deleteTask.isPending}
-
/>
diff --git a/src/features/Webhooks/v1/components/form/WebhookForm.test.tsx b/src/features/Webhooks/v1/components/form/WebhookForm.test.tsx
index 6c147f3..398bf31 100644
--- a/src/features/Webhooks/v1/components/form/WebhookForm.test.tsx
+++ b/src/features/Webhooks/v1/components/form/WebhookForm.test.tsx
@@ -110,7 +110,9 @@ describe("WebhookForm Component", () => {
expect(screen.getByText("Endpoint Details")).toBeInTheDocument();
expect(screen.getByPlaceholderText("e.g. Production Slack Alerts")).toBeInTheDocument();
- expect(screen.getByPlaceholderText("https://your-domain.com/webhooks/commdesk")).toBeInTheDocument();
+ expect(
+ screen.getByPlaceholderText("https://your-domain.com/webhooks/commdesk"),
+ ).toBeInTheDocument();
expect(screen.getByText("Subscribed Events")).toBeInTheDocument();
});
@@ -352,11 +354,7 @@ describe("WebhookForm Component", () => {
fireEvent.click(screen.getByRole("button", { name: /Create Webhook/i }));
await waitFor(() => {
- expect(mockAddToast).toHaveBeenCalledWith(
- "success",
- "Webhook created",
- expect.any(String),
- );
+ expect(mockAddToast).toHaveBeenCalledWith("success", "Webhook created", expect.any(String));
});
});
@@ -369,7 +367,11 @@ describe("WebhookForm Component", () => {
fireEvent.click(screen.getByRole("button", { name: /Create Webhook/i }));
await waitFor(() => {
- expect(mockAddToast).toHaveBeenCalledWith("error", "Error saving webhook", expect.any(String));
+ expect(mockAddToast).toHaveBeenCalledWith(
+ "error",
+ "Error saving webhook",
+ expect.any(String),
+ );
});
});
diff --git a/src/features/Webhooks/v1/hooks/useWebhooks.test.tsx b/src/features/Webhooks/v1/hooks/useWebhooks.test.tsx
index 738cb4a..fc4c3c9 100644
--- a/src/features/Webhooks/v1/hooks/useWebhooks.test.tsx
+++ b/src/features/Webhooks/v1/hooks/useWebhooks.test.tsx
@@ -48,12 +48,9 @@ describe("Webhook API Hooks Integration", () => {
updatedAt: new Date().toISOString(),
});
- const { result } = renderHook(
- () => useWebhooks({ status: "all", search: "Alpha", page: 1 }),
- {
+ const { result } = renderHook(() => useWebhooks({ status: "all", search: "Alpha", page: 1 }), {
wrapper,
- },
- );
+ });
await waitFor(() => {
expect(result.current.isSuccess).toBe(true);
diff --git a/src/features/template/LoginUserTemplate.tsx b/src/features/template/LoginUserTemplate.tsx
index 5523d61..90387ed 100644
--- a/src/features/template/LoginUserTemplate.tsx
+++ b/src/features/template/LoginUserTemplate.tsx
@@ -6,19 +6,16 @@ import BotamNavBar from "../SideBar/v1/Section/BotamNavBar";
import { useMemo } from "react";
const Organisation_Template = () => {
-
const user = useAuthStore((state) => state.user);
useMemo(() => {
-
console.log("User in Organisation_Template-->:", user);
-
- if (user?.role) {
- if (user.role !== "organization") {
- redirect("/");
- }
+ if (user?.role) {
+ if (user.role !== "organization") {
+ redirect("/");
}
+ }
}, [user]);
return (
diff --git a/src/layouts/MemberLayout/MemberLayout.tsx b/src/layouts/MemberLayout/MemberLayout.tsx
index a39872e..3ed46ba 100644
--- a/src/layouts/MemberLayout/MemberLayout.tsx
+++ b/src/layouts/MemberLayout/MemberLayout.tsx
@@ -20,7 +20,7 @@ export default function MemberLayout() {
overflow-hidden
bg-gray-50
- dark:bg-[#0a0a0a]
+ dark:bg-[#0b0d12]
text-gray-900
dark:text-white
@@ -33,14 +33,14 @@ export default function MemberLayout() {
className="
absolute inset-0
- bg-gradient-to-br
+ bg-linear-to-br
- from-indigo-50
+ from-slate-50
via-white
- to-yellow-50
+ to-cyan-50
- dark:from-[#0f172a]
- dark:via-[#09090b]
+ dark:from-[#0b0d12]
+ dark:via-[#101522]
dark:to-[#111827]
"
/>
@@ -52,13 +52,13 @@ export default function MemberLayout() {
absolute -top-40 -left-40
- w-[28rem]
- h-[28rem]
+ w-md
+ h-112
rounded-full
- bg-indigo-300/20
- dark:bg-indigo-500/10
+ bg-sky-300/20
+ dark:bg-sky-500/10
blur-3xl
"
@@ -71,13 +71,13 @@ export default function MemberLayout() {
absolute bottom-0 right-0
- w-[28rem]
- h-[28rem]
+ w-md
+ h-112
rounded-full
- bg-yellow-300/20
- dark:bg-yellow-500/10
+ bg-cyan-300/20
+ dark:bg-cyan-500/10
blur-3xl
"
@@ -94,7 +94,7 @@ export default function MemberLayout() {
rounded-full
- bg-cyan-500/5
+ bg-slate-500/5
blur-3xl
"
@@ -117,7 +117,6 @@ export default function MemberLayout() {
"
>
{/* Header */}
-
{/* Page Content */}
diff --git a/src/routes/MemberRoutes.tsx b/src/routes/MemberRoutes.tsx
index f4648da..69e1a89 100644
--- a/src/routes/MemberRoutes.tsx
+++ b/src/routes/MemberRoutes.tsx
@@ -14,6 +14,7 @@ import { Contact } from "lucide-react";
import ViewEvent from "@/features/Events/v1/Pages/ViewEvent";
import BillingPage from "@/features/Member/v1/Pages/Billing";
import MessagesPage from "@/features/Member/v1/Pages/Messages";
+import MemberPage from "@/features/Member/v1/Pages/MemberPage";
const MemberRoutes = () => {
return (
@@ -38,6 +39,8 @@ const MemberRoutes = () => {
} />
+ } />
+
} />
} />
diff --git a/src/routes/OrgRoute.tsx b/src/routes/OrgRoute.tsx
index 8497333..c8e80b4 100644
--- a/src/routes/OrgRoute.tsx
+++ b/src/routes/OrgRoute.tsx
@@ -58,9 +58,7 @@ const OrgRoute = () => {
+
} />
} />
diff --git a/test-results/.last-run.json b/test-results/.last-run.json
index cbcc1fb..f740f7c 100644
--- a/test-results/.last-run.json
+++ b/test-results/.last-run.json
@@ -1,4 +1,4 @@
{
"status": "passed",
"failedTests": []
-}
\ No newline at end of file
+}
diff --git a/tests/e2e/billing.spec.ts b/tests/e2e/billing.spec.ts
index f8f6ca3..6d62435 100644
--- a/tests/e2e/billing.spec.ts
+++ b/tests/e2e/billing.spec.ts
@@ -39,7 +39,7 @@ test.describe("Billing & Credits Wallet E2E Scenarios", () => {
test("Add Funds Flow - Successful UPI Payment", async ({ page }) => {
await expect(page.getByRole("heading", { name: "Community Wallet" })).toBeVisible();
-
+
await page.getByRole("button", { name: "Add Funds" }).first().click();
const addFundsDialog = page.getByRole("dialog", { name: "Add Funds" });
await expect(addFundsDialog).toBeVisible();
@@ -65,9 +65,9 @@ test.describe("Billing & Credits Wallet E2E Scenarios", () => {
test("Add Funds Flow - Failed Payment Simulation", async ({ page }) => {
await page.goto("/org/billing/add-funds");
-
+
await page.fill('input[type="number"]', "150");
- await page.click('#force-fail-checkbox');
+ await page.click("#force-fail-checkbox");
await page.getByRole("button", { name: /Pay/ }).click();
await expect(page.locator("text=Payment failed")).toBeVisible();
@@ -81,7 +81,7 @@ test.describe("Billing & Credits Wallet E2E Scenarios", () => {
await expect(thresholdInput).toBeDisabled();
await page.click('input[type="checkbox"]');
-
+
await expect(thresholdInput).toBeEnabled();
await thresholdInput.fill("300");
await amountInput.fill("500");
@@ -115,7 +115,7 @@ test.describe("Billing & Credits Wallet E2E Scenarios", () => {
await expect(page.locator("text=AI Summary Generated")).toBeVisible();
await page.getByRole("button", { name: "Transactions" }).click();
await expect(page.locator("text=AI_SUMMARY").first()).toBeVisible();
-
+
await page.getByRole("button", { name: "Overview" }).click();
const expectedBalanceText = (initialBalance - 15).toLocaleString("en-IN");
await expect(page.getByTestId("wallet-stat-available-value")).toHaveText(expectedBalanceText);
|