feat(payment): fix zibal refund api, add maskable fields, receipt modal, standardized buttons and order refund options
All checks were successful
Deploy Canina / deploy (push) Successful in 41s

This commit is contained in:
parsa aghaei 2026-08-22 17:01:13 +03:30
parent f9e40564cf
commit d62787cd99
22 changed files with 10934 additions and 9318 deletions

View File

@ -172,6 +172,21 @@ export class AdminController {
return { success: true, data: order };
}
@Post('orders/:id/refund-wallet')
@ApiOperation({ summary: 'استرداد مستقیم مبلغ سفارش به کیف پول کاربر' })
async refundOrderToWallet(
@Param('id') id: string,
@Body('amount') amount?: number,
@Body('reason') reason?: string,
) {
const result = await this.adminService.refundOrderToWallet(
id,
amount ? Number(amount) : undefined,
reason,
);
return result;
}
@Get('coupons')
@ApiOperation({ summary: 'لیست کد تخفیف‌ها' })
@ApiQuery({ name: 'page', required: false, description: 'شماره صفحه' })

View File

@ -615,6 +615,65 @@ export class AdminService {
});
}
async refundOrderToWallet(orderId: string, customAmount?: number, reason?: string) {
const order = await this.prisma.order.findUnique({
where: { id: orderId },
include: { user: true },
});
if (!order) {
throw new NotFoundException('سفارش مورد نظر یافت نشد');
}
if (!order.userId || !order.user) {
throw new BadRequestException('این سفارش به کاربر ثبت‌نام‌شده‌ای متصل نیست');
}
const refundAmount = customAmount !== undefined && customAmount > 0
? customAmount
: Number(order.totalAmount || 0);
if (refundAmount <= 0) {
throw new BadRequestException('مبلغ استرداد باید بیشتر از صفر باشد');
}
await this.prisma.$transaction(async (tx) => {
// 1. Increment user wallet balance
await tx.user.update({
where: { id: order.userId },
data: {
walletBalance: { increment: refundAmount },
},
});
// 2. Create wallet transaction entry
await tx.walletTransaction.create({
data: {
userId: order.userId,
amount: refundAmount,
type: 'deposit',
status: 'completed',
description: reason || `استرداد وجه سفارش لغو شده #${order.trackingNumber || order.id.slice(0, 8)} به کیف پول`,
},
});
// 3. Mark order as cancelled and note refund
await tx.order.update({
where: { id: order.id },
data: {
status: 'cancelled',
},
});
});
return {
success: true,
message: `مبلغ ${refundAmount.toLocaleString('fa-IR')} تومان با موفقیت به کیف پول کاربر بازگردانده شد.`,
refundAmount,
userId: order.userId,
};
}
async getCoupons(query: PaginationQuery = {}) {
const page = Number(query.page) || 1;
const limit = Number(query.limit) || 10;

View File

@ -25,6 +25,9 @@ async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule);
// Trust reverse proxy (Nginx / Docker ingress) to resolve real client IP from X-Forwarded-For
app.set('trust proxy', true);
// Serve static uploads folder under both /uploads/ and /api/uploads/
app.useStaticAssets(join(process.cwd(), 'uploads'), {
prefix: '/uploads/',

View File

@ -45,6 +45,23 @@ export class PaymentController {
private readonly zibalEBankService: ZibalEBankService,
) {}
private extractRealClientIp(req: Request, fallbackIp?: string): string {
const xForwardedFor = req.headers['x-forwarded-for'];
if (xForwardedFor) {
const forwardedIps = Array.isArray(xForwardedFor)
? xForwardedFor[0]
: xForwardedFor.split(',')[0];
if (forwardedIps && forwardedIps.trim()) {
return forwardedIps.trim().replace(/^::ffff:/, '');
}
}
const xRealIp = req.headers['x-real-ip'];
if (xRealIp && typeof xRealIp === 'string') {
return xRealIp.trim().replace(/^::ffff:/, '');
}
return (fallbackIp || req.ip || '').replace(/^::ffff:/, '');
}
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
@Post('zibal/initiate')
@ -56,16 +73,17 @@ export class PaymentController {
description: 'خطا در ایجاد تراکنش یا نامعتبر بودن سفارش',
})
async initiateOrderPayment(
@Req() req: { user: { id: string } },
@Req() req: Request & { user: { id: string } },
@Body() body: InitiatePaymentDto,
@Ip() ip: string,
@Headers('user-agent') userAgent?: string,
) {
const realIp = this.extractRealClientIp(req, ip);
return this.paymentService.initiateOrderPayment(
req.user.id,
body.orderId,
body.customCallbackUrl,
{ ipAddress: ip, userAgent },
{ ipAddress: realIp, userAgent },
);
}
@ -77,16 +95,17 @@ export class PaymentController {
description: 'لینک هدایت به درگاه پرداخت برای شارژ کیف پول',
})
async initiateWalletTopup(
@Req() req: { user: { id: string } },
@Req() req: Request & { user: { id: string } },
@Body() body: InitiateWalletTopupDto,
@Ip() ip: string,
@Headers('user-agent') userAgent?: string,
) {
const realIp = this.extractRealClientIp(req, ip);
return this.paymentService.initiateWalletTopup(
req.user.id,
Number(body.amount),
body.customCallbackUrl,
{ ipAddress: ip, userAgent },
{ ipAddress: realIp, userAgent },
);
}
@ -95,11 +114,13 @@ export class PaymentController {
summary: 'دریافت کال‌بک بازگشت از درگاه زیبال و تایید تراکنش',
})
async handleZibalCallback(
@Req() req: Request,
@Query() query: ZibalCallbackQueryDto,
@Res() res: Response,
@Ip() ip: string,
@Headers('user-agent') userAgent?: string,
) {
const realIp = this.extractRealClientIp(req, ip);
const trackId = query.trackId || '';
const success = query.success;
const status = query.status;
@ -119,7 +140,7 @@ export class PaymentController {
success,
status,
orderId,
{ ipAddress: ip, userAgent },
{ ipAddress: realIp, userAgent },
);
const params = new URLSearchParams({

View File

@ -540,19 +540,26 @@ export class ZibalService implements IPaymentGateway {
cardNumber?: string;
description?: string;
}) {
const accountId =
options.accountId ||
(await this.prisma.uiText.findUnique({ where: { key: 'ZIBAL_EBANK_ACCOUNT_ID' } }))?.value;
const merchant = await this.getMerchant();
const accountIdSetting = (
await this.prisma.uiText.findUnique({ where: { key: 'ZIBAL_EBANK_ACCOUNT_ID' } })
)?.value;
const payload = {
const payload: Record<string, any> = {
merchant: merchant || undefined,
merchantId: merchant || undefined,
trackId: Number(options.trackId),
accountId: accountId || 'default',
tryReverse: options.tryReverse ?? false,
amount: options.amount,
cardNumber: options.cardNumber,
amount: options.amount ? Number(options.amount) : undefined,
cardNumber: options.cardNumber || undefined,
description: options.description || 'استرداد وجه سفارش در کنینا',
};
const finalAccountId = options.accountId || accountIdSetting;
if (finalAccountId && finalAccountId.trim() !== '') {
payload.accountId = finalAccountId.trim();
}
return this.apiRequest<any>('/v1/account/refund', 'POST', payload);
}
@ -563,7 +570,10 @@ export class ZibalService implements IPaymentGateway {
refundId?: number | string;
transactionTrackId?: number | string;
}) {
const merchant = await this.getMerchant();
return this.apiRequest<any>('/v1/account/refund/inquiry', 'POST', {
merchant: merchant || undefined,
merchantId: merchant || undefined,
refundId: params.refundId ? Number(params.refundId) : undefined,
transactionTrackId: params.transactionTrackId
? Number(params.transactionTrackId)
@ -582,9 +592,12 @@ export class ZibalService implements IPaymentGateway {
fromDate?: string;
toDate?: string;
}) {
const merchant = await this.getMerchant();
return this.apiRequest<any>('/v1/account/refund/list', 'POST', {
merchant: merchant || undefined,
merchantId: merchant || undefined,
status: filter.status,
type: filter.type || 4,
type: filter.type,
page: filter.page || 1,
size: filter.size || 20,
fromDate: filter.fromDate,

View File

@ -0,0 +1,235 @@
import React from 'react';
import { Printer, X, CheckCircle2, ShieldCheck, Copy } from 'lucide-react';
import { toast } from 'react-hot-toast';
import Button from './ui/Button';
export interface TransactionReceiptData {
trackId?: string | number;
refNumber?: string | number;
amountRials?: number | string;
amountTomans?: number | string;
statusText?: string;
orderNumber?: string;
orderId?: string;
cardNumber?: string;
paidAt?: string;
createdAt?: string;
fee?: number | string;
mobile?: string;
description?: string;
psp?: string;
ip?: string;
}
interface TransactionReceiptModalProps {
isOpen: boolean;
onClose: () => void;
data: TransactionReceiptData | null;
}
export default function TransactionReceiptModal({
isOpen,
onClose,
data,
}: TransactionReceiptModalProps) {
if (!isOpen || !data) return null;
const handlePrint = () => {
window.print();
};
const handleCopy = (text: string, label: string) => {
if (!text) return;
navigator.clipboard.writeText(text);
toast.success(`${label} کپی شد`);
};
const rials =
data.amountRials !== undefined
? Number(data.amountRials)
: (Number(data.amountTomans || 0) * 10);
const tomans =
data.amountTomans !== undefined
? Number(data.amountTomans)
: Math.floor(Number(data.amountRials || 0) / 10);
return (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm print:p-0 print:bg-white">
<div className="bg-white rounded-3xl max-w-sm w-full shadow-2xl border border-gray-100 overflow-hidden print:border-none print:shadow-none font-vazir" dir="rtl">
{/* Modal Header */}
<div className="p-4 border-b border-gray-100 flex items-center justify-between print:hidden">
<h3 className="font-black text-sm text-gray-900 flex items-center gap-1.5">
<ShieldCheck className="w-4 h-4 text-emerald-600" />
<span>رسید تراکنش {data.trackId || ''}</span>
</h3>
<button
onClick={onClose}
className="text-gray-400 hover:text-gray-700 font-bold p-1 cursor-pointer"
>
<X className="w-5 h-5" />
</button>
</div>
{/* The Zigzag Receipt Slip (Matching Zibal screenshot exactly) */}
<div className="p-6 space-y-5 bg-white relative">
{/* Top Jagged Border (CSS Jagged effect) */}
<div className="w-full h-3 bg-indigo-50/50 -mt-6 mb-3 flex justify-between overflow-hidden opacity-60">
{Array.from({ length: 24 }).map((_, i) => (
<div
key={i}
className="w-0 h-0 border-l-[6px] border-l-transparent border-r-[6px] border-r-transparent border-t-[8px] border-t-white"
/>
))}
</div>
{/* Amount Display */}
<div className="text-center space-y-1">
<div className="text-2xl font-black text-emerald-600 font-mono">
{rials.toLocaleString('fa-IR')}{' '}
<span className="text-sm font-black text-emerald-700">ریال</span>
</div>
<p className="text-[11px] font-bold text-gray-400 font-mono">
({tomans.toLocaleString('fa-IR')} تومان)
</p>
</div>
{/* Data List (Aligned exactly like Zibal slip) */}
<div className="space-y-2.5 text-xs">
{/* Track ID */}
<div className="flex items-center justify-between py-1 border-b border-gray-50">
<button
type="button"
onClick={() => handleCopy(String(data.trackId || ''), 'شماره تراکنش')}
className="text-indigo-600 hover:text-indigo-800 flex items-center gap-1 font-mono font-bold cursor-pointer"
title="کپی"
>
<Copy className="w-3 h-3" />
<span>{data.trackId || '-'}</span>
</button>
<span className="text-gray-500 font-bold">شماره تراکنش</span>
</div>
{/* Reference Number */}
<div className="flex items-center justify-between py-1 border-b border-gray-50">
<button
type="button"
onClick={() => handleCopy(String(data.refNumber || ''), 'شناسه مرجع')}
className="text-indigo-600 hover:text-indigo-800 flex items-center gap-1 font-mono font-bold cursor-pointer"
title="کپی"
>
<Copy className="w-3 h-3" />
<span>{data.refNumber || '-'}</span>
</button>
<span className="text-gray-500 font-bold">شناسه مرجع</span>
</div>
{/* Paid At */}
<div className="flex items-center justify-between py-1 border-b border-gray-50">
<span className="font-mono text-gray-700 font-bold dir-ltr">
{data.paidAt || data.createdAt || '-'}
</span>
<span className="text-gray-500 font-bold">تاریخ پرداخت</span>
</div>
{/* Status */}
<div className="flex items-center justify-between py-1 border-b border-gray-50">
<span className="text-emerald-600 font-black flex items-center gap-1">
<span className="w-2 h-2 rounded-full bg-emerald-500 inline-block"></span>
<span>{data.statusText || 'پرداخت موفق'}</span>
</span>
<span className="text-gray-500 font-bold">وضعیت</span>
</div>
{/* Order ID */}
<div className="flex items-center justify-between py-1.5 px-2 bg-gray-50/80 rounded-lg">
<span className="font-mono font-bold text-gray-900">
{data.orderNumber || data.orderId || '-'}
</span>
<span className="text-gray-500 font-bold">شناسه سفارش</span>
</div>
{/* Card Number */}
<div className="flex items-center justify-between py-1 border-b border-gray-50">
<span className="font-mono text-gray-800 font-bold dir-ltr">
{data.cardNumber || '-'}
</span>
<span className="text-gray-500 font-bold">شماره کارت</span>
</div>
{/* Creation Date */}
<div className="flex items-center justify-between py-1.5 px-2 bg-gray-50/80 rounded-lg">
<span className="font-mono font-bold text-gray-700 dir-ltr">
{data.createdAt || data.paidAt || '-'}
</span>
<span className="text-gray-500 font-bold">تاریخ ایجاد</span>
</div>
{/* Mobile */}
<div className="flex items-center justify-between py-1.5 px-2 bg-gray-50/80 rounded-lg">
<span className="font-mono font-bold text-gray-800 dir-ltr">
{data.mobile || '-'}
</span>
<span className="text-gray-500 font-bold">شماره موبایل</span>
</div>
{/* Description */}
<div className="flex items-center justify-between py-1 border-b border-gray-50">
<span className="text-gray-800 font-bold max-w-[180px] truncate text-left">
{data.description || 'پرداخت آنلاین - پت شاپ کنینا'}
</span>
<span className="text-gray-500 font-bold">توضیحات</span>
</div>
{/* PSP */}
<div className="flex items-center justify-between py-1.5 px-2 bg-gray-50/80 rounded-lg">
<span className="font-bold text-indigo-700 text-[11px]">
{data.psp || 'زیبال / به‌پرداخت ملت'}
</span>
<span className="text-gray-500 font-bold">PSP</span>
</div>
{/* Real IP */}
<div className="flex items-center justify-between py-1">
<button
type="button"
onClick={() => handleCopy(String(data.ip || ''), 'آی‌پی کاربر')}
className="text-indigo-600 hover:text-indigo-800 flex items-center gap-1 font-mono font-bold cursor-pointer dir-ltr"
title="کپی آی‌پی"
>
<Copy className="w-3 h-3" />
<span>{data.ip || '-'}</span>
</button>
<span className="text-gray-500 font-bold">IP</span>
</div>
</div>
{/* Bottom Jagged Border */}
<div className="w-full h-3 bg-indigo-50/50 -mb-6 mt-4 flex justify-between overflow-hidden opacity-60">
{Array.from({ length: 24 }).map((_, i) => (
<div
key={i}
className="w-0 h-0 border-l-[6px] border-l-transparent border-r-[6px] border-r-transparent border-b-[8px] border-b-white"
/>
))}
</div>
</div>
{/* Actions / Print Footer */}
<div className="p-4 bg-gray-50/80 border-t border-gray-100 flex items-center justify-between print:hidden">
<Button variant="outline" size="sm" onClick={onClose}>
بستن
</Button>
<Button
variant="outline"
size="sm"
startIcon={Printer}
onClick={handlePrint}
className="border-gray-300 hover:border-indigo-600 text-indigo-600 hover:bg-indigo-50"
>
چاپ رسید
</Button>
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,114 @@
import React from 'react';
import { Info } from 'lucide-react';
import Spinner from './Spinner';
export type ButtonVariant =
| 'primary'
| 'secondary'
| 'danger'
| 'success'
| 'warning'
| 'outline'
| 'ghost';
export type ButtonSize = 'xs' | 'sm' | 'md' | 'lg';
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: ButtonVariant;
size?: ButtonSize;
isLoading?: boolean;
startIcon?: React.ComponentType<{ className?: string }>;
endIcon?: React.ComponentType<{ className?: string }>;
info?: string;
badge?: string | number;
fullWidth?: boolean;
}
export default function Button({
children,
variant = 'primary',
size = 'md',
isLoading = false,
startIcon: StartIcon,
endIcon: EndIcon,
info,
badge,
fullWidth = false,
className = '',
disabled,
...props
}: ButtonProps) {
// Base styles: strict whitespace-nowrap and touch-friendly cursor
const baseClasses =
'inline-flex items-center justify-center font-bold whitespace-nowrap transition-all select-none rounded-xl cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed disabled:pointer-events-none focus:outline-none focus:ring-2 focus:ring-offset-2';
// Size styling
const sizeClasses: Record<ButtonSize, string> = {
xs: 'px-2.5 py-1 text-[11px] gap-1.5',
sm: 'px-3.5 py-1.5 text-xs gap-2',
md: 'px-4 py-2.5 text-xs sm:text-sm gap-2',
lg: 'px-6 py-3 text-sm sm:text-base gap-2.5 shadow-md',
};
// Variant styling
const variantClasses: Record<ButtonVariant, string> = {
primary:
'bg-indigo-600 hover:bg-indigo-700 text-white shadow-sm shadow-indigo-200 focus:ring-indigo-500 border border-indigo-600',
secondary:
'bg-slate-100 hover:bg-slate-200 text-slate-700 focus:ring-slate-400 border border-slate-200',
danger:
'bg-rose-600 hover:bg-rose-700 text-white shadow-sm shadow-rose-200 focus:ring-rose-500 border border-rose-600',
success:
'bg-emerald-600 hover:bg-emerald-700 text-white shadow-sm shadow-emerald-200 focus:ring-emerald-500 border border-emerald-600',
warning:
'bg-amber-500 hover:bg-amber-600 text-white shadow-sm shadow-amber-200 focus:ring-amber-400 border border-amber-500',
outline:
'bg-white hover:bg-gray-50 text-gray-700 border border-gray-300 focus:ring-indigo-500',
ghost:
'bg-transparent hover:bg-gray-100 text-gray-600 focus:ring-gray-300 border border-transparent',
};
const iconSizes: Record<ButtonSize, string> = {
xs: 'w-3 h-3',
sm: 'w-3.5 h-3.5',
md: 'w-4 h-4',
lg: 'w-5 h-5',
};
return (
<button
disabled={disabled || isLoading}
className={`
${baseClasses}
${sizeClasses[size]}
${variantClasses[variant]}
${fullWidth ? 'w-full' : ''}
${className}
`}
{...props}
>
{isLoading ? (
<Spinner size={size === 'lg' ? 'md' : 'sm'} />
) : (
<>
{StartIcon && <StartIcon className={iconSizes[size]} />}
{children && <span>{children}</span>}
{badge !== undefined && (
<span className="px-1.5 py-0.5 rounded-full text-[10px] bg-white/20 text-current font-mono">
{badge}
</span>
)}
{EndIcon && <EndIcon className={iconSizes[size]} />}
{info && (
<span
className="inline-flex items-center text-current/80 hover:text-current cursor-help ml-0.5"
title={info}
>
<Info className="w-3.5 h-3.5" />
</span>
)}
</>
)}
</button>
);
}

View File

@ -0,0 +1,96 @@
import React, { useState } from 'react';
import { Eye, EyeOff, Copy, Check } from 'lucide-react';
import { toast } from 'react-hot-toast';
interface MaskableFieldProps {
value?: string | number | null;
maskedValue?: string;
isMaskedByDefault?: boolean;
label?: string;
copyable?: boolean;
className?: string;
valueClassName?: string;
dir?: 'ltr' | 'rtl';
}
export default function MaskableField({
value,
maskedValue,
isMaskedByDefault = true,
label,
copyable = true,
className = '',
valueClassName = '',
dir = 'ltr',
}: MaskableFieldProps) {
const [isRevealed, setIsRevealed] = useState(!isMaskedByDefault);
const [copied, setCopied] = useState(false);
const rawVal = value !== undefined && value !== null ? String(value) : '';
// Create mask if not provided
const generateMask = (str: string) => {
if (!str) return '-';
if (str.length <= 4) return '****';
if (str.length === 16) {
// 16-digit Card: 6037-99**-****-1234
return `${str.slice(0, 4)}-${str.slice(4, 6)}**-****-${str.slice(12)}`;
}
const visibleStart = Math.min(4, Math.floor(str.length / 3));
const visibleEnd = Math.min(4, Math.floor(str.length / 3));
return `${str.slice(0, visibleStart)}${'*'.repeat(Math.max(4, str.length - visibleStart - visibleEnd))}${str.slice(-visibleEnd)}`;
};
const displayMasked = maskedValue || generateMask(rawVal);
const currentText = isRevealed ? (rawVal || '-') : displayMasked;
const handleCopy = (e: React.MouseEvent) => {
e.stopPropagation();
if (!rawVal) return;
navigator.clipboard.writeText(rawVal);
setCopied(true);
toast.success(`${label || 'مقدار'} در کلیپ‌بورد کپی شد`);
setTimeout(() => setCopied(false), 2000);
};
return (
<div className={`inline-flex items-center gap-1.5 ${className}`}>
<span
dir={dir}
className={`font-mono text-xs select-all ${valueClassName}`}
title={isRevealed ? 'مقدار واقعی' : 'مقدار پنهان‌شده'}
>
{currentText}
</span>
{rawVal && (
<div className="inline-flex items-center gap-1">
{/* Toggle Eye Button */}
<button
type="button"
onClick={(e) => {
e.stopPropagation();
setIsRevealed(!isRevealed);
}}
className="p-1 text-gray-400 hover:text-indigo-600 hover:bg-indigo-50 rounded-md transition-colors cursor-pointer"
title={isRevealed ? 'پنهان‌سازی' : 'نمایش مقدار اصلی'}
>
{isRevealed ? <EyeOff className="w-3.5 h-3.5" /> : <Eye className="w-3.5 h-3.5" />}
</button>
{/* Copy to Clipboard Button */}
{copyable && (
<button
type="button"
onClick={handleCopy}
className="p-1 text-gray-400 hover:text-emerald-600 hover:bg-emerald-50 rounded-md transition-colors cursor-pointer"
title="کپی در کلیپ‌بورد"
>
{copied ? <Check className="w-3.5 h-3.5 text-emerald-600" /> : <Copy className="w-3.5 h-3.5" />}
</button>
)}
</div>
)}
</div>
);
}

View File

@ -22,12 +22,16 @@ import {
CreditCard,
ArrowUpDown,
ArrowUp,
ArrowDown
ArrowDown,
Undo2,
Wallet,
} from 'lucide-react';
import { toast } from 'react-hot-toast';
import api from '../services/api';
import Skeleton from '../components/ui/Skeleton';
import Spinner from '../components/ui/Spinner';
import Pagination from '../components/ui/Pagination';
import Button from '../components/ui/Button';
export interface OrderItem {
id?: string;
@ -269,16 +273,79 @@ export default function Orders() {
setIsSavingTracking(true);
await api.put(`/admin/orders/${selectedOrder.id}/status`, {
status: modalStatus,
trackingNumber: modalTrackingCode
trackingNumber: modalTrackingCode,
});
await fetchOrders();
toast.success('تغییرات سفارش با موفقیت ذخیره شد');
} catch (err) {
console.error('Failed to save tracking number', err);
toast.error('خطا در ذخیره تغییرات سفارش');
} finally {
setIsSavingTracking(false);
}
};
// Refund Order State & Handlers
const [refundModalOrder, setRefundModalOrder] = useState<Order | null>(null);
const [refundTarget, setRefundTarget] = useState<'wallet' | 'zibal'>('wallet');
const [refundAmount, setRefundAmount] = useState<string>('');
const [refundReason, setRefundReason] = useState<string>('');
const [isProcessingRefund, setIsProcessingRefund] = useState(false);
const openOrderRefundModal = (order: Order) => {
setRefundModalOrder(order);
const amt = Number(order.totalAmount || order.total || 0);
setRefundAmount(String(amt));
// If order was paid by wallet, default to wallet. If online, let admin choose.
setRefundTarget(order.paymentMethod === 'wallet' ? 'wallet' : 'zibal');
setRefundReason(`استرداد سفارش #${order.trackingNumber || order.id.slice(0, 8)}`);
};
const handleExecuteOrderRefund = async () => {
if (!refundModalOrder) return;
try {
setIsProcessingRefund(true);
if (refundTarget === 'wallet') {
const res = await api.post(`/admin/orders/${refundModalOrder.id}/refund-wallet`, {
amount: refundAmount ? Number(refundAmount) : undefined,
reason: refundReason,
});
toast.success(res.data?.message || 'مبلغ با موفقیت به کیف پول کاربر عودت داده شد.');
setRefundModalOrder(null);
await fetchOrders();
} else {
// Find transaction trackId if exists
const tx = refundModalOrder.paymentTransactions?.[0];
const trackId = tx?.trackId;
if (!trackId) {
toast.error('شماره تراکنش زیبال (Track ID) برای این سفارش یافت نشد. لطفاً از گزینه کیف پول استفاده کنید.');
return;
}
const res = await api.post('/payment/admin/refund', {
trackId,
amount: refundAmount ? Number(refundAmount) * 10 : undefined,
tryReverse: true,
description: refundReason || `استرداد وجه سفارش #${refundModalOrder.trackingNumber || refundModalOrder.id.slice(0, 8)}`,
});
if (res.data?.result === 1 || res.data?.status === 1 || res.data?.success) {
toast.success('درخواست استرداد وجه به درگاه زیبال با موفقیت ارسال شد.');
// Also mark order as cancelled
await api.put(`/admin/orders/${refundModalOrder.id}/status`, { status: 'cancelled' });
setRefundModalOrder(null);
await fetchOrders();
} else {
toast.error(res.data?.message || 'خطا در ارسال استرداد وجه به زیبال');
}
}
} catch (err: any) {
toast.error(err?.response?.data?.message || 'خطا در انجام عملیات استرداد وجه');
} finally {
setIsProcessingRefund(false);
}
};
const handlePrintInvoice = (orderToPrint: Order) => {
if (!orderToPrint) return;
const printWindow = window.open('', '_blank');
@ -649,24 +716,37 @@ export default function Orders() {
</select>
</td>
<td className="py-4 px-6">
<div className="flex items-center justify-center gap-2">
<div className="flex items-center justify-center gap-1.5">
<button
onClick={() => openOrderModal(order)}
className="text-purple-600 hover:bg-purple-50 p-2 rounded-xl transition-colors flex items-center gap-1 font-bold text-xs"
className="text-purple-600 hover:bg-purple-50 p-2 rounded-xl transition-colors flex items-center gap-1 font-bold text-xs cursor-pointer"
title="مشاهده فاکتور و جزئیات کامل"
disabled={isProcessing}
>
<Eye className="w-4 h-4" />
<span>جزئیات</span>
</button>
<button
onClick={() => handlePrintInvoice(order)}
className="text-gray-600 hover:bg-gray-100 p-2 rounded-xl transition-colors"
className="text-gray-600 hover:bg-gray-100 p-2 rounded-xl transition-colors cursor-pointer"
title="چاپ فوری فاکتور رسمی"
disabled={isProcessing}
>
<Printer className="w-4 h-4" />
</button>
{(order.status === 'cancelled' || order.status === 'pending_payment') && (
<button
onClick={() => openOrderRefundModal(order)}
className="text-rose-600 hover:bg-rose-50 p-2 rounded-xl transition-colors flex items-center gap-1 font-bold text-xs cursor-pointer"
title="استرداد وجه به کاربر (کیف پول / شاپرک)"
disabled={isProcessing}
>
<Undo2 className="w-4 h-4" />
<span>استرداد</span>
</button>
)}
</div>
</td>
</tr>
@ -886,21 +966,142 @@ export default function Orders() {
</div>
{/* Modal Footer */}
<div className="p-6 bg-gray-50 border-t border-gray-200 flex gap-4 shrink-0">
<button
<div className="p-6 bg-gray-50 border-t border-gray-200 flex flex-wrap gap-3 shrink-0">
<Button
variant="primary"
startIcon={Download}
onClick={() => handlePrintInvoice(selectedOrder)}
className="flex-1 py-3.5 bg-purple-600 text-white rounded-xl font-black text-sm flex items-center justify-center gap-2 hover:bg-purple-700 transition-all shadow-lg shadow-purple-600/20"
className="flex-1"
>
<Download className="w-4 h-4" />
دانلود و چاپ فاکتور رسمی
</button>
<button
</Button>
{(selectedOrder.status === 'cancelled' || selectedOrder.status === 'pending_payment') && (
<Button
variant="danger"
startIcon={Undo2}
onClick={() => {
const ord = selectedOrder;
setSelectedOrder(null);
openOrderRefundModal(ord);
}}
>
استرداد وجه سفارش
</Button>
)}
<Button
variant="secondary"
onClick={() => setSelectedOrder(null)}
className="px-6 py-3.5 bg-white border border-gray-200 text-gray-700 rounded-xl font-bold text-sm hover:bg-gray-100 transition-all"
>
بستن
</Button>
</div>
</div>
</div>
)}
{/* Order Refund Modal (Wallet / Zibal Gateway) */}
{refundModalOrder && (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm font-vazir" dir="rtl">
<div className="bg-white rounded-3xl w-full max-w-lg border border-gray-200 shadow-2xl p-6 sm:p-8 space-y-5">
<div className="flex items-center justify-between border-b border-gray-100 pb-4">
<h3 className="text-base font-black text-gray-900 flex items-center gap-2">
<Undo2 className="w-5 h-5 text-rose-600" />
استرداد وجه سفارش #{refundModalOrder.trackingNumber || refundModalOrder.id.slice(0, 8)}
</h3>
<button
onClick={() => setRefundModalOrder(null)}
className="text-gray-400 hover:text-gray-700 font-bold text-lg cursor-pointer"
>
</button>
</div>
<div className="space-y-4 text-xs">
{/* Destination selector */}
<div>
<label className="block font-bold text-gray-700 mb-2">روش عودت و بازگشت وجه:</label>
<div className="grid grid-cols-2 gap-3">
<button
type="button"
onClick={() => setRefundTarget('wallet')}
className={`p-4 rounded-2xl border text-right transition-all cursor-pointer flex flex-col gap-1.5 ${
refundTarget === 'wallet'
? 'border-purple-600 bg-purple-50/60 ring-2 ring-purple-600/20'
: 'border-gray-200 hover:border-gray-300'
}`}
>
<div className="flex items-center gap-2 font-black text-gray-900">
<Wallet className="w-4 h-4 text-purple-600" />
<span>شارژ کیف پول کاربر</span>
</div>
<p className="text-[11px] text-gray-500 font-medium leading-relaxed">
افزایش فوری اعتبار کیف پول جهت خریدهای بعدی مشتری
</p>
</button>
<button
type="button"
onClick={() => setRefundTarget('zibal')}
className={`p-4 rounded-2xl border text-right transition-all cursor-pointer flex flex-col gap-1.5 ${
refundTarget === 'zibal'
? 'border-rose-600 bg-rose-50/60 ring-2 ring-rose-600/20'
: 'border-gray-200 hover:border-gray-300'
}`}
>
<div className="flex items-center gap-2 font-black text-gray-900">
<CreditCard className="w-4 h-4 text-rose-600" />
<span>استرداد شاپرک (زیبال)</span>
</div>
<p className="text-[11px] text-gray-500 font-medium leading-relaxed">
برگشت مستقیم به کارت بانکی مشتری از طریق درگاه پرداخت
</p>
</button>
</div>
</div>
<div>
<label className="block font-bold text-gray-700 mb-1">مبلغ استرداد به تومان</label>
<input
type="number"
value={refundAmount}
onChange={(e) => setRefundAmount(e.target.value)}
placeholder="مبلغ استرداد"
className="w-full border border-gray-200 rounded-xl p-3 font-mono outline-none focus:ring-2 focus:ring-purple-500 font-bold"
dir="ltr"
/>
</div>
<div>
<label className="block font-bold text-gray-700 mb-1">علت استرداد</label>
<textarea
rows={2}
value={refundReason}
onChange={(e) => setRefundReason(e.target.value)}
placeholder="علت لغو سفارش یا مرجوعی کالا..."
className="w-full border border-gray-200 rounded-xl p-3 outline-none focus:ring-2 focus:ring-purple-500"
/>
</div>
<div className="flex justify-end gap-3 pt-3 border-t border-gray-100">
<Button
variant="outline"
size="sm"
onClick={() => setRefundModalOrder(null)}
>
انصراف
</Button>
<Button
variant={refundTarget === 'wallet' ? 'primary' : 'danger'}
size="sm"
isLoading={isProcessingRefund}
onClick={handleExecuteOrderRefund}
>
{refundTarget === 'wallet' ? 'تایید و افزایش اعتبار کیف پول' : 'ارسال درخواست استرداد به زیبال'}
</Button>
</div>
</div>
</div>
</div>
)}

View File

@ -25,11 +25,15 @@ import {
Code,
Send,
Undo2,
Printer,
} from 'lucide-react';
import { toast } from 'react-hot-toast';
import api from '../services/api';
import Spinner from '../components/ui/Spinner';
import Pagination from '../components/ui/Pagination';
import Button from '../components/ui/Button';
import MaskableField from '../components/ui/MaskableField';
import TransactionReceiptModal, { type TransactionReceiptData } from '../components/TransactionReceiptModal';
interface Transaction {
id: string;
@ -114,8 +118,13 @@ export default function Transactions() {
const [liveInquiryData, setLiveInquiryData] = useState<any>(null);
const [showRawLogs, setShowRawLogs] = useState(false);
// Refund Modal State
// Receipt Modal State
const [receiptModalOpen, setReceiptModalOpen] = useState(false);
const [receiptData, setReceiptData] = useState<TransactionReceiptData | null>(null);
// Refund Modal State & Confirmation
const [refundModalOpen, setRefundModalOpen] = useState(false);
const [refundConfirmOpen, setRefundConfirmOpen] = useState(false);
const [refundForm, setRefundForm] = useState({
trackId: '',
amount: '',
@ -125,6 +134,32 @@ export default function Transactions() {
});
const [isSubmittingRefund, setIsSubmittingRefund] = useState(false);
const openReceiptModal = (tx: Transaction) => {
setReceiptData({
trackId: tx.trackId || '-',
refNumber: tx.refNumber || '-',
amountTomans: tx.amount,
amountRials: tx.amountRials || Number(tx.amount) * 10,
statusText: tx.status === 'VERIFIED' ? 'پرداخت موفق' : tx.status === 'PENDING' ? 'در انتظار پرداخت' : 'ناموفق',
orderNumber: tx.order?.orderNumber || (tx.orderId ? `سفارش ${tx.orderId.slice(0, 8)}` : '-'),
orderId: tx.orderId || undefined,
cardNumber: tx.cardNumber || '-',
paidAt: tx.paidAt ? new Date(tx.paidAt).toLocaleDateString('fa-IR') : undefined,
createdAt: new Date(tx.createdAt).toLocaleDateString('fa-IR', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
}),
mobile: tx.user?.mobile || '-',
description: tx.description || 'پرداخت سفارش آنلاین کنینا',
psp: tx.gateway === 'zibal' ? 'زیبال / به‌پرداخت ملت' : tx.gateway,
ip: tx.ipAddress || '-',
});
setReceiptModalOpen(true);
};
const openRefundModal = (tx: Transaction) => {
setRefundForm({
trackId: tx.trackId || '',
@ -136,8 +171,7 @@ export default function Transactions() {
setRefundModalOpen(true);
};
const handleRefundSubmit = async (e: React.FormEvent) => {
e.preventDefault();
const executeRefund = async () => {
if (!refundForm.trackId) {
toast.error('شناسه تراکنش (Track ID) الزامی است');
return;
@ -154,6 +188,7 @@ export default function Transactions() {
if (res.data?.result === 1 || res.data?.status === 1 || res.data?.success) {
toast.success(res.data?.message || 'درخواست استرداد وجه با موفقیت به زیبال ارسال شد.');
setRefundConfirmOpen(false);
setRefundModalOpen(false);
fetchTransactions();
fetchStats();
@ -167,6 +202,16 @@ export default function Transactions() {
}
};
const handleRefundSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!refundForm.trackId) {
toast.error('شناسه تراکنش (Track ID) الزامی است');
return;
}
// Open confirmation prompt modal
setRefundConfirmOpen(true);
};
const fetchStats = async () => {
try {
const res = await api.get('/payment/admin/stats');
@ -656,12 +701,20 @@ export default function Transactions() {
setLiveInquiryData(null);
setShowRawLogs(false);
}}
className="p-2 bg-gray-100 hover:bg-purple-50 text-gray-600 hover:text-purple-600 rounded-xl transition-all"
className="p-2 bg-gray-100 hover:bg-purple-50 text-gray-600 hover:text-purple-600 rounded-xl transition-all cursor-pointer"
title="مشاهده جزئیات و عیب‌یابی"
>
<Eye className="w-4 h-4" />
</button>
<button
onClick={() => openReceiptModal(tx)}
className="p-2 bg-emerald-50 hover:bg-emerald-100 text-emerald-600 rounded-xl transition-all cursor-pointer"
title="رسید تراکنش و چاپ"
>
<Printer className="w-4 h-4" />
</button>
{tx.gateway === 'zibal' && tx.trackId && (
<button
onClick={() => {
@ -757,31 +810,47 @@ export default function Transactions() {
<div>
<span className="text-gray-400 block font-medium">شناسه پیگیری (Track ID):</span>
<span className="font-mono font-bold text-gray-900 dir-ltr inline-block">
{selectedTx.trackId || 'ثبت نشده'}
</span>
<div className="mt-0.5">
<MaskableField
value={selectedTx.trackId}
isMaskedByDefault={false}
label="شناسه پیگیری"
/>
</div>
</div>
<div>
<span className="text-gray-400 block font-medium">شماره مرجع بانکی:</span>
<span className="font-mono font-bold text-gray-900 dir-ltr inline-block">
{selectedTx.refNumber || 'ثبت نشده'}
</span>
<div className="mt-0.5">
<MaskableField
value={selectedTx.refNumber}
isMaskedByDefault={false}
label="شماره مرجع بانکی"
/>
</div>
</div>
<div>
<span className="text-gray-400 block font-medium">شماره کارت ماسک شده:</span>
<span className="font-mono font-bold text-gray-900 dir-ltr inline-block">
{selectedTx.cardNumber || 'ثبت نشده'}
</span>
<span className="text-gray-400 block font-medium">شماره کارت:</span>
<div className="mt-0.5">
<MaskableField
value={selectedTx.cardNumber}
isMaskedByDefault={true}
label="شماره کارت"
/>
</div>
</div>
{selectedTx.ipAddress && (
<div>
<span className="text-gray-400 block font-medium">IP کلاینت:</span>
<span className="font-mono font-bold text-gray-700 dir-ltr inline-block">
{selectedTx.ipAddress}
</span>
<div className="mt-0.5">
<MaskableField
value={selectedTx.ipAddress}
isMaskedByDefault={false}
label="IP کلاینت"
/>
</div>
</div>
)}
@ -893,59 +962,86 @@ export default function Transactions() {
{/* Modal Actions */}
<div className="flex items-center justify-between gap-3 pt-2">
<div className="flex items-center gap-2">
<div className="flex flex-wrap items-center gap-2">
<Button
type="button"
variant="outline"
size="sm"
startIcon={Printer}
onClick={() => {
const tx = selectedTx;
setSelectedTx(null);
openReceiptModal(tx);
}}
>
رسید و چاپ
</Button>
{selectedTx.gateway !== 'zibal' && selectedTx.gateway !== 'online' && selectedTx.status !== 'VERIFIED' && (
<button
<Button
type="button"
variant="success"
size="sm"
startIcon={CheckCircle2}
onClick={() => handleManualVerify(selectedTx.id)}
className="bg-emerald-600 hover:bg-emerald-700 text-white font-black px-4 py-2.5 rounded-xl text-xs flex items-center gap-1.5 shadow-md shadow-emerald-600/20 transition-all cursor-pointer"
>
<CheckCircle2 className="w-4 h-4" />
<span>تایید دستی واریز کارت به کارت</span>
</button>
تایید دستی کارت به کارت
</Button>
)}
{selectedTx.gateway !== 'zibal' && selectedTx.gateway !== 'online' && selectedTx.status === 'PENDING' && (
<button
<Button
type="button"
variant="outline"
size="sm"
startIcon={XCircle}
className="text-rose-600 border-rose-200 hover:bg-rose-50"
onClick={() => handleManualReject(selectedTx.id)}
className="bg-rose-50 hover:bg-rose-100 text-rose-700 border border-rose-200 font-bold px-4 py-2.5 rounded-xl text-xs flex items-center gap-1.5 transition-all cursor-pointer"
>
<XCircle className="w-4 h-4" />
<span>رد فیش کارت به کارت</span>
</button>
رد فیش
</Button>
)}
{selectedTx.gateway === 'zibal' && selectedTx.trackId && selectedTx.status === 'VERIFIED' && (
<button
<Button
type="button"
variant="danger"
size="sm"
startIcon={Undo2}
info="برگشت وجه به حساب یا کارت مشتری"
onClick={() => {
const tx = selectedTx;
setSelectedTx(null);
openRefundModal(tx);
}}
className="bg-rose-600 hover:bg-rose-700 text-white font-black px-4 py-2.5 rounded-xl text-xs flex items-center gap-1.5 shadow-md shadow-rose-600/20 transition-all cursor-pointer"
>
<Undo2 className="w-4 h-4" />
<span>استرداد وجه به مشتری (Refund / Reverse)</span>
</button>
استرداد وجه
</Button>
)}
</div>
<button
<Button
type="button"
variant="secondary"
size="sm"
onClick={() => {
setSelectedTx(null);
setLiveInquiryData(null);
setShowRawLogs(false);
}}
className="bg-gray-100 hover:bg-gray-200 text-gray-800 font-bold px-6 py-2.5 rounded-xl text-xs transition-colors cursor-pointer"
>
بستن
</button>
</Button>
</div>
</div>
</div>
)}
{/* Refund Submission Modal */}
{/* Transaction Receipt Modal */}
<TransactionReceiptModal
isOpen={receiptModalOpen}
onClose={() => setReceiptModalOpen(false)}
data={receiptData}
/>
{/* Refund Submission Form Modal */}
{refundModalOpen && (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm">
<div className="bg-white rounded-3xl w-full max-w-lg border border-gray-200 shadow-2xl p-6 sm:p-8 space-y-6">
@ -1025,26 +1121,87 @@ export default function Transactions() {
</div>
<div className="flex justify-end gap-3 pt-3">
<button
<Button
type="button"
variant="outline"
size="sm"
onClick={() => setRefundModalOpen(false)}
className="px-5 py-2.5 rounded-xl font-bold bg-gray-100 hover:bg-gray-200 text-gray-600 cursor-pointer"
>
انصراف
</button>
<button
</Button>
<Button
type="submit"
disabled={isSubmittingRefund}
className="bg-rose-600 hover:bg-rose-700 text-white font-bold px-6 py-2.5 rounded-xl transition-colors flex items-center gap-2 cursor-pointer shadow-md shadow-rose-200"
variant="danger"
size="sm"
startIcon={Send}
>
{isSubmittingRefund ? <Spinner size="sm" /> : <Send className="w-4 h-4" />}
<span>ارسال استرداد وجه به زیبال</span>
</button>
بررسی و تایید نهایی استرداد
</Button>
</div>
</form>
</div>
</div>
)}
{/* Confirmation Modal Before Sending Refund */}
{refundConfirmOpen && (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm">
<div className="bg-white rounded-3xl w-full max-w-md border border-gray-200 shadow-2xl p-6 sm:p-8 space-y-6 font-vazir" dir="rtl">
<div className="flex items-center gap-3 text-rose-600">
<div className="w-12 h-12 bg-rose-50 rounded-2xl flex items-center justify-center">
<AlertTriangle className="w-6 h-6" />
</div>
<div>
<h3 className="text-base font-black text-gray-900">تایید عملیات استرداد وجه شاپرک</h3>
<p className="text-xs text-gray-500 mt-0.5">آیا از ارسال درخواست استرداد زیر به زیبال اطمینان دارید؟</p>
</div>
</div>
<div className="bg-rose-50/60 border border-rose-100 rounded-2xl p-4 space-y-2 text-xs">
<div className="flex justify-between">
<span className="text-gray-500 font-bold">شماره تراکنش:</span>
<span className="font-mono font-black text-gray-900">{refundForm.trackId}</span>
</div>
<div className="flex justify-between">
<span className="text-gray-500 font-bold">مبلغ استرداد:</span>
<span className="font-mono font-black text-rose-700">
{refundForm.amount ? `${Number(refundForm.amount).toLocaleString('fa-IR')} تومان` : 'کل مبلغ تراکنش'}
</span>
</div>
{refundForm.cardNumber && (
<div className="flex justify-between">
<span className="text-gray-500 font-bold">کارت مقصد:</span>
<span className="font-mono font-bold text-gray-800">{refundForm.cardNumber}</span>
</div>
)}
<div className="flex justify-between">
<span className="text-gray-500 font-bold">روش بازگشت:</span>
<span className="font-bold text-gray-900">
{refundForm.tryReverse ? 'برگشت آنی شاپرک (Reverse)' : 'واریز پایا بانکی (Refund)'}
</span>
</div>
</div>
<div className="flex justify-end gap-3 pt-2">
<Button
variant="outline"
size="sm"
onClick={() => setRefundConfirmOpen(false)}
>
بازگشت و ویرایش
</Button>
<Button
variant="danger"
size="sm"
isLoading={isSubmittingRefund}
onClick={executeRefund}
>
تایید قطعی و ارسال به زیبال
</Button>
</div>
</div>
</div>
)}
</div>
);
}

View File

@ -319,53 +319,53 @@ export default function ZibalPortalPage() {
</div>
{/* Main Feature Tabs (Matching Zibal Panel layout) */}
<div className="bg-white p-2 rounded-2xl shadow-sm border border-gray-200 flex items-center justify-between overflow-x-auto gap-2">
<div className="flex items-center gap-2">
<div className="bg-white p-2 rounded-2xl shadow-sm border border-gray-200 flex items-center justify-between overflow-x-auto gap-2 no-scrollbar">
<div className="flex items-center gap-2 shrink-0">
<button
onClick={() => setActiveMainTab('refunds')}
className={`px-5 py-2.5 rounded-xl font-black text-xs sm:text-sm transition-all flex items-center gap-2 cursor-pointer ${
className={`px-5 py-2.5 rounded-xl font-black text-xs sm:text-sm transition-all flex items-center gap-2 cursor-pointer whitespace-nowrap ${
activeMainTab === 'refunds'
? 'bg-indigo-600 text-white shadow-md shadow-indigo-200'
: 'text-gray-600 hover:bg-gray-100'
}`}
>
<RotateCcw className="w-4 h-4" />
<RotateCcw className="w-4 h-4 shrink-0" />
<span>استرداد وجه</span>
</button>
<button
onClick={() => setActiveMainTab('checkouts')}
className={`px-5 py-2.5 rounded-xl font-black text-xs sm:text-sm transition-all flex items-center gap-2 cursor-pointer ${
className={`px-5 py-2.5 rounded-xl font-black text-xs sm:text-sm transition-all flex items-center gap-2 cursor-pointer whitespace-nowrap ${
activeMainTab === 'checkouts'
? 'bg-indigo-600 text-white shadow-md shadow-indigo-200'
: 'text-gray-600 hover:bg-gray-100'
}`}
>
<CreditCard className="w-4 h-4" />
<CreditCard className="w-4 h-4 shrink-0" />
<span>گزارش واریز و تسویه</span>
</button>
<button
onClick={() => setActiveMainTab('queue')}
className={`px-5 py-2.5 rounded-xl font-black text-xs sm:text-sm transition-all flex items-center gap-2 cursor-pointer ${
className={`px-5 py-2.5 rounded-xl font-black text-xs sm:text-sm transition-all flex items-center gap-2 cursor-pointer whitespace-nowrap ${
activeMainTab === 'queue'
? 'bg-indigo-600 text-white shadow-md shadow-indigo-200'
: 'text-gray-600 hover:bg-gray-100'
}`}
>
<ListOrdered className="w-4 h-4" />
<ListOrdered className="w-4 h-4 shrink-0" />
<span>صف تسویه</span>
</button>
<button
onClick={() => setActiveMainTab('gateway_transactions')}
className={`px-5 py-2.5 rounded-xl font-black text-xs sm:text-sm transition-all flex items-center gap-2 cursor-pointer ${
className={`px-5 py-2.5 rounded-xl font-black text-xs sm:text-sm transition-all flex items-center gap-2 cursor-pointer whitespace-nowrap ${
activeMainTab === 'gateway_transactions'
? 'bg-indigo-600 text-white shadow-md shadow-indigo-200'
: 'text-gray-600 hover:bg-gray-100'
}`}
>
<TrendingUp className="w-4 h-4" />
<TrendingUp className="w-4 h-4 shrink-0" />
<span>تراکنشهای درگاه (IPG)</span>
</button>
</div>

View File

@ -5,7 +5,7 @@
"3": "app.module.ts",
"4": "reviews.controller.ts",
"5": "tickets.controller.ts",
"6": "userStore.ts",
"6": "AuthService",
"7": "MediaSelector.tsx",
"8": "PetProfile.tsx",
"9": "Roles",
@ -13,12 +13,12 @@
"11": "MenuService",
"12": "adminRoutes.tsx",
"13": "PrismaService",
"14": "PetsController",
"14": "pets/pets.controller.ts",
"15": "ProductsService",
"16": "UserDashboard.tsx",
"17": "CreateVideoDto",
"18": "src/services/api.ts",
"19": "auth.controller.ts",
"19": ".adminLogin",
"20": "BE-001",
"21": "SmsService",
"22": "FE-001",
@ -36,7 +36,7 @@
"34": "CategoriesController",
"35": "B2BService",
"36": "What You Must Do When Invoked",
"37": "useSettingsStore",
"37": ".update",
"38": "UsersService",
"39": "What You Must Do When Invoked",
"40": "SslController",
@ -44,28 +44,28 @@
"42": "IngredientsService",
"43": "FaqService",
"44": "MediaController",
"45": "Media.tsx",
"45": "Coupons.tsx",
"46": "ContactService",
"47": "zibal.service.ts",
"48": "PrescriptionsService",
"49": "SmartAdvisorService",
"50": "TestimonialsService",
"51": "Role & Core Objective",
"52": ".initiateOrderPayment",
"52": ".handleZibalCallback",
"53": "ZibalEBankService",
"54": "compilerOptions",
"55": "PrescriptionsManager.tsx",
"55": "Media.tsx",
"56": "compilerOptions",
"57": "PetsController",
"58": "PaginationDto",
"59": "dependencies",
"60": "compilerOptions",
"61": "ArchivePage.tsx",
"61": "HomeClient.tsx",
"62": "BlogsController",
"63": "ApiOperation",
"64": "BannersService",
"65": "Required Review Group Closures",
"66": "Coupons.tsx",
"66": "Products.tsx",
"67": "Operational Rules & Boundaries",
"68": "Operational Rules & Boundaries",
"69": "WikiController",
@ -78,10 +78,10 @@
"76": "Operational Rules & Boundaries",
"77": "scripts",
"78": "Role & Core Objective",
"79": "HomeController",
"80": "B2BPortal.tsx",
"79": "Transactions.tsx",
"80": "SafeImage.tsx",
"81": "devDependencies",
"82": "api",
"82": "auth.controller.ts",
"83": "Orders.tsx",
"84": "devDependencies",
"85": "seed-products.ts",
@ -101,9 +101,9 @@
"99": "Comprehensive Change Log",
"100": "Operational Rules & Boundaries",
"101": "UITexts.tsx",
"102": "BlogsController",
"103": "AdminTransactionFilterDto",
"104": "CreateOrderDto",
"102": "ApiResponse",
"103": "payment.service.ts",
"104": "PetsService",
"105": "1. Summary of Integrity Repairs Performed",
"106": "@nestjs/cli",
"107": "Operational Rules & Boundaries",
@ -140,9 +140,9 @@
"138": "InitiatePaymentDto",
"139": "System Discovery",
"140": "Product Requirement Document (PRD)",
"141": "WikiController",
"142": "useCartStore",
"143": "RedisService",
"141": "PetsController",
"142": "lib/services/api.ts",
"143": "auth.service.ts",
"144": "Baseline Command Plan & Reconciled Command History",
"145": "catalog/page.tsx",
"146": "ErrorPages.tsx",
@ -173,7 +173,7 @@
"171": "🎨 Frontend & Admin Panel Technical Review (06_dev_frontend)",
"172": "🚀 SEO & Content Strategy Review (12_seo_content)",
"173": "@types/node",
"174": "shop/page.tsx",
"174": "MetricsController",
"175": "seed-ui-texts.ts",
"176": "seed-wiki.ts",
"177": "update-blog.dto.ts",
@ -185,7 +185,7 @@
"183": "Raw Finding Verification & Disposition Report",
"184": "React + TypeScript + Vite",
"185": "Select.tsx",
"186": "Reports.tsx",
"186": "RegisterDto",
"187": "ts-node",
"188": "application/README.md",
"189": "@types/express",
@ -213,14 +213,14 @@
"211": "@types/multer",
"212": "videos/page.tsx",
"213": "@testing-library/jest-dom",
"214": "checkout/page.tsx",
"214": "AdminLoginDto",
"215": "AdminController",
"216": "FormField.tsx",
"217": "Input.tsx",
"218": "Textarea.tsx",
"219": "admin-panel/tsconfig.json",
"220": "getPageMetadata",
"221": "AuthService",
"221": "AuthController",
"222": "next.config.ts",
"223": "Shabnam Font README",
"224": "AGENTS.md",
@ -237,10 +237,10 @@
"235": "typescript",
"236": "vitest",
"237": "axios",
"238": ".createCoupon",
"238": "Body",
"239": "WholesaleApplyDto",
"240": "ts-loader",
"241": "zibal-ebank.service.ts",
"241": "VerifyOtpDto",
"242": "@types/bcrypt",
"243": "app.e2e-spec.js",
"244": "blog.entity.ts",
@ -290,13 +290,18 @@
"288": "Staging Docker Compose",
"289": "tailwindcss",
"290": "@types/passport-jwt",
"291": "ClientLayout.tsx",
"291": "useSettingsStore",
"292": "@types/supertest",
"293": "typescript",
"294": "app-audit-verification.e2e-spec.d.ts",
"295": "app.e2e-spec.d.ts",
"296": "@types/react-dom",
"297": "CreateHealthLogDto",
"298": "eslint-config-prettier",
"299": "CreateReminderDto",
"300": ".sendOtp",
"301": "track/page.tsx",
"302": "dashboard/page.tsx",
"303": "eslint-plugin-prettier",
"305": "RouteErrorBoundary",
"309": "globals",

File diff suppressed because one or more lines are too long

View File

@ -15,12 +15,12 @@
"13": "PrismaService",
"14": "PetsController",
"15": "ProductsService",
"16": "useUserStore",
"16": "UserDashboard.tsx",
"17": "CreateVideoDto",
"18": "src/services/api.ts",
"19": "auth.controller.ts",
"20": "BE-001",
"21": "SettingsController",
"21": "SmsService",
"22": "FE-001",
"23": "ADM-001",
"24": "DB-001",
@ -28,7 +28,7 @@
"26": "TEST-001",
"27": "DEVOPS-001",
"28": "DOC-001",
"29": "WholesaleApplyDto",
"29": "WholesaleService",
"30": "app-audit-verification.e2e-spec.js",
"31": "JwtAuthGuard",
"32": "ZibalService",
@ -47,7 +47,7 @@
"45": "Media.tsx",
"46": "ContactService",
"47": "zibal.service.ts",
"48": "PrescriptionsController",
"48": "PrescriptionsService",
"49": "SmartAdvisorService",
"50": "TestimonialsService",
"51": "Role & Core Objective",
@ -62,7 +62,7 @@
"60": "compilerOptions",
"61": "ArchivePage.tsx",
"62": "BlogsController",
"63": "SmsService",
"63": "ApiOperation",
"64": "BannersService",
"65": "Required Review Group Closures",
"66": "Coupons.tsx",
@ -103,7 +103,7 @@
"101": "UITexts.tsx",
"102": "BlogsController",
"103": "AdminTransactionFilterDto",
"104": "prescriptions.controller.ts",
"104": "CreateOrderDto",
"105": "1. Summary of Integrity Repairs Performed",
"106": "@nestjs/cli",
"107": "Operational Rules & Boundaries",
@ -142,7 +142,7 @@
"140": "Product Requirement Document (PRD)",
"141": "WikiController",
"142": "useCartStore",
"143": "auth.service.ts",
"143": "RedisService",
"144": "Baseline Command Plan & Reconciled Command History",
"145": "catalog/page.tsx",
"146": "ErrorPages.tsx",
@ -214,12 +214,13 @@
"212": "videos/page.tsx",
"213": "@testing-library/jest-dom",
"214": "checkout/page.tsx",
"215": "eslint-config-next",
"215": "AdminController",
"216": "FormField.tsx",
"217": "Input.tsx",
"218": "Textarea.tsx",
"219": "admin-panel/tsconfig.json",
"220": "getPageMetadata",
"221": "AuthService",
"222": "next.config.ts",
"223": "Shabnam Font README",
"224": "AGENTS.md",
@ -230,12 +231,16 @@
"229": "eslint-plugin-react-refresh",
"230": "@tailwindcss/postcss",
"231": "typescript",
"232": "ProductDto",
"233": "@testing-library/react",
"234": "@types/react",
"235": "typescript",
"236": "vitest",
"237": "axios",
"238": ".createCoupon",
"239": "WholesaleApplyDto",
"240": "ts-loader",
"241": "zibal-ebank.service.ts",
"242": "@types/bcrypt",
"243": "app.e2e-spec.js",
"244": "blog.entity.ts",
@ -290,6 +295,7 @@
"293": "typescript",
"294": "app-audit-verification.e2e-spec.d.ts",
"295": "app.e2e-spec.d.ts",
"296": "@types/react-dom",
"298": "eslint-config-prettier",
"303": "eslint-plugin-prettier",
"305": "RouteErrorBoundary",

View File

@ -1,16 +1,16 @@
# Graph Report - canina (2026-08-22)
## Corpus Check
- 536 files · ~759,121 words
- 536 files · ~759,763 words
- Verdict: corpus is large enough that graph structure adds value.
## Summary
- 3887 nodes · 6840 edges · 297 communities (197 shown, 100 thin omitted)
- 3887 nodes · 6841 edges · 303 communities (203 shown, 100 thin omitted)
- Extraction: 96% EXTRACTED · 4% INFERRED · 0% AMBIGUOUS · INFERRED: 261 edges (avg confidence: 0.79)
- Token cost: 0 input · 0 output
## Graph Freshness
- Built from commit: `bf41ba03`
- Built from commit: `ee32b4c1`
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
- Run `graphify update .` after code changes (no API cost).
@ -31,12 +31,12 @@
- PrismaService
- PetsController
- ProductsService
- useUserStore
- UserDashboard.tsx
- CreateVideoDto
- src/services/api.ts
- auth.controller.ts
- BE-001
- SettingsController
- SmsService
- FE-001
- ADM-001
- DB-001
@ -44,7 +44,7 @@
- TEST-001
- DEVOPS-001
- DOC-001
- WholesaleApplyDto
- WholesaleService
- app-audit-verification.e2e-spec.js
- JwtAuthGuard
- ZibalService
@ -63,7 +63,7 @@
- Media.tsx
- ContactService
- zibal.service.ts
- PrescriptionsController
- PrescriptionsService
- SmartAdvisorService
- TestimonialsService
- Role & Core Objective
@ -78,7 +78,7 @@
- compilerOptions
- ArchivePage.tsx
- BlogsController
- SmsService
- ApiOperation
- BannersService
- Required Review Group Closures
- Coupons.tsx
@ -119,7 +119,7 @@
- UITexts.tsx
- BlogsController
- AdminTransactionFilterDto
- prescriptions.controller.ts
- CreateOrderDto
- 1. Summary of Integrity Repairs Performed
- @nestjs/cli
- Operational Rules & Boundaries
@ -158,7 +158,7 @@
- Product Requirement Document (PRD)
- WikiController
- useCartStore
- auth.service.ts
- RedisService
- Baseline Command Plan & Reconciled Command History
- catalog/page.tsx
- ErrorPages.tsx
@ -230,12 +230,13 @@
- videos/page.tsx
- @testing-library/jest-dom
- checkout/page.tsx
- eslint-config-next
- AdminController
- FormField.tsx
- Input.tsx
- Textarea.tsx
- admin-panel/tsconfig.json
- getPageMetadata
- AuthService
- next.config.ts
- Shabnam Font README
- AGENTS.md
@ -246,11 +247,15 @@
- eslint-plugin-react-refresh
- @tailwindcss/postcss
- typescript
- ProductDto
- @testing-library/react
- @types/react
- typescript
- vitest
- .createCoupon
- WholesaleApplyDto
- ts-loader
- zibal-ebank.service.ts
- @types/bcrypt
- app.e2e-spec.js
- blog.entity.ts
@ -289,6 +294,7 @@
- ClientLayout.tsx
- @types/supertest
- typescript
- @types/react-dom
- eslint-config-prettier
- eslint-plugin-prettier
- RouteErrorBoundary
@ -329,23 +335,23 @@
- **Rastikerdar Font Ecosystem** — frontend_application_public_fonts_shabnam_font_v5_0_1_readme, frontend_application_public_fonts_vazirmatn_v33_003_readme, vazir_font [EXTRACTED 0.95]
- **Canina Deployment Stack** — scripts_compose_prod, scripts_compose_stage [EXTRACTED 1.00]
## Communities (297 total, 100 thin omitted)
## Communities (303 total, 100 thin omitted)
### Community 0 - "OrdersService"
Cohesion: 0.06
Nodes (35): CreateOrderDto, OrderItemDto, ApiProperty, ApiPropertyOptional, IsArray, IsNotEmpty, IsNumber, IsOptional (+27 more)
Cohesion: 0.10
Nodes (18): OrdersController, ApiBadRequestResponse, ApiBearerAuth, ApiCreatedResponse, ApiNotFoundResponse, ApiOkResponse, ApiOperation, ApiTags (+10 more)
### Community 1 - "productService.ts"
Cohesion: 0.08
Nodes (29): BlogPost, CatalogPageSpread(), CatalogPageSpreadProps, CategoryMeta, getFormBadge(), getSpeciesBadge(), FlipbookCatalog(), FlipbookCatalogProps (+21 more)
Cohesion: 0.09
Nodes (26): BlogPost, CatalogPageSpread(), CatalogPageSpreadProps, CategoryMeta, getFormBadge(), getSpeciesBadge(), FlipbookCatalog(), FlipbookCatalogProps (+18 more)
### Community 2 - "CmsController"
Cohesion: 0.09
Nodes (24): CmsController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+16 more)
### Community 3 - "app.module.ts"
Cohesion: 0.08
Nodes (32): B2BModule, Module, BannersModule, Module, CmsModule, Module, SmsModule, Global (+24 more)
Cohesion: 0.07
Nodes (34): B2BModule, Module, BannersModule, Module, CmsModule, Module, SmsModule, Global (+26 more)
### Community 4 - "reviews.controller.ts"
Cohesion: 0.07
@ -364,8 +370,8 @@ Cohesion: 0.08
Nodes (26): ConfirmModal(), ConfirmModalProps, ImagePreviewModal(), ImagePreviewModalProps, getFileType(), Media, MediaSelector(), MediaSelectorProps (+18 more)
### Community 8 - "PetProfile.tsx"
Cohesion: 0.13
Nodes (17): CheckoutPage(), FeaturedProducts(), ProductCard(), PetProfile(), SearchResultsPage(), OrderRowSkeleton(), PetProfileSkeleton(), ProductCardSkeleton() (+9 more)
Cohesion: 0.12
Nodes (19): CheckoutPage(), FeaturedProducts(), ProductCard(), PetProfile(), PrescriptionUploadModal(), PrescriptionUploadModalProps, SearchResultsPage(), OrderRowSkeleton() (+11 more)
### Community 9 - "Roles"
Cohesion: 0.24
@ -384,8 +390,8 @@ Cohesion: 0.09
Nodes (16): App(), HeroBanner, VetTestimonial, ContactInfoItem, ContactSubmission, CategoryDist, DashboardData, Ticket (+8 more)
### Community 13 - "PrismaService"
Cohesion: 0.07
Nodes (20): CategoryQuery, MeliPayamakPattern, MeliPayamakResponse, SendPatternSmsOptions, SmsConfig, SmsLogQuery, CreateContactSubmissionDto, UpdateContactInfoItemDto (+12 more)
Cohesion: 0.08
Nodes (18): CategoryQuery, AdminLoginInput, LoginInput, RegisterInput, MeliPayamakPattern, MeliPayamakResponse, SendPatternSmsOptions, SmsConfig (+10 more)
### Community 14 - "PetsController"
Cohesion: 0.05
@ -395,9 +401,9 @@ Nodes (47): CreateHealthLogDto, ApiProperty, ApiPropertyOptional, IsNotEmpty, Is
Cohesion: 0.09
Nodes (19): GetProductsDto, ApiPropertyOptional, IsEnum, IsOptional, IsString, ProductsController, ApiOperation, ApiTags (+11 more)
### Community 16 - "useUserStore"
Cohesion: 0.12
Nodes (23): VerifyContent(), AddressModal(), AddressModalProps, AuthModal(), AuthModalProps, extractOtpFromText(), BackButton(), BackButtonProps (+15 more)
### Community 16 - "UserDashboard.tsx"
Cohesion: 0.10
Nodes (30): VerifyContent(), AddressModal(), AddressModalProps, AuthModal(), AuthModalProps, extractOtpFromText(), BackButton(), BackButtonProps (+22 more)
### Community 17 - "CreateVideoDto"
Cohesion: 0.07
@ -415,9 +421,9 @@ Nodes (42): AuthController, ApiBadRequestResponse, ApiBearerAuth, ApiOkResponse,
Cohesion: 0.06
Nodes (32): Acceptance Criteria, Affected Application, Affected Files, Alternative Direction, BE-001, Category, Completion Statement, Confidence (+24 more)
### Community 21 - "SettingsController"
Cohesion: 0.08
Nodes (24): SmsLogQueryDto, ApiPropertyOptional, IsIn, IsNumber, IsOptional, IsString, Type, SettingsController (+16 more)
### Community 21 - "SmsService"
Cohesion: 0.06
Nodes (27): SmsLogQuery, SmsService, Injectable, SmsLogQueryDto, ApiPropertyOptional, IsIn, IsNumber, IsOptional (+19 more)
### Community 22 - "FE-001"
Cohesion: 0.06
@ -447,17 +453,17 @@ Nodes (31): Acceptance Criteria, Affected Application, Affected Files, Alternati
Cohesion: 0.06
Nodes (31): Acceptance Criteria, Affected Application, Affected Files, Alternative Direction, Category, Completion Statement, Confidence, Dependencies (+23 more)
### Community 29 - "WholesaleApplyDto"
Cohesion: 0.10
Nodes (20): ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString, WholesaleApplyDto, ApiBearerAuth, ApiOperation (+12 more)
### Community 29 - "WholesaleService"
Cohesion: 0.14
Nodes (14): ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Get, Param, Post (+6 more)
### Community 30 - "app-audit-verification.e2e-spec.js"
Cohesion: 0.07
Nodes (22): AppModule, Module, CustomHttpExceptionFilter, Catch, PrismaExceptionFilter, Catch, DecimalInterceptor, Injectable (+14 more)
Cohesion: 0.08
Nodes (20): CustomHttpExceptionFilter, Catch, PrismaExceptionFilter, Catch, DecimalInterceptor, Injectable, ApiErrorResponse, ErrorDetailField (+12 more)
### Community 31 - "JwtAuthGuard"
Cohesion: 0.20
Nodes (6): JwtAuthGuard, Injectable, ROLES_KEY, RequestWithUser, RolesGuard, Injectable
Nodes (7): JwtAuthGuard, Injectable, ROLES_KEY, RequestWithUser, RolesGuard, Injectable, UserReqPayload
### Community 32 - "ZibalService"
Cohesion: 0.09
@ -480,8 +486,8 @@ Cohesion: 0.07
Nodes (26): For /graphify add and --watch, For /graphify query, For the commit hook and native CLAUDE.md integration, For --update and --cluster-only, /graphify, Honesty Rules, Interpreter guard for subcommands, Part A - Structural extraction for code files (+18 more)
### Community 37 - "useSettingsStore"
Cohesion: 0.08
Nodes (33): HomeClient(), BlogPost, BlogPreviewSection(), ContactInfoItem, DeleteConfirmModal(), DeleteConfirmModalProps, EnamadBadge(), FAQItem (+25 more)
Cohesion: 0.09
Nodes (29): HomeClient(), HomeClientProps, BlogPost, BlogPreviewSection(), ContactInfoItem, EnamadBadge(), FAQItem, FAQSection() (+21 more)
### Community 38 - "UsersService"
Cohesion: 0.06
@ -523,9 +529,9 @@ Nodes (11): ContactController, Body, Controller, Get, Param, Post, Put, Query (+
Cohesion: 0.16
Nodes (9): GatewayHealthResult, IPaymentGateway, PaymentInquiryResult, PaymentRequestOptions, PaymentRequestResult, PaymentVerifyResult, ZibalInquiryResponse, ZibalRequestResponse (+1 more)
### Community 48 - "PrescriptionsController"
Cohesion: 0.15
Nodes (12): PrescriptionsController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Get, Param (+4 more)
### Community 48 - "PrescriptionsService"
Cohesion: 0.14
Nodes (14): PrescriptionsController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Get, Param (+6 more)
### Community 49 - "SmartAdvisorService"
Cohesion: 0.13
@ -540,7 +546,7 @@ Cohesion: 0.09
Nodes (22): CREATE MODE — Normal Operation, Expected JSON Output Schema, File Scope Boundary, Forbidden Actions, MODE 1: REVIEW (called during Review Phase), MODE 2: CONTENT CREATION (standalone task from backlog), Operating Modes, Operational Rules & Boundaries (+14 more)
### Community 52 - ".initiateOrderPayment"
Cohesion: 0.18
Cohesion: 0.19
Nodes (10): ApiPropertyOptional, IsOptional, IsString, ZibalCallbackQueryDto, ApiBadRequestResponse, ApiOkResponse, Req, Res (+2 more)
### Community 54 - "compilerOptions"
@ -556,12 +562,12 @@ Cohesion: 0.08
Nodes (23): compilerOptions, allowImportingTsExtensions, erasableSyntaxOnly, jsx, lib, module, moduleDetection, moduleResolution (+15 more)
### Community 57 - "PetsController"
Cohesion: 0.15
Nodes (11): PetsController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Controller, Delete, Get (+3 more)
Cohesion: 0.10
Nodes (14): PetsController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Controller, Delete, Get (+6 more)
### Community 58 - "PaginationDto"
Cohesion: 0.06
Nodes (19): BlogsService, Injectable, BlogsModule, Module, BlogsService, Injectable, PaginationDto, SortOrder (+11 more)
Cohesion: 0.07
Nodes (15): BlogsService, Injectable, BlogsModule, Module, BlogsService, Injectable, PaginationDto, SortOrder (+7 more)
### Community 59 - "dependencies"
Cohesion: 0.05
@ -572,13 +578,17 @@ Cohesion: 0.10
Nodes (20): compilerOptions, allowImportingTsExtensions, erasableSyntaxOnly, lib, module, moduleDetection, moduleResolution, noEmit (+12 more)
### Community 61 - "ArchivePage.tsx"
Cohesion: 0.13
Nodes (15): HomeClientProps, ArchiveProductCard(), CATEGORY_MAP, ICON_MAP, BannerPlacement(), BannerPlacementProps, B2BInquiry, Banner (+7 more)
Cohesion: 0.15
Nodes (13): ArchiveProductCard(), CATEGORY_MAP, ICON_MAP, BannerPlacement(), BannerPlacementProps, B2BInquiry, Banner, DosageConfig (+5 more)
### Community 62 - "BlogsController"
Cohesion: 0.14
Nodes (16): BlogsController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Body, Controller, Delete (+8 more)
### Community 63 - "ApiOperation"
Cohesion: 0.14
Nodes (8): ApiOperation, ApiQuery, Get, Query, AdminQueryDto, ApiPropertyOptional, IsOptional, IsString
### Community 64 - "BannersService"
Cohesion: 0.13
Nodes (15): BannersController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+7 more)
@ -604,8 +614,8 @@ Cohesion: 0.13
Nodes (14): ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Body, Controller, Delete, Get (+6 more)
### Community 70 - "admin.service.ts"
Cohesion: 0.15
Nodes (14): CouponInput, CouponTargetInput, PaginationQuery, CreateUserDto, ApiProperty, ApiPropertyOptional, IsEmail, IsNotEmpty (+6 more)
Cohesion: 0.20
Nodes (13): CouponInput, CouponTargetInput, PaginationQuery, CreateUserDto, ApiProperty, ApiPropertyOptional, IsEmail, IsNotEmpty (+5 more)
### Community 71 - "getSeoConfig"
Cohesion: 0.24
@ -649,7 +659,7 @@ Nodes (11): ProductDetailModalProps, SafeImage(), SafeImageProps, DisplayVideoIt
### Community 81 - "devDependencies"
Cohesion: 0.13
Nodes (15): devDependencies, eslint, jsdom, tailwindcss, @tailwindcss/postcss, @types/node, @types/react-dom, @vitejs/plugin-react (+7 more)
Nodes (15): eslint-config-next, devDependencies, eslint, eslint-config-next, jsdom, tailwindcss, @tailwindcss/postcss, @types/node (+7 more)
### Community 82 - "api"
Cohesion: 0.15
@ -716,8 +726,8 @@ Cohesion: 0.15
Nodes (13): jest, collectCoverageFrom, coverageDirectory, moduleFileExtensions, rootDir, testEnvironment, testRegex, transform (+5 more)
### Community 98 - "AdminService"
Cohesion: 0.06
Nodes (27): AdminController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Body, Controller, Delete (+19 more)
Cohesion: 0.15
Nodes (5): Body, Param, Put, AdminService, Injectable
### Community 99 - "Comprehensive Change Log"
Cohesion: 0.15
@ -739,9 +749,9 @@ Nodes (14): BlogsController, ApiBearerAuth, ApiNotFoundResponse, ApiOkResponse,
Cohesion: 0.22
Nodes (7): AdminTransactionFilterDto, ApiPropertyOptional, IsIn, IsNumber, IsOptional, IsString, Type
### Community 104 - "prescriptions.controller.ts"
Cohesion: 0.31
Nodes (5): UserReqPayload, PrescriptionsModule, Module, PrescriptionsService, Injectable
### Community 104 - "CreateOrderDto"
Cohesion: 0.13
Nodes (17): CreateOrderDto, OrderItemDto, ApiProperty, ApiPropertyOptional, IsArray, IsNotEmpty, IsNumber, IsOptional (+9 more)
### Community 105 - "1. Summary of Integrity Repairs Performed"
Cohesion: 0.17
@ -880,16 +890,16 @@ Cohesion: 0.29
Nodes (6): 1. Executive Vision, 2. Target Audience, 3. Functional Requirements, 4. Non-Functional Requirements (Performance, Security), 5. Epic / Feature Breakdown, Product Requirement Document (PRD)
### Community 141 - "WikiController"
Cohesion: 0.21
Nodes (9): ApiNotFoundResponse, ApiOkResponse, ApiOperation, ApiTags, Controller, Get, Param, Query (+1 more)
Cohesion: 0.13
Nodes (13): ApiNotFoundResponse, ApiOkResponse, ApiOperation, ApiTags, Controller, Get, Param, Query (+5 more)
### Community 142 - "useCartStore"
Cohesion: 0.12
Nodes (11): CartDrawer(), OrderSuccess(), OrderTracking(), mockProduct, ApiErr, Order, OrderItem, OrderService (+3 more)
### Community 143 - "auth.service.ts"
### Community 143 - "RedisService"
Cohesion: 0.08
Nodes (12): ApiExcludeController, AdminLoginInput, AuthService, LoginInput, RegisterInput, Injectable, MetricsController, Controller (+4 more)
Nodes (12): ApiExcludeController, AppModule, Module, MetricsController, Controller, Get, Res, RedisModule (+4 more)
### Community 144 - "Baseline Command Plan & Reconciled Command History"
Cohesion: 0.29
@ -1027,14 +1037,34 @@ Nodes (3): COMPOSE_DOCKER_CLI_BUILD, DOCKER_BUILDKIT, deploy.sh script
Cohesion: 0.67
Nodes (3): ADR-AUTH-001: Admin Panel Authentication Architecture & Token Management, Master Task Backlog (Phase 3.3), Phase 3 Master Execution Plan
### Community 215 - "AdminController"
Cohesion: 0.18
Nodes (6): AdminController, ApiBearerAuth, ApiTags, Controller, Delete, UseGuards
### Community 220 - "getPageMetadata"
Cohesion: 0.14
Nodes (7): generateMetadata(), generateMetadata(), generateMetadata(), generateMetadata(), generateMetadata(), generateMetadata(), getPageMetadata()
### Community 221 - "AuthService"
Cohesion: 0.22
Nodes (3): AuthService, Injectable, normalizeMobile()
### Community 223 - "Shabnam Font README"
Cohesion: 0.67
Nodes (3): Shabnam Font README, Shabnam Font Sample, Vazir Font
### Community 232 - "ProductDto"
Cohesion: 0.22
Nodes (7): ProductDto, ApiPropertyOptional, IsArray, IsBoolean, IsNumber, IsOptional, IsString
### Community 239 - "WholesaleApplyDto"
Cohesion: 0.29
Nodes (6): ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString, WholesaleApplyDto
### Community 241 - "zibal-ebank.service.ts"
Cohesion: 0.40
Nodes (4): EBankCheckoutListFilter, EBankCheckoutOptions, EBankIdentifiedPaymentFilter, EBankStatementFilter
### Community 243 - "app.e2e-spec.js"
Cohesion: 0.50
Nodes (3): app_module_1, supertest_1, testing_1
@ -1048,8 +1078,8 @@ Cohesion: 0.22
Nodes (3): Props, RouteErrorBoundary, State
### Community 310 - "admin.module.ts"
Cohesion: 0.06
Nodes (21): AdminModule, Module, PetQuery, PetsService, Injectable, ReportsController, ApiBearerAuth, ApiOperation (+13 more)
Cohesion: 0.08
Nodes (15): AdminModule, Module, ReportsController, ApiBearerAuth, ApiOperation, ApiTags, Controller, Get (+7 more)
## Knowledge Gaps
- **1269 isolated node(s):** `$schema`, `collection`, `sourceRoot`, `deleteOutDir`, `name` (+1264 more)
@ -1061,15 +1091,15 @@ _Questions this graph is uniquely positioned to answer:_
- **Why does `ApiResponse` connect `src/services/api.ts` to `OrdersService`, `BlogsController`, `UsersService`, `WikiController`, `PetsController`, `HomeController`, `ProductsService`, `auth.controller.ts`?**
_High betweenness centrality (0.064) - this node is a cross-community bridge._
- **Why does `Roles()` connect `Roles` to `BannersService`, `CmsController`, `B2BService`, `reviews.controller.ts`, `tickets.controller.ts`, `SslController`, `prescriptions.controller.ts`, `IngredientsService`, `FaqService`, `MenuService`, `ContactService`, `ProductsService`, `PrescriptionsController`, `SmartAdvisorService`, `TestimonialsService`, `SettingsController`, `WholesaleApplyDto`, `JwtAuthGuard`?**
- **Why does `Roles()` connect `Roles` to `BannersService`, `CmsController`, `B2BService`, `reviews.controller.ts`, `tickets.controller.ts`, `SslController`, `IngredientsService`, `FaqService`, `MenuService`, `ContactService`, `ProductsService`, `PrescriptionsService`, `SmartAdvisorService`, `TestimonialsService`, `SmsService`, `WholesaleService`, `JwtAuthGuard`?**
_High betweenness centrality (0.062) - this node is a cross-community bridge._
- **Why does `JwtAuthGuard` connect `JwtAuthGuard` to `OrdersService`, `CmsController`, `reviews.controller.ts`, `tickets.controller.ts`, `admin.service.ts`, `UsersService`, `prescriptions.controller.ts`, `DoctorQueryDto`, `PetsController`, `ProductsService`, `CreateVideoDto`, `auth.controller.ts`, `admin.module.ts`, `PaginationDto`?**
- **Why does `JwtAuthGuard` connect `JwtAuthGuard` to `CmsController`, `reviews.controller.ts`, `tickets.controller.ts`, `admin.service.ts`, `UsersService`, `CreateOrderDto`, `DoctorQueryDto`, `PetsController`, `ProductsService`, `CreateVideoDto`, `auth.controller.ts`, `admin.module.ts`, `PetsController`, `PaginationDto`?**
_High betweenness centrality (0.032) - this node is a cross-community bridge._
- **What connects `$schema`, `collection`, `sourceRoot` to the rest of the system?**
_1269 weakly-connected nodes found - possible documentation gaps or missing edges._
- **Should `OrdersService` be split into smaller, more focused modules?**
_Cohesion score 0.06168831168831169 - nodes in this community are weakly interconnected._
_Cohesion score 0.10160427807486631 - nodes in this community are weakly interconnected._
- **Should `productService.ts` be split into smaller, more focused modules?**
_Cohesion score 0.08156028368794327 - nodes in this community are weakly interconnected._
_Cohesion score 0.08985200845665962 - nodes in this community are weakly interconnected._
- **Should `CmsController` be split into smaller, more focused modules?**
_Cohesion score 0.0899854862119013 - nodes in this community are weakly interconnected._

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,16 @@
# Graph Report - canina (2026-08-22)
## Corpus Check
- 536 files · ~759,763 words
- 539 files · ~762,534 words
- Verdict: corpus is large enough that graph structure adds value.
## Summary
- 3887 nodes · 6841 edges · 303 communities (203 shown, 100 thin omitted)
- Extraction: 96% EXTRACTED · 4% INFERRED · 0% AMBIGUOUS · INFERRED: 261 edges (avg confidence: 0.79)
- 3902 nodes · 6878 edges · 308 communities (207 shown, 101 thin omitted)
- Extraction: 96% EXTRACTED · 4% INFERRED · 0% AMBIGUOUS · INFERRED: 262 edges (avg confidence: 0.79)
- Token cost: 0 input · 0 output
## Graph Freshness
- Built from commit: `ee32b4c1`
- Built from commit: `f9e40564`
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
- Run `graphify update .` after code changes (no API cost).
@ -21,7 +21,7 @@
- app.module.ts
- reviews.controller.ts
- tickets.controller.ts
- userStore.ts
- AuthService
- MediaSelector.tsx
- PetProfile.tsx
- Roles
@ -29,12 +29,12 @@
- MenuService
- adminRoutes.tsx
- PrismaService
- PetsController
- pets/pets.controller.ts
- ProductsService
- UserDashboard.tsx
- CreateVideoDto
- src/services/api.ts
- auth.controller.ts
- .adminLogin
- BE-001
- SmsService
- FE-001
@ -52,7 +52,7 @@
- CategoriesController
- B2BService
- What You Must Do When Invoked
- useSettingsStore
- .update
- UsersService
- What You Must Do When Invoked
- SslController
@ -60,28 +60,28 @@
- IngredientsService
- FaqService
- MediaController
- Media.tsx
- Coupons.tsx
- ContactService
- zibal.service.ts
- PrescriptionsService
- SmartAdvisorService
- TestimonialsService
- Role & Core Objective
- .initiateOrderPayment
- .handleZibalCallback
- ZibalEBankService
- compilerOptions
- PrescriptionsManager.tsx
- Media.tsx
- compilerOptions
- PetsController
- PaginationDto
- dependencies
- compilerOptions
- ArchivePage.tsx
- HomeClient.tsx
- BlogsController
- ApiOperation
- BannersService
- Required Review Group Closures
- Coupons.tsx
- Products.tsx
- Operational Rules & Boundaries
- Operational Rules & Boundaries
- WikiController
@ -94,10 +94,10 @@
- Operational Rules & Boundaries
- scripts
- Role & Core Objective
- HomeController
- B2BPortal.tsx
- Transactions.tsx
- SafeImage.tsx
- devDependencies
- api
- auth.controller.ts
- Orders.tsx
- devDependencies
- seed-products.ts
@ -117,9 +117,9 @@
- Comprehensive Change Log
- Operational Rules & Boundaries
- UITexts.tsx
- BlogsController
- AdminTransactionFilterDto
- CreateOrderDto
- ApiResponse
- payment.service.ts
- PetsService
- 1. Summary of Integrity Repairs Performed
- @nestjs/cli
- Operational Rules & Boundaries
@ -156,9 +156,9 @@
- InitiatePaymentDto
- System Discovery
- Product Requirement Document (PRD)
- WikiController
- useCartStore
- RedisService
- PetsController
- lib/services/api.ts
- auth.service.ts
- Baseline Command Plan & Reconciled Command History
- catalog/page.tsx
- ErrorPages.tsx
@ -189,7 +189,7 @@
- 🎨 Frontend & Admin Panel Technical Review (06_dev_frontend)
- 🚀 SEO & Content Strategy Review (12_seo_content)
- @types/node
- shop/page.tsx
- MetricsController
- seed-ui-texts.ts
- seed-wiki.ts
- update-blog.dto.ts
@ -201,7 +201,7 @@
- Raw Finding Verification & Disposition Report
- React + TypeScript + Vite
- Select.tsx
- Reports.tsx
- RegisterDto
- ts-node
- application/README.md
- @types/express
@ -229,14 +229,14 @@
- @types/multer
- videos/page.tsx
- @testing-library/jest-dom
- checkout/page.tsx
- AdminLoginDto
- AdminController
- FormField.tsx
- Input.tsx
- Textarea.tsx
- admin-panel/tsconfig.json
- getPageMetadata
- AuthService
- AuthController
- next.config.ts
- Shabnam Font README
- AGENTS.md
@ -252,10 +252,10 @@
- @types/react
- typescript
- vitest
- .createCoupon
- Body
- WholesaleApplyDto
- ts-loader
- zibal-ebank.service.ts
- VerifyOtpDto
- @types/bcrypt
- app.e2e-spec.js
- blog.entity.ts
@ -291,11 +291,15 @@
- Production Docker Compose
- Staging Docker Compose
- @types/passport-jwt
- ClientLayout.tsx
- useSettingsStore
- @types/supertest
- typescript
- @types/react-dom
- CreateHealthLogDto
- eslint-config-prettier
- CreateReminderDto
- track/page.tsx
- dashboard/page.tsx
- eslint-plugin-prettier
- RouteErrorBoundary
- globals
@ -309,22 +313,22 @@
4. `api` - 43 edges
5. `SmsService` - 42 edges
6. `PaginationDto` - 41 edges
7. `ZibalService` - 37 edges
8. `PaymentController` - 36 edges
9. `JwtAuthGuard` - 35 edges
10. `AdminService` - 34 edges
7. `PaymentController` - 37 edges
8. `ZibalService` - 37 edges
9. `AdminService` - 35 edges
10. `JwtAuthGuard` - 35 edges
## Surprising Connections (you probably didn't know these)
- `User Profile Photo` --conceptually_related_to--> `User Roles and Capabilities` [INFERRED]
backend/uploads/1781288429353-508765350.jpg → docs/02-user-guide.md
- `AuthController` --references--> `ApiResponse` [EXTRACTED]
backend/src/auth/auth.controller.ts → frontend/admin-panel/src/types/admin.ts
- `BlogsController` --references--> `ApiResponse` [EXTRACTED]
backend/src/blogs/blogs.controller.ts → frontend/admin-panel/src/types/admin.ts
- `HomeController` --references--> `ApiResponse` [EXTRACTED]
backend/src/home/home.controller.ts → frontend/admin-panel/src/types/admin.ts
- `OrdersController` --references--> `ApiResponse` [EXTRACTED]
backend/src/orders/orders.controller.ts → frontend/admin-panel/src/types/admin.ts
- `PetsController` --references--> `ApiResponse` [EXTRACTED]
backend/src/pets/pets.controller.ts → frontend/admin-panel/src/types/admin.ts
- `ProductsController` --references--> `ApiResponse` [EXTRACTED]
backend/src/products/products.controller.ts → frontend/admin-panel/src/types/admin.ts
## Import Cycles
- 3-file cycle: `frontend/application/lib/services/api.ts -> frontend/application/lib/store/userStore.ts -> frontend/application/lib/services/authService.ts -> frontend/application/lib/services/api.ts`
@ -335,15 +339,15 @@
- **Rastikerdar Font Ecosystem** — frontend_application_public_fonts_shabnam_font_v5_0_1_readme, frontend_application_public_fonts_vazirmatn_v33_003_readme, vazir_font [EXTRACTED 0.95]
- **Canina Deployment Stack** — scripts_compose_prod, scripts_compose_stage [EXTRACTED 1.00]
## Communities (303 total, 100 thin omitted)
## Communities (308 total, 101 thin omitted)
### Community 0 - "OrdersService"
Cohesion: 0.10
Nodes (18): OrdersController, ApiBadRequestResponse, ApiBearerAuth, ApiCreatedResponse, ApiNotFoundResponse, ApiOkResponse, ApiOperation, ApiTags (+10 more)
Cohesion: 0.06
Nodes (35): CreateOrderDto, OrderItemDto, ApiProperty, ApiPropertyOptional, IsArray, IsNotEmpty, IsNumber, IsOptional (+27 more)
### Community 1 - "productService.ts"
Cohesion: 0.09
Nodes (26): BlogPost, CatalogPageSpread(), CatalogPageSpreadProps, CategoryMeta, getFormBadge(), getSpeciesBadge(), FlipbookCatalog(), FlipbookCatalogProps (+18 more)
Cohesion: 0.10
Nodes (24): BlogPost, CatalogPageSpread(), CatalogPageSpreadProps, CategoryMeta, getFormBadge(), getSpeciesBadge(), FlipbookCatalog(), FlipbookCatalogProps (+16 more)
### Community 2 - "CmsController"
Cohesion: 0.09
@ -351,7 +355,7 @@ Nodes (24): CmsController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controlle
### Community 3 - "app.module.ts"
Cohesion: 0.07
Nodes (34): B2BModule, Module, BannersModule, Module, CmsModule, Module, SmsModule, Global (+26 more)
Nodes (36): B2BModule, Module, BannersModule, Module, CmsModule, Module, SmsModule, Global (+28 more)
### Community 4 - "reviews.controller.ts"
Cohesion: 0.07
@ -359,31 +363,27 @@ Nodes (31): CreateReviewDto, ApiProperty, IsInt, IsNotEmpty, IsOptional, IsStrin
### Community 5 - "tickets.controller.ts"
Cohesion: 0.09
Nodes (29): AdminUpdateTicketDto, CreateTicketDto, ReplyTicketDto, ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString (+21 more)
### Community 6 - "userStore.ts"
Cohesion: 0.11
Nodes (10): LoginModal(), LoginModalProps, ApiErr, AuthResponse, AuthService, User, Transaction, UserProfile (+2 more)
Nodes (31): AdminUpdateTicketDto, CreateTicketDto, ReplyTicketDto, ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString (+23 more)
### Community 7 - "MediaSelector.tsx"
Cohesion: 0.08
Nodes (26): ConfirmModal(), ConfirmModalProps, ImagePreviewModal(), ImagePreviewModalProps, getFileType(), Media, MediaSelector(), MediaSelectorProps (+18 more)
Cohesion: 0.07
Nodes (26): ConfirmModal(), ConfirmModalProps, getFileType(), Media, MediaSelector(), MediaSelectorProps, BlogPost, Category (+18 more)
### Community 8 - "PetProfile.tsx"
Cohesion: 0.12
Nodes (19): CheckoutPage(), FeaturedProducts(), ProductCard(), PetProfile(), PrescriptionUploadModal(), PrescriptionUploadModalProps, SearchResultsPage(), OrderRowSkeleton() (+11 more)
Cohesion: 0.11
Nodes (18): FeaturedProducts(), ProductCard(), OrderSuccess(), PrescriptionUploadModal(), PrescriptionUploadModalProps, SearchResultsPage(), OrderRowSkeleton(), PetProfileSkeleton() (+10 more)
### Community 9 - "Roles"
Cohesion: 0.24
Nodes (13): Roles(), PaymentController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Get (+5 more)
Cohesion: 0.20
Nodes (15): Roles(), PaymentController, ApiBadRequestResponse, ApiBearerAuth, ApiOkResponse, ApiOperation, ApiTags, Body (+7 more)
### Community 10 - "DoctorQueryDto"
Cohesion: 0.09
Nodes (24): DoctorsController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+16 more)
Cohesion: 0.08
Nodes (26): DoctorsController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+18 more)
### Community 11 - "MenuService"
Cohesion: 0.10
Nodes (19): MenuController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Body, Controller, Delete (+11 more)
Cohesion: 0.12
Nodes (16): MenuController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Body, Controller, Delete (+8 more)
### Community 12 - "adminRoutes.tsx"
Cohesion: 0.09
@ -391,31 +391,31 @@ Nodes (16): App(), HeroBanner, VetTestimonial, ContactInfoItem, ContactSubmissio
### Community 13 - "PrismaService"
Cohesion: 0.08
Nodes (18): CategoryQuery, AdminLoginInput, LoginInput, RegisterInput, MeliPayamakPattern, MeliPayamakResponse, SendPatternSmsOptions, SmsConfig (+10 more)
Nodes (18): MeliPayamakPattern, MeliPayamakResponse, SendPatternSmsOptions, SmsConfig, SmsLogQuery, CreateContactSubmissionDto, UpdateContactInfoItemDto, MenuType (+10 more)
### Community 14 - "PetsController"
Cohesion: 0.05
Nodes (47): CreateHealthLogDto, ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString, CreatePetDto, ApiProperty (+39 more)
### Community 14 - "pets/pets.controller.ts"
Cohesion: 0.16
Nodes (13): CreatePetDto, ApiProperty, ApiPropertyOptional, IsArray, IsNotEmpty, IsNumber, IsOptional, IsString (+5 more)
### Community 15 - "ProductsService"
Cohesion: 0.09
Nodes (19): GetProductsDto, ApiPropertyOptional, IsEnum, IsOptional, IsString, ProductsController, ApiOperation, ApiTags (+11 more)
Cohesion: 0.13
Nodes (12): ProductsController, ApiOperation, ApiTags, Body, Controller, Get, Param, Patch (+4 more)
### Community 16 - "UserDashboard.tsx"
Cohesion: 0.10
Nodes (30): VerifyContent(), AddressModal(), AddressModalProps, AuthModal(), AuthModalProps, extractOtpFromText(), BackButton(), BackButtonProps (+22 more)
Cohesion: 0.09
Nodes (41): VerifyContent(), AddressModal(), AddressModalProps, ArchiveProductCard(), AuthModal(), AuthModalProps, extractOtpFromText(), B2BPortal() (+33 more)
### Community 17 - "CreateVideoDto"
Cohesion: 0.07
Nodes (31): CreateVideoDto, ApiProperty, ApiPropertyOptional, IsBoolean, IsNotEmpty, IsOptional, IsString, UpdateVideoDto (+23 more)
Cohesion: 0.08
Nodes (29): CreateVideoDto, ApiProperty, ApiPropertyOptional, IsBoolean, IsNotEmpty, IsOptional, IsString, UpdateVideoDto (+21 more)
### Community 18 - "src/services/api.ts"
Cohesion: 0.09
Nodes (25): ProtectedRoute(), SEARCHABLE_PAGES, Topbar(), TopbarProps, Login(), B2BManager, SeoSettingsPage, SmartAdvisorManager (+17 more)
Cohesion: 0.08
Nodes (30): Layout(), ProtectedRoute(), MenuGroup, Sidebar(), SidebarProps, SubMenuItem, SEARCHABLE_PAGES, Topbar() (+22 more)
### Community 19 - "auth.controller.ts"
Cohesion: 0.06
Nodes (42): AuthController, ApiBadRequestResponse, ApiBearerAuth, ApiOkResponse, ApiOperation, ApiTags, Body, Controller (+34 more)
### Community 19 - ".adminLogin"
Cohesion: 0.34
Nodes (9): ApiBadRequestResponse, ApiBearerAuth, ApiOkResponse, ApiOperation, Body, Post, Req, UseGuards (+1 more)
### Community 20 - "BE-001"
Cohesion: 0.06
@ -423,7 +423,7 @@ Nodes (32): Acceptance Criteria, Affected Application, Affected Files, Alternati
### Community 21 - "SmsService"
Cohesion: 0.06
Nodes (27): SmsLogQuery, SmsService, Injectable, SmsLogQueryDto, ApiPropertyOptional, IsIn, IsNumber, IsOptional (+19 more)
Nodes (26): SmsService, Injectable, SmsLogQueryDto, ApiPropertyOptional, IsIn, IsNumber, IsOptional, IsString (+18 more)
### Community 22 - "FE-001"
Cohesion: 0.06
@ -458,8 +458,8 @@ Cohesion: 0.14
Nodes (14): ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Get, Param, Post (+6 more)
### Community 30 - "app-audit-verification.e2e-spec.js"
Cohesion: 0.08
Nodes (20): CustomHttpExceptionFilter, Catch, PrismaExceptionFilter, Catch, DecimalInterceptor, Injectable, ApiErrorResponse, ErrorDetailField (+12 more)
Cohesion: 0.07
Nodes (22): AppModule, Module, CustomHttpExceptionFilter, Catch, PrismaExceptionFilter, Catch, DecimalInterceptor, Injectable (+14 more)
### Community 31 - "JwtAuthGuard"
Cohesion: 0.20
@ -467,7 +467,7 @@ Nodes (7): JwtAuthGuard, Injectable, ROLES_KEY, RequestWithUser, RolesGuard, Inj
### Community 32 - "ZibalService"
Cohesion: 0.09
Nodes (4): PaymentService, Injectable, Injectable, ZibalService
Nodes (5): Put, PaymentService, Injectable, Injectable, ZibalService
### Community 33 - "راهنمای تست سیستم (Software Testing)"
Cohesion: 0.07
@ -485,9 +485,9 @@ Nodes (15): B2BController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controlle
Cohesion: 0.07
Nodes (26): For /graphify add and --watch, For /graphify query, For the commit hook and native CLAUDE.md integration, For --update and --cluster-only, /graphify, Honesty Rules, Interpreter guard for subcommands, Part A - Structural extraction for code files (+18 more)
### Community 37 - "useSettingsStore"
Cohesion: 0.09
Nodes (29): HomeClient(), HomeClientProps, BlogPost, BlogPreviewSection(), ContactInfoItem, EnamadBadge(), FAQItem, FAQSection() (+21 more)
### Community 37 - ".update"
Cohesion: 0.24
Nodes (11): ApiNotFoundResponse, ApiOkResponse, ApiOperation, Body, Delete, Get, Param, Patch (+3 more)
### Community 38 - "UsersService"
Cohesion: 0.06
@ -517,9 +517,9 @@ Nodes (14): FaqController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controlle
Cohesion: 0.11
Nodes (16): MediaController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+8 more)
### Community 45 - "Media.tsx"
Cohesion: 0.11
Nodes (15): Pagination(), PaginationProps, getFileType(), Media, MediaManager(), Pet, GatewayHealth, Stats (+7 more)
### Community 45 - "Coupons.tsx"
Cohesion: 0.12
Nodes (12): Pagination(), PaginationProps, Coupon, CouponFormData, CouponModalProps, CouponTarget, Pet, ProductItem (+4 more)
### Community 46 - "ContactService"
Cohesion: 0.13
@ -545,17 +545,17 @@ Nodes (14): TestimonialsController, ApiBearerAuth, ApiOperation, ApiTags, Body,
Cohesion: 0.09
Nodes (22): CREATE MODE — Normal Operation, Expected JSON Output Schema, File Scope Boundary, Forbidden Actions, MODE 1: REVIEW (called during Review Phase), MODE 2: CONTENT CREATION (standalone task from backlog), Operating Modes, Operational Rules & Boundaries (+14 more)
### Community 52 - ".initiateOrderPayment"
Cohesion: 0.19
Nodes (10): ApiPropertyOptional, IsOptional, IsString, ZibalCallbackQueryDto, ApiBadRequestResponse, ApiOkResponse, Req, Res (+2 more)
### Community 52 - ".handleZibalCallback"
Cohesion: 0.25
Nodes (7): ApiPropertyOptional, IsOptional, IsString, ZibalCallbackQueryDto, Res, Headers, Ip
### Community 54 - "compilerOptions"
Cohesion: 0.06
Nodes (30): compilerOptions, allowSyntheticDefaultImports, baseUrl, declaration, emitDecoratorMetadata, esModuleInterop, experimentalDecorators, forceConsistentCasingInFileNames (+22 more)
### Community 55 - "PrescriptionsManager.tsx"
Cohesion: 0.22
Nodes (7): Badge(), BadgeProps, BadgeVariant, variantStyles, ProductItem, PrescriptionsManager, Prescription
### Community 55 - "Media.tsx"
Cohesion: 0.14
Nodes (13): Badge(), BadgeProps, BadgeVariant, variantStyles, ImagePreviewModal(), ImagePreviewModalProps, getFileType(), Media (+5 more)
### Community 56 - "compilerOptions"
Cohesion: 0.08
@ -566,8 +566,8 @@ Cohesion: 0.10
Nodes (14): PetsController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Controller, Delete, Get (+6 more)
### Community 58 - "PaginationDto"
Cohesion: 0.07
Nodes (15): BlogsService, Injectable, BlogsModule, Module, BlogsService, Injectable, PaginationDto, SortOrder (+7 more)
Cohesion: 0.06
Nodes (24): BlogsService, Injectable, BlogsModule, Module, BlogsService, Injectable, PaginationDto, SortOrder (+16 more)
### Community 59 - "dependencies"
Cohesion: 0.05
@ -577,16 +577,16 @@ Nodes (41): dependencies, bcrypt, bcryptjs, class-transformer, class-validator,
Cohesion: 0.10
Nodes (20): compilerOptions, allowImportingTsExtensions, erasableSyntaxOnly, lib, module, moduleDetection, moduleResolution, noEmit (+12 more)
### Community 61 - "ArchivePage.tsx"
Cohesion: 0.15
Nodes (13): ArchiveProductCard(), CATEGORY_MAP, ICON_MAP, BannerPlacement(), BannerPlacementProps, B2BInquiry, Banner, DosageConfig (+5 more)
### Community 61 - "HomeClient.tsx"
Cohesion: 0.11
Nodes (20): HomeClient(), HomeClientProps, CATEGORY_MAP, ICON_MAP, BannerPlacement(), BannerPlacementProps, FAQItem, FAQSection() (+12 more)
### Community 62 - "BlogsController"
Cohesion: 0.14
Nodes (16): BlogsController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Body, Controller, Delete (+8 more)
### Community 63 - "ApiOperation"
Cohesion: 0.14
Cohesion: 0.17
Nodes (8): ApiOperation, ApiQuery, Get, Query, AdminQueryDto, ApiPropertyOptional, IsOptional, IsString
### Community 64 - "BannersService"
@ -597,9 +597,9 @@ Nodes (15): BannersController, ApiBearerAuth, ApiOperation, ApiTags, Body, Contr
Cohesion: 0.10
Nodes (19): 10. Orders Backend, 11. Settings & Administrative Backend, 12. Prisma Schema, Migrations & Seed, 13. Redis & Temporary Auth State, 14. Unit & E2E Tests, 15. Docker, NGINX, Prometheus & Deployment Config, 16. Documentation & OpenAPI Artifacts, 1. Storefront Shell & Routing (+11 more)
### Community 66 - "Coupons.tsx"
Cohesion: 0.10
Nodes (15): formatPriceWithCommas(), PriceInput(), PriceInputProps, toEnglishDigits(), Coupon, CouponFormData, CouponModalProps, CouponTarget (+7 more)
### Community 66 - "Products.tsx"
Cohesion: 0.16
Nodes (10): formatPriceWithCommas(), PriceInput(), PriceInputProps, toEnglishDigits(), Category, Product, FinancialSettingsPage, Products (+2 more)
### Community 67 - "Operational Rules & Boundaries"
Cohesion: 0.11
@ -614,8 +614,8 @@ Cohesion: 0.13
Nodes (14): ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Body, Controller, Delete, Get (+6 more)
### Community 70 - "admin.service.ts"
Cohesion: 0.20
Nodes (13): CouponInput, CouponTargetInput, PaginationQuery, CreateUserDto, ApiProperty, ApiPropertyOptional, IsEmail, IsNotEmpty (+5 more)
Cohesion: 0.16
Nodes (14): CouponTargetInput, PaginationQuery, CreateUserDto, ApiProperty, ApiPropertyOptional, IsEmail, IsNotEmpty, IsNumber (+6 more)
### Community 71 - "getSeoConfig"
Cohesion: 0.24
@ -649,21 +649,21 @@ Nodes (17): concurrently, devDependencies, concurrently, name, private, scripts,
Cohesion: 0.12
Nodes (16): 1. Active Secret Scanning (All Modified Files), 2. Docker Container Verification (if Dockerfile exists), 3. CI/CD Basic Check (if `.github/workflows/` exists), 4. Failure Routing Protocol, 5. Forbidden Actions, ENFORCE MODE — Normal Operation, Expected JSON Output Schema, Operational Rules & Boundaries (+8 more)
### Community 79 - "HomeController"
Cohesion: 0.16
Nodes (10): HomeController, ApiOkResponse, ApiOperation, ApiTags, Controller, Get, HomeModule, Module (+2 more)
### Community 79 - "Transactions.tsx"
Cohesion: 0.15
Nodes (13): TransactionReceiptData, TransactionReceiptModal(), TransactionReceiptModalProps, Button(), ButtonProps, ButtonSize, ButtonVariant, MaskableField() (+5 more)
### Community 80 - "B2BPortal.tsx"
Cohesion: 0.16
Nodes (11): ProductDetailModalProps, SafeImage(), SafeImageProps, DisplayVideoItem, FALLBACK_VIDEOS, TestimonialItem, PLAYBACK_RATES, VideoModalPlayer() (+3 more)
### Community 80 - "SafeImage.tsx"
Cohesion: 0.11
Nodes (16): BlogPost, BlogPreviewSection(), ProductDetailModalProps, SafeImage(), SafeImageProps, Testimonial, TestimonialsSection(), DisplayVideoItem (+8 more)
### Community 81 - "devDependencies"
Cohesion: 0.13
Nodes (15): eslint-config-next, devDependencies, eslint, eslint-config-next, jsdom, tailwindcss, @tailwindcss/postcss, @types/node (+7 more)
### Community 82 - "api"
Cohesion: 0.15
Nodes (10): Layout(), MenuGroup, Sidebar(), SidebarProps, SubMenuItem, MENU_TABS, MenuItem, MenuType (+2 more)
### Community 82 - "auth.controller.ts"
Cohesion: 0.18
Nodes (10): LoginDto, ApiProperty, IsNotEmpty, IsString, MinLength, SendOtpDto, ApiProperty, IsNotEmpty (+2 more)
### Community 83 - "Orders.tsx"
Cohesion: 0.14
@ -726,8 +726,8 @@ Cohesion: 0.15
Nodes (13): jest, collectCoverageFrom, coverageDirectory, moduleFileExtensions, rootDir, testEnvironment, testRegex, transform (+5 more)
### Community 98 - "AdminService"
Cohesion: 0.15
Nodes (5): Body, Param, Put, AdminService, Injectable
Cohesion: 0.13
Nodes (5): Delete, Param, Put, AdminService, Injectable
### Community 99 - "Comprehensive Change Log"
Cohesion: 0.15
@ -741,17 +741,13 @@ Nodes (11): 1. Detect Test Runner from Tech Stack, 2. Execution Output Capture (
Cohesion: 0.10
Nodes (18): ICON_REGISTRY, IconPickerModal(), IconPickerModalProps, COLORS, RichTextEditor(), RichTextEditorProps, ToggleSwitch(), ToggleSwitchProps (+10 more)
### Community 102 - "BlogsController"
Cohesion: 0.17
Nodes (14): BlogsController, ApiBearerAuth, ApiNotFoundResponse, ApiOkResponse, ApiOperation, ApiTags, Body, Controller (+6 more)
### Community 102 - "ApiResponse"
Cohesion: 0.06
Nodes (34): BlogsController, ApiBearerAuth, ApiNotFoundResponse, ApiOkResponse, ApiOperation, ApiTags, Body, Controller (+26 more)
### Community 103 - "AdminTransactionFilterDto"
Cohesion: 0.22
Nodes (7): AdminTransactionFilterDto, ApiPropertyOptional, IsIn, IsNumber, IsOptional, IsString, Type
### Community 104 - "CreateOrderDto"
Cohesion: 0.13
Nodes (17): CreateOrderDto, OrderItemDto, ApiProperty, ApiPropertyOptional, IsArray, IsNotEmpty, IsNumber, IsOptional (+9 more)
### Community 103 - "payment.service.ts"
Cohesion: 0.20
Nodes (8): AdminTransactionFilterDto, ApiPropertyOptional, IsIn, IsNumber, IsOptional, IsString, Type, ClientMetadata
### Community 105 - "1. Summary of Integrity Repairs Performed"
Cohesion: 0.17
@ -826,8 +822,8 @@ Cohesion: 0.20
Nodes (9): Arch Linux, Contributors, Install, Known problems for variable version, License, Sahel-Font, To Do (variable), طریقه استفاده از نسخه متغیر variable (+1 more)
### Community 124 - "Spinner.tsx"
Cohesion: 0.11
Nodes (12): Spinner(), FAQ, ProductReview, Reviews(), toPersianDigits(), SslStatus, FAQManager, PaymentGatewaysPage (+4 more)
Cohesion: 0.07
Nodes (21): Spinner(), MENU_TABS, MenuItem, MenuType, BestSellerItem, CategoryDistItem, COLORS, CustomTooltipProps (+13 more)
### Community 125 - "Sahel-Font"
Cohesion: 0.20
@ -889,17 +885,17 @@ Nodes (7): Active Core Applications, Current Architecture, Evidence-Based Status
Cohesion: 0.29
Nodes (6): 1. Executive Vision, 2. Target Audience, 3. Functional Requirements, 4. Non-Functional Requirements (Performance, Security), 5. Epic / Feature Breakdown, Product Requirement Document (PRD)
### Community 141 - "WikiController"
Cohesion: 0.13
Nodes (13): ApiNotFoundResponse, ApiOkResponse, ApiOperation, ApiTags, Controller, Get, Param, Query (+5 more)
### Community 141 - "PetsController"
Cohesion: 0.18
Nodes (9): PetsController, ApiBadRequestResponse, ApiBearerAuth, ApiCreatedResponse, ApiTags, Controller, UploadedFile, UseGuards (+1 more)
### Community 142 - "useCartStore"
Cohesion: 0.12
Nodes (11): CartDrawer(), OrderSuccess(), OrderTracking(), mockProduct, ApiErr, Order, OrderItem, OrderService (+3 more)
### Community 143 - "RedisService"
### Community 142 - "lib/services/api.ts"
Cohesion: 0.08
Nodes (12): ApiExcludeController, AppModule, Module, MetricsController, Controller, Get, Res, RedisModule (+4 more)
Nodes (19): ContactInfoItem, api, ApiErrorPayload, baseURL, ApiErr, AuthResponse, User, ApiErr (+11 more)
### Community 143 - "auth.service.ts"
Cohesion: 0.14
Nodes (8): AdminLoginInput, LoginInput, RegisterInput, RedisModule, Global, Module, RedisService, Injectable
### Community 144 - "Baseline Command Plan & Reconciled Command History"
Cohesion: 0.29
@ -997,6 +993,10 @@ Nodes (3): Architectural Overview, Critical Review Findings & Required Enhanceme
Cohesion: 0.50
Nodes (3): Overview & Content Foundation, SEO & Content Requirements, 🚀 SEO & Content Strategy Review (12_seo_content)
### Community 174 - "MetricsController"
Cohesion: 0.20
Nodes (5): ApiExcludeController, MetricsController, Controller, Get, Res
### Community 180 - "graphify reference: add a URL and watch a folder"
Cohesion: 0.50
Nodes (3): For /graphify add, For --watch, graphify reference: add a URL and watch a folder
@ -1021,9 +1021,9 @@ Nodes (3): Expanding the ESLint configuration, React Compiler, React + TypeScrip
Cohesion: 0.50
Nodes (3): Select, SelectOption, SelectProps
### Community 186 - "Reports.tsx"
Cohesion: 0.20
Nodes (7): BestSellerItem, CategoryDistItem, COLORS, CustomTooltipProps, ReportData, TopCouponItem, Reports
### Community 186 - "RegisterDto"
Cohesion: 0.22
Nodes (8): RegisterDto, ApiProperty, ApiPropertyOptional, IsEmail, IsNotEmpty, IsOptional, IsString, MinLength
### Community 188 - "application/README.md"
Cohesion: 0.50
@ -1037,41 +1037,57 @@ Nodes (3): COMPOSE_DOCKER_CLI_BUILD, DOCKER_BUILDKIT, deploy.sh script
Cohesion: 0.67
Nodes (3): ADR-AUTH-001: Admin Panel Authentication Architecture & Token Management, Master Task Backlog (Phase 3.3), Phase 3 Master Execution Plan
### Community 214 - "AdminLoginDto"
Cohesion: 0.29
Nodes (6): AdminLoginDto, ApiProperty, IsEmail, IsNotEmpty, IsString, MinLength
### Community 215 - "AdminController"
Cohesion: 0.18
Nodes (6): AdminController, ApiBearerAuth, ApiTags, Controller, Delete, UseGuards
Cohesion: 0.25
Nodes (5): AdminController, ApiBearerAuth, ApiTags, Controller, UseGuards
### Community 220 - "getPageMetadata"
Cohesion: 0.14
Nodes (7): generateMetadata(), generateMetadata(), generateMetadata(), generateMetadata(), generateMetadata(), generateMetadata(), getPageMetadata()
Cohesion: 0.13
Nodes (8): generateMetadata(), generateMetadata(), generateMetadata(), generateMetadata(), generateMetadata(), generateMetadata(), ArchivePage(), getPageMetadata()
### Community 221 - "AuthService"
Cohesion: 0.22
Nodes (3): AuthService, Injectable, normalizeMobile()
### Community 221 - "AuthController"
Cohesion: 0.20
Nodes (5): AuthController, ApiTags, Controller, AuthService, Injectable
### Community 223 - "Shabnam Font README"
Cohesion: 0.67
Nodes (3): Shabnam Font README, Shabnam Font Sample, Vazir Font
### Community 232 - "ProductDto"
Cohesion: 0.22
Cohesion: 0.20
Nodes (7): ProductDto, ApiPropertyOptional, IsArray, IsBoolean, IsNumber, IsOptional, IsString
### Community 238 - "Body"
Cohesion: 0.21
Nodes (3): Body, Post, CouponInput
### Community 239 - "WholesaleApplyDto"
Cohesion: 0.29
Nodes (6): ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString, WholesaleApplyDto
### Community 241 - "zibal-ebank.service.ts"
Cohesion: 0.40
Nodes (4): EBankCheckoutListFilter, EBankCheckoutOptions, EBankIdentifiedPaymentFilter, EBankStatementFilter
### Community 241 - "VerifyOtpDto"
Cohesion: 0.29
Nodes (6): ApiProperty, IsNotEmpty, IsString, Matches, VerifyOtpDto, Length
### Community 243 - "app.e2e-spec.js"
Cohesion: 0.50
Nodes (3): app_module_1, supertest_1, testing_1
### Community 291 - "ClientLayout.tsx"
Cohesion: 0.18
Nodes (11): ClientLayout(), B2BPortal(), BrandLogo(), BrandLogoProps, Footer(), MaintenancePage(), NetworkBanner(), useNetworkStatus() (+3 more)
### Community 291 - "useSettingsStore"
Cohesion: 0.13
Nodes (20): ClientLayout(), BrandLogo(), BrandLogoProps, EnamadBadge(), Footer(), MENU_ICONS, MaintenancePage(), NetworkBanner() (+12 more)
### Community 297 - "CreateHealthLogDto"
Cohesion: 0.29
Nodes (6): CreateHealthLogDto, ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString
### Community 299 - "CreateReminderDto"
Cohesion: 0.29
Nodes (6): CreateReminderDto, ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString
### Community 305 - "RouteErrorBoundary"
Cohesion: 0.22
@ -1079,27 +1095,27 @@ Nodes (3): Props, RouteErrorBoundary, State
### Community 310 - "admin.module.ts"
Cohesion: 0.08
Nodes (15): AdminModule, Module, ReportsController, ApiBearerAuth, ApiOperation, ApiTags, Controller, Get (+7 more)
Nodes (16): AdminModule, Module, CategoryQuery, ReportsController, ApiBearerAuth, ApiOperation, ApiTags, Controller (+8 more)
## Knowledge Gaps
- **1269 isolated node(s):** `$schema`, `collection`, `sourceRoot`, `deleteOutDir`, `name` (+1264 more)
- **1274 isolated node(s):** `$schema`, `collection`, `sourceRoot`, `deleteOutDir`, `name` (+1269 more)
These have ≤1 connection - possible missing edges or undocumented components.
- **100 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
- **101 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
## Suggested Questions
_Questions this graph is uniquely positioned to answer:_
- **Why does `ApiResponse` connect `src/services/api.ts` to `OrdersService`, `BlogsController`, `UsersService`, `WikiController`, `PetsController`, `HomeController`, `ProductsService`, `auth.controller.ts`?**
- **Why does `ApiResponse` connect `ApiResponse` to `OrdersService`, `UsersService`, `PetsController`, `ProductsService`, `src/services/api.ts`, `AuthController`?**
_High betweenness centrality (0.064) - this node is a cross-community bridge._
- **Why does `Roles()` connect `Roles` to `BannersService`, `CmsController`, `B2BService`, `reviews.controller.ts`, `tickets.controller.ts`, `SslController`, `IngredientsService`, `FaqService`, `MenuService`, `ContactService`, `ProductsService`, `PrescriptionsService`, `SmartAdvisorService`, `TestimonialsService`, `SmsService`, `WholesaleService`, `JwtAuthGuard`?**
_High betweenness centrality (0.062) - this node is a cross-community bridge._
- **Why does `JwtAuthGuard` connect `JwtAuthGuard` to `CmsController`, `reviews.controller.ts`, `tickets.controller.ts`, `admin.service.ts`, `UsersService`, `CreateOrderDto`, `DoctorQueryDto`, `PetsController`, `ProductsService`, `CreateVideoDto`, `auth.controller.ts`, `admin.module.ts`, `PetsController`, `PaginationDto`?**
_High betweenness centrality (0.032) - this node is a cross-community bridge._
- **Why does `Roles()` connect `Roles` to `BannersService`, `ZibalService`, `CmsController`, `B2BService`, `reviews.controller.ts`, `tickets.controller.ts`, `SslController`, `IngredientsService`, `FaqService`, `MenuService`, `ContactService`, `ProductsService`, `PrescriptionsService`, `SmartAdvisorService`, `TestimonialsService`, `SmsService`, `WholesaleService`, `JwtAuthGuard`?**
_High betweenness centrality (0.060) - this node is a cross-community bridge._
- **Why does `PrismaService` connect `PrismaService` to `OrdersService`, `CmsController`, `app.module.ts`, `reviews.controller.ts`, `tickets.controller.ts`, `DoctorQueryDto`, `MenuService`, `pets/pets.controller.ts`, `auth.service.ts`, `ProductsService`, `CreateVideoDto`, `SmsService`, `WholesaleService`, `ZibalService`, `CategoriesController`, `B2BService`, `UsersService`, `IngredientsService`, `FaqService`, `MediaController`, `MetricsController`, `ContactService`, `zibal.service.ts`, `PrescriptionsService`, `SmartAdvisorService`, `TestimonialsService`, `ZibalEBankService`, `admin.module.ts`, `PetsController`, `PaginationDto`, `BannersService`, `admin.service.ts`, `seo.module.ts`, `AuthController`, `ApiResponse`, `payment.service.ts`, `PetsService`, `WholesaleApplyDto`?**
_High betweenness centrality (0.040) - this node is a cross-community bridge._
- **What connects `$schema`, `collection`, `sourceRoot` to the rest of the system?**
_1269 weakly-connected nodes found - possible documentation gaps or missing edges._
_1274 weakly-connected nodes found - possible documentation gaps or missing edges._
- **Should `OrdersService` be split into smaller, more focused modules?**
_Cohesion score 0.10160427807486631 - nodes in this community are weakly interconnected._
_Cohesion score 0.059932659932659935 - nodes in this community are weakly interconnected._
- **Should `productService.ts` be split into smaller, more focused modules?**
_Cohesion score 0.08985200845665962 - nodes in this community are weakly interconnected._
_Cohesion score 0.09619450317124736 - nodes in this community are weakly interconnected._
- **Should `CmsController` be split into smaller, more focused modules?**
_Cohesion score 0.0899854862119013 - nodes in this community are weakly interconnected._

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff