feat(perf): implement comprehensive Core Web Vitals, ISR caching, and media optimizations
All checks were successful
Deploy Canina / deploy (push) Successful in 1m48s
All checks were successful
Deploy Canina / deploy (push) Successful in 1m48s
This commit is contained in:
parent
2a31a9f8e4
commit
fe6a900665
76
backend/package-lock.json
generated
76
backend/package-lock.json
generated
@ -21,6 +21,7 @@
|
|||||||
"bcryptjs": "^3.0.3",
|
"bcryptjs": "^3.0.3",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.15.1",
|
"class-validator": "^0.15.1",
|
||||||
|
"compression": "^1.8.1",
|
||||||
"helmet": "^8.2.0",
|
"helmet": "^8.2.0",
|
||||||
"ioredis": "^5.11.0",
|
"ioredis": "^5.11.0",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
@ -38,6 +39,7 @@
|
|||||||
"@nestjs/testing": "^11.0.1",
|
"@nestjs/testing": "^11.0.1",
|
||||||
"@types/bcrypt": "^6.0.0",
|
"@types/bcrypt": "^6.0.0",
|
||||||
"@types/bcryptjs": "^2.4.6",
|
"@types/bcryptjs": "^2.4.6",
|
||||||
|
"@types/compression": "^1.8.1",
|
||||||
"@types/express": "^5.0.0",
|
"@types/express": "^5.0.0",
|
||||||
"@types/jest": "^30.0.0",
|
"@types/jest": "^30.0.0",
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
@ -2855,6 +2857,17 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/compression": {
|
||||||
|
"version": "1.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.8.1.tgz",
|
||||||
|
"integrity": "sha512-kCFuWS0ebDbmxs0AXYn6e2r2nrGAb5KwQhknjSPSPgJcGd8+HVSILlUyFhGqML2gk39HcG7D1ydW9/qpYkN00Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/express": "*",
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/connect": {
|
"node_modules/@types/connect": {
|
||||||
"version": "3.4.38",
|
"version": "3.4.38",
|
||||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
|
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
|
||||||
@ -4911,6 +4924,60 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/compressible": {
|
||||||
|
"version": "2.0.18",
|
||||||
|
"resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
|
||||||
|
"integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"mime-db": ">= 1.43.0 < 2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/compression": {
|
||||||
|
"version": "1.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz",
|
||||||
|
"integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"bytes": "3.1.2",
|
||||||
|
"compressible": "~2.0.18",
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"negotiator": "~0.6.4",
|
||||||
|
"on-headers": "~1.1.0",
|
||||||
|
"safe-buffer": "5.2.1",
|
||||||
|
"vary": "~1.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/compression/node_modules/debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/compression/node_modules/ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/compression/node_modules/negotiator": {
|
||||||
|
"version": "0.6.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz",
|
||||||
|
"integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/concat-map": {
|
"node_modules/concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
@ -8223,6 +8290,15 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/on-headers": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/once": {
|
"node_modules/once": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
|
|||||||
@ -33,6 +33,7 @@
|
|||||||
"bcryptjs": "^3.0.3",
|
"bcryptjs": "^3.0.3",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.15.1",
|
"class-validator": "^0.15.1",
|
||||||
|
"compression": "^1.8.1",
|
||||||
"helmet": "^8.2.0",
|
"helmet": "^8.2.0",
|
||||||
"ioredis": "^5.11.0",
|
"ioredis": "^5.11.0",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
@ -50,6 +51,7 @@
|
|||||||
"@nestjs/testing": "^11.0.1",
|
"@nestjs/testing": "^11.0.1",
|
||||||
"@types/bcrypt": "^6.0.0",
|
"@types/bcrypt": "^6.0.0",
|
||||||
"@types/bcryptjs": "^2.4.6",
|
"@types/bcryptjs": "^2.4.6",
|
||||||
|
"@types/compression": "^1.8.1",
|
||||||
"@types/express": "^5.0.0",
|
"@types/express": "^5.0.0",
|
||||||
"@types/jest": "^30.0.0",
|
"@types/jest": "^30.0.0",
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import { CreateUserDto, UpdateUserDto } from './dto/user.dto';
|
|||||||
import { ProductDto } from './dto/product.dto';
|
import { ProductDto } from './dto/product.dto';
|
||||||
import { normalizeMobile } from '../common/utils/phone.utils';
|
import { normalizeMobile } from '../common/utils/phone.utils';
|
||||||
import { CANONICAL_ALIASES } from '../settings/settings.service';
|
import { CANONICAL_ALIASES } from '../settings/settings.service';
|
||||||
|
import { RevalidationService } from '../common/revalidation/revalidation.service';
|
||||||
|
|
||||||
export class PaginationQuery {
|
export class PaginationQuery {
|
||||||
page?: number | string;
|
page?: number | string;
|
||||||
@ -51,6 +52,7 @@ export class AdminService {
|
|||||||
constructor(
|
constructor(
|
||||||
private prisma: PrismaService,
|
private prisma: PrismaService,
|
||||||
private redisService: RedisService,
|
private redisService: RedisService,
|
||||||
|
private revalidationService: RevalidationService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async getDashboardStats() {
|
async getDashboardStats() {
|
||||||
@ -456,6 +458,10 @@ export class AdminService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.revalidationService.revalidateProduct(
|
||||||
|
product.slug,
|
||||||
|
product.artNo,
|
||||||
|
);
|
||||||
return product;
|
return product;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -516,11 +522,15 @@ export class AdminService {
|
|||||||
metaDescription: data.metaDescription,
|
metaDescription: data.metaDescription,
|
||||||
keywords: data.keywords,
|
keywords: data.keywords,
|
||||||
canonicalUrl: data.canonicalUrl,
|
canonicalUrl: data.canonicalUrl,
|
||||||
stockStatus: data.stockStatus !== undefined ? data.stockStatus : undefined,
|
stockStatus:
|
||||||
|
data.stockStatus !== undefined ? data.stockStatus : undefined,
|
||||||
noIndex: data.noIndex !== undefined ? data.noIndex : undefined,
|
noIndex: data.noIndex !== undefined ? data.noIndex : undefined,
|
||||||
noFollow: data.noFollow !== undefined ? data.noFollow : undefined,
|
noFollow: data.noFollow !== undefined ? data.noFollow : undefined,
|
||||||
ogImage: data.ogImage !== undefined ? data.ogImage : undefined,
|
ogImage: data.ogImage !== undefined ? data.ogImage : undefined,
|
||||||
featuredImageAlt: data.featuredImageAlt !== undefined ? data.featuredImageAlt : undefined,
|
featuredImageAlt:
|
||||||
|
data.featuredImageAlt !== undefined
|
||||||
|
? data.featuredImageAlt
|
||||||
|
: undefined,
|
||||||
slug: data.slug || data.artNo,
|
slug: data.slug || data.artNo,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -539,13 +549,25 @@ export class AdminService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.revalidationService.revalidateProduct(
|
||||||
|
product.slug,
|
||||||
|
product.artNo,
|
||||||
|
);
|
||||||
return product;
|
return product;
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteProduct(id: string) {
|
async deleteProduct(id: string) {
|
||||||
return this.prisma.product.delete({
|
const existing = await this.prisma.product.findUnique({ where: { id } });
|
||||||
|
const res = await this.prisma.product.delete({
|
||||||
where: { id },
|
where: { id },
|
||||||
});
|
});
|
||||||
|
if (existing) {
|
||||||
|
await this.revalidationService.revalidateProduct(
|
||||||
|
existing.slug,
|
||||||
|
existing.artNo,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOrders(query: PaginationQuery = {}) {
|
async getOrders(query: PaginationQuery = {}) {
|
||||||
|
|||||||
@ -37,7 +37,12 @@ export class BlogsController {
|
|||||||
@ApiQuery({ name: 'status', required: false })
|
@ApiQuery({ name: 'status', required: false })
|
||||||
@ApiQuery({ name: 'categoryId', required: false })
|
@ApiQuery({ name: 'categoryId', required: false })
|
||||||
async getBlogs(
|
async getBlogs(
|
||||||
@Query() query: PaginationDto & { status?: string; categoryId?: string; tagId?: string },
|
@Query()
|
||||||
|
query: PaginationDto & {
|
||||||
|
status?: string;
|
||||||
|
categoryId?: string;
|
||||||
|
tagId?: string;
|
||||||
|
},
|
||||||
) {
|
) {
|
||||||
return this.blogsService.getBlogs(query);
|
return this.blogsService.getBlogs(query);
|
||||||
}
|
}
|
||||||
@ -120,10 +125,7 @@ export class BlogsController {
|
|||||||
|
|
||||||
@Put(':id')
|
@Put(':id')
|
||||||
@ApiOperation({ summary: 'ویرایش مقاله' })
|
@ApiOperation({ summary: 'ویرایش مقاله' })
|
||||||
async updateBlog(
|
async updateBlog(@Param('id') id: string, @Body() body: any) {
|
||||||
@Param('id') id: string,
|
|
||||||
@Body() body: any,
|
|
||||||
) {
|
|
||||||
const data = await this.blogsService.updateBlog(id, body);
|
const data = await this.blogsService.updateBlog(id, body);
|
||||||
return { success: true, data };
|
return { success: true, data };
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,12 +2,22 @@ import { Injectable, NotFoundException } from '@nestjs/common';
|
|||||||
import { Prisma } from '@prisma/client';
|
import { Prisma } from '@prisma/client';
|
||||||
import { PrismaService } from '../prisma/prisma.service';
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
import { PaginationDto } from '../common/dto/pagination.dto';
|
import { PaginationDto } from '../common/dto/pagination.dto';
|
||||||
|
import { RevalidationService } from '../common/revalidation/revalidation.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class BlogsService {
|
export class BlogsService {
|
||||||
constructor(private prisma: PrismaService) {}
|
constructor(
|
||||||
|
private prisma: PrismaService,
|
||||||
|
private revalidationService: RevalidationService,
|
||||||
|
) {}
|
||||||
|
|
||||||
async getBlogs(query: PaginationDto & { status?: string; categoryId?: string; tagId?: string }) {
|
async getBlogs(
|
||||||
|
query: PaginationDto & {
|
||||||
|
status?: string;
|
||||||
|
categoryId?: string;
|
||||||
|
tagId?: string;
|
||||||
|
},
|
||||||
|
) {
|
||||||
const page = Number(query.page) || 1;
|
const page = Number(query.page) || 1;
|
||||||
const limit = Number(query.limit) || 10;
|
const limit = Number(query.limit) || 10;
|
||||||
const skip = (page - 1) * limit;
|
const skip = (page - 1) * limit;
|
||||||
@ -37,7 +47,9 @@ export class BlogsService {
|
|||||||
take: limit,
|
take: limit,
|
||||||
orderBy: { createdAt: 'desc' },
|
orderBy: { createdAt: 'desc' },
|
||||||
include: {
|
include: {
|
||||||
author: { select: { id: true, firstName: true, lastName: true, email: true } },
|
author: {
|
||||||
|
select: { id: true, firstName: true, lastName: true, email: true },
|
||||||
|
},
|
||||||
category: { select: { id: true, name: true, slug: true } },
|
category: { select: { id: true, name: true, slug: true } },
|
||||||
tags: { select: { id: true, name: true, slug: true } },
|
tags: { select: { id: true, name: true, slug: true } },
|
||||||
_count: { select: { comments: true } },
|
_count: { select: { comments: true } },
|
||||||
@ -87,7 +99,10 @@ export class BlogsService {
|
|||||||
for (const tName of tagNames) {
|
for (const tName of tagNames) {
|
||||||
const trimmed = String(tName).trim();
|
const trimmed = String(tName).trim();
|
||||||
if (trimmed) {
|
if (trimmed) {
|
||||||
const slug = trimmed.toLowerCase().replace(/\s+/g, '-').replace(/[^\w\u0600-\u06FF-]+/g, '');
|
const slug = trimmed
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/\s+/g, '-')
|
||||||
|
.replace(/[^\w\u0600-\u06FF-]+/g, '');
|
||||||
tagConnectOrCreate.push({
|
tagConnectOrCreate.push({
|
||||||
where: { name: trimmed },
|
where: { name: trimmed },
|
||||||
create: { name: trimmed, slug: slug || `tag-${Date.now()}` },
|
create: { name: trimmed, slug: slug || `tag-${Date.now()}` },
|
||||||
@ -101,13 +116,19 @@ export class BlogsService {
|
|||||||
const createPayload: Prisma.BlogCreateInput = {
|
const createPayload: Prisma.BlogCreateInput = {
|
||||||
...rest,
|
...rest,
|
||||||
readingTime: readingTime ? Number(readingTime) : 3,
|
readingTime: readingTime ? Number(readingTime) : 3,
|
||||||
publishedAt: publishedAt ? new Date(publishedAt) : (rest.status === 'PUBLISHED' ? new Date() : null),
|
publishedAt: publishedAt
|
||||||
|
? new Date(publishedAt)
|
||||||
|
: rest.status === 'PUBLISHED'
|
||||||
|
? new Date()
|
||||||
|
: null,
|
||||||
author: { connect: { id: targetAuthorId } },
|
author: { connect: { id: targetAuthorId } },
|
||||||
...(categoryId ? { category: { connect: { id: categoryId } } } : {}),
|
...(categoryId ? { category: { connect: { id: categoryId } } } : {}),
|
||||||
...(tagConnectOrCreate.length > 0 ? { tags: { connectOrCreate: tagConnectOrCreate } } : {}),
|
...(tagConnectOrCreate.length > 0
|
||||||
|
? { tags: { connectOrCreate: tagConnectOrCreate } }
|
||||||
|
: {}),
|
||||||
};
|
};
|
||||||
|
|
||||||
return this.prisma.blog.create({
|
const created = await this.prisma.blog.create({
|
||||||
data: createPayload,
|
data: createPayload,
|
||||||
include: {
|
include: {
|
||||||
author: { select: { firstName: true, lastName: true } },
|
author: { select: { firstName: true, lastName: true } },
|
||||||
@ -115,6 +136,9 @@ export class BlogsService {
|
|||||||
tags: true,
|
tags: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await this.revalidationService.revalidateBlog(created.slug);
|
||||||
|
return created;
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateBlog(id: string, data: any) {
|
async updateBlog(id: string, data: any) {
|
||||||
@ -133,8 +157,12 @@ export class BlogsService {
|
|||||||
|
|
||||||
const updatePayload: Prisma.BlogUpdateInput = {
|
const updatePayload: Prisma.BlogUpdateInput = {
|
||||||
...rest,
|
...rest,
|
||||||
...(readingTime !== undefined ? { readingTime: Number(readingTime) } : {}),
|
...(readingTime !== undefined
|
||||||
...(publishedAt !== undefined ? { publishedAt: publishedAt ? new Date(publishedAt) : null } : {}),
|
? { readingTime: Number(readingTime) }
|
||||||
|
: {}),
|
||||||
|
...(publishedAt !== undefined
|
||||||
|
? { publishedAt: publishedAt ? new Date(publishedAt) : null }
|
||||||
|
: {}),
|
||||||
...(authorId ? { author: { connect: { id: authorId } } } : {}),
|
...(authorId ? { author: { connect: { id: authorId } } } : {}),
|
||||||
...(categoryId !== undefined
|
...(categoryId !== undefined
|
||||||
? categoryId
|
? categoryId
|
||||||
@ -148,7 +176,10 @@ export class BlogsService {
|
|||||||
for (const tName of tagNames) {
|
for (const tName of tagNames) {
|
||||||
const trimmed = String(tName).trim();
|
const trimmed = String(tName).trim();
|
||||||
if (trimmed) {
|
if (trimmed) {
|
||||||
const slug = trimmed.toLowerCase().replace(/\s+/g, '-').replace(/[^\w\u0600-\u06FF-]+/g, '');
|
const slug = trimmed
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/\s+/g, '-')
|
||||||
|
.replace(/[^\w\u0600-\u06FF-]+/g, '');
|
||||||
tagConnectOrCreate.push({
|
tagConnectOrCreate.push({
|
||||||
where: { name: trimmed },
|
where: { name: trimmed },
|
||||||
create: { name: trimmed, slug: slug || `tag-${Date.now()}` },
|
create: { name: trimmed, slug: slug || `tag-${Date.now()}` },
|
||||||
@ -161,7 +192,7 @@ export class BlogsService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.prisma.blog.update({
|
const updated = await this.prisma.blog.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: updatePayload,
|
data: updatePayload,
|
||||||
include: {
|
include: {
|
||||||
@ -170,18 +201,29 @@ export class BlogsService {
|
|||||||
tags: true,
|
tags: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await this.revalidationService.revalidateBlog(updated.slug);
|
||||||
|
return updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteBlog(id: string) {
|
async deleteBlog(id: string) {
|
||||||
const blog = await this.prisma.blog.findUnique({ where: { id } });
|
const blog = await this.prisma.blog.findUnique({ where: { id } });
|
||||||
if (!blog) throw new NotFoundException('Blog not found');
|
if (!blog) throw new NotFoundException('Blog not found');
|
||||||
return this.prisma.blog.delete({ where: { id } });
|
const res = await this.prisma.blog.delete({ where: { id } });
|
||||||
|
await this.revalidationService.revalidateBlog(blog.slug);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authors (Users who can write)
|
// Authors (Users who can write)
|
||||||
async getAuthors() {
|
async getAuthors() {
|
||||||
return this.prisma.user.findMany({
|
return this.prisma.user.findMany({
|
||||||
select: { id: true, firstName: true, lastName: true, email: true, role: true },
|
select: {
|
||||||
|
id: true,
|
||||||
|
firstName: true,
|
||||||
|
lastName: true,
|
||||||
|
email: true,
|
||||||
|
role: true,
|
||||||
|
},
|
||||||
orderBy: { firstName: 'asc' },
|
orderBy: { firstName: 'asc' },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -221,7 +263,12 @@ export class BlogsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async createBlogTag(data: { name: string; slug?: string }) {
|
async createBlogTag(data: { name: string; slug?: string }) {
|
||||||
const slug = data.slug || data.name.toLowerCase().replace(/\s+/g, '-').replace(/[^\w\u0600-\u06FF-]+/g, '');
|
const slug =
|
||||||
|
data.slug ||
|
||||||
|
data.name
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/\s+/g, '-')
|
||||||
|
.replace(/[^\w\u0600-\u06FF-]+/g, '');
|
||||||
return this.prisma.blogTag.create({
|
return this.prisma.blogTag.create({
|
||||||
data: { name: data.name, slug: slug || `tag-${Date.now()}` },
|
data: { name: data.name, slug: slug || `tag-${Date.now()}` },
|
||||||
});
|
});
|
||||||
@ -267,14 +314,31 @@ export class BlogsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async updateCommentStatus(id: string, status: 'APPROVED' | 'REJECTED') {
|
async updateCommentStatus(id: string, status: 'APPROVED' | 'REJECTED') {
|
||||||
const comment = await this.prisma.blogComment.findUnique({ where: { id } });
|
const comment = await this.prisma.blogComment.findUnique({
|
||||||
|
where: { id },
|
||||||
|
include: { blog: { select: { slug: true } } },
|
||||||
|
});
|
||||||
if (!comment) throw new NotFoundException('Comment not found');
|
if (!comment) throw new NotFoundException('Comment not found');
|
||||||
return this.prisma.blogComment.update({ where: { id }, data: { status } });
|
const updated = await this.prisma.blogComment.update({
|
||||||
|
where: { id },
|
||||||
|
data: { status },
|
||||||
|
});
|
||||||
|
if (comment.blog?.slug) {
|
||||||
|
await this.revalidationService.revalidateBlog(comment.blog.slug);
|
||||||
|
}
|
||||||
|
return updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteComment(id: string) {
|
async deleteComment(id: string) {
|
||||||
const comment = await this.prisma.blogComment.findUnique({ where: { id } });
|
const comment = await this.prisma.blogComment.findUnique({
|
||||||
|
where: { id },
|
||||||
|
include: { blog: { select: { slug: true } } },
|
||||||
|
});
|
||||||
if (!comment) throw new NotFoundException('Comment not found');
|
if (!comment) throw new NotFoundException('Comment not found');
|
||||||
return this.prisma.blogComment.delete({ where: { id } });
|
const res = await this.prisma.blogComment.delete({ where: { id } });
|
||||||
|
if (comment.blog?.slug) {
|
||||||
|
await this.revalidationService.revalidateBlog(comment.blog.slug);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -198,7 +198,10 @@ export class ProductDto {
|
|||||||
@IsArray()
|
@IsArray()
|
||||||
symptoms?: string[];
|
symptoms?: string[];
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: 'وضعیت موجودی کالا (IN_STOCK, OUT_OF_STOCK, PRE_ORDER, DISCONTINUED)' })
|
@ApiPropertyOptional({
|
||||||
|
description:
|
||||||
|
'وضعیت موجودی کالا (IN_STOCK, OUT_OF_STOCK, PRE_ORDER, DISCONTINUED)',
|
||||||
|
})
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
stockStatus?: 'IN_STOCK' | 'OUT_OF_STOCK' | 'PRE_ORDER' | 'DISCONTINUED';
|
stockStatus?: 'IN_STOCK' | 'OUT_OF_STOCK' | 'PRE_ORDER' | 'DISCONTINUED';
|
||||||
@ -208,12 +211,16 @@ export class ProductDto {
|
|||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
noIndex?: boolean;
|
noIndex?: boolean;
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: 'عدم دنبال کردن لینکها توسط موتورهای جستجو' })
|
@ApiPropertyOptional({
|
||||||
|
description: 'عدم دنبال کردن لینکها توسط موتورهای جستجو',
|
||||||
|
})
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
noFollow?: boolean;
|
noFollow?: boolean;
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: 'تصویر کارت شبکههای اجتماعی (og:image)' })
|
@ApiPropertyOptional({
|
||||||
|
description: 'تصویر کارت شبکههای اجتماعی (og:image)',
|
||||||
|
})
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
ogImage?: string | null;
|
ogImage?: string | null;
|
||||||
|
|||||||
@ -33,11 +33,13 @@ import { ReviewsModule } from './reviews/reviews.module';
|
|||||||
import { DoctorsModule } from './doctors/doctors.module';
|
import { DoctorsModule } from './doctors/doctors.module';
|
||||||
import { FaqModule } from './faq/faq.module';
|
import { FaqModule } from './faq/faq.module';
|
||||||
import { MenuModule } from './menu/menu.module';
|
import { MenuModule } from './menu/menu.module';
|
||||||
|
import { RevalidationModule } from './common/revalidation/revalidation.module';
|
||||||
|
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from 'express';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
RevalidationModule,
|
||||||
SmsModule,
|
SmsModule,
|
||||||
ContactModule,
|
ContactModule,
|
||||||
PrismaModule,
|
PrismaModule,
|
||||||
|
|||||||
@ -29,10 +29,7 @@ export class BlogsService {
|
|||||||
const whereClause: Prisma.BlogWhereInput = {
|
const whereClause: Prisma.BlogWhereInput = {
|
||||||
isPublished: true,
|
isPublished: true,
|
||||||
status: 'PUBLISHED',
|
status: 'PUBLISHED',
|
||||||
OR: [
|
OR: [{ publishedAt: null }, { publishedAt: { lte: now } }],
|
||||||
{ publishedAt: null },
|
|
||||||
{ publishedAt: { lte: now } },
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (search) {
|
if (search) {
|
||||||
@ -67,7 +64,9 @@ export class BlogsService {
|
|||||||
where: whereClause,
|
where: whereClause,
|
||||||
skip,
|
skip,
|
||||||
take: Number(limit),
|
take: Number(limit),
|
||||||
orderBy: { [sortBy === 'createdAt' ? 'createdAt' : 'publishedAt']: sortOrder },
|
orderBy: {
|
||||||
|
[sortBy === 'createdAt' ? 'createdAt' : 'publishedAt']: sortOrder,
|
||||||
|
},
|
||||||
include: {
|
include: {
|
||||||
author: {
|
author: {
|
||||||
select: { firstName: true, lastName: true },
|
select: { firstName: true, lastName: true },
|
||||||
@ -184,7 +183,11 @@ export class BlogsService {
|
|||||||
let relatedBlogs: any[] = [];
|
let relatedBlogs: any[] = [];
|
||||||
if (blog.relatedBlogIds && blog.relatedBlogIds.length > 0) {
|
if (blog.relatedBlogIds && blog.relatedBlogIds.length > 0) {
|
||||||
relatedBlogs = await this.prisma.blog.findMany({
|
relatedBlogs = await this.prisma.blog.findMany({
|
||||||
where: { id: { in: blog.relatedBlogIds }, isPublished: true, status: 'PUBLISHED' },
|
where: {
|
||||||
|
id: { in: blog.relatedBlogIds },
|
||||||
|
isPublished: true,
|
||||||
|
status: 'PUBLISHED',
|
||||||
|
},
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
title: true,
|
title: true,
|
||||||
@ -219,10 +222,12 @@ export class BlogsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Increment view count asynchronously
|
// Increment view count asynchronously
|
||||||
await this.prisma.blog.update({
|
await this.prisma.blog
|
||||||
where: { id: blog.id },
|
.update({
|
||||||
data: { viewCount: { increment: 1 } },
|
where: { id: blog.id },
|
||||||
}).catch(() => null);
|
data: { viewCount: { increment: 1 } },
|
||||||
|
})
|
||||||
|
.catch(() => null);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...blog,
|
...blog,
|
||||||
@ -237,7 +242,11 @@ export class BlogsService {
|
|||||||
orderBy: { order: 'asc' },
|
orderBy: { order: 'asc' },
|
||||||
include: {
|
include: {
|
||||||
children: true,
|
children: true,
|
||||||
_count: { select: { blogs: { where: { isPublished: true, status: 'PUBLISHED' } } } },
|
_count: {
|
||||||
|
select: {
|
||||||
|
blogs: { where: { isPublished: true, status: 'PUBLISHED' } },
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -246,7 +255,11 @@ export class BlogsService {
|
|||||||
return this.prisma.blogTag.findMany({
|
return this.prisma.blogTag.findMany({
|
||||||
orderBy: { createdAt: 'desc' },
|
orderBy: { createdAt: 'desc' },
|
||||||
include: {
|
include: {
|
||||||
_count: { select: { blogs: { where: { isPublished: true, status: 'PUBLISHED' } } } },
|
_count: {
|
||||||
|
select: {
|
||||||
|
blogs: { where: { isPublished: true, status: 'PUBLISHED' } },
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
9
backend/src/common/revalidation/revalidation.module.ts
Normal file
9
backend/src/common/revalidation/revalidation.module.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { Global, Module } from '@nestjs/common';
|
||||||
|
import { RevalidationService } from './revalidation.service';
|
||||||
|
|
||||||
|
@Global()
|
||||||
|
@Module({
|
||||||
|
providers: [RevalidationService],
|
||||||
|
exports: [RevalidationService],
|
||||||
|
})
|
||||||
|
export class RevalidationModule {}
|
||||||
123
backend/src/common/revalidation/revalidation.service.ts
Normal file
123
backend/src/common/revalidation/revalidation.service.ts
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class RevalidationService {
|
||||||
|
private readonly logger = new Logger(RevalidationService.name);
|
||||||
|
|
||||||
|
private get frontendUrl(): string {
|
||||||
|
const raw =
|
||||||
|
process.env.FRONTEND_URL ||
|
||||||
|
process.env.NEXT_PUBLIC_SITE_URL ||
|
||||||
|
'http://localhost:3000';
|
||||||
|
return raw.replace(/\/+$/, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
private get secret(): string {
|
||||||
|
return (
|
||||||
|
process.env.REVALIDATION_SECRET || 'canina_revalidation_secret_key_2026'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async revalidateTag(tag: string): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
const url = `${this.frontendUrl}/api/revalidate`;
|
||||||
|
const res = await fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'x-revalidate-secret': this.secret,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ tag, secret: this.secret }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
this.logger.warn(
|
||||||
|
`Failed to revalidate tag [${tag}] on frontend: ${res.status} ${res.statusText}`,
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.log(`Successfully revalidated cache tag [${tag}]`);
|
||||||
|
return true;
|
||||||
|
} catch (err: any) {
|
||||||
|
this.logger.debug(
|
||||||
|
`Revalidation tag request skipped or frontend offline: ${err?.message}`,
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async revalidatePath(path: string): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
const url = `${this.frontendUrl}/api/revalidate`;
|
||||||
|
const res = await fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'x-revalidate-secret': this.secret,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ path, secret: this.secret }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
this.logger.warn(
|
||||||
|
`Failed to revalidate path [${path}] on frontend: ${res.status} ${res.statusText}`,
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.log(`Successfully revalidated cache path [${path}]`);
|
||||||
|
return true;
|
||||||
|
} catch (err: any) {
|
||||||
|
this.logger.debug(
|
||||||
|
`Revalidation path request skipped or frontend offline: ${err?.message}`,
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async revalidateProduct(slug?: string, artNo?: string): Promise<void> {
|
||||||
|
const tasks: Promise<boolean>[] = [
|
||||||
|
this.revalidateTag('products'),
|
||||||
|
this.revalidateTag('home-data'),
|
||||||
|
this.revalidatePath('/shop'),
|
||||||
|
this.revalidatePath('/'),
|
||||||
|
];
|
||||||
|
|
||||||
|
if (slug) {
|
||||||
|
tasks.push(this.revalidateTag(`product-${slug}`));
|
||||||
|
tasks.push(this.revalidatePath(`/shop/${slug}`));
|
||||||
|
}
|
||||||
|
if (artNo && artNo !== slug) {
|
||||||
|
tasks.push(this.revalidateTag(`product-${artNo}`));
|
||||||
|
tasks.push(this.revalidatePath(`/shop/${artNo}`));
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.allSettled(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
async revalidateBlog(slug?: string): Promise<void> {
|
||||||
|
const tasks: Promise<boolean>[] = [
|
||||||
|
this.revalidateTag('blogs'),
|
||||||
|
this.revalidateTag('home-data'),
|
||||||
|
this.revalidatePath('/blog'),
|
||||||
|
this.revalidatePath('/'),
|
||||||
|
];
|
||||||
|
|
||||||
|
if (slug) {
|
||||||
|
tasks.push(this.revalidateTag(`blog-${slug}`));
|
||||||
|
tasks.push(this.revalidatePath(`/blog/${slug}`));
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.allSettled(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
async revalidateSettings(): Promise<void> {
|
||||||
|
await Promise.allSettled([
|
||||||
|
this.revalidateTag('settings'),
|
||||||
|
this.revalidateTag('home-data'),
|
||||||
|
this.revalidateTag('seo-config'),
|
||||||
|
this.revalidatePath('/'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,6 +8,7 @@ import { CustomHttpExceptionFilter } from './common/filters/http-exception.filte
|
|||||||
import { PrismaExceptionFilter } from './common/filters/prisma-exception.filter';
|
import { PrismaExceptionFilter } from './common/filters/prisma-exception.filter';
|
||||||
import { DecimalInterceptor } from './common/interceptors/decimal.interceptor';
|
import { DecimalInterceptor } from './common/interceptors/decimal.interceptor';
|
||||||
import helmet from 'helmet';
|
import helmet from 'helmet';
|
||||||
|
import compression from 'compression';
|
||||||
|
|
||||||
// Ensure BigInt can be serialized to JSON across all NestJS / Express responses
|
// Ensure BigInt can be serialized to JSON across all NestJS / Express responses
|
||||||
(BigInt.prototype as any).toJSON = function () {
|
(BigInt.prototype as any).toJSON = function () {
|
||||||
@ -46,6 +47,8 @@ async function bootstrap() {
|
|||||||
...staticUploadOptions,
|
...staticUploadOptions,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.use(compression());
|
||||||
|
|
||||||
app.use(
|
app.use(
|
||||||
helmet({
|
helmet({
|
||||||
contentSecurityPolicy: false, // Avoid blocking Swagger UI scripts and assets
|
contentSecurityPolicy: false, // Avoid blocking Swagger UI scripts and assets
|
||||||
|
|||||||
@ -2,10 +2,14 @@ import { Injectable, NotFoundException } from '@nestjs/common';
|
|||||||
import { PrismaService } from '../prisma/prisma.service';
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
import { GetProductsDto } from './dto/get-products.dto';
|
import { GetProductsDto } from './dto/get-products.dto';
|
||||||
import { Prisma } from '@prisma/client';
|
import { Prisma } from '@prisma/client';
|
||||||
|
import { RevalidationService } from '../common/revalidation/revalidation.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ProductsService {
|
export class ProductsService {
|
||||||
constructor(private prisma: PrismaService) {}
|
constructor(
|
||||||
|
private prisma: PrismaService,
|
||||||
|
private revalidationService: RevalidationService,
|
||||||
|
) {}
|
||||||
|
|
||||||
async findAll(filters: GetProductsDto, userRole?: string) {
|
async findAll(filters: GetProductsDto, userRole?: string) {
|
||||||
const {
|
const {
|
||||||
@ -272,9 +276,14 @@ export class ProductsService {
|
|||||||
if (!product) {
|
if (!product) {
|
||||||
throw new NotFoundException(`Product with ID ${id} not found`);
|
throw new NotFoundException(`Product with ID ${id} not found`);
|
||||||
}
|
}
|
||||||
return this.prisma.product.update({
|
const updated = await this.prisma.product.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: { dosageConfig },
|
data: { dosageConfig },
|
||||||
});
|
});
|
||||||
|
await this.revalidationService.revalidateProduct(
|
||||||
|
product.slug,
|
||||||
|
product.artNo,
|
||||||
|
);
|
||||||
|
return updated;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,12 +7,16 @@ import {
|
|||||||
import { PrismaService } from '../prisma/prisma.service';
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
import { CreateReviewDto } from './dto/create-review.dto';
|
import { CreateReviewDto } from './dto/create-review.dto';
|
||||||
import { UpdateReviewDto } from './dto/update-review.dto';
|
import { UpdateReviewDto } from './dto/update-review.dto';
|
||||||
|
import { RevalidationService } from '../common/revalidation/revalidation.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ReviewsService {
|
export class ReviewsService {
|
||||||
private readonly logger = new Logger(ReviewsService.name);
|
private readonly logger = new Logger(ReviewsService.name);
|
||||||
|
|
||||||
constructor(private readonly prisma: PrismaService) {}
|
constructor(
|
||||||
|
private readonly prisma: PrismaService,
|
||||||
|
private readonly revalidationService: RevalidationService,
|
||||||
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 1. Submit a new review for a product (User/Guest)
|
* 1. Submit a new review for a product (User/Guest)
|
||||||
@ -203,6 +207,7 @@ export class ReviewsService {
|
|||||||
async updateReviewStatus(id: string, dto: UpdateReviewDto) {
|
async updateReviewStatus(id: string, dto: UpdateReviewDto) {
|
||||||
const review = await this.prisma.productReview.findUnique({
|
const review = await this.prisma.productReview.findUnique({
|
||||||
where: { id },
|
where: { id },
|
||||||
|
include: { product: { select: { slug: true, artNo: true } } },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!review) {
|
if (!review) {
|
||||||
@ -218,6 +223,13 @@ export class ReviewsService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (review.product) {
|
||||||
|
await this.revalidationService.revalidateProduct(
|
||||||
|
review.product.slug,
|
||||||
|
review.product.artNo,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
message: 'وضعیت دیدگاه با موفقیت بروزرسانی شد',
|
message: 'وضعیت دیدگاه با موفقیت بروزرسانی شد',
|
||||||
@ -231,6 +243,7 @@ export class ReviewsService {
|
|||||||
async deleteReview(id: string) {
|
async deleteReview(id: string) {
|
||||||
const review = await this.prisma.productReview.findUnique({
|
const review = await this.prisma.productReview.findUnique({
|
||||||
where: { id },
|
where: { id },
|
||||||
|
include: { product: { select: { slug: true, artNo: true } } },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!review) {
|
if (!review) {
|
||||||
@ -239,6 +252,13 @@ export class ReviewsService {
|
|||||||
|
|
||||||
await this.prisma.productReview.delete({ where: { id } });
|
await this.prisma.productReview.delete({ where: { id } });
|
||||||
|
|
||||||
|
if (review.product) {
|
||||||
|
await this.revalidationService.revalidateProduct(
|
||||||
|
review.product.slug,
|
||||||
|
review.product.artNo,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
message: 'دیدگاه با موفقیت حذف شد',
|
message: 'دیدگاه با موفقیت حذف شد',
|
||||||
|
|||||||
@ -6,11 +6,13 @@ import Footer from "../components/Footer";
|
|||||||
import { NetworkBanner } from "../components/NetworkBanner";
|
import { NetworkBanner } from "../components/NetworkBanner";
|
||||||
import { Toaster, toast } from "sonner";
|
import { Toaster, toast } from "sonner";
|
||||||
import { usePathname, useRouter } from 'next/navigation';
|
import { usePathname, useRouter } from 'next/navigation';
|
||||||
|
import dynamic from 'next/dynamic';
|
||||||
|
|
||||||
|
const CartDrawer = dynamic(() => import("../components/CartDrawer"), { ssr: false });
|
||||||
|
const LoginModal = dynamic(() => import("../components/LoginModal"), { ssr: false });
|
||||||
|
const AuthModal = dynamic(() => import("../components/AuthModal"), { ssr: false });
|
||||||
|
const B2BPortal = dynamic(() => import("../components/B2BPortal"), { ssr: false });
|
||||||
|
|
||||||
import CartDrawer from "../components/CartDrawer";
|
|
||||||
import LoginModal from "../components/LoginModal";
|
|
||||||
import AuthModal from "../components/AuthModal";
|
|
||||||
import B2BPortal from "../components/B2BPortal";
|
|
||||||
import { useUserStore } from "../lib/store/userStore";
|
import { useUserStore } from "../lib/store/userStore";
|
||||||
import { useSettingsStore } from "../lib/store/settingsStore";
|
import { useSettingsStore } from "../lib/store/settingsStore";
|
||||||
import { useUIStore } from "../lib/store/uiStore";
|
import { useUIStore } from "../lib/store/uiStore";
|
||||||
|
|||||||
69
frontend/application/app/api/revalidate/route.ts
Normal file
69
frontend/application/app/api/revalidate/route.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
|
import { revalidateTag, revalidatePath } from 'next/cache';
|
||||||
|
|
||||||
|
export async function GET(request: NextRequest) {
|
||||||
|
return handleRevalidate(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function POST(request: NextRequest) {
|
||||||
|
return handleRevalidate(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleRevalidate(request: NextRequest) {
|
||||||
|
const { searchParams } = new URL(request.url);
|
||||||
|
let secret = searchParams.get('secret') || request.headers.get('x-revalidate-secret');
|
||||||
|
let tag = searchParams.get('tag');
|
||||||
|
let path = searchParams.get('path');
|
||||||
|
|
||||||
|
// Check auth header if bearer token provided
|
||||||
|
const authHeader = request.headers.get('authorization');
|
||||||
|
if (authHeader && authHeader.startsWith('Bearer ')) {
|
||||||
|
secret = authHeader.substring(7);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.method === 'POST') {
|
||||||
|
try {
|
||||||
|
const body = await request.json().catch(() => ({}));
|
||||||
|
if (body && typeof body === 'object') {
|
||||||
|
if (body.secret) secret = body.secret;
|
||||||
|
if (body.tag) tag = body.tag;
|
||||||
|
if (body.path) path = body.path;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Ignore body read error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const expectedSecret = process.env.REVALIDATION_SECRET || 'canina_revalidation_secret_key_2026';
|
||||||
|
if (!secret || secret !== expectedSecret) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ success: false, message: 'Invalid revalidation secret token' },
|
||||||
|
{ status: 401 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tag && !path) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ success: false, message: 'Missing tag or path parameter for cache revalidation' },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const revalidated: { tag?: string; path?: string } = {};
|
||||||
|
|
||||||
|
if (tag) {
|
||||||
|
revalidateTag(tag, 'default');
|
||||||
|
revalidated.tag = tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path) {
|
||||||
|
revalidatePath(path);
|
||||||
|
revalidated.path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.json({
|
||||||
|
revalidated: true,
|
||||||
|
...revalidated,
|
||||||
|
now: Date.now(),
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -24,7 +24,12 @@ async function getBlog(rawSlug: string) {
|
|||||||
const slug = decodeURIComponent(rawSlug);
|
const slug = decodeURIComponent(rawSlug);
|
||||||
const apiUrl = process.env.INTERNAL_API_URL || process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4001/api';
|
const apiUrl = process.env.INTERNAL_API_URL || process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4001/api';
|
||||||
const apiBase = apiUrl.endsWith('/api') ? apiUrl : `${apiUrl.replace(/\/$/, '')}/api`;
|
const apiBase = apiUrl.endsWith('/api') ? apiUrl : `${apiUrl.replace(/\/$/, '')}/api`;
|
||||||
const res = await fetch(`${apiBase}/blogs/${encodeURIComponent(slug)}`, { next: { revalidate: 60 } });
|
const res = await fetch(`${apiBase}/blogs/${encodeURIComponent(slug)}`, {
|
||||||
|
next: {
|
||||||
|
tags: ['blogs', `blog-${slug}`],
|
||||||
|
revalidate: 3600,
|
||||||
|
},
|
||||||
|
});
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
console.error(`[Blog] Failed to fetch blog ${slug}: ${res.status}`);
|
console.error(`[Blog] Failed to fetch blog ${slug}: ${res.status}`);
|
||||||
return null;
|
return null;
|
||||||
@ -37,7 +42,7 @@ async function getBlog(rawSlug: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const revalidate = 60;
|
export const revalidate = 3600;
|
||||||
|
|
||||||
export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise<Metadata> {
|
export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise<Metadata> {
|
||||||
const { slug } = await params;
|
const { slug } = await params;
|
||||||
@ -301,9 +306,10 @@ export default async function BlogPostPage({ params }: { params: Promise<{ slug:
|
|||||||
src={blog.imageUrl}
|
src={blog.imageUrl}
|
||||||
alt={blog.imageAlt || blog.title}
|
alt={blog.imageAlt || blog.title}
|
||||||
fill
|
fill
|
||||||
|
priority
|
||||||
|
sizes="(max-width: 1024px) 100vw, 800px"
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-cover"
|
||||||
unoptimized
|
unoptimized
|
||||||
priority
|
|
||||||
/>
|
/>
|
||||||
{blog.imageCaption && (
|
{blog.imageCaption && (
|
||||||
<div className="absolute bottom-0 inset-x-0 bg-black/60 backdrop-blur-sm text-white text-xs font-medium py-2 px-4 text-center">
|
<div className="absolute bottom-0 inset-x-0 bg-black/60 backdrop-blur-sm text-white text-xs font-medium py-2 px-4 text-center">
|
||||||
|
|||||||
@ -10,11 +10,15 @@ export async function generateMetadata(): Promise<Metadata> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const revalidate = 3600;
|
||||||
|
|
||||||
async function getCategories() {
|
async function getCategories() {
|
||||||
const apiUrl = process.env.INTERNAL_API_URL || process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4001/api';
|
const apiUrl = process.env.INTERNAL_API_URL || process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4001/api';
|
||||||
const apiBase = apiUrl.endsWith('/api') ? apiUrl : `${apiUrl.replace(/\/$/, '')}/api`;
|
const apiBase = apiUrl.endsWith('/api') ? apiUrl : `${apiUrl.replace(/\/$/, '')}/api`;
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${apiBase}/blogs/categories`, { next: { revalidate: 60 } });
|
const res = await fetch(`${apiBase}/blogs/categories`, {
|
||||||
|
next: { tags: ['blogs', 'blog-categories'], revalidate: 3600 },
|
||||||
|
});
|
||||||
if (!res.ok) return [];
|
if (!res.ok) return [];
|
||||||
const json = await res.json();
|
const json = await res.json();
|
||||||
return json.data || json || [];
|
return json.data || json || [];
|
||||||
@ -28,7 +32,9 @@ async function getBlogs() {
|
|||||||
const apiUrl = process.env.INTERNAL_API_URL || process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4001/api';
|
const apiUrl = process.env.INTERNAL_API_URL || process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4001/api';
|
||||||
const apiBase = apiUrl.endsWith('/api') ? apiUrl : `${apiUrl.replace(/\/$/, '')}/api`;
|
const apiBase = apiUrl.endsWith('/api') ? apiUrl : `${apiUrl.replace(/\/$/, '')}/api`;
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${apiBase}/blogs?limit=50`, { next: { revalidate: 60 } });
|
const res = await fetch(`${apiBase}/blogs?limit=50`, {
|
||||||
|
next: { tags: ['blogs'], revalidate: 3600 },
|
||||||
|
});
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
console.error(`[Blog] Failed to fetch blogs: ${res.status}`);
|
console.error(`[Blog] Failed to fetch blogs: ${res.status}`);
|
||||||
return [];
|
return [];
|
||||||
|
|||||||
@ -70,13 +70,13 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
|
||||||
@theme {
|
@theme {
|
||||||
--font-sans: "Vazirmatn";
|
--font-sans: var(--font-vazir), "Vazirmatn", system-ui, sans-serif;
|
||||||
--font-vazir: "Vazirmatn";
|
--font-vazir: var(--font-vazir), "Vazirmatn", sans-serif;
|
||||||
--font-shabnam: "Shabnam";
|
--font-shabnam: "Shabnam", sans-serif;
|
||||||
--font-sahel: "Sahel";
|
--font-sahel: "Sahel", sans-serif;
|
||||||
--font-lalezar: "Lalezar", cursive;
|
--font-lalezar: var(--font-lalezar), "Lalezar", cursive;
|
||||||
--font-nastaliq: "IranNastaliq", serif;
|
--font-nastaliq: "IranNastaliq", serif;
|
||||||
--font-iranian-sans: "IranianSans";
|
--font-iranian-sans: "IranianSans", sans-serif;
|
||||||
|
|
||||||
/* Brand Colors */
|
/* Brand Colors */
|
||||||
--color-canina-blue: #1E4053;
|
--color-canina-blue: #1E4053;
|
||||||
@ -99,11 +99,11 @@
|
|||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
:root {
|
:root {
|
||||||
font-family: "Vazirmatn" !important;
|
font-family: var(--font-vazir), "Vazirmatn", sans-serif !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
html, body {
|
html, body {
|
||||||
font-family: "Vazirmatn" !important;
|
font-family: var(--font-vazir), "Vazirmatn", sans-serif !important;
|
||||||
@apply bg-medical-gray-50 text-medical-gray-900 antialiased max-w-[100vw] overflow-x-hidden;
|
@apply bg-medical-gray-50 text-medical-gray-900 antialiased max-w-[100vw] overflow-x-hidden;
|
||||||
font-feature-settings: "ss01", "ss02", "cv01", "cv11";
|
font-feature-settings: "ss01", "ss02", "cv01", "cv11";
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import type { Metadata } from 'next';
|
|||||||
import './globals.css';
|
import './globals.css';
|
||||||
import ClientLayout from './ClientLayout';
|
import ClientLayout from './ClientLayout';
|
||||||
import { getSeoConfig } from '../lib/seo';
|
import { getSeoConfig } from '../lib/seo';
|
||||||
|
import { vazirmatn, lalezar } from '../lib/fonts';
|
||||||
|
|
||||||
export async function generateMetadata(): Promise<Metadata> {
|
export async function generateMetadata(): Promise<Metadata> {
|
||||||
const config = await getSeoConfig();
|
const config = await getSeoConfig();
|
||||||
@ -118,7 +119,7 @@ export default function RootLayout({
|
|||||||
const safeJsonLd = JSON.stringify(jsonLd).replace(/</g, '\\u003c');
|
const safeJsonLd = JSON.stringify(jsonLd).replace(/</g, '\\u003c');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<html lang="fa" dir="rtl" suppressHydrationWarning>
|
<html lang="fa" dir="rtl" className={`${vazirmatn.variable} ${lalezar.variable}`} suppressHydrationWarning>
|
||||||
<body className="min-h-screen bg-medical-gray-50 font-sans" suppressHydrationWarning>
|
<body className="min-h-screen bg-medical-gray-50 font-sans" suppressHydrationWarning>
|
||||||
<script
|
<script
|
||||||
type="application/ld+json"
|
type="application/ld+json"
|
||||||
|
|||||||
@ -4,6 +4,8 @@ import type { Metadata } from 'next';
|
|||||||
import { Suspense } from 'react';
|
import { Suspense } from 'react';
|
||||||
import { getSeoConfig, formatPageTitle } from "../../../lib/seo";
|
import { getSeoConfig, formatPageTitle } from "../../../lib/seo";
|
||||||
|
|
||||||
|
export const revalidate = 3600;
|
||||||
|
|
||||||
export async function generateMetadata(
|
export async function generateMetadata(
|
||||||
{ params }: { params: Promise<{ slug: string }> }
|
{ params }: { params: Promise<{ slug: string }> }
|
||||||
): Promise<Metadata> {
|
): Promise<Metadata> {
|
||||||
@ -117,8 +119,9 @@ export default async function ShopProductPage({ params }: { params: Promise<{ sl
|
|||||||
...(product.images || [])
|
...(product.images || [])
|
||||||
])).filter(Boolean) as string[];
|
])).filter(Boolean) as string[];
|
||||||
|
|
||||||
// 1 year valid price offer
|
// 1 year valid price offer based on product timestamp
|
||||||
const priceValidUntil = new Date(Date.now() + 365 * 24 * 60 * 60 * 1000).toISOString().split('T')[0];
|
const baseTime = product.updatedAt ? new Date(product.updatedAt).getTime() : 1771939200000;
|
||||||
|
const priceValidUntil = new Date(baseTime + 365 * 24 * 60 * 60 * 1000).toISOString().split('T')[0];
|
||||||
|
|
||||||
// Process approved reviews and ratings
|
// Process approved reviews and ratings
|
||||||
const approvedReviews = (product.reviews || []).filter(r => r.status === 'APPROVED' || !r.status);
|
const approvedReviews = (product.reviews || []).filter(r => r.status === 'APPROVED' || !r.status);
|
||||||
|
|||||||
@ -128,7 +128,13 @@ const ArchiveProductCard: React.FC<{ product: Product }> = ({ product }) => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="aspect-square bg-medical-gray-50 flex items-center justify-center p-8 overflow-hidden relative">
|
<div className="aspect-square bg-medical-gray-50 flex items-center justify-center p-8 overflow-hidden relative">
|
||||||
<SafeImage src={product.image} alt={product.name} className="w-full h-full group-hover:scale-110 transition-transform duration-700" imgClassName="object-contain" />
|
<SafeImage
|
||||||
|
src={product.image}
|
||||||
|
alt={product.name}
|
||||||
|
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 25vw)"
|
||||||
|
className="w-full h-full group-hover:scale-110 transition-transform duration-700"
|
||||||
|
imgClassName="object-contain"
|
||||||
|
/>
|
||||||
<div className="absolute bottom-4 left-4 flex gap-2">
|
<div className="absolute bottom-4 left-4 flex gap-2">
|
||||||
{(product.suitableFor === "سگ" || product.suitableFor === "هر دو") && (
|
{(product.suitableFor === "سگ" || product.suitableFor === "هر دو") && (
|
||||||
<div className="w-7 h-7 bg-white rounded-lg flex items-center justify-center text-medical-gray-400 border border-medical-gray-100 shadow-sm">
|
<div className="w-7 h-7 bg-white rounded-lg flex items-center justify-center text-medical-gray-400 border border-medical-gray-100 shadow-sm">
|
||||||
|
|||||||
@ -245,6 +245,8 @@ export default function BlogPage({
|
|||||||
src={featuredPost.image}
|
src={featuredPost.image}
|
||||||
alt={featuredPost.imageAlt || featuredPost.title}
|
alt={featuredPost.imageAlt || featuredPost.title}
|
||||||
fill
|
fill
|
||||||
|
priority
|
||||||
|
sizes="(max-width: 1024px) 100vw, 50vw"
|
||||||
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-700"
|
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-700"
|
||||||
unoptimized
|
unoptimized
|
||||||
/>
|
/>
|
||||||
@ -314,6 +316,7 @@ export default function BlogPage({
|
|||||||
src={post.image}
|
src={post.image}
|
||||||
alt={post.imageAlt || post.title}
|
alt={post.imageAlt || post.title}
|
||||||
fill
|
fill
|
||||||
|
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
|
||||||
className="w-full h-full object-cover group-hover:scale-108 transition-transform duration-700"
|
className="w-full h-full object-cover group-hover:scale-108 transition-transform duration-700"
|
||||||
unoptimized
|
unoptimized
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -122,11 +122,13 @@ export default function BlogPreviewSection() {
|
|||||||
transition={{ delay: idx * 0.1 }}
|
transition={{ delay: idx * 0.1 }}
|
||||||
className="bg-medical-gray-50/60 rounded-[2rem] border border-medical-gray-100 overflow-hidden hover:shadow-lg hover:-translate-y-1 transition-all flex flex-col group"
|
className="bg-medical-gray-50/60 rounded-[2rem] border border-medical-gray-100 overflow-hidden hover:shadow-lg hover:-translate-y-1 transition-all flex flex-col group"
|
||||||
>
|
>
|
||||||
<Link href={`/blog/${blog.slug}`} className="block relative h-52 overflow-hidden bg-medical-gray-100">
|
<Link href={`/blog/${blog.slug}`} className="block relative aspect-video w-full overflow-hidden bg-medical-gray-100">
|
||||||
<SafeImage
|
<SafeImage
|
||||||
src={image}
|
src={image}
|
||||||
alt={blog.title}
|
alt={blog.title}
|
||||||
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"
|
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
|
||||||
|
className="w-full h-full"
|
||||||
|
imgClassName="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
|||||||
@ -71,6 +71,7 @@ function ProductCard({ product }: { product: Product }) {
|
|||||||
<SafeImage
|
<SafeImage
|
||||||
src={product.image}
|
src={product.image}
|
||||||
alt={nameFa}
|
alt={nameFa}
|
||||||
|
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 25vw)"
|
||||||
className="w-full h-full group-hover:scale-105 transition-transform duration-500"
|
className="w-full h-full group-hover:scale-105 transition-transform duration-500"
|
||||||
imgClassName="object-contain max-h-[170px] mx-auto"
|
imgClassName="object-contain max-h-[170px] mx-auto"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -184,6 +184,7 @@ export default function PodcastInlinePlayer({ podcast }: PodcastInlinePlayerProp
|
|||||||
<audio
|
<audio
|
||||||
ref={audioRef}
|
ref={audioRef}
|
||||||
src={podcast.audioUrl}
|
src={podcast.audioUrl}
|
||||||
|
preload="none"
|
||||||
onPlay={() => setIsPlaying(true)}
|
onPlay={() => setIsPlaying(true)}
|
||||||
onPause={() => setIsPlaying(false)}
|
onPause={() => setIsPlaying(false)}
|
||||||
onTimeUpdate={() => {
|
onTimeUpdate={() => {
|
||||||
|
|||||||
@ -65,12 +65,14 @@ const useCalculatorStore = create<CalculatorState>((set) => ({
|
|||||||
setWeight: (weight) => set({ weight }),
|
setWeight: (weight) => set({ weight }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
import dynamic from 'next/dynamic';
|
||||||
import Tooltip from "./Tooltip";
|
import Tooltip from "./Tooltip";
|
||||||
import SafeImage from "./SafeImage";
|
import SafeImage from "./SafeImage";
|
||||||
import ProductReviews from "./ProductReviews";
|
|
||||||
import VideoModalPlayer from "./VideoModalPlayer";
|
|
||||||
import PodcastInlinePlayer from "./PodcastInlinePlayer";
|
import PodcastInlinePlayer from "./PodcastInlinePlayer";
|
||||||
|
|
||||||
|
const VideoModalPlayer = dynamic(() => import("./VideoModalPlayer"), { ssr: false });
|
||||||
|
const ProductReviews = dynamic(() => import("./ProductReviews"), { ssr: false });
|
||||||
|
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
export default function ProductPage({ productSlug }: { productSlug: string }) {
|
export default function ProductPage({ productSlug }: { productSlug: string }) {
|
||||||
@ -293,6 +295,8 @@ export default function ProductPage({ productSlug }: { productSlug: string }) {
|
|||||||
<SafeImage
|
<SafeImage
|
||||||
src={activeImage || product.image}
|
src={activeImage || product.image}
|
||||||
alt={product.name}
|
alt={product.name}
|
||||||
|
priority={true}
|
||||||
|
sizes="(max-width: 768px) 100vw, 400px"
|
||||||
className="w-full h-full"
|
className="w-full h-full"
|
||||||
imgClassName="max-h-[240px] sm:max-h-[300px] w-auto object-contain drop-shadow-2xl group-hover:scale-105 transition-transform duration-700 mx-auto my-auto"
|
imgClassName="max-h-[240px] sm:max-h-[300px] w-auto object-contain drop-shadow-2xl group-hover:scale-105 transition-transform duration-700 mx-auto my-auto"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -10,6 +10,11 @@ interface SafeImageProps extends Omit<React.ImgHTMLAttributes<HTMLImageElement>,
|
|||||||
className?: string;
|
className?: string;
|
||||||
imgClassName?: string;
|
imgClassName?: string;
|
||||||
fallbackText?: string;
|
fallbackText?: string;
|
||||||
|
priority?: boolean;
|
||||||
|
sizes?: string;
|
||||||
|
quality?: number;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SafeImage({
|
export default function SafeImage({
|
||||||
@ -17,10 +22,15 @@ export default function SafeImage({
|
|||||||
alt = "تصویر مکمل کنینا",
|
alt = "تصویر مکمل کنینا",
|
||||||
className,
|
className,
|
||||||
imgClassName,
|
imgClassName,
|
||||||
fallbackText = "در حال بروزرسانی تصویر..."
|
fallbackText = "در حال بروزرسانی تصویر...",
|
||||||
|
priority = false,
|
||||||
|
sizes = "(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 400px",
|
||||||
|
quality = 85,
|
||||||
|
width = 500,
|
||||||
|
height = 500,
|
||||||
}: SafeImageProps) {
|
}: SafeImageProps) {
|
||||||
const [error, setError] = React.useState(false);
|
const [error, setError] = React.useState(false);
|
||||||
const [loading, setLoading] = React.useState(true);
|
const [loading, setLoading] = React.useState(!priority);
|
||||||
|
|
||||||
// Check if external domain is in allowed remote patterns or relative path
|
// Check if external domain is in allowed remote patterns or relative path
|
||||||
const isExternal = typeof src === 'string' && (src.startsWith('http://') || src.startsWith('https://'));
|
const isExternal = typeof src === 'string' && (src.startsWith('http://') || src.startsWith('https://'));
|
||||||
@ -43,7 +53,7 @@ export default function SafeImage({
|
|||||||
if (!src || error) {
|
if (!src || error) {
|
||||||
return (
|
return (
|
||||||
<div className={cn(
|
<div className={cn(
|
||||||
"flex flex-col items-center justify-center bg-medical-gray-50/90 border border-medical-gray-100 p-2 text-center w-full h-full rounded-2xl relative overflow-hidden select-none",
|
"flex flex-col items-center justify-center bg-medical-gray-50/90 border border-medical-gray-100 p-2 text-center w-full h-full rounded-2xl relative overflow-hidden select-none aspect-square",
|
||||||
className
|
className
|
||||||
)}>
|
)}>
|
||||||
<Image
|
<Image
|
||||||
@ -63,7 +73,7 @@ export default function SafeImage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn("relative overflow-hidden group flex items-center justify-center", className)}>
|
<div className={cn("relative overflow-hidden group flex items-center justify-center", className)}>
|
||||||
{loading && (
|
{loading && !priority && (
|
||||||
<div className="absolute inset-0 bg-medical-gray-50 animate-pulse flex items-center justify-center z-10">
|
<div className="absolute inset-0 bg-medical-gray-50 animate-pulse flex items-center justify-center z-10">
|
||||||
<Sparkles className="w-6 h-6 text-canina-blue/20" />
|
<Sparkles className="w-6 h-6 text-canina-blue/20" />
|
||||||
</div>
|
</div>
|
||||||
@ -71,14 +81,17 @@ export default function SafeImage({
|
|||||||
<Image
|
<Image
|
||||||
src={src}
|
src={src}
|
||||||
alt={alt}
|
alt={alt}
|
||||||
width={500}
|
width={width}
|
||||||
height={500}
|
height={height}
|
||||||
loading="lazy"
|
priority={priority}
|
||||||
|
loading={priority ? undefined : "lazy"}
|
||||||
|
sizes={sizes}
|
||||||
|
quality={quality}
|
||||||
unoptimized={isBlobOrData || !isAllowedHost}
|
unoptimized={isBlobOrData || !isAllowedHost}
|
||||||
className={cn(
|
className={cn(
|
||||||
"transition-all duration-700 w-full h-full",
|
"transition-all duration-500 w-full h-full object-contain",
|
||||||
imgClassName,
|
imgClassName,
|
||||||
loading ? "opacity-0 scale-110" : "opacity-100 scale-100"
|
loading && !priority ? "opacity-0 scale-105" : "opacity-100 scale-100"
|
||||||
)}
|
)}
|
||||||
onLoad={() => setLoading(false)}
|
onLoad={() => setLoading(false)}
|
||||||
onError={() => setError(true)}
|
onError={() => setError(true)}
|
||||||
|
|||||||
28
frontend/application/lib/fonts.ts
Normal file
28
frontend/application/lib/fonts.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import localFont from 'next/font/local';
|
||||||
|
|
||||||
|
export const vazirmatn = localFont({
|
||||||
|
src: [
|
||||||
|
{
|
||||||
|
path: '../public/fonts/vazirmatn-v33.003/fonts/webfonts/Vazirmatn[wght].woff2',
|
||||||
|
style: 'normal',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
variable: '--font-vazir',
|
||||||
|
display: 'swap',
|
||||||
|
fallback: ['system-ui', '-apple-system', 'Segoe UI', 'Roboto', 'Arial', 'sans-serif'],
|
||||||
|
preload: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const lalezar = localFont({
|
||||||
|
src: [
|
||||||
|
{
|
||||||
|
path: '../public/fonts/Lalezar-Font-fontyab.com_/Lalezar-Font/TTF/Lalezar-Regular.ttf',
|
||||||
|
weight: '400',
|
||||||
|
style: 'normal',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
variable: '--font-lalezar',
|
||||||
|
display: 'swap',
|
||||||
|
fallback: ['cursive', 'sans-serif'],
|
||||||
|
preload: false,
|
||||||
|
});
|
||||||
@ -232,6 +232,17 @@ export class ProductService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async serverFetch(endpoint: string, tags: string[] = ['products'], revalidate = 3600) {
|
||||||
|
const apiUrl = process.env.INTERNAL_API_URL || process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4001/api';
|
||||||
|
const apiBase = apiUrl.endsWith('/api') ? apiUrl : `${apiUrl.replace(/\/$/, '')}/api`;
|
||||||
|
const cleanEndpoint = endpoint.startsWith('/') ? endpoint : `/${endpoint}`;
|
||||||
|
const res = await fetch(`${apiBase}${cleanEndpoint}`, {
|
||||||
|
next: { tags, revalidate },
|
||||||
|
});
|
||||||
|
if (!res.ok) throw new Error(`Fetch failed: ${res.status}`);
|
||||||
|
return res.json();
|
||||||
|
}
|
||||||
|
|
||||||
public async getProducts(filters?: {
|
public async getProducts(filters?: {
|
||||||
category?: string;
|
category?: string;
|
||||||
petType?: PetType | "all";
|
petType?: PetType | "all";
|
||||||
@ -253,6 +264,13 @@ export class ProductService {
|
|||||||
params.append('limit', String(filters?.limit || 12));
|
params.append('limit', String(filters?.limit || 12));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (typeof window === 'undefined') {
|
||||||
|
const json = await this.serverFetch(`/products?${params.toString()}`, ['products'], 3600);
|
||||||
|
return {
|
||||||
|
data: json.data.map((item: Record<string, unknown>) => this.mapBackendToFrontend(item)),
|
||||||
|
meta: json.meta,
|
||||||
|
};
|
||||||
|
}
|
||||||
const response = await api.get(`/products?${params.toString()}`);
|
const response = await api.get(`/products?${params.toString()}`);
|
||||||
return {
|
return {
|
||||||
data: response.data.data.map((item: Record<string, unknown>) => this.mapBackendToFrontend(item)),
|
data: response.data.data.map((item: Record<string, unknown>) => this.mapBackendToFrontend(item)),
|
||||||
@ -266,6 +284,10 @@ export class ProductService {
|
|||||||
|
|
||||||
public async getProductById(id: string): Promise<Product | null> {
|
public async getProductById(id: string): Promise<Product | null> {
|
||||||
try {
|
try {
|
||||||
|
if (typeof window === 'undefined') {
|
||||||
|
const data = await this.serverFetch(`/products/${id}`, ['products'], 3600);
|
||||||
|
return this.mapBackendToFrontend(data);
|
||||||
|
}
|
||||||
const response = await api.get(`/products/${id}`);
|
const response = await api.get(`/products/${id}`);
|
||||||
return this.mapBackendToFrontend(response.data);
|
return this.mapBackendToFrontend(response.data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -276,6 +298,10 @@ export class ProductService {
|
|||||||
|
|
||||||
public async getProductBySlug(slug: string): Promise<Product | null> {
|
public async getProductBySlug(slug: string): Promise<Product | null> {
|
||||||
try {
|
try {
|
||||||
|
if (typeof window === 'undefined') {
|
||||||
|
const data = await this.serverFetch(`/products/${encodeURIComponent(slug)}`, ['products', `product-${slug}`], 3600);
|
||||||
|
return this.mapBackendToFrontend(data);
|
||||||
|
}
|
||||||
const response = await api.get(`/products/${slug}`);
|
const response = await api.get(`/products/${slug}`);
|
||||||
return this.mapBackendToFrontend(response.data);
|
return this.mapBackendToFrontend(response.data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -12,50 +12,62 @@ const securityHeaders = [
|
|||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
images: {
|
images: {
|
||||||
formats: ['image/avif', 'image/webp'],
|
formats: ['image/avif', 'image/webp'],
|
||||||
|
minimumCacheTTL: 31536000,
|
||||||
remotePatterns: [
|
remotePatterns: [
|
||||||
{
|
{
|
||||||
protocol: 'https',
|
protocol: 'https',
|
||||||
hostname: 'canina.ir',
|
hostname: 'canina.ir',
|
||||||
|
pathname: '/**',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
protocol: 'https',
|
protocol: 'https',
|
||||||
hostname: 'api.canina.ir',
|
hostname: 'api.canina.ir',
|
||||||
|
pathname: '/**',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
protocol: 'https',
|
protocol: 'https',
|
||||||
hostname: 'stage.canina.ir',
|
hostname: 'stage.canina.ir',
|
||||||
|
pathname: '/**',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
protocol: 'https',
|
protocol: 'https',
|
||||||
hostname: 'stageapi.canina.ir',
|
hostname: 'stageapi.canina.ir',
|
||||||
|
pathname: '/**',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
protocol: 'https',
|
protocol: 'https',
|
||||||
hostname: 'apicanina.parsaaghayi.ir',
|
hostname: 'apicanina.parsaaghayi.ir',
|
||||||
|
pathname: '/**',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
protocol: 'https',
|
protocol: 'https',
|
||||||
hostname: 'canina.de',
|
hostname: 'canina.de',
|
||||||
|
pathname: '/**',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
protocol: 'https',
|
protocol: 'https',
|
||||||
hostname: 'www.canina.de',
|
hostname: 'www.canina.de',
|
||||||
|
pathname: '/**',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
protocol: 'https',
|
protocol: 'https',
|
||||||
hostname: 'images.unsplash.com',
|
hostname: 'images.unsplash.com',
|
||||||
|
pathname: '/**',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
protocol: 'https',
|
protocol: 'https',
|
||||||
hostname: 'plus.unsplash.com',
|
hostname: 'plus.unsplash.com',
|
||||||
|
pathname: '/**',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
protocol: 'http',
|
protocol: 'http',
|
||||||
hostname: 'localhost',
|
hostname: 'localhost',
|
||||||
|
pathname: '/**',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
protocol: 'http',
|
protocol: 'http',
|
||||||
hostname: '127.0.0.1',
|
hostname: '127.0.0.1',
|
||||||
|
pathname: '/**',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -68,12 +80,12 @@ const nextConfig: NextConfig = {
|
|||||||
];
|
];
|
||||||
},
|
},
|
||||||
async rewrites() {
|
async rewrites() {
|
||||||
// Only proxy /api in development; in production nginx handles it
|
// Only proxy /api in development; exclude local Next.js route handlers such as /api/revalidate
|
||||||
if (process.env.NODE_ENV === 'development') {
|
if (process.env.NODE_ENV === 'development') {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
source: '/api/:path*',
|
source: '/api/((?!revalidate).*)',
|
||||||
destination: 'http://localhost:4001/api/:path*',
|
destination: 'http://localhost:4001/api/$1',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,22 +1,22 @@
|
|||||||
{
|
{
|
||||||
"0": "Roles",
|
"0": "Roles",
|
||||||
"1": "app.module.ts",
|
"1": "app.module.ts",
|
||||||
"2": "SmsService",
|
"2": "SettingsController",
|
||||||
"3": "productService.ts",
|
"3": "ProductService",
|
||||||
"4": "ProductPage.tsx",
|
"4": "SafeImage.tsx",
|
||||||
"5": "CmsController",
|
"5": "CmsController",
|
||||||
"6": "tickets.controller.ts",
|
"6": "tickets.controller.ts",
|
||||||
"7": "PaginationDto",
|
"7": "PaginationDto",
|
||||||
"8": "ReportsController",
|
"8": "admin.module.ts",
|
||||||
"9": "devDependencies",
|
"9": "devDependencies",
|
||||||
"10": "reviews.controller.ts",
|
"10": "ReviewsController",
|
||||||
"11": "Spinner.tsx",
|
"11": "Spinner.tsx",
|
||||||
"12": "PetProfile.tsx",
|
"12": "useCartStore",
|
||||||
"13": "app-audit-verification.e2e-spec.js",
|
"13": "app-audit-verification.e2e-spec.js",
|
||||||
"14": "lib/services/api.ts",
|
"14": "lib/services/api.ts",
|
||||||
"15": "src/services/api.ts",
|
"15": "src/services/api.ts",
|
||||||
"16": "DoctorQueryDto",
|
"16": "DoctorQueryDto",
|
||||||
"17": "auth.service.ts",
|
"17": "RedisService",
|
||||||
"18": "JwtAuthGuard",
|
"18": "JwtAuthGuard",
|
||||||
"19": "ProductsService",
|
"19": "ProductsService",
|
||||||
"20": "CreateVideoDto",
|
"20": "CreateVideoDto",
|
||||||
@ -32,7 +32,7 @@
|
|||||||
"30": "DEVOPS-001",
|
"30": "DEVOPS-001",
|
||||||
"31": "DOC-001",
|
"31": "DOC-001",
|
||||||
"32": "adminRoutes.tsx",
|
"32": "adminRoutes.tsx",
|
||||||
"33": "WholesaleService",
|
"33": "WholesaleApplyDto",
|
||||||
"34": "B2BService",
|
"34": "B2BService",
|
||||||
"35": "AuthController",
|
"35": "AuthController",
|
||||||
"36": "FaqService",
|
"36": "FaqService",
|
||||||
@ -45,13 +45,13 @@
|
|||||||
"43": "BannersService",
|
"43": "BannersService",
|
||||||
"44": "TestimonialsService",
|
"44": "TestimonialsService",
|
||||||
"45": "What You Must Do When Invoked",
|
"45": "What You Must Do When Invoked",
|
||||||
"46": "AdminService",
|
"46": "ApiOperation",
|
||||||
"47": "IngredientsService",
|
"47": "IngredientsService",
|
||||||
"48": "Reports.tsx",
|
"48": "Reports.tsx",
|
||||||
"49": "devDependencies",
|
"49": "devDependencies",
|
||||||
"50": "devDependencies",
|
"50": "devDependencies",
|
||||||
"51": "BlogsController",
|
"51": "BlogsController",
|
||||||
"52": "PrescriptionsController",
|
"52": "PrescriptionsService",
|
||||||
"53": "SmartAdvisorService",
|
"53": "SmartAdvisorService",
|
||||||
"54": "UsersService",
|
"54": "UsersService",
|
||||||
"55": "UITexts.tsx",
|
"55": "UITexts.tsx",
|
||||||
@ -60,13 +60,13 @@
|
|||||||
"58": "ContactService",
|
"58": "ContactService",
|
||||||
"59": "compilerOptions",
|
"59": "compilerOptions",
|
||||||
"60": "admin.service.ts",
|
"60": "admin.service.ts",
|
||||||
"61": "payment.controller.ts",
|
"61": "CreateEBankCheckoutDto",
|
||||||
"62": "api",
|
"62": "api",
|
||||||
"63": "dependencies",
|
"63": "dependencies",
|
||||||
"64": "compilerOptions",
|
"64": "compilerOptions",
|
||||||
"65": "AdminTransactionFilterDto",
|
"65": "AdminTransactionFilterDto",
|
||||||
"66": "AdminQueryDto",
|
"66": "AdminQueryDto",
|
||||||
"67": "admin.module.ts",
|
"67": "PetsController",
|
||||||
"68": "BlogsController",
|
"68": "BlogsController",
|
||||||
"69": "Required Review Group Closures",
|
"69": "Required Review Group Closures",
|
||||||
"70": "compilerOptions",
|
"70": "compilerOptions",
|
||||||
@ -84,15 +84,15 @@
|
|||||||
"82": "scripts",
|
"82": "scripts",
|
||||||
"83": "dependencies",
|
"83": "dependencies",
|
||||||
"84": "Role & Core Objective",
|
"84": "Role & Core Objective",
|
||||||
"85": "WholesaleApplyDto",
|
"85": "SettingsService",
|
||||||
"86": "zibal.service.ts",
|
"86": "zibal.service.ts",
|
||||||
"87": "dependencies",
|
"87": "dependencies",
|
||||||
"88": "useSettingsStore",
|
"88": "useSettingsStore",
|
||||||
"89": "seed-products.ts",
|
"89": "seed-products.ts",
|
||||||
"90": "MetricsController",
|
"90": "HomeClient.tsx",
|
||||||
"91": "Reconciled Audit Roles & Assignments",
|
"91": "Reconciled Audit Roles & Assignments",
|
||||||
"92": "OrdersService",
|
"92": "OrdersService",
|
||||||
"93": "useCartStore",
|
"93": "ProductPage.tsx",
|
||||||
"94": "Phase 3.1 — Human Review Preparation and Master Backlog Critique",
|
"94": "Phase 3.1 — Human Review Preparation and Master Backlog Critique",
|
||||||
"95": "getSeoConfig",
|
"95": "getSeoConfig",
|
||||||
"96": "compilerOptions",
|
"96": "compilerOptions",
|
||||||
@ -112,7 +112,7 @@
|
|||||||
"110": "Operational Rules & Boundaries",
|
"110": "Operational Rules & Boundaries",
|
||||||
"111": "Operational Rules & Boundaries",
|
"111": "Operational Rules & Boundaries",
|
||||||
"112": "Operational Rules & Boundaries",
|
"112": "Operational Rules & Boundaries",
|
||||||
"113": "Reviews.tsx",
|
"113": "reviews.controller.ts",
|
||||||
"114": "AppService",
|
"114": "AppService",
|
||||||
"115": "catalog/page.tsx",
|
"115": "catalog/page.tsx",
|
||||||
"116": "Vazirmatn Changelog",
|
"116": "Vazirmatn Changelog",
|
||||||
@ -121,13 +121,13 @@
|
|||||||
"119": "compilerOptions",
|
"119": "compilerOptions",
|
||||||
"120": "compilerOptions",
|
"120": "compilerOptions",
|
||||||
"121": "backend/README.md",
|
"121": "backend/README.md",
|
||||||
"122": "bcryptjs",
|
"122": "SmsService",
|
||||||
"123": "helmet",
|
"123": "BlogsService",
|
||||||
"124": "Repository Map",
|
"124": "Repository Map",
|
||||||
"125": "validate_integrity.js",
|
"125": "validate_integrity.js",
|
||||||
"126": "admin-panel/package.json",
|
"126": "admin-panel/package.json",
|
||||||
"127": "Sahel-Font",
|
"127": "Sahel-Font",
|
||||||
"128": "js-yaml",
|
"128": "AdminService",
|
||||||
"129": "seo.ts",
|
"129": "seo.ts",
|
||||||
"130": "Sahel-Font",
|
"130": "Sahel-Font",
|
||||||
"131": "Role & Core Objective",
|
"131": "Role & Core Objective",
|
||||||
@ -146,15 +146,15 @@
|
|||||||
"144": "@testing-library/react",
|
"144": "@testing-library/react",
|
||||||
"145": "eslint-config-prettier",
|
"145": "eslint-config-prettier",
|
||||||
"146": "System Discovery",
|
"146": "System Discovery",
|
||||||
"147": "PrescriptionsManager.tsx",
|
"147": "RevalidationService",
|
||||||
"148": "eslint",
|
"148": "AdminController",
|
||||||
"149": "SmsSettingsPage.tsx",
|
"149": "SmsSettingsPage.tsx",
|
||||||
"150": "Product Requirement Document (PRD)",
|
"150": "Product Requirement Document (PRD)",
|
||||||
"151": "@nestjs/jwt",
|
"151": "AuthService",
|
||||||
"152": "@nestjs/cli",
|
"152": "@nestjs/cli",
|
||||||
"153": "exclude",
|
"153": "exclude",
|
||||||
"154": "Baseline Command Plan & Reconciled Command History",
|
"154": "Baseline Command Plan & Reconciled Command History",
|
||||||
"155": "@nestjs/swagger",
|
"155": "Blogs.tsx",
|
||||||
"156": "contact/page.tsx",
|
"156": "contact/page.tsx",
|
||||||
"157": "ErrorPages.tsx",
|
"157": "ErrorPages.tsx",
|
||||||
"158": "@nestjs/throttler",
|
"158": "@nestjs/throttler",
|
||||||
@ -178,13 +178,13 @@
|
|||||||
"176": "passport-jwt",
|
"176": "passport-jwt",
|
||||||
"177": "blog/page.tsx",
|
"177": "blog/page.tsx",
|
||||||
"178": "prettier",
|
"178": "prettier",
|
||||||
"179": "@prisma/client",
|
"179": "BlogsService",
|
||||||
"180": "API Contract Specification",
|
"180": "API Contract Specification",
|
||||||
"181": "⚙️ Backend Technical Review (05_dev_backend)",
|
"181": "⚙️ Backend Technical Review (05_dev_backend)",
|
||||||
"182": "🎨 Frontend & Admin Panel Technical Review (06_dev_frontend)",
|
"182": "🎨 Frontend & Admin Panel Technical Review (06_dev_frontend)",
|
||||||
"183": "🚀 SEO & Content Strategy Review (12_seo_content)",
|
"183": "🚀 SEO & Content Strategy Review (12_seo_content)",
|
||||||
"184": "swagger-ui-express",
|
"184": "swagger-ui-express",
|
||||||
"185": "@types/node",
|
"185": "ApiResponse",
|
||||||
"186": "seed-ui-texts.ts",
|
"186": "seed-ui-texts.ts",
|
||||||
"187": "seed-wiki.ts",
|
"187": "seed-wiki.ts",
|
||||||
"188": "update-blog.dto.ts",
|
"188": "update-blog.dto.ts",
|
||||||
@ -198,7 +198,7 @@
|
|||||||
"196": "Select.tsx",
|
"196": "Select.tsx",
|
||||||
"197": "@eslint/eslintrc",
|
"197": "@eslint/eslintrc",
|
||||||
"198": "videos/page.tsx",
|
"198": "videos/page.tsx",
|
||||||
"199": "useUserStore",
|
"199": "auth.service.ts",
|
||||||
"200": "application/README.md",
|
"200": "application/README.md",
|
||||||
"201": "deploy.sh",
|
"201": "deploy.sh",
|
||||||
"202": "🔒 Security & Performance Review (09_devops_security)",
|
"202": "🔒 Security & Performance Review (09_devops_security)",
|
||||||
@ -238,7 +238,7 @@
|
|||||||
"236": "@types/bcrypt",
|
"236": "@types/bcrypt",
|
||||||
"237": "ts-jest",
|
"237": "ts-jest",
|
||||||
"238": "tsconfig-paths",
|
"238": "tsconfig-paths",
|
||||||
"239": "@types/bcryptjs",
|
"239": "RegisterDto",
|
||||||
"240": "typescript-eslint",
|
"240": "typescript-eslint",
|
||||||
"241": "blog.entity.ts",
|
"241": "blog.entity.ts",
|
||||||
"242": "home.entity.ts",
|
"242": "home.entity.ts",
|
||||||
@ -263,6 +263,7 @@
|
|||||||
"261": "User Login API",
|
"261": "User Login API",
|
||||||
"262": "User Logout API",
|
"262": "User Logout API",
|
||||||
"263": "generate-openapi.d.ts",
|
"263": "generate-openapi.d.ts",
|
||||||
|
"264": "InitiatePaymentDto",
|
||||||
"265": "@types/express",
|
"265": "@types/express",
|
||||||
"266": "@types/jest",
|
"266": "@types/jest",
|
||||||
"267": "@types/js-yaml",
|
"267": "@types/js-yaml",
|
||||||
@ -302,12 +303,32 @@
|
|||||||
"301": "app.e2e-spec.js",
|
"301": "app.e2e-spec.js",
|
||||||
"302": "typescript-eslint",
|
"302": "typescript-eslint",
|
||||||
"303": "@testing-library/jest-dom",
|
"303": "@testing-library/jest-dom",
|
||||||
|
"304": "SmsLogQueryDto",
|
||||||
"305": "@types/react",
|
"305": "@types/react",
|
||||||
"306": "globals",
|
"306": "globals",
|
||||||
|
"307": "SendOtpDto",
|
||||||
"308": "vitest",
|
"308": "vitest",
|
||||||
"309": "axios",
|
"309": "axios",
|
||||||
"310": "tailwindcss",
|
"310": "tailwindcss",
|
||||||
"311": "track/page.tsx",
|
"311": "track/page.tsx",
|
||||||
|
"312": ".addComment",
|
||||||
|
"313": "MenuManager.tsx",
|
||||||
|
"314": "layout.tsx",
|
||||||
"315": "typescript",
|
"315": "typescript",
|
||||||
"318": "MaskableField.tsx"
|
"316": "trust-seals/page.tsx",
|
||||||
|
"317": "revalidate/route.ts",
|
||||||
|
"318": "MaskableField.tsx",
|
||||||
|
"319": ".getDashboardStats",
|
||||||
|
"320": ".addPattern",
|
||||||
|
"321": "class-validator",
|
||||||
|
"322": "ioredis",
|
||||||
|
"323": "@nestjs/common",
|
||||||
|
"324": "@nestjs/passport",
|
||||||
|
"325": "@nestjs/platform-express",
|
||||||
|
"326": "passport",
|
||||||
|
"327": "reflect-metadata",
|
||||||
|
"328": "eslint-plugin-prettier",
|
||||||
|
"329": "@types/compression",
|
||||||
|
"330": "@types/passport-jwt",
|
||||||
|
"331": "@types/supertest"
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -3,12 +3,12 @@
|
|||||||
"1": "app.module.ts",
|
"1": "app.module.ts",
|
||||||
"2": "SmsService",
|
"2": "SmsService",
|
||||||
"3": "productService.ts",
|
"3": "productService.ts",
|
||||||
"4": "SafeImage.tsx",
|
"4": "ProductPage.tsx",
|
||||||
"5": "CmsController",
|
"5": "CmsController",
|
||||||
"6": "tickets.controller.ts",
|
"6": "tickets.controller.ts",
|
||||||
"7": "PaginationDto",
|
"7": "PaginationDto",
|
||||||
"8": "admin.module.ts",
|
"8": "ReportsController",
|
||||||
"9": "eslint-plugin-prettier",
|
"9": "devDependencies",
|
||||||
"10": "reviews.controller.ts",
|
"10": "reviews.controller.ts",
|
||||||
"11": "Spinner.tsx",
|
"11": "Spinner.tsx",
|
||||||
"12": "PetProfile.tsx",
|
"12": "PetProfile.tsx",
|
||||||
@ -60,13 +60,13 @@
|
|||||||
"58": "ContactService",
|
"58": "ContactService",
|
||||||
"59": "compilerOptions",
|
"59": "compilerOptions",
|
||||||
"60": "admin.service.ts",
|
"60": "admin.service.ts",
|
||||||
"61": "CreateEBankCheckoutDto",
|
"61": "payment.controller.ts",
|
||||||
"62": "api",
|
"62": "api",
|
||||||
"63": "dependencies",
|
"63": "dependencies",
|
||||||
"64": "compilerOptions",
|
"64": "compilerOptions",
|
||||||
"65": "ProductPage.tsx",
|
"65": "AdminTransactionFilterDto",
|
||||||
"66": "AdminQueryDto",
|
"66": "AdminQueryDto",
|
||||||
"67": "PetsController",
|
"67": "admin.module.ts",
|
||||||
"68": "BlogsController",
|
"68": "BlogsController",
|
||||||
"69": "Required Review Group Closures",
|
"69": "Required Review Group Closures",
|
||||||
"70": "compilerOptions",
|
"70": "compilerOptions",
|
||||||
@ -76,8 +76,8 @@
|
|||||||
"74": "WikiController",
|
"74": "WikiController",
|
||||||
"75": "PetsController",
|
"75": "PetsController",
|
||||||
"76": "PaymentService",
|
"76": "PaymentService",
|
||||||
"77": "SeoService",
|
"77": "seo.module.ts",
|
||||||
"78": "AuthService",
|
"78": "route.ts",
|
||||||
"79": "🏢 AI Software Agency — Master Orchestration Protocol v3",
|
"79": "🏢 AI Software Agency — Master Orchestration Protocol v3",
|
||||||
"80": "Operational Rules & Boundaries",
|
"80": "Operational Rules & Boundaries",
|
||||||
"81": "Operational Rules & Boundaries",
|
"81": "Operational Rules & Boundaries",
|
||||||
@ -87,12 +87,12 @@
|
|||||||
"85": "WholesaleApplyDto",
|
"85": "WholesaleApplyDto",
|
||||||
"86": "zibal.service.ts",
|
"86": "zibal.service.ts",
|
||||||
"87": "dependencies",
|
"87": "dependencies",
|
||||||
"88": "HomeClient.tsx",
|
"88": "useSettingsStore",
|
||||||
"89": "seed-products.ts",
|
"89": "seed-products.ts",
|
||||||
"90": "MetricsController",
|
"90": "MetricsController",
|
||||||
"91": "Reconciled Audit Roles & Assignments",
|
"91": "Reconciled Audit Roles & Assignments",
|
||||||
"92": "OrdersService",
|
"92": "OrdersService",
|
||||||
"93": "OrderService",
|
"93": "useCartStore",
|
||||||
"94": "Phase 3.1 — Human Review Preparation and Master Backlog Critique",
|
"94": "Phase 3.1 — Human Review Preparation and Master Backlog Critique",
|
||||||
"95": "getSeoConfig",
|
"95": "getSeoConfig",
|
||||||
"96": "compilerOptions",
|
"96": "compilerOptions",
|
||||||
@ -106,7 +106,7 @@
|
|||||||
"104": "Coupons.tsx",
|
"104": "Coupons.tsx",
|
||||||
"105": "Operational Rules & Boundaries",
|
"105": "Operational Rules & Boundaries",
|
||||||
"106": "auth.controller.ts",
|
"106": "auth.controller.ts",
|
||||||
"107": "zibal-ebank.service.ts",
|
"107": "shop/page.tsx",
|
||||||
"108": "PrismaService",
|
"108": "PrismaService",
|
||||||
"109": "1. Summary of Integrity Repairs Performed",
|
"109": "1. Summary of Integrity Repairs Performed",
|
||||||
"110": "Operational Rules & Boundaries",
|
"110": "Operational Rules & Boundaries",
|
||||||
@ -121,19 +121,19 @@
|
|||||||
"119": "compilerOptions",
|
"119": "compilerOptions",
|
||||||
"120": "compilerOptions",
|
"120": "compilerOptions",
|
||||||
"121": "backend/README.md",
|
"121": "backend/README.md",
|
||||||
"122": "wiki/page.tsx",
|
"122": "bcryptjs",
|
||||||
"123": "@types/passport-jwt",
|
"123": "helmet",
|
||||||
"124": "Repository Map",
|
"124": "Repository Map",
|
||||||
"125": "validate_integrity.js",
|
"125": "validate_integrity.js",
|
||||||
"126": "admin-panel/package.json",
|
"126": "admin-panel/package.json",
|
||||||
"127": "Sahel-Font",
|
"127": "Sahel-Font",
|
||||||
"128": "@types/supertest",
|
"128": "js-yaml",
|
||||||
"129": "seo.ts",
|
"129": "seo.ts",
|
||||||
"130": "Sahel-Font",
|
"130": "Sahel-Font",
|
||||||
"131": "Role & Core Objective",
|
"131": "Role & Core Objective",
|
||||||
"132": "orchestrate.py",
|
"132": "orchestrate.py",
|
||||||
"133": "backend/package.json",
|
"133": "backend/package.json",
|
||||||
"134": "typescript",
|
"134": "@nestjs/core",
|
||||||
"135": "graphify reference: extra exports and benchmark",
|
"135": "graphify reference: extra exports and benchmark",
|
||||||
"136": "Phase 2 Final Quality Gate Summary Report",
|
"136": "Phase 2 Final Quality Gate Summary Report",
|
||||||
"137": "Task Modifications Log",
|
"137": "Task Modifications Log",
|
||||||
@ -147,14 +147,17 @@
|
|||||||
"145": "eslint-config-prettier",
|
"145": "eslint-config-prettier",
|
||||||
"146": "System Discovery",
|
"146": "System Discovery",
|
||||||
"147": "PrescriptionsManager.tsx",
|
"147": "PrescriptionsManager.tsx",
|
||||||
"148": "devDependencies",
|
"148": "eslint",
|
||||||
"149": "SmsSettingsPage.tsx",
|
"149": "SmsSettingsPage.tsx",
|
||||||
"150": "Product Requirement Document (PRD)",
|
"150": "Product Requirement Document (PRD)",
|
||||||
|
"151": "@nestjs/jwt",
|
||||||
"152": "@nestjs/cli",
|
"152": "@nestjs/cli",
|
||||||
"153": "exclude",
|
"153": "exclude",
|
||||||
"154": "Baseline Command Plan & Reconciled Command History",
|
"154": "Baseline Command Plan & Reconciled Command History",
|
||||||
|
"155": "@nestjs/swagger",
|
||||||
"156": "contact/page.tsx",
|
"156": "contact/page.tsx",
|
||||||
"157": "ErrorPages.tsx",
|
"157": "ErrorPages.tsx",
|
||||||
|
"158": "@nestjs/throttler",
|
||||||
"159": "with-vpn.sh",
|
"159": "with-vpn.sh",
|
||||||
"160": "Architecture Specification",
|
"160": "Architecture Specification",
|
||||||
"161": "Project Health Audit Report",
|
"161": "Project Health Audit Report",
|
||||||
@ -172,13 +175,15 @@
|
|||||||
"173": "Phase 3 Audit Traceability Matrix",
|
"173": "Phase 3 Audit Traceability Matrix",
|
||||||
"174": "rebuild_honest_ledger.js",
|
"174": "rebuild_honest_ledger.js",
|
||||||
"175": "validate_evidence_grade.js",
|
"175": "validate_evidence_grade.js",
|
||||||
"176": "InitiatePaymentDto",
|
"176": "passport-jwt",
|
||||||
"177": "blog/page.tsx",
|
"177": "blog/page.tsx",
|
||||||
"178": "prettier",
|
"178": "prettier",
|
||||||
|
"179": "@prisma/client",
|
||||||
"180": "API Contract Specification",
|
"180": "API Contract Specification",
|
||||||
"181": "⚙️ Backend Technical Review (05_dev_backend)",
|
"181": "⚙️ Backend Technical Review (05_dev_backend)",
|
||||||
"182": "🎨 Frontend & Admin Panel Technical Review (06_dev_frontend)",
|
"182": "🎨 Frontend & Admin Panel Technical Review (06_dev_frontend)",
|
||||||
"183": "🚀 SEO & Content Strategy Review (12_seo_content)",
|
"183": "🚀 SEO & Content Strategy Review (12_seo_content)",
|
||||||
|
"184": "swagger-ui-express",
|
||||||
"185": "@types/node",
|
"185": "@types/node",
|
||||||
"186": "seed-ui-texts.ts",
|
"186": "seed-ui-texts.ts",
|
||||||
"187": "seed-wiki.ts",
|
"187": "seed-wiki.ts",
|
||||||
@ -191,8 +196,9 @@
|
|||||||
"194": "Raw Finding Verification & Disposition Report",
|
"194": "Raw Finding Verification & Disposition Report",
|
||||||
"195": "React + TypeScript + Vite",
|
"195": "React + TypeScript + Vite",
|
||||||
"196": "Select.tsx",
|
"196": "Select.tsx",
|
||||||
|
"197": "@eslint/eslintrc",
|
||||||
"198": "videos/page.tsx",
|
"198": "videos/page.tsx",
|
||||||
"199": "useSettingsStore",
|
"199": "useUserStore",
|
||||||
"200": "application/README.md",
|
"200": "application/README.md",
|
||||||
"201": "deploy.sh",
|
"201": "deploy.sh",
|
||||||
"202": "🔒 Security & Performance Review (09_devops_security)",
|
"202": "🔒 Security & Performance Review (09_devops_security)",
|
||||||
@ -216,15 +222,24 @@
|
|||||||
"220": "Input.tsx",
|
"220": "Input.tsx",
|
||||||
"221": "Textarea.tsx",
|
"221": "Textarea.tsx",
|
||||||
"222": "admin-panel/tsconfig.json",
|
"222": "admin-panel/tsconfig.json",
|
||||||
|
"223": "@eslint/js",
|
||||||
|
"224": "jest",
|
||||||
"225": "next.config.ts",
|
"225": "next.config.ts",
|
||||||
"226": "Shabnam Font README",
|
"226": "Shabnam Font README",
|
||||||
"227": "AGENTS.md",
|
"227": "AGENTS.md",
|
||||||
"228": "rules/graphify.md",
|
"228": "rules/graphify.md",
|
||||||
"229": ".agents/workflows/graphify.md",
|
"229": ".agents/workflows/graphify.md",
|
||||||
"230": "instructions.md",
|
"230": "instructions.md",
|
||||||
|
"231": "@nestjs/schematics",
|
||||||
"232": "ts-loader",
|
"232": "ts-loader",
|
||||||
"233": "ts-node",
|
"233": "ts-node",
|
||||||
|
"234": "@nestjs/testing",
|
||||||
|
"235": "source-map-support",
|
||||||
"236": "@types/bcrypt",
|
"236": "@types/bcrypt",
|
||||||
|
"237": "ts-jest",
|
||||||
|
"238": "tsconfig-paths",
|
||||||
|
"239": "@types/bcryptjs",
|
||||||
|
"240": "typescript-eslint",
|
||||||
"241": "blog.entity.ts",
|
"241": "blog.entity.ts",
|
||||||
"242": "home.entity.ts",
|
"242": "home.entity.ts",
|
||||||
"243": "wiki.entity.ts",
|
"243": "wiki.entity.ts",
|
||||||
@ -281,7 +296,7 @@
|
|||||||
"295": "eslint-plugin-react-refresh",
|
"295": "eslint-plugin-react-refresh",
|
||||||
"296": "tailwindcss",
|
"296": "tailwindcss",
|
||||||
"297": "ZibalEBankService",
|
"297": "ZibalEBankService",
|
||||||
"298": ".initiateOrderPayment",
|
"298": "ZibalCallbackQueryDto",
|
||||||
"299": "@tailwindcss/postcss",
|
"299": "@tailwindcss/postcss",
|
||||||
"300": "typescript",
|
"300": "typescript",
|
||||||
"301": "app.e2e-spec.js",
|
"301": "app.e2e-spec.js",
|
||||||
@ -293,7 +308,6 @@
|
|||||||
"309": "axios",
|
"309": "axios",
|
||||||
"310": "tailwindcss",
|
"310": "tailwindcss",
|
||||||
"311": "track/page.tsx",
|
"311": "track/page.tsx",
|
||||||
"313": "app/page.tsx",
|
|
||||||
"315": "typescript",
|
"315": "typescript",
|
||||||
"318": "MaskableField.tsx"
|
"318": "MaskableField.tsx"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
# Graph Report - canina (2026-08-24)
|
# Graph Report - canina (2026-08-24)
|
||||||
|
|
||||||
## Corpus Check
|
## Corpus Check
|
||||||
- 544 files · ~1,308,767 words
|
- 545 files · ~1,310,484 words
|
||||||
- Verdict: corpus is large enough that graph structure adds value.
|
- Verdict: corpus is large enough that graph structure adds value.
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
- 3936 nodes · 7093 edges · 297 communities (195 shown, 102 thin omitted)
|
- 3940 nodes · 7098 edges · 311 communities (193 shown, 118 thin omitted)
|
||||||
- Extraction: 96% EXTRACTED · 4% INFERRED · 0% AMBIGUOUS · INFERRED: 270 edges (avg confidence: 0.79)
|
- Extraction: 96% EXTRACTED · 4% INFERRED · 0% AMBIGUOUS · INFERRED: 270 edges (avg confidence: 0.79)
|
||||||
- Token cost: 0 input · 0 output
|
- Token cost: 0 input · 0 output
|
||||||
|
|
||||||
@ -19,12 +19,12 @@
|
|||||||
- app.module.ts
|
- app.module.ts
|
||||||
- SmsService
|
- SmsService
|
||||||
- productService.ts
|
- productService.ts
|
||||||
- SafeImage.tsx
|
- ProductPage.tsx
|
||||||
- CmsController
|
- CmsController
|
||||||
- tickets.controller.ts
|
- tickets.controller.ts
|
||||||
- PaginationDto
|
- PaginationDto
|
||||||
- admin.module.ts
|
- ReportsController
|
||||||
- eslint-plugin-prettier
|
- devDependencies
|
||||||
- reviews.controller.ts
|
- reviews.controller.ts
|
||||||
- Spinner.tsx
|
- Spinner.tsx
|
||||||
- PetProfile.tsx
|
- PetProfile.tsx
|
||||||
@ -76,13 +76,13 @@
|
|||||||
- ContactService
|
- ContactService
|
||||||
- compilerOptions
|
- compilerOptions
|
||||||
- admin.service.ts
|
- admin.service.ts
|
||||||
- CreateEBankCheckoutDto
|
- payment.controller.ts
|
||||||
- api
|
- api
|
||||||
- dependencies
|
- dependencies
|
||||||
- compilerOptions
|
- compilerOptions
|
||||||
- ProductPage.tsx
|
- AdminTransactionFilterDto
|
||||||
- AdminQueryDto
|
- AdminQueryDto
|
||||||
- PetsController
|
- admin.module.ts
|
||||||
- BlogsController
|
- BlogsController
|
||||||
- Required Review Group Closures
|
- Required Review Group Closures
|
||||||
- compilerOptions
|
- compilerOptions
|
||||||
@ -92,8 +92,8 @@
|
|||||||
- WikiController
|
- WikiController
|
||||||
- PetsController
|
- PetsController
|
||||||
- PaymentService
|
- PaymentService
|
||||||
- SeoService
|
- seo.module.ts
|
||||||
- AuthService
|
- route.ts
|
||||||
- 🏢 AI Software Agency — Master Orchestration Protocol v3
|
- 🏢 AI Software Agency — Master Orchestration Protocol v3
|
||||||
- Operational Rules & Boundaries
|
- Operational Rules & Boundaries
|
||||||
- Operational Rules & Boundaries
|
- Operational Rules & Boundaries
|
||||||
@ -103,12 +103,12 @@
|
|||||||
- WholesaleApplyDto
|
- WholesaleApplyDto
|
||||||
- zibal.service.ts
|
- zibal.service.ts
|
||||||
- dependencies
|
- dependencies
|
||||||
- HomeClient.tsx
|
- useSettingsStore
|
||||||
- seed-products.ts
|
- seed-products.ts
|
||||||
- MetricsController
|
- MetricsController
|
||||||
- Reconciled Audit Roles & Assignments
|
- Reconciled Audit Roles & Assignments
|
||||||
- OrdersService
|
- OrdersService
|
||||||
- OrderService
|
- useCartStore
|
||||||
- Phase 3.1 — Human Review Preparation and Master Backlog Critique
|
- Phase 3.1 — Human Review Preparation and Master Backlog Critique
|
||||||
- getSeoConfig
|
- getSeoConfig
|
||||||
- compilerOptions
|
- compilerOptions
|
||||||
@ -122,7 +122,7 @@
|
|||||||
- Coupons.tsx
|
- Coupons.tsx
|
||||||
- Operational Rules & Boundaries
|
- Operational Rules & Boundaries
|
||||||
- auth.controller.ts
|
- auth.controller.ts
|
||||||
- zibal-ebank.service.ts
|
- shop/page.tsx
|
||||||
- PrismaService
|
- PrismaService
|
||||||
- 1. Summary of Integrity Repairs Performed
|
- 1. Summary of Integrity Repairs Performed
|
||||||
- Operational Rules & Boundaries
|
- Operational Rules & Boundaries
|
||||||
@ -137,19 +137,19 @@
|
|||||||
- compilerOptions
|
- compilerOptions
|
||||||
- compilerOptions
|
- compilerOptions
|
||||||
- backend/README.md
|
- backend/README.md
|
||||||
- wiki/page.tsx
|
- bcryptjs
|
||||||
- @types/passport-jwt
|
- helmet
|
||||||
- Repository Map
|
- Repository Map
|
||||||
- validate_integrity.js
|
- validate_integrity.js
|
||||||
- admin-panel/package.json
|
- admin-panel/package.json
|
||||||
- Sahel-Font
|
- Sahel-Font
|
||||||
- @types/supertest
|
- js-yaml
|
||||||
- seo.ts
|
- seo.ts
|
||||||
- Sahel-Font
|
- Sahel-Font
|
||||||
- Role & Core Objective
|
- Role & Core Objective
|
||||||
- orchestrate.py
|
- orchestrate.py
|
||||||
- backend/package.json
|
- backend/package.json
|
||||||
- typescript
|
- @nestjs/core
|
||||||
- graphify reference: extra exports and benchmark
|
- graphify reference: extra exports and benchmark
|
||||||
- Phase 2 Final Quality Gate Summary Report
|
- Phase 2 Final Quality Gate Summary Report
|
||||||
- Task Modifications Log
|
- Task Modifications Log
|
||||||
@ -163,14 +163,17 @@
|
|||||||
- eslint-config-prettier
|
- eslint-config-prettier
|
||||||
- System Discovery
|
- System Discovery
|
||||||
- PrescriptionsManager.tsx
|
- PrescriptionsManager.tsx
|
||||||
- devDependencies
|
- eslint
|
||||||
- SmsSettingsPage.tsx
|
- SmsSettingsPage.tsx
|
||||||
- Product Requirement Document (PRD)
|
- Product Requirement Document (PRD)
|
||||||
|
- @nestjs/jwt
|
||||||
- @nestjs/cli
|
- @nestjs/cli
|
||||||
- exclude
|
- exclude
|
||||||
- Baseline Command Plan & Reconciled Command History
|
- Baseline Command Plan & Reconciled Command History
|
||||||
|
- @nestjs/swagger
|
||||||
- contact/page.tsx
|
- contact/page.tsx
|
||||||
- ErrorPages.tsx
|
- ErrorPages.tsx
|
||||||
|
- @nestjs/throttler
|
||||||
- with-vpn.sh
|
- with-vpn.sh
|
||||||
- Architecture Specification
|
- Architecture Specification
|
||||||
- Project Health Audit Report
|
- Project Health Audit Report
|
||||||
@ -188,13 +191,15 @@
|
|||||||
- Phase 3 Audit Traceability Matrix
|
- Phase 3 Audit Traceability Matrix
|
||||||
- rebuild_honest_ledger.js
|
- rebuild_honest_ledger.js
|
||||||
- validate_evidence_grade.js
|
- validate_evidence_grade.js
|
||||||
- InitiatePaymentDto
|
- passport-jwt
|
||||||
- blog/page.tsx
|
- blog/page.tsx
|
||||||
- prettier
|
- prettier
|
||||||
|
- @prisma/client
|
||||||
- API Contract Specification
|
- API Contract Specification
|
||||||
- ⚙️ Backend Technical Review (05_dev_backend)
|
- ⚙️ Backend Technical Review (05_dev_backend)
|
||||||
- 🎨 Frontend & Admin Panel Technical Review (06_dev_frontend)
|
- 🎨 Frontend & Admin Panel Technical Review (06_dev_frontend)
|
||||||
- 🚀 SEO & Content Strategy Review (12_seo_content)
|
- 🚀 SEO & Content Strategy Review (12_seo_content)
|
||||||
|
- swagger-ui-express
|
||||||
- @types/node
|
- @types/node
|
||||||
- seed-ui-texts.ts
|
- seed-ui-texts.ts
|
||||||
- seed-wiki.ts
|
- seed-wiki.ts
|
||||||
@ -207,8 +212,9 @@
|
|||||||
- Raw Finding Verification & Disposition Report
|
- Raw Finding Verification & Disposition Report
|
||||||
- React + TypeScript + Vite
|
- React + TypeScript + Vite
|
||||||
- Select.tsx
|
- Select.tsx
|
||||||
|
- @eslint/eslintrc
|
||||||
- videos/page.tsx
|
- videos/page.tsx
|
||||||
- useSettingsStore
|
- useUserStore
|
||||||
- application/README.md
|
- application/README.md
|
||||||
- deploy.sh
|
- deploy.sh
|
||||||
- 🔒 Security & Performance Review (09_devops_security)
|
- 🔒 Security & Performance Review (09_devops_security)
|
||||||
@ -232,15 +238,24 @@
|
|||||||
- Input.tsx
|
- Input.tsx
|
||||||
- Textarea.tsx
|
- Textarea.tsx
|
||||||
- admin-panel/tsconfig.json
|
- admin-panel/tsconfig.json
|
||||||
|
- @eslint/js
|
||||||
|
- jest
|
||||||
- next.config.ts
|
- next.config.ts
|
||||||
- Shabnam Font README
|
- Shabnam Font README
|
||||||
- AGENTS.md
|
- AGENTS.md
|
||||||
- rules/graphify.md
|
- rules/graphify.md
|
||||||
- .agents/workflows/graphify.md
|
- .agents/workflows/graphify.md
|
||||||
- instructions.md
|
- instructions.md
|
||||||
|
- @nestjs/schematics
|
||||||
- ts-loader
|
- ts-loader
|
||||||
- ts-node
|
- ts-node
|
||||||
|
- @nestjs/testing
|
||||||
|
- source-map-support
|
||||||
- @types/bcrypt
|
- @types/bcrypt
|
||||||
|
- ts-jest
|
||||||
|
- tsconfig-paths
|
||||||
|
- @types/bcryptjs
|
||||||
|
- typescript-eslint
|
||||||
- blog.entity.ts
|
- blog.entity.ts
|
||||||
- home.entity.ts
|
- home.entity.ts
|
||||||
- wiki.entity.ts
|
- wiki.entity.ts
|
||||||
@ -282,7 +297,7 @@
|
|||||||
- eslint-plugin-react-refresh
|
- eslint-plugin-react-refresh
|
||||||
- tailwindcss
|
- tailwindcss
|
||||||
- ZibalEBankService
|
- ZibalEBankService
|
||||||
- .initiateOrderPayment
|
- ZibalCallbackQueryDto
|
||||||
- @tailwindcss/postcss
|
- @tailwindcss/postcss
|
||||||
- typescript
|
- typescript
|
||||||
- app.e2e-spec.js
|
- app.e2e-spec.js
|
||||||
@ -292,7 +307,6 @@
|
|||||||
- globals
|
- globals
|
||||||
- vitest
|
- vitest
|
||||||
- track/page.tsx
|
- track/page.tsx
|
||||||
- app/page.tsx
|
|
||||||
- typescript
|
- typescript
|
||||||
- MaskableField.tsx
|
- MaskableField.tsx
|
||||||
|
|
||||||
@ -325,15 +339,15 @@
|
|||||||
- 3-file cycle: `frontend/application/lib/services/api.ts -> frontend/application/lib/store/userStore.ts -> frontend/application/lib/store/cartStore.ts -> frontend/application/lib/services/api.ts`
|
- 3-file cycle: `frontend/application/lib/services/api.ts -> frontend/application/lib/store/userStore.ts -> frontend/application/lib/store/cartStore.ts -> frontend/application/lib/services/api.ts`
|
||||||
- 4-file cycle: `frontend/application/lib/services/api.ts -> frontend/application/lib/store/userStore.ts -> frontend/application/lib/store/cartStore.ts -> frontend/application/lib/services/orderService.ts -> frontend/application/lib/services/api.ts`
|
- 4-file cycle: `frontend/application/lib/services/api.ts -> frontend/application/lib/store/userStore.ts -> frontend/application/lib/store/cartStore.ts -> frontend/application/lib/services/orderService.ts -> frontend/application/lib/services/api.ts`
|
||||||
|
|
||||||
## Communities (297 total, 102 thin omitted)
|
## Communities (311 total, 118 thin omitted)
|
||||||
|
|
||||||
### Community 0 - "Roles"
|
### Community 0 - "Roles"
|
||||||
Cohesion: 0.24
|
Cohesion: 0.18
|
||||||
Nodes (13): Roles(), PaymentController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Get (+5 more)
|
Nodes (19): Roles(), PaymentController, ApiBadRequestResponse, ApiBearerAuth, ApiOkResponse, ApiOperation, ApiTags, Body (+11 more)
|
||||||
|
|
||||||
### Community 1 - "app.module.ts"
|
### Community 1 - "app.module.ts"
|
||||||
Cohesion: 0.06
|
Cohesion: 0.06
|
||||||
Nodes (40): B2BModule, Module, BannersModule, Module, CmsModule, Module, SmsModule, Global (+32 more)
|
Nodes (38): B2BModule, Module, BannersModule, Module, CmsModule, Module, SmsModule, Global (+30 more)
|
||||||
|
|
||||||
### Community 2 - "SmsService"
|
### Community 2 - "SmsService"
|
||||||
Cohesion: 0.06
|
Cohesion: 0.06
|
||||||
@ -341,11 +355,11 @@ Nodes (27): SmsLogQuery, SmsService, Injectable, SmsLogQueryDto, ApiPropertyOpti
|
|||||||
|
|
||||||
### Community 3 - "productService.ts"
|
### Community 3 - "productService.ts"
|
||||||
Cohesion: 0.08
|
Cohesion: 0.08
|
||||||
Nodes (28): BlogCategory, BlogPostItem, CatalogPageSpread(), CatalogPageSpreadProps, CategoryMeta, getFormBadge(), getSpeciesBadge(), FlipbookCatalog() (+20 more)
|
Nodes (29): BlogCategory, BlogPostItem, CatalogPageSpread(), CatalogPageSpreadProps, CategoryMeta, getFormBadge(), getSpeciesBadge(), FlipbookCatalog() (+21 more)
|
||||||
|
|
||||||
### Community 4 - "SafeImage.tsx"
|
### Community 4 - "ProductPage.tsx"
|
||||||
Cohesion: 0.09
|
Cohesion: 0.07
|
||||||
Nodes (19): BlogPost, OrderDetailsModal(), OrderDetailsModalProps, PLAYBACK_RATES, PodcastPlayerModal(), PodcastPlayerModalProps, SafeImage(), SafeImageProps (+11 more)
|
Nodes (27): OrderDetailsModal(), OrderDetailsModalProps, PLAYBACK_RATES, PodcastInlinePlayer(), PodcastInlinePlayerProps, PLAYBACK_RATES, PodcastPlayerModal(), PodcastPlayerModalProps (+19 more)
|
||||||
|
|
||||||
### Community 5 - "CmsController"
|
### Community 5 - "CmsController"
|
||||||
Cohesion: 0.09
|
Cohesion: 0.09
|
||||||
@ -359,9 +373,13 @@ Nodes (29): AdminUpdateTicketDto, CreateTicketDto, ReplyTicketDto, ApiProperty,
|
|||||||
Cohesion: 0.06
|
Cohesion: 0.06
|
||||||
Nodes (27): BlogsModule, Module, BlogFilterDto, BlogsService, Injectable, PaginationDto, SortOrder, ApiPropertyOptional (+19 more)
|
Nodes (27): BlogsModule, Module, BlogFilterDto, BlogsService, Injectable, PaginationDto, SortOrder, ApiPropertyOptional (+19 more)
|
||||||
|
|
||||||
### Community 8 - "admin.module.ts"
|
### Community 8 - "ReportsController"
|
||||||
Cohesion: 0.08
|
Cohesion: 0.17
|
||||||
Nodes (17): AdminModule, Module, ReportsController, ApiBearerAuth, ApiOperation, ApiTags, Controller, Get (+9 more)
|
Nodes (9): ReportsController, ApiBearerAuth, ApiOperation, ApiTags, Controller, Get, UseGuards, ReportsService (+1 more)
|
||||||
|
|
||||||
|
### Community 9 - "devDependencies"
|
||||||
|
Cohesion: 0.22
|
||||||
|
Nodes (9): devDependencies, eslint-plugin-prettier, @types/passport-jwt, @types/supertest, typescript, typescript, eslint-plugin-prettier, @types/passport-jwt (+1 more)
|
||||||
|
|
||||||
### Community 10 - "reviews.controller.ts"
|
### Community 10 - "reviews.controller.ts"
|
||||||
Cohesion: 0.07
|
Cohesion: 0.07
|
||||||
@ -372,8 +390,8 @@ Cohesion: 0.07
|
|||||||
Nodes (38): ConfirmModal(), ConfirmModalProps, getFileType(), Media, MediaSelector(), MediaSelectorProps, maxWidthClasses, Modal() (+30 more)
|
Nodes (38): ConfirmModal(), ConfirmModalProps, getFileType(), Media, MediaSelector(), MediaSelectorProps, maxWidthClasses, Modal() (+30 more)
|
||||||
|
|
||||||
### Community 12 - "PetProfile.tsx"
|
### Community 12 - "PetProfile.tsx"
|
||||||
Cohesion: 0.11
|
Cohesion: 0.14
|
||||||
Nodes (18): FeaturedProducts(), ProductCard(), Header(), MENU_ICONS, OrderSuccess(), SearchResultsPage(), OrderRowSkeleton(), PetProfileSkeleton() (+10 more)
|
Nodes (17): ArchiveProductCard(), CATEGORY_MAP, ICON_MAP, FeaturedProducts(), ProductCard(), OrderRowSkeleton(), PetProfileSkeleton(), ProductCardSkeleton() (+9 more)
|
||||||
|
|
||||||
### Community 13 - "app-audit-verification.e2e-spec.js"
|
### Community 13 - "app-audit-verification.e2e-spec.js"
|
||||||
Cohesion: 0.07
|
Cohesion: 0.07
|
||||||
@ -381,7 +399,7 @@ Nodes (22): AppModule, Module, CustomHttpExceptionFilter, Catch, PrismaException
|
|||||||
|
|
||||||
### Community 14 - "lib/services/api.ts"
|
### Community 14 - "lib/services/api.ts"
|
||||||
Cohesion: 0.09
|
Cohesion: 0.09
|
||||||
Nodes (33): VerifyContent(), AuthModal(), AuthModalProps, extractOtpFromText(), B2BPortal(), CartDrawer(), ContactInfoItem, FAQItem (+25 more)
|
Nodes (16): ContactInfoItem, PrescriptionUploadModal(), PrescriptionUploadModalProps, Testimonial, api, ApiErrorPayload, BASE_DOMAIN, baseURL (+8 more)
|
||||||
|
|
||||||
### Community 15 - "src/services/api.ts"
|
### Community 15 - "src/services/api.ts"
|
||||||
Cohesion: 0.09
|
Cohesion: 0.09
|
||||||
@ -408,8 +426,8 @@ Cohesion: 0.08
|
|||||||
Nodes (29): CreateVideoDto, ApiProperty, ApiPropertyOptional, IsBoolean, IsNotEmpty, IsOptional, IsString, UpdateVideoDto (+21 more)
|
Nodes (29): CreateVideoDto, ApiProperty, ApiPropertyOptional, IsBoolean, IsNotEmpty, IsOptional, IsString, UpdateVideoDto (+21 more)
|
||||||
|
|
||||||
### Community 22 - "UserDashboard.tsx"
|
### Community 22 - "UserDashboard.tsx"
|
||||||
Cohesion: 0.12
|
Cohesion: 0.09
|
||||||
Nodes (23): AddressModal(), AddressModalProps, BackButton(), BackButtonProps, CheckoutPage(), DeleteConfirmModal(), DeleteConfirmModalProps, HeaderButton() (+15 more)
|
Nodes (31): VerifyContent(), AddressModal(), AddressModalProps, BackButton(), BackButtonProps, BlogPost, BlogPreviewSection(), CheckoutPage() (+23 more)
|
||||||
|
|
||||||
### Community 23 - "MenuService"
|
### Community 23 - "MenuService"
|
||||||
Cohesion: 0.12
|
Cohesion: 0.12
|
||||||
@ -537,7 +555,7 @@ Nodes (14): SmartAdvisorController, ApiBearerAuth, ApiOperation, ApiTags, Body,
|
|||||||
|
|
||||||
### Community 54 - "UsersService"
|
### Community 54 - "UsersService"
|
||||||
Cohesion: 0.06
|
Cohesion: 0.06
|
||||||
Nodes (41): DEFAULT_JWT_REFRESH_SECRET, DEFAULT_JWT_SECRET, getJwtSecret(), AuthModule, Module, JwtPayload, JwtStrategy, Injectable (+33 more)
|
Nodes (42): DEFAULT_JWT_REFRESH_SECRET, DEFAULT_JWT_SECRET, getJwtSecret(), AuthModule, Module, JwtPayload, JwtStrategy, Injectable (+34 more)
|
||||||
|
|
||||||
### Community 55 - "UITexts.tsx"
|
### Community 55 - "UITexts.tsx"
|
||||||
Cohesion: 0.09
|
Cohesion: 0.09
|
||||||
@ -563,33 +581,33 @@ Nodes (30): compilerOptions, allowSyntheticDefaultImports, baseUrl, declaration,
|
|||||||
Cohesion: 0.12
|
Cohesion: 0.12
|
||||||
Nodes (20): CouponInput, CouponTargetInput, PaginationQuery, ProductDto, ApiPropertyOptional, IsArray, IsBoolean, IsNumber (+12 more)
|
Nodes (20): CouponInput, CouponTargetInput, PaginationQuery, ProductDto, ApiPropertyOptional, IsArray, IsBoolean, IsNumber (+12 more)
|
||||||
|
|
||||||
### Community 61 - "CreateEBankCheckoutDto"
|
### Community 61 - "payment.controller.ts"
|
||||||
Cohesion: 0.22
|
Cohesion: 0.11
|
||||||
Nodes (8): CreateEBankCheckoutDto, ApiProperty, ApiPropertyOptional, IsNotEmpty, IsNumber, IsOptional, IsString, Min
|
Nodes (19): CreateEBankCheckoutDto, ApiProperty, ApiPropertyOptional, IsNotEmpty, IsNumber, IsOptional, IsString, Min (+11 more)
|
||||||
|
|
||||||
### Community 62 - "api"
|
### Community 62 - "api"
|
||||||
Cohesion: 0.13
|
Cohesion: 0.13
|
||||||
Nodes (13): Layout(), MenuGroup, Sidebar(), SidebarProps, SubMenuItem, LiveMonitoringSummary, SEARCHABLE_PAGES, Topbar() (+5 more)
|
Nodes (13): Layout(), MenuGroup, Sidebar(), SidebarProps, SubMenuItem, LiveMonitoringSummary, SEARCHABLE_PAGES, Topbar() (+5 more)
|
||||||
|
|
||||||
### Community 63 - "dependencies"
|
### Community 63 - "dependencies"
|
||||||
Cohesion: 0.05
|
Cohesion: 0.10
|
||||||
Nodes (41): dependencies, bcrypt, bcryptjs, class-transformer, class-validator, helmet, ioredis, js-yaml (+33 more)
|
Nodes (21): dependencies, bcrypt, class-transformer, class-validator, ioredis, @nestjs/common, @nestjs/passport, @nestjs/platform-express (+13 more)
|
||||||
|
|
||||||
### Community 64 - "compilerOptions"
|
### Community 64 - "compilerOptions"
|
||||||
Cohesion: 0.10
|
Cohesion: 0.10
|
||||||
Nodes (20): compilerOptions, allowImportingTsExtensions, erasableSyntaxOnly, lib, module, moduleDetection, moduleResolution, noEmit (+12 more)
|
Nodes (20): compilerOptions, allowImportingTsExtensions, erasableSyntaxOnly, lib, module, moduleDetection, moduleResolution, noEmit (+12 more)
|
||||||
|
|
||||||
### Community 65 - "ProductPage.tsx"
|
### Community 65 - "AdminTransactionFilterDto"
|
||||||
Cohesion: 0.13
|
Cohesion: 0.25
|
||||||
Nodes (13): PLAYBACK_RATES, PodcastInlinePlayer(), PodcastInlinePlayerProps, CalculatorState, ICON_MAP, ProductPage(), useCalculatorStore, ProductReviews() (+5 more)
|
Nodes (7): AdminTransactionFilterDto, ApiPropertyOptional, IsIn, IsNumber, IsOptional, IsString, Type
|
||||||
|
|
||||||
### Community 66 - "AdminQueryDto"
|
### Community 66 - "AdminQueryDto"
|
||||||
Cohesion: 0.17
|
Cohesion: 0.17
|
||||||
Nodes (7): ApiQuery, Get, Query, AdminQueryDto, ApiPropertyOptional, IsOptional, IsString
|
Nodes (7): ApiQuery, Get, Query, AdminQueryDto, ApiPropertyOptional, IsOptional, IsString
|
||||||
|
|
||||||
### Community 67 - "PetsController"
|
### Community 67 - "admin.module.ts"
|
||||||
Cohesion: 0.10
|
Cohesion: 0.06
|
||||||
Nodes (14): PetsController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Controller, Delete, Get (+6 more)
|
Nodes (23): AdminModule, Module, PetsController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Controller (+15 more)
|
||||||
|
|
||||||
### Community 68 - "BlogsController"
|
### Community 68 - "BlogsController"
|
||||||
Cohesion: 0.16
|
Cohesion: 0.16
|
||||||
@ -605,7 +623,7 @@ Nodes (23): compilerOptions, allowImportingTsExtensions, erasableSyntaxOnly, jsx
|
|||||||
|
|
||||||
### Community 71 - "getPageMetadata"
|
### Community 71 - "getPageMetadata"
|
||||||
Cohesion: 0.13
|
Cohesion: 0.13
|
||||||
Nodes (8): generateMetadata(), generateMetadata(), generateMetadata(), generateMetadata(), generateMetadata(), generateMetadata(), ArchivePage(), getPageMetadata()
|
Nodes (8): generateMetadata(), generateMetadata(), generateMetadata(), generateMetadata(), generateMetadata(), generateMetadata(), generateMetadata(), getPageMetadata()
|
||||||
|
|
||||||
### Community 72 - "Operational Rules & Boundaries"
|
### Community 72 - "Operational Rules & Boundaries"
|
||||||
Cohesion: 0.11
|
Cohesion: 0.11
|
||||||
@ -623,13 +641,13 @@ Nodes (14): ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Body, Controller, De
|
|||||||
Cohesion: 0.05
|
Cohesion: 0.05
|
||||||
Nodes (47): CreateHealthLogDto, ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString, CreatePetDto, ApiProperty (+39 more)
|
Nodes (47): CreateHealthLogDto, ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString, CreatePetDto, ApiProperty (+39 more)
|
||||||
|
|
||||||
### Community 76 - "PaymentService"
|
### Community 77 - "seo.module.ts"
|
||||||
Cohesion: 0.11
|
Cohesion: 0.16
|
||||||
Nodes (9): AdminTransactionFilterDto, ApiPropertyOptional, IsIn, IsNumber, IsOptional, IsString, Type, PaymentService (+1 more)
|
Nodes (10): SeoController, ApiOperation, ApiTags, Controller, Get, Param, SeoModule, Module (+2 more)
|
||||||
|
|
||||||
### Community 77 - "SeoService"
|
### Community 78 - "route.ts"
|
||||||
Cohesion: 0.18
|
Cohesion: 0.67
|
||||||
Nodes (8): SeoController, ApiOperation, ApiTags, Controller, Get, Param, SeoService, Injectable
|
Nodes (3): dynamic, escapeXml(), GET()
|
||||||
|
|
||||||
### Community 79 - "🏢 AI Software Agency — Master Orchestration Protocol v3"
|
### Community 79 - "🏢 AI Software Agency — Master Orchestration Protocol v3"
|
||||||
Cohesion: 0.11
|
Cohesion: 0.11
|
||||||
@ -667,9 +685,9 @@ Nodes (9): GatewayHealthResult, IPaymentGateway, PaymentInquiryResult, PaymentRe
|
|||||||
Cohesion: 0.12
|
Cohesion: 0.12
|
||||||
Nodes (17): dependencies, axios, lucide-react, motion, next, react, react-dom, sonner (+9 more)
|
Nodes (17): dependencies, axios, lucide-react, motion, next, react, react-dom, sonner (+9 more)
|
||||||
|
|
||||||
### Community 88 - "HomeClient.tsx"
|
### Community 88 - "useSettingsStore"
|
||||||
Cohesion: 0.11
|
Cohesion: 0.08
|
||||||
Nodes (19): HomeClientProps, ArchiveProductCard(), CATEGORY_MAP, ICON_MAP, BannerPlacement(), BannerPlacementProps, BlogPreviewSection(), FAQSection() (+11 more)
|
Nodes (32): HomeClient(), HomeClientProps, generateMetadata(), getHomeData(), Home(), BannerPlacement(), BannerPlacementProps, BrandLogo() (+24 more)
|
||||||
|
|
||||||
### Community 89 - "seed-products.ts"
|
### Community 89 - "seed-products.ts"
|
||||||
Cohesion: 0.17
|
Cohesion: 0.17
|
||||||
@ -687,12 +705,16 @@ Nodes (15): 10. Documentation Engineer, 1. Lead Architect / Orchestrator, 2. Rea
|
|||||||
Cohesion: 0.06
|
Cohesion: 0.06
|
||||||
Nodes (35): CreateOrderDto, OrderItemDto, ApiProperty, ApiPropertyOptional, IsArray, IsNotEmpty, IsNumber, IsOptional (+27 more)
|
Nodes (35): CreateOrderDto, OrderItemDto, ApiProperty, ApiPropertyOptional, IsArray, IsNotEmpty, IsNumber, IsOptional (+27 more)
|
||||||
|
|
||||||
|
### Community 93 - "useCartStore"
|
||||||
|
Cohesion: 0.12
|
||||||
|
Nodes (11): CartDrawer(), Header(), OrderSuccess(), mockProduct, ApiErr, Order, OrderItem, OrderService (+3 more)
|
||||||
|
|
||||||
### Community 94 - "Phase 3.1 — Human Review Preparation and Master Backlog Critique"
|
### Community 94 - "Phase 3.1 — Human Review Preparation and Master Backlog Critique"
|
||||||
Cohesion: 0.13
|
Cohesion: 0.13
|
||||||
Nodes (14): 10. Dependency & Wave Adjustments, 11. Acceptance Criteria Refinements, 12. Business and Product Decision Classification, 13. Final Recommended Backlog Summary, 1. Executive Assessment, 2. Findings That Require No Change, 3. Findings Requiring Task Refinements, 4. Tasks Requiring Splitting (+6 more)
|
Nodes (14): 10. Dependency & Wave Adjustments, 11. Acceptance Criteria Refinements, 12. Business and Product Decision Classification, 13. Final Recommended Backlog Summary, 1. Executive Assessment, 2. Findings That Require No Change, 3. Findings Requiring Task Refinements, 4. Tasks Requiring Splitting (+6 more)
|
||||||
|
|
||||||
### Community 95 - "getSeoConfig"
|
### Community 95 - "getSeoConfig"
|
||||||
Cohesion: 0.24
|
Cohesion: 0.22
|
||||||
Nodes (11): BlogPostPage(), generateMetadata(), getBlog(), revalidate, generateMetadata(), generateMetadata(), generateMetadata(), getWikiTerm() (+3 more)
|
Nodes (11): BlogPostPage(), generateMetadata(), getBlog(), revalidate, generateMetadata(), generateMetadata(), generateMetadata(), getWikiTerm() (+3 more)
|
||||||
|
|
||||||
### Community 96 - "compilerOptions"
|
### Community 96 - "compilerOptions"
|
||||||
@ -735,13 +757,9 @@ Nodes (11): 1. Detect Test Runner from Tech Stack, 2. Execution Output Capture (
|
|||||||
Cohesion: 0.08
|
Cohesion: 0.08
|
||||||
Nodes (25): AdminLoginDto, ApiProperty, IsEmail, IsNotEmpty, IsString, MinLength, LoginDto, ApiProperty (+17 more)
|
Nodes (25): AdminLoginDto, ApiProperty, IsEmail, IsNotEmpty, IsString, MinLength, LoginDto, ApiProperty (+17 more)
|
||||||
|
|
||||||
### Community 107 - "zibal-ebank.service.ts"
|
|
||||||
Cohesion: 0.40
|
|
||||||
Nodes (4): EBankCheckoutListFilter, EBankCheckoutOptions, EBankIdentifiedPaymentFilter, EBankStatementFilter
|
|
||||||
|
|
||||||
### Community 108 - "PrismaService"
|
### Community 108 - "PrismaService"
|
||||||
Cohesion: 0.07
|
Cohesion: 0.08
|
||||||
Nodes (19): CategoryQuery, WikiQuery, MeliPayamakPattern, MeliPayamakResponse, SendPatternSmsOptions, SmsConfig, CreateContactSubmissionDto, UpdateContactInfoItemDto (+11 more)
|
Nodes (17): CategoryQuery, MeliPayamakPattern, MeliPayamakResponse, SendPatternSmsOptions, SmsConfig, CreateContactSubmissionDto, UpdateContactInfoItemDto, MenuType (+9 more)
|
||||||
|
|
||||||
### Community 109 - "1. Summary of Integrity Repairs Performed"
|
### Community 109 - "1. Summary of Integrity Repairs Performed"
|
||||||
Cohesion: 0.17
|
Cohesion: 0.17
|
||||||
@ -808,8 +826,8 @@ Cohesion: 0.20
|
|||||||
Nodes (9): Arch Linux, Contributors, Install, Known problems for variable version, License, Sahel-Font, To Do (variable), طریقه استفاده از نسخه متغیر variable (+1 more)
|
Nodes (9): Arch Linux, Contributors, Install, Known problems for variable version, License, Sahel-Font, To Do (variable), طریقه استفاده از نسخه متغیر variable (+1 more)
|
||||||
|
|
||||||
### Community 129 - "seo.ts"
|
### Community 129 - "seo.ts"
|
||||||
Cohesion: 0.20
|
Cohesion: 0.29
|
||||||
Nodes (5): generateMetadata(), generateMetadata(), DEFAULT_SEO_CONFIG, PageMetadataOptions, SeoConfig
|
Nodes (4): generateMetadata(), DEFAULT_SEO_CONFIG, PageMetadataOptions, SeoConfig
|
||||||
|
|
||||||
### Community 130 - "Sahel-Font"
|
### Community 130 - "Sahel-Font"
|
||||||
Cohesion: 0.20
|
Cohesion: 0.20
|
||||||
@ -871,10 +889,6 @@ Nodes (7): Active Core Applications, Current Architecture, Evidence-Based Status
|
|||||||
Cohesion: 0.18
|
Cohesion: 0.18
|
||||||
Nodes (9): Badge(), BadgeProps, BadgeVariant, variantStyles, ImagePreviewModal(), ImagePreviewModalProps, ProductItem, PrescriptionsManager (+1 more)
|
Nodes (9): Badge(), BadgeProps, BadgeVariant, variantStyles, ImagePreviewModal(), ImagePreviewModalProps, ProductItem, PrescriptionsManager (+1 more)
|
||||||
|
|
||||||
### Community 148 - "devDependencies"
|
|
||||||
Cohesion: 0.09
|
|
||||||
Nodes (23): devDependencies, eslint, @eslint/eslintrc, @eslint/js, jest, @nestjs/schematics, @nestjs/testing, source-map-support (+15 more)
|
|
||||||
|
|
||||||
### Community 149 - "SmsSettingsPage.tsx"
|
### Community 149 - "SmsSettingsPage.tsx"
|
||||||
Cohesion: 0.29
|
Cohesion: 0.29
|
||||||
Nodes (7): PatternItem, SmsConfigState, SmsLogItem, SmsLogStats, SmsSettingsPage(), toPersianDigits(), SmsSettingsPage
|
Nodes (7): PatternItem, SmsConfigState, SmsLogItem, SmsLogStats, SmsSettingsPage(), toPersianDigits(), SmsSettingsPage
|
||||||
@ -959,10 +973,6 @@ Nodes (4): evidenceList, ledger, mandatoryMap, mandatoryScope
|
|||||||
Cohesion: 0.40
|
Cohesion: 0.40
|
||||||
Nodes (4): activeFiles, errors, validationOutput, warnings
|
Nodes (4): activeFiles, errors, validationOutput, warnings
|
||||||
|
|
||||||
### Community 176 - "InitiatePaymentDto"
|
|
||||||
Cohesion: 0.43
|
|
||||||
Nodes (7): InitiatePaymentDto, InitiateWalletTopupDto, ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString
|
|
||||||
|
|
||||||
### Community 177 - "blog/page.tsx"
|
### Community 177 - "blog/page.tsx"
|
||||||
Cohesion: 0.47
|
Cohesion: 0.47
|
||||||
Nodes (5): Blog(), generateMetadata(), getBlogs(), getCategories(), BlogPage()
|
Nodes (5): Blog(), generateMetadata(), getBlogs(), getCategories(), BlogPage()
|
||||||
@ -1007,9 +1017,9 @@ Nodes (3): Expanding the ESLint configuration, React Compiler, React + TypeScrip
|
|||||||
Cohesion: 0.50
|
Cohesion: 0.50
|
||||||
Nodes (3): Select, SelectOption, SelectProps
|
Nodes (3): Select, SelectOption, SelectProps
|
||||||
|
|
||||||
### Community 199 - "useSettingsStore"
|
### Community 199 - "useUserStore"
|
||||||
Cohesion: 0.11
|
Cohesion: 0.15
|
||||||
Nodes (22): ClientLayout(), HomeClient(), BrandLogo(), BrandLogoProps, EnamadBadge(), Footer(), MaintenancePage(), NetworkBanner() (+14 more)
|
Nodes (17): ClientLayout(), AuthModal(), AuthModalProps, extractOtpFromText(), B2BPortal(), LoginModal(), LoginModalProps, NetworkBanner() (+9 more)
|
||||||
|
|
||||||
### Community 200 - "application/README.md"
|
### Community 200 - "application/README.md"
|
||||||
Cohesion: 0.50
|
Cohesion: 0.50
|
||||||
@ -1027,37 +1037,33 @@ Nodes (3): ADR-AUTH-001: Admin Panel Authentication Architecture & Token Managem
|
|||||||
Cohesion: 0.67
|
Cohesion: 0.67
|
||||||
Nodes (3): Shabnam Font README, Shabnam Font Sample, Vazir Font
|
Nodes (3): Shabnam Font README, Shabnam Font Sample, Vazir Font
|
||||||
|
|
||||||
### Community 298 - ".initiateOrderPayment"
|
### Community 298 - "ZibalCallbackQueryDto"
|
||||||
Cohesion: 0.20
|
Cohesion: 0.40
|
||||||
Nodes (10): ApiPropertyOptional, IsOptional, IsString, ZibalCallbackQueryDto, ApiBadRequestResponse, ApiOkResponse, Req, Res (+2 more)
|
Nodes (4): ApiPropertyOptional, IsOptional, IsString, ZibalCallbackQueryDto
|
||||||
|
|
||||||
### Community 301 - "app.e2e-spec.js"
|
### Community 301 - "app.e2e-spec.js"
|
||||||
Cohesion: 0.50
|
Cohesion: 0.50
|
||||||
Nodes (3): app_module_1, supertest_1, testing_1
|
Nodes (3): app_module_1, supertest_1, testing_1
|
||||||
|
|
||||||
### Community 313 - "app/page.tsx"
|
|
||||||
Cohesion: 0.67
|
|
||||||
Nodes (3): generateMetadata(), getHomeData(), Home()
|
|
||||||
|
|
||||||
## Knowledge Gaps
|
## Knowledge Gaps
|
||||||
- **1285 isolated node(s):** `$schema`, `collection`, `sourceRoot`, `deleteOutDir`, `name` (+1280 more)
|
- **1286 isolated node(s):** `$schema`, `collection`, `sourceRoot`, `deleteOutDir`, `name` (+1281 more)
|
||||||
These have ≤1 connection - possible missing edges or undocumented components.
|
These have ≤1 connection - possible missing edges or undocumented components.
|
||||||
- **102 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
|
- **118 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
|
||||||
|
|
||||||
## Suggested Questions
|
## Suggested Questions
|
||||||
_Questions this graph is uniquely positioned to answer:_
|
_Questions this graph is uniquely positioned to answer:_
|
||||||
|
|
||||||
- **Why does `ApiResponse` connect `src/services/api.ts` to `HomeController`, `AuthController`, `BlogsController`, `PaginationDto`, `PetsController`, `ProductsService`, `UsersService`, `OrdersService`?**
|
- **Why does `ApiResponse` connect `src/services/api.ts` to `HomeController`, `AuthController`, `BlogsController`, `PaginationDto`, `PetsController`, `ProductsService`, `UsersService`, `OrdersService`?**
|
||||||
_High betweenness centrality (0.077) - this node is a cross-community bridge._
|
_High betweenness centrality (0.077) - this node is a cross-community bridge._
|
||||||
- **Why does `Roles()` connect `Roles` to `WholesaleService`, `B2BService`, `SmsService`, `FaqService`, `CmsController`, `tickets.controller.ts`, `SslController`, `BannersService`, `reviews.controller.ts`, `TestimonialsService`, `IngredientsService`, `JwtAuthGuard`, `ProductsService`, `PrescriptionsController`, `SmartAdvisorService`, `MenuService`, `ContactService`?**
|
- **Why does `Roles()` connect `Roles` to `SmsService`, `CmsController`, `tickets.controller.ts`, `reviews.controller.ts`, `JwtAuthGuard`, `ProductsService`, `MenuService`, `WholesaleService`, `B2BService`, `FaqService`, `SslController`, `BannersService`, `TestimonialsService`, `IngredientsService`, `PrescriptionsController`, `SmartAdvisorService`, `ContactService`, `payment.controller.ts`, `AdminTransactionFilterDto`?**
|
||||||
_High betweenness centrality (0.055) - this node is a cross-community bridge._
|
_High betweenness centrality (0.055) - this node is a cross-community bridge._
|
||||||
- **Why does `JwtAuthGuard` connect `JwtAuthGuard` to `PetsController`, `CmsController`, `tickets.controller.ts`, `PaginationDto`, `admin.module.ts`, `auth.controller.ts`, `PetsController`, `reviews.controller.ts`, `OrdersService`, `DoctorQueryDto`, `ProductsService`, `CreateVideoDto`, `UsersService`, `admin.service.ts`?**
|
- **Why does `JwtAuthGuard` connect `JwtAuthGuard` to `admin.module.ts`, `CmsController`, `tickets.controller.ts`, `PaginationDto`, `auth.controller.ts`, `PetsController`, `reviews.controller.ts`, `OrdersService`, `DoctorQueryDto`, `ProductsService`, `CreateVideoDto`, `UsersService`, `admin.service.ts`, `payment.controller.ts`?**
|
||||||
_High betweenness centrality (0.038) - this node is a cross-community bridge._
|
_High betweenness centrality (0.038) - this node is a cross-community bridge._
|
||||||
- **What connects `$schema`, `collection`, `sourceRoot` to the rest of the system?**
|
- **What connects `$schema`, `collection`, `sourceRoot` to the rest of the system?**
|
||||||
_1285 weakly-connected nodes found - possible documentation gaps or missing edges._
|
_1286 weakly-connected nodes found - possible documentation gaps or missing edges._
|
||||||
- **Should `app.module.ts` be split into smaller, more focused modules?**
|
- **Should `app.module.ts` be split into smaller, more focused modules?**
|
||||||
_Cohesion score 0.061016949152542375 - nodes in this community are weakly interconnected._
|
_Cohesion score 0.06390977443609022 - nodes in this community are weakly interconnected._
|
||||||
- **Should `SmsService` be split into smaller, more focused modules?**
|
- **Should `SmsService` be split into smaller, more focused modules?**
|
||||||
_Cohesion score 0.055040197897340756 - nodes in this community are weakly interconnected._
|
_Cohesion score 0.055040197897340756 - nodes in this community are weakly interconnected._
|
||||||
- **Should `productService.ts` be split into smaller, more focused modules?**
|
- **Should `productService.ts` be split into smaller, more focused modules?**
|
||||||
_Cohesion score 0.0782312925170068 - nodes in this community are weakly interconnected._
|
_Cohesion score 0.07607843137254902 - 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
@ -1,38 +1,38 @@
|
|||||||
# Graph Report - canina (2026-08-24)
|
# Graph Report - canina (2026-08-24)
|
||||||
|
|
||||||
## Corpus Check
|
## Corpus Check
|
||||||
- 545 files · ~1,310,484 words
|
- 552 files · ~1,314,001 words
|
||||||
- Verdict: corpus is large enough that graph structure adds value.
|
- Verdict: corpus is large enough that graph structure adds value.
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
- 3940 nodes · 7098 edges · 311 communities (193 shown, 118 thin omitted)
|
- 3987 nodes · 7189 edges · 332 communities (207 shown, 125 thin omitted)
|
||||||
- Extraction: 96% EXTRACTED · 4% INFERRED · 0% AMBIGUOUS · INFERRED: 270 edges (avg confidence: 0.79)
|
- Extraction: 96% EXTRACTED · 4% INFERRED · 0% AMBIGUOUS · INFERRED: 281 edges (avg confidence: 0.79)
|
||||||
- Token cost: 0 input · 0 output
|
- Token cost: 0 input · 0 output
|
||||||
|
|
||||||
## Graph Freshness
|
## Graph Freshness
|
||||||
- Built from commit: `eb63d22a`
|
- Built from commit: `2a31a9f8`
|
||||||
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
|
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
|
||||||
- Run `graphify update .` after code changes (no API cost).
|
- Run `graphify update .` after code changes (no API cost).
|
||||||
|
|
||||||
## Community Hubs (Navigation)
|
## Community Hubs (Navigation)
|
||||||
- Roles
|
- Roles
|
||||||
- app.module.ts
|
- app.module.ts
|
||||||
- SmsService
|
- SettingsController
|
||||||
- productService.ts
|
- ProductService
|
||||||
- ProductPage.tsx
|
- SafeImage.tsx
|
||||||
- CmsController
|
- CmsController
|
||||||
- tickets.controller.ts
|
- tickets.controller.ts
|
||||||
- PaginationDto
|
- PaginationDto
|
||||||
- ReportsController
|
- admin.module.ts
|
||||||
- devDependencies
|
- devDependencies
|
||||||
- reviews.controller.ts
|
- ReviewsController
|
||||||
- Spinner.tsx
|
- Spinner.tsx
|
||||||
- PetProfile.tsx
|
- useCartStore
|
||||||
- app-audit-verification.e2e-spec.js
|
- app-audit-verification.e2e-spec.js
|
||||||
- lib/services/api.ts
|
- lib/services/api.ts
|
||||||
- src/services/api.ts
|
- src/services/api.ts
|
||||||
- DoctorQueryDto
|
- DoctorQueryDto
|
||||||
- auth.service.ts
|
- RedisService
|
||||||
- JwtAuthGuard
|
- JwtAuthGuard
|
||||||
- ProductsService
|
- ProductsService
|
||||||
- CreateVideoDto
|
- CreateVideoDto
|
||||||
@ -48,7 +48,7 @@
|
|||||||
- DEVOPS-001
|
- DEVOPS-001
|
||||||
- DOC-001
|
- DOC-001
|
||||||
- adminRoutes.tsx
|
- adminRoutes.tsx
|
||||||
- WholesaleService
|
- WholesaleApplyDto
|
||||||
- B2BService
|
- B2BService
|
||||||
- AuthController
|
- AuthController
|
||||||
- FaqService
|
- FaqService
|
||||||
@ -61,13 +61,13 @@
|
|||||||
- BannersService
|
- BannersService
|
||||||
- TestimonialsService
|
- TestimonialsService
|
||||||
- What You Must Do When Invoked
|
- What You Must Do When Invoked
|
||||||
- AdminService
|
- ApiOperation
|
||||||
- IngredientsService
|
- IngredientsService
|
||||||
- Reports.tsx
|
- Reports.tsx
|
||||||
- devDependencies
|
- devDependencies
|
||||||
- devDependencies
|
- devDependencies
|
||||||
- BlogsController
|
- BlogsController
|
||||||
- PrescriptionsController
|
- PrescriptionsService
|
||||||
- SmartAdvisorService
|
- SmartAdvisorService
|
||||||
- UsersService
|
- UsersService
|
||||||
- UITexts.tsx
|
- UITexts.tsx
|
||||||
@ -76,13 +76,13 @@
|
|||||||
- ContactService
|
- ContactService
|
||||||
- compilerOptions
|
- compilerOptions
|
||||||
- admin.service.ts
|
- admin.service.ts
|
||||||
- payment.controller.ts
|
- CreateEBankCheckoutDto
|
||||||
- api
|
- api
|
||||||
- dependencies
|
- dependencies
|
||||||
- compilerOptions
|
- compilerOptions
|
||||||
- AdminTransactionFilterDto
|
- AdminTransactionFilterDto
|
||||||
- AdminQueryDto
|
- AdminQueryDto
|
||||||
- admin.module.ts
|
- PetsController
|
||||||
- BlogsController
|
- BlogsController
|
||||||
- Required Review Group Closures
|
- Required Review Group Closures
|
||||||
- compilerOptions
|
- compilerOptions
|
||||||
@ -100,15 +100,15 @@
|
|||||||
- scripts
|
- scripts
|
||||||
- dependencies
|
- dependencies
|
||||||
- Role & Core Objective
|
- Role & Core Objective
|
||||||
- WholesaleApplyDto
|
- SettingsService
|
||||||
- zibal.service.ts
|
- zibal.service.ts
|
||||||
- dependencies
|
- dependencies
|
||||||
- useSettingsStore
|
- useSettingsStore
|
||||||
- seed-products.ts
|
- seed-products.ts
|
||||||
- MetricsController
|
- HomeClient.tsx
|
||||||
- Reconciled Audit Roles & Assignments
|
- Reconciled Audit Roles & Assignments
|
||||||
- OrdersService
|
- OrdersService
|
||||||
- useCartStore
|
- ProductPage.tsx
|
||||||
- Phase 3.1 — Human Review Preparation and Master Backlog Critique
|
- Phase 3.1 — Human Review Preparation and Master Backlog Critique
|
||||||
- getSeoConfig
|
- getSeoConfig
|
||||||
- compilerOptions
|
- compilerOptions
|
||||||
@ -128,7 +128,7 @@
|
|||||||
- Operational Rules & Boundaries
|
- Operational Rules & Boundaries
|
||||||
- Operational Rules & Boundaries
|
- Operational Rules & Boundaries
|
||||||
- Operational Rules & Boundaries
|
- Operational Rules & Boundaries
|
||||||
- Reviews.tsx
|
- reviews.controller.ts
|
||||||
- AppService
|
- AppService
|
||||||
- catalog/page.tsx
|
- catalog/page.tsx
|
||||||
- Vazirmatn Changelog
|
- Vazirmatn Changelog
|
||||||
@ -137,13 +137,13 @@
|
|||||||
- compilerOptions
|
- compilerOptions
|
||||||
- compilerOptions
|
- compilerOptions
|
||||||
- backend/README.md
|
- backend/README.md
|
||||||
- bcryptjs
|
- SmsService
|
||||||
- helmet
|
- BlogsService
|
||||||
- Repository Map
|
- Repository Map
|
||||||
- validate_integrity.js
|
- validate_integrity.js
|
||||||
- admin-panel/package.json
|
- admin-panel/package.json
|
||||||
- Sahel-Font
|
- Sahel-Font
|
||||||
- js-yaml
|
- AdminService
|
||||||
- seo.ts
|
- seo.ts
|
||||||
- Sahel-Font
|
- Sahel-Font
|
||||||
- Role & Core Objective
|
- Role & Core Objective
|
||||||
@ -162,15 +162,15 @@
|
|||||||
- @testing-library/react
|
- @testing-library/react
|
||||||
- eslint-config-prettier
|
- eslint-config-prettier
|
||||||
- System Discovery
|
- System Discovery
|
||||||
- PrescriptionsManager.tsx
|
- RevalidationService
|
||||||
- eslint
|
- AdminController
|
||||||
- SmsSettingsPage.tsx
|
- SmsSettingsPage.tsx
|
||||||
- Product Requirement Document (PRD)
|
- Product Requirement Document (PRD)
|
||||||
- @nestjs/jwt
|
- AuthService
|
||||||
- @nestjs/cli
|
- @nestjs/cli
|
||||||
- exclude
|
- exclude
|
||||||
- Baseline Command Plan & Reconciled Command History
|
- Baseline Command Plan & Reconciled Command History
|
||||||
- @nestjs/swagger
|
- Blogs.tsx
|
||||||
- contact/page.tsx
|
- contact/page.tsx
|
||||||
- ErrorPages.tsx
|
- ErrorPages.tsx
|
||||||
- @nestjs/throttler
|
- @nestjs/throttler
|
||||||
@ -194,13 +194,13 @@
|
|||||||
- passport-jwt
|
- passport-jwt
|
||||||
- blog/page.tsx
|
- blog/page.tsx
|
||||||
- prettier
|
- prettier
|
||||||
- @prisma/client
|
- BlogsService
|
||||||
- API Contract Specification
|
- API Contract Specification
|
||||||
- ⚙️ Backend Technical Review (05_dev_backend)
|
- ⚙️ Backend Technical Review (05_dev_backend)
|
||||||
- 🎨 Frontend & Admin Panel Technical Review (06_dev_frontend)
|
- 🎨 Frontend & Admin Panel Technical Review (06_dev_frontend)
|
||||||
- 🚀 SEO & Content Strategy Review (12_seo_content)
|
- 🚀 SEO & Content Strategy Review (12_seo_content)
|
||||||
- swagger-ui-express
|
- swagger-ui-express
|
||||||
- @types/node
|
- ApiResponse
|
||||||
- seed-ui-texts.ts
|
- seed-ui-texts.ts
|
||||||
- seed-wiki.ts
|
- seed-wiki.ts
|
||||||
- update-blog.dto.ts
|
- update-blog.dto.ts
|
||||||
@ -214,7 +214,7 @@
|
|||||||
- Select.tsx
|
- Select.tsx
|
||||||
- @eslint/eslintrc
|
- @eslint/eslintrc
|
||||||
- videos/page.tsx
|
- videos/page.tsx
|
||||||
- useUserStore
|
- auth.service.ts
|
||||||
- application/README.md
|
- application/README.md
|
||||||
- deploy.sh
|
- deploy.sh
|
||||||
- 🔒 Security & Performance Review (09_devops_security)
|
- 🔒 Security & Performance Review (09_devops_security)
|
||||||
@ -254,7 +254,7 @@
|
|||||||
- @types/bcrypt
|
- @types/bcrypt
|
||||||
- ts-jest
|
- ts-jest
|
||||||
- tsconfig-paths
|
- tsconfig-paths
|
||||||
- @types/bcryptjs
|
- RegisterDto
|
||||||
- typescript-eslint
|
- typescript-eslint
|
||||||
- blog.entity.ts
|
- blog.entity.ts
|
||||||
- home.entity.ts
|
- home.entity.ts
|
||||||
@ -275,6 +275,7 @@
|
|||||||
- start.sh
|
- start.sh
|
||||||
- User Login API
|
- User Login API
|
||||||
- User Logout API
|
- User Logout API
|
||||||
|
- InitiatePaymentDto
|
||||||
- @types/express
|
- @types/express
|
||||||
- @types/jest
|
- @types/jest
|
||||||
- @types/js-yaml
|
- @types/js-yaml
|
||||||
@ -303,12 +304,31 @@
|
|||||||
- app.e2e-spec.js
|
- app.e2e-spec.js
|
||||||
- typescript-eslint
|
- typescript-eslint
|
||||||
- @testing-library/jest-dom
|
- @testing-library/jest-dom
|
||||||
|
- SmsLogQueryDto
|
||||||
- @types/react
|
- @types/react
|
||||||
- globals
|
- globals
|
||||||
|
- SendOtpDto
|
||||||
- vitest
|
- vitest
|
||||||
- track/page.tsx
|
- track/page.tsx
|
||||||
|
- .addComment
|
||||||
|
- MenuManager.tsx
|
||||||
|
- layout.tsx
|
||||||
- typescript
|
- typescript
|
||||||
|
- trust-seals/page.tsx
|
||||||
|
- revalidate/route.ts
|
||||||
- MaskableField.tsx
|
- MaskableField.tsx
|
||||||
|
- .addPattern
|
||||||
|
- class-validator
|
||||||
|
- ioredis
|
||||||
|
- @nestjs/common
|
||||||
|
- @nestjs/passport
|
||||||
|
- @nestjs/platform-express
|
||||||
|
- passport
|
||||||
|
- reflect-metadata
|
||||||
|
- eslint-plugin-prettier
|
||||||
|
- @types/compression
|
||||||
|
- @types/passport-jwt
|
||||||
|
- @types/supertest
|
||||||
|
|
||||||
## God Nodes (most connected - your core abstractions)
|
## God Nodes (most connected - your core abstractions)
|
||||||
1. `Roles()` - 106 edges
|
1. `Roles()` - 106 edges
|
||||||
@ -339,27 +359,27 @@
|
|||||||
- 3-file cycle: `frontend/application/lib/services/api.ts -> frontend/application/lib/store/userStore.ts -> frontend/application/lib/store/cartStore.ts -> frontend/application/lib/services/api.ts`
|
- 3-file cycle: `frontend/application/lib/services/api.ts -> frontend/application/lib/store/userStore.ts -> frontend/application/lib/store/cartStore.ts -> frontend/application/lib/services/api.ts`
|
||||||
- 4-file cycle: `frontend/application/lib/services/api.ts -> frontend/application/lib/store/userStore.ts -> frontend/application/lib/store/cartStore.ts -> frontend/application/lib/services/orderService.ts -> frontend/application/lib/services/api.ts`
|
- 4-file cycle: `frontend/application/lib/services/api.ts -> frontend/application/lib/store/userStore.ts -> frontend/application/lib/store/cartStore.ts -> frontend/application/lib/services/orderService.ts -> frontend/application/lib/services/api.ts`
|
||||||
|
|
||||||
## Communities (311 total, 118 thin omitted)
|
## Communities (332 total, 125 thin omitted)
|
||||||
|
|
||||||
### Community 0 - "Roles"
|
### Community 0 - "Roles"
|
||||||
Cohesion: 0.18
|
Cohesion: 0.18
|
||||||
Nodes (19): Roles(), PaymentController, ApiBadRequestResponse, ApiBearerAuth, ApiOkResponse, ApiOperation, ApiTags, Body (+11 more)
|
Nodes (19): Roles(), PaymentController, ApiBadRequestResponse, ApiBearerAuth, ApiOkResponse, ApiOperation, ApiTags, Body (+11 more)
|
||||||
|
|
||||||
### Community 1 - "app.module.ts"
|
### Community 1 - "app.module.ts"
|
||||||
Cohesion: 0.06
|
|
||||||
Nodes (38): B2BModule, Module, BannersModule, Module, CmsModule, Module, SmsModule, Global (+30 more)
|
|
||||||
|
|
||||||
### Community 2 - "SmsService"
|
|
||||||
Cohesion: 0.06
|
|
||||||
Nodes (27): SmsLogQuery, SmsService, Injectable, SmsLogQueryDto, ApiPropertyOptional, IsIn, IsNumber, IsOptional (+19 more)
|
|
||||||
|
|
||||||
### Community 3 - "productService.ts"
|
|
||||||
Cohesion: 0.08
|
|
||||||
Nodes (29): BlogCategory, BlogPostItem, CatalogPageSpread(), CatalogPageSpreadProps, CategoryMeta, getFormBadge(), getSpeciesBadge(), FlipbookCatalog() (+21 more)
|
|
||||||
|
|
||||||
### Community 4 - "ProductPage.tsx"
|
|
||||||
Cohesion: 0.07
|
Cohesion: 0.07
|
||||||
Nodes (27): OrderDetailsModal(), OrderDetailsModalProps, PLAYBACK_RATES, PodcastInlinePlayer(), PodcastInlinePlayerProps, PLAYBACK_RATES, PodcastPlayerModal(), PodcastPlayerModalProps (+19 more)
|
Nodes (36): B2BModule, Module, BannersModule, Module, CmsModule, Module, SmsModule, Global (+28 more)
|
||||||
|
|
||||||
|
### Community 2 - "SettingsController"
|
||||||
|
Cohesion: 0.17
|
||||||
|
Nodes (14): SettingsController, ApiBearerAuth, ApiOkResponse, ApiOperation, ApiTags, Body, Controller, Delete (+6 more)
|
||||||
|
|
||||||
|
### Community 3 - "ProductService"
|
||||||
|
Cohesion: 0.06
|
||||||
|
Nodes (33): dynamic, revalidate, DEFAULT_CATEGORIES, dynamic, revalidate, dynamic, revalidate, BlogCategory (+25 more)
|
||||||
|
|
||||||
|
### Community 4 - "SafeImage.tsx"
|
||||||
|
Cohesion: 0.12
|
||||||
|
Nodes (16): OrderDetailsModalProps, PLAYBACK_RATES, PodcastPlayerModal(), PodcastPlayerModalProps, VideoModalPlayer, SafeImage(), SafeImageProps, DisplayVideoItem (+8 more)
|
||||||
|
|
||||||
### Community 5 - "CmsController"
|
### Community 5 - "CmsController"
|
||||||
Cohesion: 0.09
|
Cohesion: 0.09
|
||||||
@ -367,67 +387,67 @@ Nodes (24): CmsController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controlle
|
|||||||
|
|
||||||
### Community 6 - "tickets.controller.ts"
|
### Community 6 - "tickets.controller.ts"
|
||||||
Cohesion: 0.09
|
Cohesion: 0.09
|
||||||
Nodes (29): AdminUpdateTicketDto, CreateTicketDto, ReplyTicketDto, ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString (+21 more)
|
Nodes (31): AdminUpdateTicketDto, CreateTicketDto, ReplyTicketDto, ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString (+23 more)
|
||||||
|
|
||||||
### Community 7 - "PaginationDto"
|
### Community 7 - "PaginationDto"
|
||||||
Cohesion: 0.06
|
Cohesion: 0.07
|
||||||
Nodes (27): BlogsModule, Module, BlogFilterDto, BlogsService, Injectable, PaginationDto, SortOrder, ApiPropertyOptional (+19 more)
|
Nodes (25): BlogFilterDto, PaginationDto, SortOrder, ApiPropertyOptional, IsEnum, IsInt, IsOptional, IsString (+17 more)
|
||||||
|
|
||||||
### Community 8 - "ReportsController"
|
### Community 8 - "admin.module.ts"
|
||||||
Cohesion: 0.17
|
Cohesion: 0.08
|
||||||
Nodes (9): ReportsController, ApiBearerAuth, ApiOperation, ApiTags, Controller, Get, UseGuards, ReportsService (+1 more)
|
Nodes (17): AdminModule, Module, MediaService, Injectable, ReportsController, ApiBearerAuth, ApiOperation, ApiTags (+9 more)
|
||||||
|
|
||||||
### Community 9 - "devDependencies"
|
### Community 9 - "devDependencies"
|
||||||
Cohesion: 0.22
|
Cohesion: 0.22
|
||||||
Nodes (9): devDependencies, eslint-plugin-prettier, @types/passport-jwt, @types/supertest, typescript, typescript, eslint-plugin-prettier, @types/passport-jwt (+1 more)
|
Nodes (9): devDependencies, eslint, @types/bcryptjs, @types/node, typescript, eslint, @types/node, typescript (+1 more)
|
||||||
|
|
||||||
### Community 10 - "reviews.controller.ts"
|
### Community 10 - "ReviewsController"
|
||||||
Cohesion: 0.07
|
Cohesion: 0.14
|
||||||
Nodes (31): CreateReviewDto, ApiProperty, IsInt, IsNotEmpty, IsOptional, IsString, Min, ApiProperty (+23 more)
|
Nodes (14): ReviewsController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+6 more)
|
||||||
|
|
||||||
### Community 11 - "Spinner.tsx"
|
### Community 11 - "Spinner.tsx"
|
||||||
Cohesion: 0.07
|
Cohesion: 0.09
|
||||||
Nodes (38): ConfirmModal(), ConfirmModalProps, getFileType(), Media, MediaSelector(), MediaSelectorProps, maxWidthClasses, Modal() (+30 more)
|
Nodes (30): ConfirmModal(), ConfirmModalProps, getFileType(), Media, MediaSelector(), MediaSelectorProps, maxWidthClasses, Modal() (+22 more)
|
||||||
|
|
||||||
### Community 12 - "PetProfile.tsx"
|
### Community 12 - "useCartStore"
|
||||||
Cohesion: 0.14
|
Cohesion: 0.11
|
||||||
Nodes (17): ArchiveProductCard(), CATEGORY_MAP, ICON_MAP, FeaturedProducts(), ProductCard(), OrderRowSkeleton(), PetProfileSkeleton(), ProductCardSkeleton() (+9 more)
|
Nodes (26): ArchiveProductCard(), B2BPortal(), CheckoutPage(), FeaturedProducts(), ProductCard(), Header(), MENU_ICONS, OrderSuccess() (+18 more)
|
||||||
|
|
||||||
### Community 13 - "app-audit-verification.e2e-spec.js"
|
### Community 13 - "app-audit-verification.e2e-spec.js"
|
||||||
Cohesion: 0.07
|
Cohesion: 0.07
|
||||||
Nodes (22): AppModule, Module, CustomHttpExceptionFilter, Catch, PrismaExceptionFilter, Catch, DecimalInterceptor, Injectable (+14 more)
|
Nodes (22): AppModule, Module, CustomHttpExceptionFilter, Catch, PrismaExceptionFilter, Catch, DecimalInterceptor, Injectable (+14 more)
|
||||||
|
|
||||||
### Community 14 - "lib/services/api.ts"
|
### Community 14 - "lib/services/api.ts"
|
||||||
Cohesion: 0.09
|
Cohesion: 0.06
|
||||||
Nodes (16): ContactInfoItem, PrescriptionUploadModal(), PrescriptionUploadModalProps, Testimonial, api, ApiErrorPayload, BASE_DOMAIN, baseURL (+8 more)
|
Nodes (26): LoginModal, ContactInfoItem, LoginModal(), LoginModalProps, Testimonial, TestimonialsSection(), api, ApiErrorPayload (+18 more)
|
||||||
|
|
||||||
### Community 15 - "src/services/api.ts"
|
### Community 15 - "src/services/api.ts"
|
||||||
Cohesion: 0.09
|
Cohesion: 0.09
|
||||||
Nodes (24): ProtectedRoute(), Login(), B2BManager, SeoSettingsPage, SystemSettingsPage, ApiErrorPayload, failedQueue, useAdminAuthStore (+16 more)
|
Nodes (25): ProtectedRoute(), Login(), B2BManager, SeoSettingsPage, SmartAdvisorManager, SystemSettingsPage, ApiErrorPayload, failedQueue (+17 more)
|
||||||
|
|
||||||
### Community 16 - "DoctorQueryDto"
|
### Community 16 - "DoctorQueryDto"
|
||||||
Cohesion: 0.09
|
Cohesion: 0.09
|
||||||
Nodes (24): DoctorsController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+16 more)
|
Nodes (24): DoctorsController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+16 more)
|
||||||
|
|
||||||
### Community 17 - "auth.service.ts"
|
### Community 17 - "RedisService"
|
||||||
Cohesion: 0.08
|
Cohesion: 0.10
|
||||||
Nodes (13): AdminLoginInput, AuthService, LoginInput, RegisterInput, Injectable, SendOtpDto, ApiProperty, IsNotEmpty (+5 more)
|
Nodes (10): ApiExcludeController, MetricsController, Controller, Get, Res, RedisModule, Global, Module (+2 more)
|
||||||
|
|
||||||
### Community 18 - "JwtAuthGuard"
|
### Community 18 - "JwtAuthGuard"
|
||||||
Cohesion: 0.19
|
Cohesion: 0.16
|
||||||
Nodes (7): JwtAuthGuard, Injectable, ROLES_KEY, RequestWithUser, RolesGuard, Injectable, UserReqPayload
|
Nodes (8): JwtAuthGuard, Injectable, B2BWholesaleOrderItem, ROLES_KEY, RequestWithUser, RolesGuard, Injectable, UserReqPayload
|
||||||
|
|
||||||
### Community 19 - "ProductsService"
|
### Community 19 - "ProductsService"
|
||||||
Cohesion: 0.09
|
Cohesion: 0.10
|
||||||
Nodes (19): GetProductsDto, ApiPropertyOptional, IsEnum, IsOptional, IsString, ProductsController, ApiOperation, ApiTags (+11 more)
|
Nodes (19): GetProductsDto, ApiPropertyOptional, IsEnum, IsOptional, IsString, ProductsController, ApiOperation, ApiTags (+11 more)
|
||||||
|
|
||||||
### Community 20 - "CreateVideoDto"
|
### Community 20 - "CreateVideoDto"
|
||||||
Cohesion: 0.08
|
Cohesion: 0.09
|
||||||
Nodes (29): CreateVideoDto, ApiProperty, ApiPropertyOptional, IsBoolean, IsNotEmpty, IsOptional, IsString, UpdateVideoDto (+21 more)
|
Nodes (24): CreateVideoDto, ApiProperty, ApiPropertyOptional, IsBoolean, IsNotEmpty, IsOptional, IsString, UpdateVideoDto (+16 more)
|
||||||
|
|
||||||
### Community 22 - "UserDashboard.tsx"
|
### Community 22 - "UserDashboard.tsx"
|
||||||
Cohesion: 0.09
|
Cohesion: 0.10
|
||||||
Nodes (31): VerifyContent(), AddressModal(), AddressModalProps, BackButton(), BackButtonProps, BlogPost, BlogPreviewSection(), CheckoutPage() (+23 more)
|
Nodes (26): VerifyContent(), AddressModal(), AddressModalProps, BackButton(), BackButtonProps, BlogPost, BlogPreviewSection(), DeleteConfirmModal() (+18 more)
|
||||||
|
|
||||||
### Community 23 - "MenuService"
|
### Community 23 - "MenuService"
|
||||||
Cohesion: 0.12
|
Cohesion: 0.12
|
||||||
@ -466,19 +486,19 @@ Cohesion: 0.06
|
|||||||
Nodes (31): Acceptance Criteria, Affected Application, Affected Files, Alternative Direction, Category, Completion Statement, Confidence, Dependencies (+23 more)
|
Nodes (31): Acceptance Criteria, Affected Application, Affected Files, Alternative Direction, Category, Completion Statement, Confidence, Dependencies (+23 more)
|
||||||
|
|
||||||
### Community 32 - "adminRoutes.tsx"
|
### Community 32 - "adminRoutes.tsx"
|
||||||
Cohesion: 0.06
|
Cohesion: 0.07
|
||||||
Nodes (24): App(), CategoryDist, DashboardData, SslStatus, WholesaleRequest, AdminRouteConfig, BannersManager, Categories (+16 more)
|
Nodes (21): App(), CategoryDist, DashboardData, WholesaleRequest, AdminRouteConfig, BannersManager, Categories, Dashboard (+13 more)
|
||||||
|
|
||||||
### Community 33 - "WholesaleService"
|
### Community 33 - "WholesaleApplyDto"
|
||||||
Cohesion: 0.14
|
Cohesion: 0.10
|
||||||
Nodes (14): ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Get, Param, Post (+6 more)
|
Nodes (20): ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString, WholesaleApplyDto, ApiBearerAuth, ApiOperation (+12 more)
|
||||||
|
|
||||||
### Community 34 - "B2BService"
|
### Community 34 - "B2BService"
|
||||||
Cohesion: 0.13
|
Cohesion: 0.14
|
||||||
Nodes (15): B2BController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Get, Param (+7 more)
|
Nodes (14): B2BController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Get, Param (+6 more)
|
||||||
|
|
||||||
### Community 35 - "AuthController"
|
### Community 35 - "AuthController"
|
||||||
Cohesion: 0.25
|
Cohesion: 0.27
|
||||||
Nodes (12): AuthController, ApiBadRequestResponse, ApiBearerAuth, ApiOkResponse, ApiOperation, ApiTags, Body, Controller (+4 more)
|
Nodes (12): AuthController, ApiBadRequestResponse, ApiBearerAuth, ApiOkResponse, ApiOperation, ApiTags, Body, Controller (+4 more)
|
||||||
|
|
||||||
### Community 36 - "FaqService"
|
### Community 36 - "FaqService"
|
||||||
@ -490,8 +510,8 @@ Cohesion: 0.07
|
|||||||
Nodes (25): اجرای بکاند, اجرای فرانتاند, اجرای پروژه در سرور با PM2, برای راهاندازی بکاند:, برای راهاندازی فرانتاند Next.js:, بیلد کردن پروژه (Building), ذخیره پروسهها تا پس از ریاستارت سرور قطع نشوند:, راهاندازی و انتشار سیستم (Setup & Deployment) (+17 more)
|
Nodes (25): اجرای بکاند, اجرای فرانتاند, اجرای پروژه در سرور با PM2, برای راهاندازی بکاند:, برای راهاندازی فرانتاند Next.js:, بیلد کردن پروژه (Building), ذخیره پروسهها تا پس از ریاستارت سرور قطع نشوند:, راهاندازی و انتشار سیستم (Setup & Deployment) (+17 more)
|
||||||
|
|
||||||
### Community 38 - "Button.tsx"
|
### Community 38 - "Button.tsx"
|
||||||
Cohesion: 0.09
|
Cohesion: 0.07
|
||||||
Nodes (22): TransactionReceiptData, TransactionReceiptModal(), TransactionReceiptModalProps, Button(), ButtonProps, ButtonSize, ButtonVariant, ThSort() (+14 more)
|
Nodes (26): TransactionReceiptData, TransactionReceiptModal(), TransactionReceiptModalProps, Button(), ButtonProps, ButtonSize, ButtonVariant, ThSort() (+18 more)
|
||||||
|
|
||||||
### Community 39 - "CategoriesController"
|
### Community 39 - "CategoriesController"
|
||||||
Cohesion: 0.10
|
Cohesion: 0.10
|
||||||
@ -499,7 +519,7 @@ Nodes (16): CategoriesController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags
|
|||||||
|
|
||||||
### Community 40 - "MediaController"
|
### Community 40 - "MediaController"
|
||||||
Cohesion: 0.11
|
Cohesion: 0.11
|
||||||
Nodes (16): MediaController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+8 more)
|
Nodes (14): MediaController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Get (+6 more)
|
||||||
|
|
||||||
### Community 41 - "What You Must Do When Invoked"
|
### Community 41 - "What You Must Do When Invoked"
|
||||||
Cohesion: 0.07
|
Cohesion: 0.07
|
||||||
@ -521,9 +541,9 @@ Nodes (14): TestimonialsController, ApiBearerAuth, ApiOperation, ApiTags, Body,
|
|||||||
Cohesion: 0.07
|
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)
|
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 46 - "AdminService"
|
### Community 46 - "ApiOperation"
|
||||||
Cohesion: 0.09
|
Cohesion: 0.15
|
||||||
Nodes (13): AdminController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Delete, Param (+5 more)
|
Nodes (3): ApiOperation, Body, Put
|
||||||
|
|
||||||
### Community 47 - "IngredientsService"
|
### Community 47 - "IngredientsService"
|
||||||
Cohesion: 0.13
|
Cohesion: 0.13
|
||||||
@ -542,12 +562,12 @@ Cohesion: 0.13
|
|||||||
Nodes (15): eslint-config-next, devDependencies, eslint, eslint-config-next, jsdom, tailwindcss, @tailwindcss/postcss, @types/node (+7 more)
|
Nodes (15): eslint-config-next, devDependencies, eslint, eslint-config-next, jsdom, tailwindcss, @tailwindcss/postcss, @types/node (+7 more)
|
||||||
|
|
||||||
### Community 51 - "BlogsController"
|
### Community 51 - "BlogsController"
|
||||||
Cohesion: 0.07
|
Cohesion: 0.14
|
||||||
Nodes (18): BlogsController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Body, Controller, Delete (+10 more)
|
Nodes (16): BlogsController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Body, Controller, Delete (+8 more)
|
||||||
|
|
||||||
### Community 52 - "PrescriptionsController"
|
### Community 52 - "PrescriptionsService"
|
||||||
Cohesion: 0.15
|
Cohesion: 0.14
|
||||||
Nodes (12): PrescriptionsController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Get, Param (+4 more)
|
Nodes (14): PrescriptionsController, ApiBearerAuth, ApiOperation, ApiTags, Body, Controller, Get, Param (+6 more)
|
||||||
|
|
||||||
### Community 53 - "SmartAdvisorService"
|
### Community 53 - "SmartAdvisorService"
|
||||||
Cohesion: 0.13
|
Cohesion: 0.13
|
||||||
@ -555,15 +575,15 @@ Nodes (14): SmartAdvisorController, ApiBearerAuth, ApiOperation, ApiTags, Body,
|
|||||||
|
|
||||||
### Community 54 - "UsersService"
|
### Community 54 - "UsersService"
|
||||||
Cohesion: 0.06
|
Cohesion: 0.06
|
||||||
Nodes (42): DEFAULT_JWT_REFRESH_SECRET, DEFAULT_JWT_SECRET, getJwtSecret(), AuthModule, Module, JwtPayload, JwtStrategy, Injectable (+34 more)
|
Nodes (41): DEFAULT_JWT_REFRESH_SECRET, DEFAULT_JWT_SECRET, getJwtSecret(), AuthModule, Module, JwtPayload, JwtStrategy, Injectable (+33 more)
|
||||||
|
|
||||||
### Community 55 - "UITexts.tsx"
|
### Community 55 - "UITexts.tsx"
|
||||||
Cohesion: 0.09
|
Cohesion: 0.09
|
||||||
Nodes (20): ICON_REGISTRY, IconPickerModal(), IconPickerModalProps, ActiveStates, PRESET_BG_COLORS, PRESET_COLORS, RichTextEditor(), RichTextEditorProps (+12 more)
|
Nodes (19): ICON_REGISTRY, IconPickerModal(), IconPickerModalProps, ImagePreviewModal(), ImagePreviewModalProps, ToggleSwitch(), ToggleSwitchProps, ProductItem (+11 more)
|
||||||
|
|
||||||
### Community 56 - "Orders.tsx"
|
### Community 56 - "Orders.tsx"
|
||||||
Cohesion: 0.11
|
Cohesion: 0.09
|
||||||
Nodes (15): Skeleton(), MonitoringStats, getPaymentMethodLabel(), Order, ORDER_STATUS_MAP, OrderItem, Orders(), PaymentTx (+7 more)
|
Nodes (19): Badge(), BadgeProps, BadgeVariant, variantStyles, Skeleton(), MonitoringStats, getPaymentMethodLabel(), Order (+11 more)
|
||||||
|
|
||||||
### Community 57 - "Role & Core Objective"
|
### Community 57 - "Role & Core Objective"
|
||||||
Cohesion: 0.09
|
Cohesion: 0.09
|
||||||
@ -578,12 +598,12 @@ Cohesion: 0.06
|
|||||||
Nodes (30): compilerOptions, allowSyntheticDefaultImports, baseUrl, declaration, emitDecoratorMetadata, esModuleInterop, experimentalDecorators, forceConsistentCasingInFileNames (+22 more)
|
Nodes (30): compilerOptions, allowSyntheticDefaultImports, baseUrl, declaration, emitDecoratorMetadata, esModuleInterop, experimentalDecorators, forceConsistentCasingInFileNames (+22 more)
|
||||||
|
|
||||||
### Community 60 - "admin.service.ts"
|
### Community 60 - "admin.service.ts"
|
||||||
Cohesion: 0.12
|
Cohesion: 0.13
|
||||||
Nodes (20): CouponInput, CouponTargetInput, PaginationQuery, ProductDto, ApiPropertyOptional, IsArray, IsBoolean, IsNumber (+12 more)
|
Nodes (21): CouponInput, CouponTargetInput, PaginationQuery, ProductDto, ApiPropertyOptional, IsArray, IsBoolean, IsNumber (+13 more)
|
||||||
|
|
||||||
### Community 61 - "payment.controller.ts"
|
### Community 61 - "CreateEBankCheckoutDto"
|
||||||
Cohesion: 0.11
|
Cohesion: 0.22
|
||||||
Nodes (19): CreateEBankCheckoutDto, ApiProperty, ApiPropertyOptional, IsNotEmpty, IsNumber, IsOptional, IsString, Min (+11 more)
|
Nodes (8): CreateEBankCheckoutDto, ApiProperty, ApiPropertyOptional, IsNotEmpty, IsNumber, IsOptional, IsString, Min
|
||||||
|
|
||||||
### Community 62 - "api"
|
### Community 62 - "api"
|
||||||
Cohesion: 0.13
|
Cohesion: 0.13
|
||||||
@ -591,7 +611,7 @@ Nodes (13): Layout(), MenuGroup, Sidebar(), SidebarProps, SubMenuItem, LiveMonit
|
|||||||
|
|
||||||
### Community 63 - "dependencies"
|
### Community 63 - "dependencies"
|
||||||
Cohesion: 0.10
|
Cohesion: 0.10
|
||||||
Nodes (21): dependencies, bcrypt, class-transformer, class-validator, ioredis, @nestjs/common, @nestjs/passport, @nestjs/platform-express (+13 more)
|
Nodes (21): dependencies, bcrypt, bcryptjs, class-transformer, compression, helmet, js-yaml, @nestjs/jwt (+13 more)
|
||||||
|
|
||||||
### Community 64 - "compilerOptions"
|
### Community 64 - "compilerOptions"
|
||||||
Cohesion: 0.10
|
Cohesion: 0.10
|
||||||
@ -602,16 +622,16 @@ Cohesion: 0.25
|
|||||||
Nodes (7): AdminTransactionFilterDto, ApiPropertyOptional, IsIn, IsNumber, IsOptional, IsString, Type
|
Nodes (7): AdminTransactionFilterDto, ApiPropertyOptional, IsIn, IsNumber, IsOptional, IsString, Type
|
||||||
|
|
||||||
### Community 66 - "AdminQueryDto"
|
### Community 66 - "AdminQueryDto"
|
||||||
Cohesion: 0.17
|
Cohesion: 0.18
|
||||||
Nodes (7): ApiQuery, Get, Query, AdminQueryDto, ApiPropertyOptional, IsOptional, IsString
|
Nodes (7): ApiQuery, Get, Query, AdminQueryDto, ApiPropertyOptional, IsOptional, IsString
|
||||||
|
|
||||||
### Community 67 - "admin.module.ts"
|
### Community 67 - "PetsController"
|
||||||
Cohesion: 0.06
|
Cohesion: 0.10
|
||||||
Nodes (23): AdminModule, Module, PetsController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Controller (+15 more)
|
Nodes (14): PetsController, ApiBearerAuth, ApiOperation, ApiQuery, ApiTags, Controller, Delete, Get (+6 more)
|
||||||
|
|
||||||
### Community 68 - "BlogsController"
|
### Community 68 - "BlogsController"
|
||||||
Cohesion: 0.16
|
Cohesion: 0.23
|
||||||
Nodes (14): BlogsController, ApiBearerAuth, ApiNotFoundResponse, ApiOkResponse, ApiOperation, ApiTags, Body, Controller (+6 more)
|
Nodes (9): BlogsController, ApiNotFoundResponse, ApiOkResponse, ApiOperation, ApiTags, Controller, Get, Param (+1 more)
|
||||||
|
|
||||||
### Community 69 - "Required Review Group Closures"
|
### Community 69 - "Required Review Group Closures"
|
||||||
Cohesion: 0.10
|
Cohesion: 0.10
|
||||||
@ -622,8 +642,8 @@ Cohesion: 0.08
|
|||||||
Nodes (23): compilerOptions, allowImportingTsExtensions, erasableSyntaxOnly, jsx, lib, module, moduleDetection, moduleResolution (+15 more)
|
Nodes (23): compilerOptions, allowImportingTsExtensions, erasableSyntaxOnly, jsx, lib, module, moduleDetection, moduleResolution (+15 more)
|
||||||
|
|
||||||
### Community 71 - "getPageMetadata"
|
### Community 71 - "getPageMetadata"
|
||||||
Cohesion: 0.13
|
Cohesion: 0.14
|
||||||
Nodes (8): generateMetadata(), generateMetadata(), generateMetadata(), generateMetadata(), generateMetadata(), generateMetadata(), generateMetadata(), getPageMetadata()
|
Nodes (9): generateMetadata(), generateMetadata(), generateMetadata(), generateMetadata(), getHomeData(), Home(), generateMetadata(), generateMetadata() (+1 more)
|
||||||
|
|
||||||
### Community 72 - "Operational Rules & Boundaries"
|
### Community 72 - "Operational Rules & Boundaries"
|
||||||
Cohesion: 0.11
|
Cohesion: 0.11
|
||||||
@ -673,10 +693,6 @@ Nodes (21): dependencies, axios, lucide-react, react, react-dom, react-hot-toast
|
|||||||
Cohesion: 0.12
|
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)
|
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 85 - "WholesaleApplyDto"
|
|
||||||
Cohesion: 0.29
|
|
||||||
Nodes (6): ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString, WholesaleApplyDto
|
|
||||||
|
|
||||||
### Community 86 - "zibal.service.ts"
|
### Community 86 - "zibal.service.ts"
|
||||||
Cohesion: 0.16
|
Cohesion: 0.16
|
||||||
Nodes (9): GatewayHealthResult, IPaymentGateway, PaymentInquiryResult, PaymentRequestOptions, PaymentRequestResult, PaymentVerifyResult, ZibalInquiryResponse, ZibalRequestResponse (+1 more)
|
Nodes (9): GatewayHealthResult, IPaymentGateway, PaymentInquiryResult, PaymentRequestOptions, PaymentRequestResult, PaymentVerifyResult, ZibalInquiryResponse, ZibalRequestResponse (+1 more)
|
||||||
@ -686,36 +702,36 @@ Cohesion: 0.12
|
|||||||
Nodes (17): dependencies, axios, lucide-react, motion, next, react, react-dom, sonner (+9 more)
|
Nodes (17): dependencies, axios, lucide-react, motion, next, react, react-dom, sonner (+9 more)
|
||||||
|
|
||||||
### Community 88 - "useSettingsStore"
|
### Community 88 - "useSettingsStore"
|
||||||
Cohesion: 0.08
|
Cohesion: 0.09
|
||||||
Nodes (32): HomeClient(), HomeClientProps, generateMetadata(), getHomeData(), Home(), BannerPlacement(), BannerPlacementProps, BrandLogo() (+24 more)
|
Nodes (29): AuthModal, B2BPortal, CartDrawer, ClientLayout(), AuthModal(), AuthModalProps, extractOtpFromText(), BrandLogo() (+21 more)
|
||||||
|
|
||||||
### Community 89 - "seed-products.ts"
|
### Community 89 - "seed-products.ts"
|
||||||
Cohesion: 0.17
|
Cohesion: 0.17
|
||||||
Nodes (12): prisma, main(), prisma, determineSuitableFor(), findOrCreateCategory(), getProductImageUrl(), main(), parseSize() (+4 more)
|
Nodes (12): prisma, main(), prisma, determineSuitableFor(), findOrCreateCategory(), getProductImageUrl(), main(), parseSize() (+4 more)
|
||||||
|
|
||||||
### Community 90 - "MetricsController"
|
### Community 90 - "HomeClient.tsx"
|
||||||
Cohesion: 0.29
|
Cohesion: 0.15
|
||||||
Nodes (5): ApiExcludeController, MetricsController, Controller, Get, Res
|
Nodes (16): HomeClient(), HomeClientProps, CATEGORY_MAP, ICON_MAP, BannerPlacement(), BannerPlacementProps, Hero(), StatCounter() (+8 more)
|
||||||
|
|
||||||
### Community 91 - "Reconciled Audit Roles & Assignments"
|
### Community 91 - "Reconciled Audit Roles & Assignments"
|
||||||
Cohesion: 0.12
|
Cohesion: 0.12
|
||||||
Nodes (15): 10. Documentation Engineer, 1. Lead Architect / Orchestrator, 2. React / Vite Storefront Auditor, 3. NestJS Backend Auditor, 4. Admin Features Auditor, 5. Database & Data Integrity Auditor, 6. Security Auditor, 7. TypeScript & Code Quality Auditor (+7 more)
|
Nodes (15): 10. Documentation Engineer, 1. Lead Architect / Orchestrator, 2. React / Vite Storefront Auditor, 3. NestJS Backend Auditor, 4. Admin Features Auditor, 5. Database & Data Integrity Auditor, 6. Security Auditor, 7. TypeScript & Code Quality Auditor (+7 more)
|
||||||
|
|
||||||
### Community 92 - "OrdersService"
|
### Community 92 - "OrdersService"
|
||||||
Cohesion: 0.06
|
Cohesion: 0.07
|
||||||
Nodes (35): CreateOrderDto, OrderItemDto, ApiProperty, ApiPropertyOptional, IsArray, IsNotEmpty, IsNumber, IsOptional (+27 more)
|
Nodes (29): CreateOrderDto, OrderItemDto, ApiProperty, ApiPropertyOptional, IsArray, IsNotEmpty, IsNumber, IsOptional (+21 more)
|
||||||
|
|
||||||
### Community 93 - "useCartStore"
|
### Community 93 - "ProductPage.tsx"
|
||||||
Cohesion: 0.12
|
Cohesion: 0.12
|
||||||
Nodes (11): CartDrawer(), Header(), OrderSuccess(), mockProduct, ApiErr, Order, OrderItem, OrderService (+3 more)
|
Nodes (16): PLAYBACK_RATES, PodcastInlinePlayer(), PodcastInlinePlayerProps, CalculatorState, ICON_MAP, ProductPage(), ProductReviews, useCalculatorStore (+8 more)
|
||||||
|
|
||||||
### Community 94 - "Phase 3.1 — Human Review Preparation and Master Backlog Critique"
|
### Community 94 - "Phase 3.1 — Human Review Preparation and Master Backlog Critique"
|
||||||
Cohesion: 0.13
|
Cohesion: 0.13
|
||||||
Nodes (14): 10. Dependency & Wave Adjustments, 11. Acceptance Criteria Refinements, 12. Business and Product Decision Classification, 13. Final Recommended Backlog Summary, 1. Executive Assessment, 2. Findings That Require No Change, 3. Findings Requiring Task Refinements, 4. Tasks Requiring Splitting (+6 more)
|
Nodes (14): 10. Dependency & Wave Adjustments, 11. Acceptance Criteria Refinements, 12. Business and Product Decision Classification, 13. Final Recommended Backlog Summary, 1. Executive Assessment, 2. Findings That Require No Change, 3. Findings Requiring Task Refinements, 4. Tasks Requiring Splitting (+6 more)
|
||||||
|
|
||||||
### Community 95 - "getSeoConfig"
|
### Community 95 - "getSeoConfig"
|
||||||
Cohesion: 0.22
|
Cohesion: 0.26
|
||||||
Nodes (11): BlogPostPage(), generateMetadata(), getBlog(), revalidate, generateMetadata(), generateMetadata(), generateMetadata(), getWikiTerm() (+3 more)
|
Nodes (11): BlogPostPage(), generateMetadata(), getBlog(), revalidate, generateMetadata(), revalidate, generateMetadata(), getWikiTerm() (+3 more)
|
||||||
|
|
||||||
### Community 96 - "compilerOptions"
|
### Community 96 - "compilerOptions"
|
||||||
Cohesion: 0.06
|
Cohesion: 0.06
|
||||||
@ -754,12 +770,12 @@ Cohesion: 0.17
|
|||||||
Nodes (11): 1. Detect Test Runner from Tech Stack, 2. Execution Output Capture (Mandatory), 3. Acceptance Criteria Verification, 4. Failure Routing Protocol, 5. Success Routing Protocol, 6. Forbidden Actions, Expected JSON Output Schema, Operational Rules & Boundaries (+3 more)
|
Nodes (11): 1. Detect Test Runner from Tech Stack, 2. Execution Output Capture (Mandatory), 3. Acceptance Criteria Verification, 4. Failure Routing Protocol, 5. Success Routing Protocol, 6. Forbidden Actions, Expected JSON Output Schema, Operational Rules & Boundaries (+3 more)
|
||||||
|
|
||||||
### Community 106 - "auth.controller.ts"
|
### Community 106 - "auth.controller.ts"
|
||||||
Cohesion: 0.08
|
Cohesion: 0.16
|
||||||
Nodes (25): AdminLoginDto, ApiProperty, IsEmail, IsNotEmpty, IsString, MinLength, LoginDto, ApiProperty (+17 more)
|
Nodes (11): AdminLoginDto, ApiProperty, IsEmail, IsNotEmpty, IsString, MinLength, LoginDto, ApiProperty (+3 more)
|
||||||
|
|
||||||
### Community 108 - "PrismaService"
|
### Community 108 - "PrismaService"
|
||||||
Cohesion: 0.08
|
Cohesion: 0.07
|
||||||
Nodes (17): CategoryQuery, MeliPayamakPattern, MeliPayamakResponse, SendPatternSmsOptions, SmsConfig, CreateContactSubmissionDto, UpdateContactInfoItemDto, MenuType (+9 more)
|
Nodes (20): CategoryQuery, MeliPayamakPattern, MeliPayamakResponse, SendPatternSmsOptions, SmsConfig, SmsLogQuery, CreateContactSubmissionDto, UpdateContactInfoItemDto (+12 more)
|
||||||
|
|
||||||
### Community 109 - "1. Summary of Integrity Repairs Performed"
|
### Community 109 - "1. Summary of Integrity Repairs Performed"
|
||||||
Cohesion: 0.17
|
Cohesion: 0.17
|
||||||
@ -777,9 +793,9 @@ Nodes (10): 1. Mark Active Task as Complete, 2. Documentation Updates, 3. Dynami
|
|||||||
Cohesion: 0.18
|
Cohesion: 0.18
|
||||||
Nodes (10): 1. Pre-Deployment Checklist, 2. Build Verification, 3. Deployment Strategy (Based on Target), 4. Post-Deployment Health Check, 5. Forbidden Actions, Expected JSON Output Schema, Operational Rules & Boundaries, Required Output Artifacts (What files to write/update) (+2 more)
|
Nodes (10): 1. Pre-Deployment Checklist, 2. Build Verification, 3. Deployment Strategy (Based on Target), 4. Post-Deployment Health Check, 5. Forbidden Actions, Expected JSON Output Schema, Operational Rules & Boundaries, Required Output Artifacts (What files to write/update) (+2 more)
|
||||||
|
|
||||||
### Community 113 - "Reviews.tsx"
|
### Community 113 - "reviews.controller.ts"
|
||||||
Cohesion: 0.50
|
Cohesion: 0.13
|
||||||
Nodes (4): ProductReview, Reviews(), toPersianDigits(), Reviews
|
Nodes (17): CreateReviewDto, ApiProperty, IsInt, IsNotEmpty, IsOptional, IsString, Min, ApiProperty (+9 more)
|
||||||
|
|
||||||
### Community 114 - "AppService"
|
### Community 114 - "AppService"
|
||||||
Cohesion: 0.29
|
Cohesion: 0.29
|
||||||
@ -825,9 +841,13 @@ Nodes (9): name, private, scripts, build, dev, lint, preview, type (+1 more)
|
|||||||
Cohesion: 0.20
|
Cohesion: 0.20
|
||||||
Nodes (9): Arch Linux, Contributors, Install, Known problems for variable version, License, Sahel-Font, To Do (variable), طریقه استفاده از نسخه متغیر variable (+1 more)
|
Nodes (9): Arch Linux, Contributors, Install, Known problems for variable version, License, Sahel-Font, To Do (variable), طریقه استفاده از نسخه متغیر variable (+1 more)
|
||||||
|
|
||||||
|
### Community 128 - "AdminService"
|
||||||
|
Cohesion: 0.16
|
||||||
|
Nodes (4): Delete, Param, AdminService, Injectable
|
||||||
|
|
||||||
### Community 129 - "seo.ts"
|
### Community 129 - "seo.ts"
|
||||||
Cohesion: 0.29
|
Cohesion: 0.18
|
||||||
Nodes (4): generateMetadata(), DEFAULT_SEO_CONFIG, PageMetadataOptions, SeoConfig
|
Nodes (6): generateMetadata(), generateMetadata(), SearchResultsPage(), DEFAULT_SEO_CONFIG, PageMetadataOptions, SeoConfig
|
||||||
|
|
||||||
### Community 130 - "Sahel-Font"
|
### Community 130 - "Sahel-Font"
|
||||||
Cohesion: 0.20
|
Cohesion: 0.20
|
||||||
@ -885,9 +905,13 @@ Nodes (6): app_module_1, core_1, fs, path, swagger_1, yaml
|
|||||||
Cohesion: 0.25
|
Cohesion: 0.25
|
||||||
Nodes (7): Active Core Applications, Current Architecture, Evidence-Based Status Matrix, Excluded / Non-Auditable Artifacts, Major Business Domains Discovered, System Discovery, Technical Architecture Summary
|
Nodes (7): Active Core Applications, Current Architecture, Evidence-Based Status Matrix, Excluded / Non-Auditable Artifacts, Major Business Domains Discovered, System Discovery, Technical Architecture Summary
|
||||||
|
|
||||||
### Community 147 - "PrescriptionsManager.tsx"
|
### Community 147 - "RevalidationService"
|
||||||
Cohesion: 0.18
|
Cohesion: 0.14
|
||||||
Nodes (9): Badge(), BadgeProps, BadgeVariant, variantStyles, ImagePreviewModal(), ImagePreviewModalProps, ProductItem, PrescriptionsManager (+1 more)
|
Nodes (5): RevalidationModule, Global, Module, RevalidationService, Injectable
|
||||||
|
|
||||||
|
### Community 148 - "AdminController"
|
||||||
|
Cohesion: 0.16
|
||||||
|
Nodes (6): AdminController, ApiBearerAuth, ApiTags, Controller, Post, UseGuards
|
||||||
|
|
||||||
### Community 149 - "SmsSettingsPage.tsx"
|
### Community 149 - "SmsSettingsPage.tsx"
|
||||||
Cohesion: 0.29
|
Cohesion: 0.29
|
||||||
@ -897,6 +921,10 @@ Nodes (7): PatternItem, SmsConfigState, SmsLogItem, SmsLogStats, SmsSettingsPage
|
|||||||
Cohesion: 0.29
|
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)
|
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 151 - "AuthService"
|
||||||
|
Cohesion: 0.19
|
||||||
|
Nodes (3): AuthService, Injectable, normalizeMobile()
|
||||||
|
|
||||||
### Community 153 - "exclude"
|
### Community 153 - "exclude"
|
||||||
Cohesion: 0.22
|
Cohesion: 0.22
|
||||||
Nodes (8): exclude, extends, dist, node_modules, prisma, **/*spec.ts, test, ./tsconfig.json
|
Nodes (8): exclude, extends, dist, node_modules, prisma, **/*spec.ts, test, ./tsconfig.json
|
||||||
@ -905,6 +933,10 @@ Nodes (8): exclude, extends, dist, node_modules, prisma, **/*spec.ts, test, ./ts
|
|||||||
Cohesion: 0.29
|
Cohesion: 0.29
|
||||||
Nodes (6): Attempted Command Execution Log, Backend `backend/package.json` Scripts, Baseline Command Plan & Reconciled Command History, Package Scripts Safety Analysis, Permitted Safe Checks for Phase 2, Root `package.json` Scripts
|
Nodes (6): Attempted Command Execution Log, Backend `backend/package.json` Scripts, Baseline Command Plan & Reconciled Command History, Package Scripts Safety Analysis, Permitted Safe Checks for Phase 2, Root `package.json` Scripts
|
||||||
|
|
||||||
|
### Community 155 - "Blogs.tsx"
|
||||||
|
Cohesion: 0.15
|
||||||
|
Nodes (11): ActiveStates, PRESET_BG_COLORS, PRESET_COLORS, RichTextEditor(), RichTextEditorProps, AuthorUser, BlogCategory, BlogPost (+3 more)
|
||||||
|
|
||||||
### Community 159 - "with-vpn.sh"
|
### Community 159 - "with-vpn.sh"
|
||||||
Cohesion: 0.62
|
Cohesion: 0.62
|
||||||
Nodes (6): cleanup(), log(), with-vpn.sh script, start_vpn(), stop_vpn(), vpn_is_up()
|
Nodes (6): cleanup(), log(), with-vpn.sh script, start_vpn(), stop_vpn(), vpn_is_up()
|
||||||
@ -974,8 +1006,12 @@ Cohesion: 0.40
|
|||||||
Nodes (4): activeFiles, errors, validationOutput, warnings
|
Nodes (4): activeFiles, errors, validationOutput, warnings
|
||||||
|
|
||||||
### Community 177 - "blog/page.tsx"
|
### Community 177 - "blog/page.tsx"
|
||||||
Cohesion: 0.47
|
Cohesion: 0.38
|
||||||
Nodes (5): Blog(), generateMetadata(), getBlogs(), getCategories(), BlogPage()
|
Nodes (6): Blog(), generateMetadata(), getBlogs(), getCategories(), revalidate, BlogPage()
|
||||||
|
|
||||||
|
### Community 179 - "BlogsService"
|
||||||
|
Cohesion: 0.15
|
||||||
|
Nodes (4): BlogsModule, Module, BlogsService, Injectable
|
||||||
|
|
||||||
### Community 180 - "API Contract Specification"
|
### Community 180 - "API Contract Specification"
|
||||||
Cohesion: 0.50
|
Cohesion: 0.50
|
||||||
@ -993,6 +1029,10 @@ Nodes (3): Architectural Overview, Critical Review Findings & Required Enhanceme
|
|||||||
Cohesion: 0.50
|
Cohesion: 0.50
|
||||||
Nodes (3): Overview & Content Foundation, SEO & Content Requirements, 🚀 SEO & Content Strategy Review (12_seo_content)
|
Nodes (3): Overview & Content Foundation, SEO & Content Requirements, 🚀 SEO & Content Strategy Review (12_seo_content)
|
||||||
|
|
||||||
|
### Community 185 - "ApiResponse"
|
||||||
|
Cohesion: 0.19
|
||||||
|
Nodes (10): ApiNotFoundResponse, ApiOkResponse, ApiOperation, ApiTags, Controller, Get, Param, Query (+2 more)
|
||||||
|
|
||||||
### Community 191 - "graphify reference: add a URL and watch a folder"
|
### Community 191 - "graphify reference: add a URL and watch a folder"
|
||||||
Cohesion: 0.50
|
Cohesion: 0.50
|
||||||
Nodes (3): For /graphify add, For --watch, graphify reference: add a URL and watch a folder
|
Nodes (3): For /graphify add, For --watch, graphify reference: add a URL and watch a folder
|
||||||
@ -1017,9 +1057,9 @@ Nodes (3): Expanding the ESLint configuration, React Compiler, React + TypeScrip
|
|||||||
Cohesion: 0.50
|
Cohesion: 0.50
|
||||||
Nodes (3): Select, SelectOption, SelectProps
|
Nodes (3): Select, SelectOption, SelectProps
|
||||||
|
|
||||||
### Community 199 - "useUserStore"
|
### Community 199 - "auth.service.ts"
|
||||||
Cohesion: 0.15
|
Cohesion: 0.20
|
||||||
Nodes (17): ClientLayout(), AuthModal(), AuthModalProps, extractOtpFromText(), B2BPortal(), LoginModal(), LoginModalProps, NetworkBanner() (+9 more)
|
Nodes (9): AdminLoginInput, LoginInput, RegisterInput, ApiProperty, IsNotEmpty, IsString, Matches, VerifyOtpDto (+1 more)
|
||||||
|
|
||||||
### Community 200 - "application/README.md"
|
### Community 200 - "application/README.md"
|
||||||
Cohesion: 0.50
|
Cohesion: 0.50
|
||||||
@ -1037,6 +1077,14 @@ Nodes (3): ADR-AUTH-001: Admin Panel Authentication Architecture & Token Managem
|
|||||||
Cohesion: 0.67
|
Cohesion: 0.67
|
||||||
Nodes (3): Shabnam Font README, Shabnam Font Sample, Vazir Font
|
Nodes (3): Shabnam Font README, Shabnam Font Sample, Vazir Font
|
||||||
|
|
||||||
|
### Community 239 - "RegisterDto"
|
||||||
|
Cohesion: 0.22
|
||||||
|
Nodes (8): RegisterDto, ApiProperty, ApiPropertyOptional, IsEmail, IsNotEmpty, IsOptional, IsString, MinLength
|
||||||
|
|
||||||
|
### Community 264 - "InitiatePaymentDto"
|
||||||
|
Cohesion: 0.43
|
||||||
|
Nodes (7): InitiatePaymentDto, InitiateWalletTopupDto, ApiProperty, ApiPropertyOptional, IsNotEmpty, IsOptional, IsString
|
||||||
|
|
||||||
### Community 298 - "ZibalCallbackQueryDto"
|
### Community 298 - "ZibalCallbackQueryDto"
|
||||||
Cohesion: 0.40
|
Cohesion: 0.40
|
||||||
Nodes (4): ApiPropertyOptional, IsOptional, IsString, ZibalCallbackQueryDto
|
Nodes (4): ApiPropertyOptional, IsOptional, IsString, ZibalCallbackQueryDto
|
||||||
@ -1045,25 +1093,49 @@ Nodes (4): ApiPropertyOptional, IsOptional, IsString, ZibalCallbackQueryDto
|
|||||||
Cohesion: 0.50
|
Cohesion: 0.50
|
||||||
Nodes (3): app_module_1, supertest_1, testing_1
|
Nodes (3): app_module_1, supertest_1, testing_1
|
||||||
|
|
||||||
|
### Community 304 - "SmsLogQueryDto"
|
||||||
|
Cohesion: 0.25
|
||||||
|
Nodes (7): SmsLogQueryDto, ApiPropertyOptional, IsIn, IsNumber, IsOptional, IsString, Type
|
||||||
|
|
||||||
|
### Community 307 - "SendOtpDto"
|
||||||
|
Cohesion: 0.33
|
||||||
|
Nodes (5): SendOtpDto, ApiProperty, IsNotEmpty, IsString, Matches
|
||||||
|
|
||||||
|
### Community 312 - ".addComment"
|
||||||
|
Cohesion: 0.33
|
||||||
|
Nodes (5): ApiBearerAuth, Body, Post, Request, UseGuards
|
||||||
|
|
||||||
|
### Community 313 - "MenuManager.tsx"
|
||||||
|
Cohesion: 0.33
|
||||||
|
Nodes (4): MENU_TABS, MenuItem, MenuType, MenuManager
|
||||||
|
|
||||||
|
### Community 314 - "layout.tsx"
|
||||||
|
Cohesion: 0.47
|
||||||
|
Nodes (3): generateMetadata(), lalezar, vazirmatn
|
||||||
|
|
||||||
|
### Community 317 - "revalidate/route.ts"
|
||||||
|
Cohesion: 0.83
|
||||||
|
Nodes (3): GET(), handleRevalidate(), POST()
|
||||||
|
|
||||||
## Knowledge Gaps
|
## Knowledge Gaps
|
||||||
- **1286 isolated node(s):** `$schema`, `collection`, `sourceRoot`, `deleteOutDir`, `name` (+1281 more)
|
- **1297 isolated node(s):** `$schema`, `collection`, `sourceRoot`, `deleteOutDir`, `name` (+1292 more)
|
||||||
These have ≤1 connection - possible missing edges or undocumented components.
|
These have ≤1 connection - possible missing edges or undocumented components.
|
||||||
- **118 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
|
- **125 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
|
||||||
|
|
||||||
## Suggested Questions
|
## Suggested Questions
|
||||||
_Questions this graph is uniquely positioned to answer:_
|
_Questions this graph is uniquely positioned to answer:_
|
||||||
|
|
||||||
- **Why does `ApiResponse` connect `src/services/api.ts` to `HomeController`, `AuthController`, `BlogsController`, `PaginationDto`, `PetsController`, `ProductsService`, `UsersService`, `OrdersService`?**
|
- **Why does `ApiResponse` connect `ApiResponse` to `HomeController`, `AuthController`, `BlogsController`, `PetsController`, `src/services/api.ts`, `ProductsService`, `UsersService`, `OrdersService`?**
|
||||||
_High betweenness centrality (0.077) - this node is a cross-community bridge._
|
_High betweenness centrality (0.075) - this node is a cross-community bridge._
|
||||||
- **Why does `Roles()` connect `Roles` to `SmsService`, `CmsController`, `tickets.controller.ts`, `reviews.controller.ts`, `JwtAuthGuard`, `ProductsService`, `MenuService`, `WholesaleService`, `B2BService`, `FaqService`, `SslController`, `BannersService`, `TestimonialsService`, `IngredientsService`, `PrescriptionsController`, `SmartAdvisorService`, `ContactService`, `payment.controller.ts`, `AdminTransactionFilterDto`?**
|
- **Why does `Roles()` connect `Roles` to `SettingsController`, `CmsController`, `tickets.controller.ts`, `ReviewsController`, `JwtAuthGuard`, `ProductsService`, `MenuService`, `WholesaleApplyDto`, `B2BService`, `FaqService`, `SslController`, `BannersService`, `TestimonialsService`, `IngredientsService`, `PrescriptionsService`, `SmartAdvisorService`, `ContactService`, `.addPattern`, `AdminTransactionFilterDto`, `reviews.controller.ts`?**
|
||||||
_High betweenness centrality (0.055) - this node is a cross-community bridge._
|
_High betweenness centrality (0.063) - this node is a cross-community bridge._
|
||||||
- **Why does `JwtAuthGuard` connect `JwtAuthGuard` to `admin.module.ts`, `CmsController`, `tickets.controller.ts`, `PaginationDto`, `auth.controller.ts`, `PetsController`, `reviews.controller.ts`, `OrdersService`, `DoctorQueryDto`, `ProductsService`, `CreateVideoDto`, `UsersService`, `admin.service.ts`, `payment.controller.ts`?**
|
- **Why does `JwtAuthGuard` connect `JwtAuthGuard` to `PetsController`, `CmsController`, `tickets.controller.ts`, `PaginationDto`, `admin.module.ts`, `auth.controller.ts`, `PetsController`, `PrismaService`, `DoctorQueryDto`, `reviews.controller.ts`, `ProductsService`, `admin.service.ts`?**
|
||||||
_High betweenness centrality (0.038) - this node is a cross-community bridge._
|
_High betweenness centrality (0.036) - this node is a cross-community bridge._
|
||||||
- **What connects `$schema`, `collection`, `sourceRoot` to the rest of the system?**
|
- **What connects `$schema`, `collection`, `sourceRoot` to the rest of the system?**
|
||||||
_1286 weakly-connected nodes found - possible documentation gaps or missing edges._
|
_1297 weakly-connected nodes found - possible documentation gaps or missing edges._
|
||||||
- **Should `app.module.ts` be split into smaller, more focused modules?**
|
- **Should `app.module.ts` be split into smaller, more focused modules?**
|
||||||
_Cohesion score 0.06390977443609022 - nodes in this community are weakly interconnected._
|
_Cohesion score 0.06708595387840671 - nodes in this community are weakly interconnected._
|
||||||
- **Should `SmsService` be split into smaller, more focused modules?**
|
- **Should `ProductService` be split into smaller, more focused modules?**
|
||||||
_Cohesion score 0.055040197897340756 - nodes in this community are weakly interconnected._
|
_Cohesion score 0.06393442622950819 - nodes in this community are weakly interconnected._
|
||||||
- **Should `productService.ts` be split into smaller, more focused modules?**
|
- **Should `SafeImage.tsx` be split into smaller, more focused modules?**
|
||||||
_Cohesion score 0.07607843137254902 - nodes in this community are weakly interconnected._
|
_Cohesion score 0.11692307692307692 - nodes in this community are weakly interconnected._
|
||||||
File diff suppressed because one or more lines are too long
2
graphify-out/cache/stat-index.json
vendored
2
graphify-out/cache/stat-index.json
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
14364
graphify-out/graph.json
14364
graphify-out/graph.json
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user