task-l
This commit is contained in:
@@ -40,7 +40,12 @@ export async function GET(request: NextRequest) {
|
||||
userId: session.user.id,
|
||||
membershipRole: membership.role,
|
||||
businessRole: membership.businessRole,
|
||||
permissions: membership.permissions
|
||||
permissions: membership.permissions,
|
||||
branchScopeIds: membership.branchScopeIds ?? [],
|
||||
productTypeScopeIds: membership.productTypeScopeIds ?? [],
|
||||
ownershipScope: membership.ownershipScope ?? 'organization',
|
||||
branchScopeMode: membership.branchScopeMode === 'assigned' ? 'assigned' : 'all',
|
||||
productScopeMode: membership.productScopeMode === 'assigned' ? 'assigned' : 'all'
|
||||
},
|
||||
{
|
||||
dateFrom: searchParams.get('dateFrom'),
|
||||
|
||||
@@ -15,7 +15,12 @@ export async function GET(request: NextRequest) {
|
||||
userId: session.user.id,
|
||||
membershipRole: membership.role,
|
||||
businessRole: membership.businessRole,
|
||||
permissions: membership.permissions
|
||||
permissions: membership.permissions,
|
||||
branchScopeIds: membership.branchScopeIds ?? [],
|
||||
productTypeScopeIds: membership.productTypeScopeIds ?? [],
|
||||
ownershipScope: membership.ownershipScope ?? 'organization',
|
||||
branchScopeMode: membership.branchScopeMode === 'assigned' ? 'assigned' : 'all',
|
||||
productScopeMode: membership.productScopeMode === 'assigned' ? 'assigned' : 'all'
|
||||
},
|
||||
{
|
||||
dateFrom: searchParams.get('dateFrom'),
|
||||
|
||||
@@ -17,13 +17,25 @@ export async function POST(request: NextRequest, { params }: Params) {
|
||||
permission: PERMISSIONS.crmEnquiryAssign
|
||||
});
|
||||
const payload = enquiryAssignmentRequestSchema.parse(await request.json());
|
||||
const before = await getEnquiryDetail(id, organization.id, {
|
||||
const accessContext = {
|
||||
organizationId: organization.id,
|
||||
userId: session.user.id,
|
||||
membershipRole: membership.role,
|
||||
businessRole: membership.businessRole
|
||||
});
|
||||
const updated = await assignEnquiryToUser(id, organization.id, session.user.id, payload);
|
||||
businessRole: membership.businessRole,
|
||||
branchScopeIds: membership.branchScopeIds ?? [],
|
||||
productTypeScopeIds: membership.productTypeScopeIds ?? [],
|
||||
ownershipScope: membership.ownershipScope ?? 'organization',
|
||||
branchScopeMode: membership.branchScopeMode === 'assigned' ? 'assigned' : 'all',
|
||||
productScopeMode: membership.productScopeMode === 'assigned' ? 'assigned' : 'all'
|
||||
};
|
||||
const before = await getEnquiryDetail(id, organization.id, accessContext);
|
||||
const updated = await assignEnquiryToUser(
|
||||
id,
|
||||
organization.id,
|
||||
session.user.id,
|
||||
payload,
|
||||
accessContext
|
||||
);
|
||||
|
||||
await auditAction({
|
||||
organizationId: organization.id,
|
||||
|
||||
@@ -13,12 +13,18 @@ export async function GET(_request: NextRequest, { params }: Params) {
|
||||
const { organization, session, membership } = await requireOrganizationAccess({
|
||||
permission: PERMISSIONS.crmEnquiryRead
|
||||
});
|
||||
const items = await listEnquiryProjectParties(id, organization.id, {
|
||||
const accessContext = {
|
||||
organizationId: organization.id,
|
||||
userId: session.user.id,
|
||||
membershipRole: membership.role,
|
||||
businessRole: membership.businessRole
|
||||
});
|
||||
businessRole: membership.businessRole,
|
||||
branchScopeIds: membership.branchScopeIds ?? [],
|
||||
productTypeScopeIds: membership.productTypeScopeIds ?? [],
|
||||
ownershipScope: membership.ownershipScope ?? 'organization',
|
||||
branchScopeMode: membership.branchScopeMode === 'assigned' ? 'assigned' : 'all',
|
||||
productScopeMode: membership.productScopeMode === 'assigned' ? 'assigned' : 'all'
|
||||
};
|
||||
const items = await listEnquiryProjectParties(id, organization.id, accessContext);
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
|
||||
@@ -26,13 +26,19 @@ export async function PATCH(request: NextRequest, { params }: Params) {
|
||||
permission: PERMISSIONS.crmEnquiryFollowupUpdate
|
||||
});
|
||||
const payload = followupRequestSchema.parse(await request.json());
|
||||
const accessContext = {
|
||||
organizationId: organization.id,
|
||||
userId: session.user.id,
|
||||
membershipRole: membership.role,
|
||||
businessRole: membership.businessRole,
|
||||
branchScopeIds: membership.branchScopeIds ?? [],
|
||||
productTypeScopeIds: membership.productTypeScopeIds ?? [],
|
||||
ownershipScope: membership.ownershipScope ?? 'organization',
|
||||
branchScopeMode: membership.branchScopeMode === 'assigned' ? 'assigned' : 'all',
|
||||
productScopeMode: membership.productScopeMode === 'assigned' ? 'assigned' : 'all'
|
||||
};
|
||||
const before = (
|
||||
await listEnquiryFollowups(id, organization.id, {
|
||||
organizationId: organization.id,
|
||||
userId: session.user.id,
|
||||
membershipRole: membership.role,
|
||||
businessRole: membership.businessRole
|
||||
})
|
||||
await listEnquiryFollowups(id, organization.id, accessContext)
|
||||
).find(
|
||||
(item) => item.id === followupId
|
||||
);
|
||||
@@ -47,12 +53,7 @@ export async function PATCH(request: NextRequest, { params }: Params) {
|
||||
organization.id,
|
||||
session.user.id,
|
||||
payload,
|
||||
{
|
||||
organizationId: organization.id,
|
||||
userId: session.user.id,
|
||||
membershipRole: membership.role,
|
||||
businessRole: membership.businessRole
|
||||
}
|
||||
accessContext
|
||||
);
|
||||
|
||||
await auditUpdate({
|
||||
@@ -95,13 +96,19 @@ export async function DELETE(_request: NextRequest, { params }: Params) {
|
||||
const { organization, session, membership } = await requireOrganizationAccess({
|
||||
permission: PERMISSIONS.crmEnquiryFollowupDelete
|
||||
});
|
||||
const accessContext = {
|
||||
organizationId: organization.id,
|
||||
userId: session.user.id,
|
||||
membershipRole: membership.role,
|
||||
businessRole: membership.businessRole,
|
||||
branchScopeIds: membership.branchScopeIds ?? [],
|
||||
productTypeScopeIds: membership.productTypeScopeIds ?? [],
|
||||
ownershipScope: membership.ownershipScope ?? 'organization',
|
||||
branchScopeMode: membership.branchScopeMode === 'assigned' ? 'assigned' : 'all',
|
||||
productScopeMode: membership.productScopeMode === 'assigned' ? 'assigned' : 'all'
|
||||
};
|
||||
const before = (
|
||||
await listEnquiryFollowups(id, organization.id, {
|
||||
organizationId: organization.id,
|
||||
userId: session.user.id,
|
||||
membershipRole: membership.role,
|
||||
businessRole: membership.businessRole
|
||||
})
|
||||
await listEnquiryFollowups(id, organization.id, accessContext)
|
||||
).find(
|
||||
(item) => item.id === followupId
|
||||
);
|
||||
@@ -115,12 +122,7 @@ export async function DELETE(_request: NextRequest, { params }: Params) {
|
||||
followupId,
|
||||
organization.id,
|
||||
session.user.id,
|
||||
{
|
||||
organizationId: organization.id,
|
||||
userId: session.user.id,
|
||||
membershipRole: membership.role,
|
||||
businessRole: membership.businessRole
|
||||
}
|
||||
accessContext
|
||||
);
|
||||
|
||||
await auditDelete({
|
||||
|
||||
@@ -24,12 +24,18 @@ export async function GET(_request: NextRequest, { params }: Params) {
|
||||
const { organization, session, membership } = await requireOrganizationAccess({
|
||||
permission: PERMISSIONS.crmEnquiryFollowupRead
|
||||
});
|
||||
const items = await listEnquiryFollowups(id, organization.id, {
|
||||
const accessContext = {
|
||||
organizationId: organization.id,
|
||||
userId: session.user.id,
|
||||
membershipRole: membership.role,
|
||||
businessRole: membership.businessRole
|
||||
});
|
||||
businessRole: membership.businessRole,
|
||||
branchScopeIds: membership.branchScopeIds ?? [],
|
||||
productTypeScopeIds: membership.productTypeScopeIds ?? [],
|
||||
ownershipScope: membership.ownershipScope ?? 'organization',
|
||||
branchScopeMode: membership.branchScopeMode === 'assigned' ? 'assigned' : 'all',
|
||||
productScopeMode: membership.productScopeMode === 'assigned' ? 'assigned' : 'all'
|
||||
};
|
||||
const items = await listEnquiryFollowups(id, organization.id, accessContext);
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
@@ -57,17 +63,23 @@ export async function POST(request: NextRequest, { params }: Params) {
|
||||
permission: PERMISSIONS.crmEnquiryFollowupCreate
|
||||
});
|
||||
const payload = followupRequestSchema.parse(await request.json());
|
||||
const accessContext = {
|
||||
organizationId: organization.id,
|
||||
userId: session.user.id,
|
||||
membershipRole: membership.role,
|
||||
businessRole: membership.businessRole,
|
||||
branchScopeIds: membership.branchScopeIds ?? [],
|
||||
productTypeScopeIds: membership.productTypeScopeIds ?? [],
|
||||
ownershipScope: membership.ownershipScope ?? 'organization',
|
||||
branchScopeMode: membership.branchScopeMode === 'assigned' ? 'assigned' : 'all',
|
||||
productScopeMode: membership.productScopeMode === 'assigned' ? 'assigned' : 'all'
|
||||
};
|
||||
const created = await createEnquiryFollowup(
|
||||
id,
|
||||
organization.id,
|
||||
session.user.id,
|
||||
payload,
|
||||
{
|
||||
organizationId: organization.id,
|
||||
userId: session.user.id,
|
||||
membershipRole: membership.role,
|
||||
businessRole: membership.businessRole
|
||||
}
|
||||
accessContext
|
||||
);
|
||||
|
||||
await auditCreate({
|
||||
|
||||
@@ -17,13 +17,25 @@ export async function POST(request: NextRequest, { params }: Params) {
|
||||
permission: PERMISSIONS.crmEnquiryReassign
|
||||
});
|
||||
const payload = enquiryAssignmentRequestSchema.parse(await request.json());
|
||||
const before = await getEnquiryDetail(id, organization.id, {
|
||||
const accessContext = {
|
||||
organizationId: organization.id,
|
||||
userId: session.user.id,
|
||||
membershipRole: membership.role,
|
||||
businessRole: membership.businessRole
|
||||
});
|
||||
const updated = await reassignEnquiryToUser(id, organization.id, session.user.id, payload);
|
||||
businessRole: membership.businessRole,
|
||||
branchScopeIds: membership.branchScopeIds ?? [],
|
||||
productTypeScopeIds: membership.productTypeScopeIds ?? [],
|
||||
ownershipScope: membership.ownershipScope ?? 'organization',
|
||||
branchScopeMode: membership.branchScopeMode === 'assigned' ? 'assigned' : 'all',
|
||||
productScopeMode: membership.productScopeMode === 'assigned' ? 'assigned' : 'all'
|
||||
};
|
||||
const before = await getEnquiryDetail(id, organization.id, accessContext);
|
||||
const updated = await reassignEnquiryToUser(
|
||||
id,
|
||||
organization.id,
|
||||
session.user.id,
|
||||
payload,
|
||||
accessContext
|
||||
);
|
||||
|
||||
await auditAction({
|
||||
organizationId: organization.id,
|
||||
|
||||
@@ -32,7 +32,12 @@ export async function GET(_request: NextRequest, { params }: Params) {
|
||||
organizationId: organization.id,
|
||||
userId: session.user.id,
|
||||
membershipRole: membership.role,
|
||||
businessRole: membership.businessRole
|
||||
businessRole: membership.businessRole,
|
||||
branchScopeIds: membership.branchScopeIds ?? [],
|
||||
productTypeScopeIds: membership.productTypeScopeIds ?? [],
|
||||
ownershipScope: membership.ownershipScope ?? 'organization',
|
||||
branchScopeMode: membership.branchScopeMode === 'assigned' ? 'assigned' : 'all',
|
||||
productScopeMode: membership.productScopeMode === 'assigned' ? 'assigned' : 'all'
|
||||
};
|
||||
const [enquiry, activity] = await Promise.all([
|
||||
getEnquiryDetail(id, organization.id, accessContext),
|
||||
@@ -66,18 +71,19 @@ export async function PATCH(request: NextRequest, { params }: Params) {
|
||||
permission: PERMISSIONS.crmEnquiryUpdate
|
||||
});
|
||||
const payload = enquiryRequestSchema.parse(await request.json());
|
||||
const before = await getEnquiryDetail(id, organization.id, {
|
||||
const accessContext = {
|
||||
organizationId: organization.id,
|
||||
userId: session.user.id,
|
||||
membershipRole: membership.role,
|
||||
businessRole: membership.businessRole
|
||||
});
|
||||
const updated = await updateEnquiry(id, organization.id, session.user.id, payload, {
|
||||
organizationId: organization.id,
|
||||
userId: session.user.id,
|
||||
membershipRole: membership.role,
|
||||
businessRole: membership.businessRole
|
||||
});
|
||||
businessRole: membership.businessRole,
|
||||
branchScopeIds: membership.branchScopeIds ?? [],
|
||||
productTypeScopeIds: membership.productTypeScopeIds ?? [],
|
||||
ownershipScope: membership.ownershipScope ?? 'organization',
|
||||
branchScopeMode: membership.branchScopeMode === 'assigned' ? 'assigned' : 'all',
|
||||
productScopeMode: membership.productScopeMode === 'assigned' ? 'assigned' : 'all'
|
||||
};
|
||||
const before = await getEnquiryDetail(id, organization.id, accessContext);
|
||||
const updated = await updateEnquiry(id, organization.id, session.user.id, payload, accessContext);
|
||||
|
||||
await auditUpdate({
|
||||
organizationId: organization.id,
|
||||
@@ -120,18 +126,19 @@ export async function DELETE(_request: NextRequest, { params }: Params) {
|
||||
const { organization, session, membership } = await requireOrganizationAccess({
|
||||
permission: PERMISSIONS.crmEnquiryDelete
|
||||
});
|
||||
const before = await getEnquiryDetail(id, organization.id, {
|
||||
const accessContext = {
|
||||
organizationId: organization.id,
|
||||
userId: session.user.id,
|
||||
membershipRole: membership.role,
|
||||
businessRole: membership.businessRole
|
||||
});
|
||||
const updated = await softDeleteEnquiry(id, organization.id, session.user.id, {
|
||||
organizationId: organization.id,
|
||||
userId: session.user.id,
|
||||
membershipRole: membership.role,
|
||||
businessRole: membership.businessRole
|
||||
});
|
||||
businessRole: membership.businessRole,
|
||||
branchScopeIds: membership.branchScopeIds ?? [],
|
||||
productTypeScopeIds: membership.productTypeScopeIds ?? [],
|
||||
ownershipScope: membership.ownershipScope ?? 'organization',
|
||||
branchScopeMode: membership.branchScopeMode === 'assigned' ? 'assigned' : 'all',
|
||||
productScopeMode: membership.productScopeMode === 'assigned' ? 'assigned' : 'all'
|
||||
};
|
||||
const before = await getEnquiryDetail(id, organization.id, accessContext);
|
||||
const updated = await softDeleteEnquiry(id, organization.id, session.user.id, accessContext);
|
||||
|
||||
await auditDelete({
|
||||
organizationId: organization.id,
|
||||
|
||||
@@ -29,13 +29,19 @@ export async function GET(request: NextRequest) {
|
||||
const branch = searchParams.get('branch') ?? undefined;
|
||||
const customer = searchParams.get('customer') ?? undefined;
|
||||
const sort = searchParams.get('sort') ?? undefined;
|
||||
const accessContext = {
|
||||
organizationId: organization.id,
|
||||
userId: session.user.id,
|
||||
membershipRole: membership.role,
|
||||
businessRole: membership.businessRole,
|
||||
branchScopeIds: membership.branchScopeIds ?? [],
|
||||
productTypeScopeIds: membership.productTypeScopeIds ?? [],
|
||||
ownershipScope: membership.ownershipScope ?? 'organization',
|
||||
branchScopeMode: membership.branchScopeMode === 'assigned' ? 'assigned' : 'all',
|
||||
productScopeMode: membership.productScopeMode === 'assigned' ? 'assigned' : 'all'
|
||||
};
|
||||
const result = await listEnquiries(
|
||||
{
|
||||
organizationId: organization.id,
|
||||
userId: session.user.id,
|
||||
membershipRole: membership.role,
|
||||
businessRole: membership.businessRole
|
||||
},
|
||||
accessContext,
|
||||
{
|
||||
page,
|
||||
limit,
|
||||
@@ -79,10 +85,21 @@ export async function POST(request: NextRequest) {
|
||||
permission: PERMISSIONS.crmEnquiryCreate
|
||||
});
|
||||
const payload = enquiryRequestSchema.parse(await request.json());
|
||||
const accessContext = {
|
||||
organizationId: organization.id,
|
||||
userId: session.user.id,
|
||||
membershipRole: membership.role,
|
||||
businessRole: membership.businessRole,
|
||||
branchScopeIds: membership.branchScopeIds ?? [],
|
||||
productTypeScopeIds: membership.productTypeScopeIds ?? [],
|
||||
ownershipScope: membership.ownershipScope ?? 'organization',
|
||||
branchScopeMode: membership.branchScopeMode === 'assigned' ? 'assigned' : 'all',
|
||||
productScopeMode: membership.productScopeMode === 'assigned' ? 'assigned' : 'all'
|
||||
};
|
||||
const created = await createEnquiry(
|
||||
organization.id,
|
||||
session.user.id,
|
||||
membership.businessRole,
|
||||
accessContext,
|
||||
payload
|
||||
);
|
||||
|
||||
|
||||
45
src/app/api/crm/settings/roles/[code]/clone/route.ts
Normal file
45
src/app/api/crm/settings/roles/[code]/clone/route.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { z } from 'zod';
|
||||
import { cloneCrmRoleProfile } from '@/features/foundation/role-management/server/service';
|
||||
import { PERMISSIONS } from '@/lib/auth/rbac';
|
||||
import { AuthError, requireOrganizationAccess } from '@/lib/auth/session';
|
||||
|
||||
const cloneRoleSchema = z.object({
|
||||
code: z.string().min(2),
|
||||
name: z.string().min(2),
|
||||
description: z.string().nullable().optional()
|
||||
});
|
||||
|
||||
type RouteProps = {
|
||||
params: Promise<{ code: string }>;
|
||||
};
|
||||
|
||||
export async function POST(request: NextRequest, props: RouteProps) {
|
||||
try {
|
||||
const { organization, session } = await requireOrganizationAccess({
|
||||
permission: PERMISSIONS.crmRoleCreate
|
||||
});
|
||||
const payload = cloneRoleSchema.parse(await request.json());
|
||||
const { code } = await props.params;
|
||||
const role = await cloneCrmRoleProfile(organization.id, session.user.id, code, payload);
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: true,
|
||||
message: 'CRM role cloned successfully',
|
||||
role
|
||||
},
|
||||
{ status: 201 }
|
||||
);
|
||||
} catch (error) {
|
||||
if (error instanceof AuthError) {
|
||||
return NextResponse.json({ message: error.message }, { status: error.status });
|
||||
}
|
||||
|
||||
if (error instanceof z.ZodError) {
|
||||
return NextResponse.json({ message: error.issues[0]?.message ?? 'Invalid payload' }, { status: 400 });
|
||||
}
|
||||
|
||||
return NextResponse.json({ message: 'Unable to clone CRM role' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
60
src/app/api/crm/settings/roles/[code]/route.ts
Normal file
60
src/app/api/crm/settings/roles/[code]/route.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { z } from 'zod';
|
||||
import { updateCrmRoleProfile } from '@/features/foundation/role-management/server/service';
|
||||
import {
|
||||
APPROVAL_AUTHORITIES,
|
||||
ALL_PERMISSIONS,
|
||||
OWNERSHIP_SCOPES,
|
||||
PERMISSIONS,
|
||||
SCOPE_MODES,
|
||||
type Permission
|
||||
} from '@/lib/auth/rbac';
|
||||
import { AuthError, requireOrganizationAccess } from '@/lib/auth/session';
|
||||
|
||||
const updateRoleSchema = z.object({
|
||||
name: z.string().min(2),
|
||||
description: z.string().nullable().optional(),
|
||||
permissions: z
|
||||
.array(z.string())
|
||||
.refine(
|
||||
(values) => values.every((value) => ALL_PERMISSIONS.includes(value as Permission)),
|
||||
'Invalid permission key'
|
||||
)
|
||||
.transform((values) => values as Permission[]),
|
||||
branchScopeMode: z.enum(SCOPE_MODES),
|
||||
productScopeMode: z.enum(SCOPE_MODES),
|
||||
ownershipScope: z.enum(OWNERSHIP_SCOPES),
|
||||
approvalAuthority: z.enum(APPROVAL_AUTHORITIES),
|
||||
isActive: z.boolean()
|
||||
});
|
||||
|
||||
type RouteProps = {
|
||||
params: Promise<{ code: string }>;
|
||||
};
|
||||
|
||||
export async function PATCH(request: NextRequest, props: RouteProps) {
|
||||
try {
|
||||
const { organization, session } = await requireOrganizationAccess({
|
||||
permission: PERMISSIONS.crmRoleUpdate
|
||||
});
|
||||
const payload = updateRoleSchema.parse(await request.json());
|
||||
const { code } = await props.params;
|
||||
const role = await updateCrmRoleProfile(organization.id, session.user.id, code, payload);
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: 'CRM role updated successfully',
|
||||
role
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof AuthError) {
|
||||
return NextResponse.json({ message: error.message }, { status: error.status });
|
||||
}
|
||||
|
||||
if (error instanceof z.ZodError) {
|
||||
return NextResponse.json({ message: error.issues[0]?.message ?? 'Invalid payload' }, { status: 400 });
|
||||
}
|
||||
|
||||
return NextResponse.json({ message: 'Unable to update CRM role' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
31
src/app/api/crm/settings/roles/route.ts
Normal file
31
src/app/api/crm/settings/roles/route.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { z } from 'zod';
|
||||
import { listCrmRoleProfiles } from '@/features/foundation/role-management/server/service';
|
||||
import { PERMISSIONS } from '@/lib/auth/rbac';
|
||||
import { AuthError, requireOrganizationAccess } from '@/lib/auth/session';
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
const { organization, session } = await requireOrganizationAccess({
|
||||
permission: PERMISSIONS.crmRoleRead
|
||||
});
|
||||
const result = await listCrmRoleProfiles(organization.id, session.user.id);
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
time: new Date().toISOString(),
|
||||
message: 'CRM roles loaded successfully',
|
||||
...result
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof AuthError) {
|
||||
return NextResponse.json({ message: error.message }, { status: error.status });
|
||||
}
|
||||
|
||||
if (error instanceof z.ZodError) {
|
||||
return NextResponse.json({ message: error.issues[0]?.message ?? 'Invalid request' }, { status: 400 });
|
||||
}
|
||||
|
||||
return NextResponse.json({ message: 'Unable to load CRM roles' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user