Phone formatting works, start card moved, registration styling changed
This commit is contained in:
@@ -47,6 +47,15 @@ const DynamicFormField = ({
|
|||||||
const hasError = errors.length > 0;
|
const hasError = errors.length > 0;
|
||||||
const errorMessage = errors[0];
|
const errorMessage = errors[0];
|
||||||
|
|
||||||
|
const formatPhoneNumber = (rawValue) => {
|
||||||
|
const digits = String(rawValue || '').replace(/\D/g, '').slice(0, 10);
|
||||||
|
if (digits.length <= 3) return digits;
|
||||||
|
if (digits.length <= 6) {
|
||||||
|
return `(${digits.slice(0, 3)}) ${digits.slice(3)}`;
|
||||||
|
}
|
||||||
|
return `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6)}`;
|
||||||
|
};
|
||||||
|
|
||||||
// Common input className
|
// Common input className
|
||||||
const inputClassName = `h-14 rounded-xl border-2 ${
|
const inputClassName = `h-14 rounded-xl border-2 ${
|
||||||
hasError
|
hasError
|
||||||
@@ -59,6 +68,11 @@ const DynamicFormField = ({
|
|||||||
const { value: newValue, type: inputType, checked } = e.target;
|
const { value: newValue, type: inputType, checked } = e.target;
|
||||||
if (inputType === 'checkbox') {
|
if (inputType === 'checkbox') {
|
||||||
onChange(id, checked);
|
onChange(id, checked);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (type === 'phone') {
|
||||||
|
onChange(id, formatPhoneNumber(newValue));
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
onChange(id, newValue);
|
onChange(id, newValue);
|
||||||
}
|
}
|
||||||
@@ -111,6 +125,8 @@ const DynamicFormField = ({
|
|||||||
value={value || ''}
|
value={value || ''}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
|
inputMode={type === 'phone' ? 'numeric' : undefined}
|
||||||
|
maxLength={type === 'phone' ? 14 : undefined}
|
||||||
className={inputClassName}
|
className={inputClassName}
|
||||||
data-testid={`field-${id}`}
|
data-testid={`field-${id}`}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ import {
|
|||||||
Zap,
|
Zap,
|
||||||
Copy,
|
Copy,
|
||||||
X,
|
X,
|
||||||
|
Grip
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
|
||||||
// Field type icons
|
// Field type icons
|
||||||
@@ -319,11 +320,11 @@ const AdminRegistrationBuilder = () => {
|
|||||||
steps: prev.steps.map((s) =>
|
steps: prev.steps.map((s) =>
|
||||||
s.id === selectedStep
|
s.id === selectedStep
|
||||||
? {
|
? {
|
||||||
...s,
|
...s,
|
||||||
sections: s.sections
|
sections: s.sections
|
||||||
.filter((sec) => sec.id !== sectionId)
|
.filter((sec) => sec.id !== sectionId)
|
||||||
.map((sec, idx) => ({ ...sec, order: idx + 1 })),
|
.map((sec, idx) => ({ ...sec, order: idx + 1 })),
|
||||||
}
|
}
|
||||||
: s
|
: s
|
||||||
),
|
),
|
||||||
}));
|
}));
|
||||||
@@ -361,13 +362,13 @@ const AdminRegistrationBuilder = () => {
|
|||||||
steps: prev.steps.map((s) =>
|
steps: prev.steps.map((s) =>
|
||||||
s.id === selectedStep
|
s.id === selectedStep
|
||||||
? {
|
? {
|
||||||
...s,
|
...s,
|
||||||
sections: s.sections.map((sec) =>
|
sections: s.sections.map((sec) =>
|
||||||
sec.id === selectedSection
|
sec.id === selectedSection
|
||||||
? { ...sec, fields: [...(sec.fields || []), newField] }
|
? { ...sec, fields: [...(sec.fields || []), newField] }
|
||||||
: sec
|
: sec
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
: s
|
: s
|
||||||
),
|
),
|
||||||
}));
|
}));
|
||||||
@@ -395,18 +396,18 @@ const AdminRegistrationBuilder = () => {
|
|||||||
steps: prev.steps.map((s) =>
|
steps: prev.steps.map((s) =>
|
||||||
s.id === selectedStep
|
s.id === selectedStep
|
||||||
? {
|
? {
|
||||||
...s,
|
...s,
|
||||||
sections: s.sections.map((sec) =>
|
sections: s.sections.map((sec) =>
|
||||||
sec.id === selectedSection
|
sec.id === selectedSection
|
||||||
? {
|
? {
|
||||||
...sec,
|
...sec,
|
||||||
fields: sec.fields
|
fields: sec.fields
|
||||||
.filter((f) => f.id !== fieldId)
|
.filter((f) => f.id !== fieldId)
|
||||||
.map((f, idx) => ({ ...f, order: idx + 1 })),
|
.map((f, idx) => ({ ...f, order: idx + 1 })),
|
||||||
}
|
}
|
||||||
: sec
|
: sec
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
: s
|
: s
|
||||||
),
|
),
|
||||||
}));
|
}));
|
||||||
@@ -423,18 +424,18 @@ const AdminRegistrationBuilder = () => {
|
|||||||
steps: prev.steps.map((s) =>
|
steps: prev.steps.map((s) =>
|
||||||
s.id === selectedStep
|
s.id === selectedStep
|
||||||
? {
|
? {
|
||||||
...s,
|
...s,
|
||||||
sections: s.sections.map((sec) =>
|
sections: s.sections.map((sec) =>
|
||||||
sec.id === selectedSection
|
sec.id === selectedSection
|
||||||
? {
|
? {
|
||||||
...sec,
|
...sec,
|
||||||
fields: sec.fields.map((f) =>
|
fields: sec.fields.map((f) =>
|
||||||
f.id === fieldId ? { ...f, ...updates } : f
|
f.id === fieldId ? { ...f, ...updates } : f
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
: sec
|
: sec
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
: s
|
: s
|
||||||
),
|
),
|
||||||
}));
|
}));
|
||||||
@@ -492,132 +493,137 @@ const AdminRegistrationBuilder = () => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Main Builder Layout */}
|
{/* Main Builder Layout */}
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-6">
|
{/* Left Sidebar - Steps & Sections */}
|
||||||
{/* Left Sidebar - Steps & Sections */}
|
<div className="lg:col-span-3">
|
||||||
<div className="lg:col-span-3">
|
<Card className="p-4">
|
||||||
<Card className="p-4">
|
<div className="flex justify-between items-center mb-4">
|
||||||
<div className="flex justify-between items-center mb-4">
|
<h2 className="text-lg font-semibold">Steps</h2>
|
||||||
<h2 className="text-lg font-semibold">Steps</h2>
|
<Button size="sm" variant="ghost" onClick={() => setAddStepDialogOpen(true)}>
|
||||||
<Button size="sm" variant="ghost" onClick={() => setAddStepDialogOpen(true)}>
|
<Plus className="h-4 w-4" />
|
||||||
<Plus className="h-4 w-4" />
|
</Button>
|
||||||
</Button>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="flex space-y-2">
|
||||||
{sortedSteps.map((step, index) => (
|
{sortedSteps.map((step, index) => (
|
||||||
<div
|
<div
|
||||||
key={step.id}
|
key={step.id}
|
||||||
className={`p-3 rounded-lg border cursor-pointer transition-colors ${
|
className={`p-3 rounded-t-lg border cursor-pointer transition-colors ${selectedStep === step.id
|
||||||
selectedStep === step.id
|
? ' bg-brand-lavender/10 border-b-4 border-b-brand-dark-lavender'
|
||||||
|
: ''
|
||||||
|
}`}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedStep(step.id);
|
||||||
|
setSelectedSection(null);
|
||||||
|
setSelectedField(null);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="font-medium">Step: </div>
|
||||||
|
<span className="font-medium text-sm">{step.title}</span>
|
||||||
|
</div>
|
||||||
|
{/* Mod Buttons */}
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
{/* <Button
|
||||||
|
size="icon"
|
||||||
|
variant="ghost"
|
||||||
|
className="h-6 w-6"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleMoveStep(step.id, 'up');
|
||||||
|
}}
|
||||||
|
disabled={index === 0}
|
||||||
|
>
|
||||||
|
<ChevronUp className="h-3 w-3" />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="icon"
|
||||||
|
variant="ghost"
|
||||||
|
className="h-6 w-6"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleMoveStep(step.id, 'down');
|
||||||
|
}}
|
||||||
|
disabled={index === sortedSteps.length - 1}
|
||||||
|
>
|
||||||
|
<ChevronDown className="h-3 w-3" />
|
||||||
|
</Button> */}
|
||||||
|
<Button
|
||||||
|
size="icon"
|
||||||
|
variant="ghost"
|
||||||
|
className="h-6 w-6 text-red-500 hover:text-white ml-2"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleDeleteStep(step.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Trash2 className="size-3" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sections for selected step */}
|
||||||
|
{currentStep && (
|
||||||
|
<>
|
||||||
|
<div className="flex justify-between items-center mt-6 mb-4">
|
||||||
|
<h2 className="text-lg font-semibold">Sections</h2>
|
||||||
|
<Button size="sm" variant="ghost" onClick={() => setAddSectionDialogOpen(true)}>
|
||||||
|
<Plus className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
{sortedSections.map((section) => (
|
||||||
|
<div
|
||||||
|
key={section.id}
|
||||||
|
className={`p-3 rounded-lg border cursor-pointer transition-colors ${selectedSection === section.id
|
||||||
? 'border-brand-purple bg-brand-lavender/10'
|
? 'border-brand-purple bg-brand-lavender/10'
|
||||||
: 'border-gray-200 hover:border-gray-300'
|
: 'border-gray-200 hover:border-gray-300'
|
||||||
}`}
|
}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedStep(step.id);
|
setSelectedSection(section.id);
|
||||||
setSelectedSection(null);
|
setSelectedField(null);
|
||||||
setSelectedField(null);
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex items-center justify-between">
|
<span className="text-sm">{section.title}</span>
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<GripVertical className="h-4 w-4 text-gray-400" />
|
|
||||||
<span className="font-medium text-sm">{step.title}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
<Button
|
|
||||||
size="icon"
|
|
||||||
variant="ghost"
|
|
||||||
className="h-6 w-6"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
handleMoveStep(step.id, 'up');
|
|
||||||
}}
|
|
||||||
disabled={index === 0}
|
|
||||||
>
|
|
||||||
<ChevronUp className="h-3 w-3" />
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
size="icon"
|
|
||||||
variant="ghost"
|
|
||||||
className="h-6 w-6"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
handleMoveStep(step.id, 'down');
|
|
||||||
}}
|
|
||||||
disabled={index === sortedSteps.length - 1}
|
|
||||||
>
|
|
||||||
<ChevronDown className="h-3 w-3" />
|
|
||||||
</Button>
|
|
||||||
<Button
|
<Button
|
||||||
size="icon"
|
size="icon"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
className="h-6 w-6 text-red-500 hover:text-red-700"
|
className="h-6 w-6 text-red-500 hover:text-red-700"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
handleDeleteStep(step.id);
|
handleDeleteSection(section.id);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Trash2 className="h-3 w-3" />
|
<Trash2 className="h-3 w-3" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
))}
|
||||||
))}
|
</div>
|
||||||
</div>
|
</>
|
||||||
|
)}
|
||||||
{/* Sections for selected step */}
|
</Card>
|
||||||
{currentStep && (
|
</div>
|
||||||
<>
|
<div className="grid grid-cols-1 lg:grid-cols-12 gap-6">
|
||||||
<div className="flex justify-between items-center mt-6 mb-4">
|
|
||||||
<h2 className="text-lg font-semibold">Sections</h2>
|
|
||||||
<Button size="sm" variant="ghost" onClick={() => setAddSectionDialogOpen(true)}>
|
|
||||||
<Plus className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
{sortedSections.map((section) => (
|
|
||||||
<div
|
|
||||||
key={section.id}
|
|
||||||
className={`p-3 rounded-lg border cursor-pointer transition-colors ${
|
|
||||||
selectedSection === section.id
|
|
||||||
? 'border-brand-purple bg-brand-lavender/10'
|
|
||||||
: 'border-gray-200 hover:border-gray-300'
|
|
||||||
}`}
|
|
||||||
onClick={() => {
|
|
||||||
setSelectedSection(section.id);
|
|
||||||
setSelectedField(null);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<span className="text-sm">{section.title}</span>
|
|
||||||
<Button
|
|
||||||
size="icon"
|
|
||||||
variant="ghost"
|
|
||||||
className="h-6 w-6 text-red-500 hover:text-red-700"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
handleDeleteSection(section.id);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Trash2 className="h-3 w-3" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Center - Form Canvas */}
|
{/* Center - Form Canvas */}
|
||||||
<div className="lg:col-span-6">
|
<div className="lg:col-span-9">
|
||||||
|
|
||||||
<Card className="p-6">
|
<Card className="p-6">
|
||||||
|
|
||||||
<div className="flex justify-between items-center mb-6">
|
<div className="flex justify-between items-center mb-6">
|
||||||
<h2 className="text-xl font-semibold">
|
<div className='relative -mx-11 -my-2 flex gap-2 items-center'>
|
||||||
{currentStep?.title || 'Select a step'}
|
<Grip className="size-10 text-gray-400 py-2 bg-background" />
|
||||||
</h2>
|
|
||||||
|
<h2 className="text-xl font-semibold">
|
||||||
|
{currentStep?.title || 'Select a step'}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
{selectedSection && (
|
{selectedSection && (
|
||||||
<Button size="sm" onClick={() => setAddFieldDialogOpen(true)}>
|
<Button size="sm" onClick={() => setAddFieldDialogOpen(true)}>
|
||||||
<Plus className="h-4 w-4 mr-2" />
|
<Plus className="h-4 w-4 mr-2" />
|
||||||
@@ -637,11 +643,10 @@ const AdminRegistrationBuilder = () => {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={section.id}
|
key={section.id}
|
||||||
className={`mb-6 p-4 rounded-lg border-2 ${
|
className={`mb-6 p-4 rounded-lg border-2 ${selectedSection === section.id
|
||||||
selectedSection === section.id
|
? 'border-brand-purple'
|
||||||
? 'border-brand-purple'
|
: 'border-dashed border-gray-200'
|
||||||
: 'border-dashed border-gray-200'
|
}`}
|
||||||
}`}
|
|
||||||
onClick={() => setSelectedSection(section.id)}
|
onClick={() => setSelectedSection(section.id)}
|
||||||
>
|
>
|
||||||
<h3 className="text-lg font-medium mb-4">{section.title}</h3>
|
<h3 className="text-lg font-medium mb-4">{section.title}</h3>
|
||||||
@@ -656,11 +661,10 @@ const AdminRegistrationBuilder = () => {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={field.id}
|
key={field.id}
|
||||||
className={`p-3 rounded-lg border cursor-pointer transition-all ${
|
className={`p-3 rounded-lg border cursor-pointer transition-all ${selectedField === field.id
|
||||||
selectedField === field.id
|
? 'border-brand-purple bg-brand-lavender/5 ring-2 ring-brand-purple/20'
|
||||||
? 'border-brand-purple bg-brand-lavender/5 ring-2 ring-brand-purple/20'
|
: 'border-gray-200 hover:border-gray-300'
|
||||||
: 'border-gray-200 hover:border-gray-300'
|
} ${field.is_fixed ? 'bg-gray-50' : ''}`}
|
||||||
} ${field.is_fixed ? 'bg-gray-50' : ''}`}
|
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setSelectedSection(section.id);
|
setSelectedSection(section.id);
|
||||||
@@ -719,23 +723,7 @@ const AdminRegistrationBuilder = () => {
|
|||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* Conditional Rules */}
|
|
||||||
<Card className="p-6 mt-6">
|
|
||||||
<div className="flex justify-between items-center mb-4">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Zap className="h-5 w-5 text-yellow-500" />
|
|
||||||
<h2 className="text-lg font-semibold">Conditional Rules</h2>
|
|
||||||
</div>
|
|
||||||
<Button size="sm" variant="outline" onClick={() => setConditionalDialogOpen(true)}>
|
|
||||||
<Settings className="h-4 w-4 mr-2" />
|
|
||||||
Manage Rules
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="text-sm text-muted-foreground">
|
|
||||||
{schema?.conditional_rules?.length || 0} conditional rule(s) configured
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right Sidebar - Field Properties */}
|
{/* Right Sidebar - Field Properties */}
|
||||||
@@ -912,6 +900,23 @@ const AdminRegistrationBuilder = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
|
{/* Conditional Rules */}
|
||||||
|
<Card className="p-6 mt-6">
|
||||||
|
<div className="flex flex-col justify-between items-center mb-4">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Zap className="h-5 w-5 text-yellow-500" />
|
||||||
|
<h2 className="text-lg font-semibold">Conditional Rules</h2>
|
||||||
|
</div>
|
||||||
|
<Button size="sm" variant="outline" onClick={() => setConditionalDialogOpen(true)}>
|
||||||
|
<Settings className="h-4 w-4 mr-2" />
|
||||||
|
Manage Rules
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-sm text-muted-foreground">
|
||||||
|
{schema?.conditional_rules?.length || 0} conditional rule(s) configured
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1120,13 +1125,12 @@ const AdminRegistrationBuilder = () => {
|
|||||||
.map((field) => (
|
.map((field) => (
|
||||||
<div
|
<div
|
||||||
key={field.id}
|
key={field.id}
|
||||||
className={`${
|
className={`${field.width === 'full'
|
||||||
field.width === 'full'
|
? 'col-span-2'
|
||||||
? 'col-span-2'
|
: field.width === 'third'
|
||||||
: field.width === 'third'
|
|
||||||
? 'col-span-1'
|
? 'col-span-1'
|
||||||
: 'col-span-1'
|
: 'col-span-1'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Label>
|
<Label>
|
||||||
{field.label}
|
{field.label}
|
||||||
|
|||||||
@@ -354,13 +354,7 @@ const AdminValidations = () => {
|
|||||||
Quick Overview
|
Quick Overview
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-2 md:grid-cols-5 gap-4">
|
<div className="grid grid-cols-2 md:grid-cols-5 gap-4">
|
||||||
<StatCard
|
|
||||||
title="Total Pending"
|
|
||||||
value={loading ? '-' : pendingUsers.length}
|
|
||||||
icon={Users}
|
|
||||||
iconBgClass="text-brand-purple"
|
|
||||||
dataTestId="stat-total-users"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<StatCard
|
<StatCard
|
||||||
title="Awaiting Email"
|
title="Awaiting Email"
|
||||||
@@ -394,7 +388,13 @@ const AdminValidations = () => {
|
|||||||
dataTestId="stat-rejected"
|
dataTestId="stat-rejected"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<StatCard
|
||||||
|
title="Total Pending"
|
||||||
|
value={loading ? '-' : pendingUsers.length}
|
||||||
|
icon={Users}
|
||||||
|
iconBgClass="text-brand-purple"
|
||||||
|
dataTestId="stat-total-users"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user