feat: Add isSystem property to profile sections and isSearchableOnly to profile fields, including UI updates for display and management.
This commit is contained in:
@@ -55,6 +55,7 @@ export default function SectionDetailPage() {
|
|||||||
sortOrder: 0,
|
sortOrder: 0,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
isRequired: false,
|
isRequired: false,
|
||||||
|
isSearchableOnly: false,
|
||||||
options: [],
|
options: [],
|
||||||
rangeConfig: undefined,
|
rangeConfig: undefined,
|
||||||
});
|
});
|
||||||
@@ -110,6 +111,7 @@ export default function SectionDetailPage() {
|
|||||||
sortOrder: fields.length,
|
sortOrder: fields.length,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
isRequired: false,
|
isRequired: false,
|
||||||
|
isSearchableOnly: false,
|
||||||
options: [],
|
options: [],
|
||||||
rangeConfig: undefined,
|
rangeConfig: undefined,
|
||||||
});
|
});
|
||||||
@@ -130,6 +132,7 @@ export default function SectionDetailPage() {
|
|||||||
sortOrder: field.sortOrder,
|
sortOrder: field.sortOrder,
|
||||||
isActive: field.isActive,
|
isActive: field.isActive,
|
||||||
isRequired: field.isRequired,
|
isRequired: field.isRequired,
|
||||||
|
isSearchableOnly: field.isSearchableOnly,
|
||||||
options: field.options || [],
|
options: field.options || [],
|
||||||
rangeConfig: field.rangeConfig || undefined,
|
rangeConfig: field.rangeConfig || undefined,
|
||||||
});
|
});
|
||||||
@@ -322,6 +325,11 @@ export default function SectionDetailPage() {
|
|||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
{section.icon && <span className="text-2xl mr-2">{section.icon}</span>}
|
{section.icon && <span className="text-2xl mr-2">{section.icon}</span>}
|
||||||
<h1 className="text-2xl font-bold text-gray-900">{section.name}</h1>
|
<h1 className="text-2xl font-bold text-gray-900">{section.name}</h1>
|
||||||
|
{section.isSystem && (
|
||||||
|
<span className="ml-3 px-2 py-1 text-xs font-semibold rounded-full bg-amber-100 text-amber-800">
|
||||||
|
System
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
{section.isGlobal && (
|
{section.isGlobal && (
|
||||||
<span className="ml-3 px-2 py-1 text-xs font-semibold rounded-full bg-purple-100 text-purple-800">
|
<span className="ml-3 px-2 py-1 text-xs font-semibold rounded-full bg-purple-100 text-purple-800">
|
||||||
Global
|
Global
|
||||||
@@ -382,11 +390,16 @@ export default function SectionDetailPage() {
|
|||||||
<div key={field.id} className="px-6 py-4 hover:bg-gray-50">
|
<div key={field.id} className="px-6 py-4 hover:bg-gray-50">
|
||||||
<div className="flex justify-between items-start">
|
<div className="flex justify-between items-start">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center flex-wrap gap-2">
|
||||||
<span className="text-gray-400 mr-3 text-sm">#{index + 1}</span>
|
<span className="text-gray-400 mr-1 text-sm">#{index + 1}</span>
|
||||||
<span className="font-medium text-gray-900">{field.name}</span>
|
<span className="font-medium text-gray-900">{field.name}</span>
|
||||||
{field.isRequired && (
|
{field.isRequired && (
|
||||||
<span className="ml-2 text-red-500 text-xs">*required</span>
|
<span className="text-red-500 text-xs">*required</span>
|
||||||
|
)}
|
||||||
|
{field.isSearchableOnly && (
|
||||||
|
<span className="px-2 py-0.5 text-xs font-medium rounded-full bg-amber-100 text-amber-800">
|
||||||
|
Search Only
|
||||||
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-1 flex items-center space-x-3 text-sm">
|
<div className="mt-1 flex items-center space-x-3 text-sm">
|
||||||
@@ -647,6 +660,28 @@ export default function SectionDetailPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Search Only Toggle */}
|
||||||
|
<div className="border border-amber-200 rounded-lg p-4 bg-amber-50">
|
||||||
|
<div className="flex items-start justify-between">
|
||||||
|
<div className="flex-1">
|
||||||
|
<label htmlFor="fieldIsSearchableOnly" className="text-sm font-medium text-gray-700">
|
||||||
|
Search Only (Hidden from Public Profile)
|
||||||
|
</label>
|
||||||
|
<p className="text-xs text-gray-500 mt-1">
|
||||||
|
This field will be shown in the agent edit form and used for search filtering,
|
||||||
|
but will NOT be visible on the public agent profile page.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="fieldIsSearchableOnly"
|
||||||
|
checked={fieldForm.isSearchableOnly}
|
||||||
|
onChange={(e) => setFieldForm({ ...fieldForm, isSearchableOnly: e.target.checked })}
|
||||||
|
className="mt-1 rounded border-gray-300 text-amber-600 focus:ring-amber-500"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Options Editor for SELECT, RADIO, MULTI_SELECT, CHECKBOX_GROUP */}
|
{/* Options Editor for SELECT, RADIO, MULTI_SELECT, CHECKBOX_GROUP */}
|
||||||
{REQUIRES_OPTIONS.includes(fieldForm.fieldType) && (
|
{REQUIRES_OPTIONS.includes(fieldForm.fieldType) && (
|
||||||
<div className="border border-gray-200 rounded-lg p-4">
|
<div className="border border-gray-200 rounded-lg p-4">
|
||||||
|
|||||||
@@ -284,15 +284,22 @@ export default function ProfileSectionsPage() {
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-6 py-4 whitespace-nowrap">
|
<td className="px-6 py-4 whitespace-nowrap">
|
||||||
<span
|
<div className="flex items-center space-x-2">
|
||||||
className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${
|
{section.isSystem && (
|
||||||
section.isGlobal
|
<span className="inline-flex px-2 py-1 text-xs font-semibold rounded-full bg-amber-100 text-amber-800">
|
||||||
? 'bg-purple-100 text-purple-800'
|
System
|
||||||
: 'bg-gray-100 text-gray-600'
|
</span>
|
||||||
}`}
|
)}
|
||||||
>
|
<span
|
||||||
{section.isGlobal ? 'Global' : 'Specific'}
|
className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${
|
||||||
</span>
|
section.isGlobal
|
||||||
|
? 'bg-purple-100 text-purple-800'
|
||||||
|
: 'bg-gray-100 text-gray-600'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{section.isGlobal ? 'Global' : 'Specific'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-6 py-4 whitespace-nowrap">
|
<td className="px-6 py-4 whitespace-nowrap">
|
||||||
<button
|
<button
|
||||||
@@ -319,12 +326,21 @@ export default function ProfileSectionsPage() {
|
|||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</button>
|
</button>
|
||||||
<button
|
{section.isSystem ? (
|
||||||
onClick={() => openDeleteModal(section)}
|
<span
|
||||||
className="text-red-600 hover:text-red-900"
|
className="text-gray-400 cursor-not-allowed"
|
||||||
>
|
title="System sections cannot be deleted"
|
||||||
Delete
|
>
|
||||||
</button>
|
Delete
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
onClick={() => openDeleteModal(section)}
|
||||||
|
className="text-red-600 hover:text-red-900"
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ export interface ProfileField {
|
|||||||
sortOrder: number;
|
sortOrder: number;
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
isRequired: boolean;
|
isRequired: boolean;
|
||||||
|
isSearchableOnly: boolean;
|
||||||
validation: FieldValidation | null;
|
validation: FieldValidation | null;
|
||||||
options: FieldOption[] | null;
|
options: FieldOption[] | null;
|
||||||
rangeConfig: RangeConfig | null;
|
rangeConfig: RangeConfig | null;
|
||||||
@@ -90,6 +91,7 @@ export interface CreateFieldDto {
|
|||||||
sortOrder?: number;
|
sortOrder?: number;
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
isRequired?: boolean;
|
isRequired?: boolean;
|
||||||
|
isSearchableOnly?: boolean;
|
||||||
validation?: FieldValidation;
|
validation?: FieldValidation;
|
||||||
options?: FieldOption[];
|
options?: FieldOption[];
|
||||||
rangeConfig?: RangeConfig;
|
rangeConfig?: RangeConfig;
|
||||||
@@ -105,6 +107,7 @@ export interface UpdateFieldDto {
|
|||||||
sortOrder?: number;
|
sortOrder?: number;
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
isRequired?: boolean;
|
isRequired?: boolean;
|
||||||
|
isSearchableOnly?: boolean;
|
||||||
validation?: FieldValidation;
|
validation?: FieldValidation;
|
||||||
options?: FieldOption[];
|
options?: FieldOption[];
|
||||||
rangeConfig?: RangeConfig;
|
rangeConfig?: RangeConfig;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export interface ProfileSection {
|
|||||||
sortOrder: number;
|
sortOrder: number;
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
isGlobal: boolean;
|
isGlobal: boolean;
|
||||||
|
isSystem: boolean;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
fields?: ProfileField[];
|
fields?: ProfileField[];
|
||||||
|
|||||||
Reference in New Issue
Block a user