feat: Add Source Serif 4 and Fractul font families and integrate them across the admin UI.

This commit is contained in:
pradeepkumar
2026-02-11 03:22:06 +05:30
parent 4bdb9fe1f7
commit 3dd98d137e
99 changed files with 829 additions and 454 deletions

View File

@@ -141,7 +141,7 @@ export default function AgentTypeSectionsPage() {
if (isLoading) {
return (
<div className="flex items-center justify-center min-h-[400px]">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-[#f5a623]"></div>
</div>
);
}
@@ -150,34 +150,34 @@ export default function AgentTypeSectionsPage() {
<div>
{/* Breadcrumb */}
<div className="mb-6">
<nav className="flex items-center space-x-2 text-sm text-gray-500">
<Link href="/dashboard/agent-types" className="hover:text-blue-600">
<nav className="flex items-center space-x-2 text-sm text-[#666666]">
<Link href="/dashboard/agent-types" className="hover:text-[#f5a623]">
Agent Types
</Link>
<span>/</span>
<span className="text-gray-900 font-medium">{data?.agentType.name || 'Loading...'}</span>
<span className="text-[#00293d] font-medium">{data?.agentType.name || 'Loading...'}</span>
<span>/</span>
<span className="text-gray-900">Sections</span>
<span className="text-[#00293d]">Sections</span>
</nav>
</div>
{/* Page Title */}
<div className="mb-6">
<h1 className="text-2xl font-bold text-gray-900">
<h1 className="text-2xl font-bold text-[#00293d] font-fractul">
Manage Sections for {data?.agentType.name}
</h1>
<p className="text-gray-500">
<p className="text-[#666666] font-serif">
Customize the order and requirements of profile sections for this agent type.
Drag to reorder or use the arrow buttons.
</p>
</div>
{/* Sections List */}
<div className="bg-white rounded-lg shadow">
<div className="px-6 py-4 border-b border-gray-200">
<div className="bg-white rounded-xl shadow-sm border border-[#e5e7eb]">
<div className="px-6 py-4 border-b border-[#e5e7eb]">
<div className="flex justify-between items-center">
<div className="flex items-center space-x-4">
<span className="text-sm text-gray-600">
<span className="text-sm text-[#666666]">
{sections.length} section{sections.length !== 1 ? 's' : ''}
</span>
{hasChanges && (
@@ -192,14 +192,14 @@ export default function AgentTypeSectionsPage() {
<button
onClick={handleReset}
disabled={isSaving}
className="px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors disabled:opacity-50"
className="px-4 py-2 border border-[#e5e7eb] text-[#00293d] rounded-xl hover:bg-[#f5f9f8] transition-colors disabled:opacity-50"
>
Reset
</button>
<button
onClick={handleSave}
disabled={isSaving}
className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50 flex items-center"
className="px-4 py-2 bg-[#f5a623] text-white rounded-xl hover:bg-[#e09620] transition-colors disabled:opacity-50 flex items-center"
>
{isSaving ? (
<>
@@ -227,13 +227,13 @@ export default function AgentTypeSectionsPage() {
{sections.length === 0 ? (
<div className="text-center py-12">
<svg className="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<svg className="mx-auto h-12 w-12 text-[#9ca3af]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
</svg>
<p className="mt-2 text-gray-500">No sections assigned to this agent type</p>
<p className="mt-2 text-[#666666] font-serif">No sections assigned to this agent type</p>
<Link
href="/dashboard/profile-sections"
className="mt-4 inline-block px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium rounded-lg transition-colors"
className="mt-4 inline-block px-4 py-2 bg-[#f5a623] hover:bg-[#e09620] text-white text-sm font-medium rounded-lg transition-colors"
>
Go to Profile Sections
</Link>
@@ -247,34 +247,34 @@ export default function AgentTypeSectionsPage() {
onDragStart={() => handleDragStart(index)}
onDragOver={(e) => handleDragOver(e, index)}
onDragEnd={handleDragEnd}
className={`bg-white rounded-lg shadow p-4 flex items-center justify-between hover:shadow-md cursor-move transition-all ${
draggedIndex === index ? 'ring-2 ring-blue-300 ring-offset-2 shadow-lg' : ''
className={`bg-white rounded-xl shadow-sm border border-[#e5e7eb] p-4 flex items-center justify-between hover:shadow-md cursor-move transition-all ${
draggedIndex === index ? 'ring-2 ring-[#f5a623]/30 ring-offset-2 shadow-lg' : ''
}`}
>
<div className="flex items-center space-x-4">
{/* Drag Handle */}
<div className="text-gray-400 cursor-grab">
<div className="text-[#9ca3af] cursor-grab">
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path d="M8 6a2 2 0 1 1-4 0 2 2 0 0 1 4 0zm0 6a2 2 0 1 1-4 0 2 2 0 0 1 4 0zm0 6a2 2 0 1 1-4 0 2 2 0 0 1 4 0zm8-12a2 2 0 1 1-4 0 2 2 0 0 1 4 0zm0 6a2 2 0 1 1-4 0 2 2 0 0 1 4 0zm0 6a2 2 0 1 1-4 0 2 2 0 0 1 4 0z" />
</svg>
</div>
{/* Order Number */}
<div className="w-8 h-8 flex items-center justify-center rounded-full bg-gray-100 text-gray-600 text-sm font-medium">
<div className="w-8 h-8 flex items-center justify-center rounded-full bg-[#e8f0ee] text-[#666666] text-sm font-medium">
{index + 1}
</div>
{/* Section Info */}
<div>
<div className="flex items-center space-x-2">
<span className="font-medium text-gray-900">{section.name}</span>
<span className="font-medium text-[#00293d]">{section.name}</span>
{section.isGlobal && (
<span className="px-2 py-0.5 text-xs font-semibold rounded-full bg-purple-100 text-purple-800">
<span className="px-2 py-0.5 text-xs font-semibold rounded-full bg-[#fff7ed] text-[#f5a623]">
Global
</span>
)}
{section.isSystem && (
<span className="px-2 py-0.5 text-xs font-semibold rounded-full bg-blue-100 text-blue-800">
<span className="px-2 py-0.5 text-xs font-semibold rounded-full bg-[#e8f0ee] text-[#5ba4a4]">
System
</span>
)}
@@ -284,7 +284,7 @@ export default function AgentTypeSectionsPage() {
</span>
)}
</div>
<div className="text-sm text-gray-500">
<div className="text-sm text-[#666666]">
{section.description || 'No description'}
</div>
</div>
@@ -297,9 +297,9 @@ export default function AgentTypeSectionsPage() {
type="checkbox"
checked={section.isRequired}
onChange={() => toggleRequired(index)}
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
className="rounded border-[#e5e7eb] text-[#f5a623] focus:ring-[#f5a623]"
/>
<span className="text-sm text-gray-600">Required</span>
<span className="text-sm text-[#666666]">Required</span>
</label>
{/* Move Buttons */}
@@ -307,7 +307,7 @@ export default function AgentTypeSectionsPage() {
<button
onClick={() => moveSection(index, 'up')}
disabled={index === 0}
className="p-1.5 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded disabled:opacity-30 disabled:cursor-not-allowed"
className="p-1.5 text-[#9ca3af] hover:text-[#666666] hover:bg-[#e8f0ee] rounded disabled:opacity-30 disabled:cursor-not-allowed"
title="Move up"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -317,7 +317,7 @@ export default function AgentTypeSectionsPage() {
<button
onClick={() => moveSection(index, 'down')}
disabled={index === sections.length - 1}
className="p-1.5 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded disabled:opacity-30 disabled:cursor-not-allowed"
className="p-1.5 text-[#9ca3af] hover:text-[#666666] hover:bg-[#e8f0ee] rounded disabled:opacity-30 disabled:cursor-not-allowed"
title="Move down"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -333,9 +333,9 @@ export default function AgentTypeSectionsPage() {
</div>
{/* Info Box */}
<div className="mt-6 p-4 bg-blue-50 rounded-lg border border-blue-200">
<h3 className="text-sm font-semibold text-blue-800 mb-2">How Section Ordering Works</h3>
<ul className="text-sm text-blue-700 space-y-1">
<div className="mt-6 p-4 bg-[#f5f9f8] rounded-xl border border-[#e5e7eb]">
<h3 className="text-sm font-semibold text-[#00293d] mb-2 font-fractul">How Section Ordering Works</h3>
<ul className="text-sm text-[#666666] space-y-1 font-serif">
<li><strong>Global sections</strong> (purple badge) are automatically included for all agent types.</li>
<li><strong>System sections</strong> (blue badge) cannot be deleted, only hidden.</li>
<li><strong>Custom Order</strong> (yellow badge) indicates this agent type has a custom order for that section.</li>

View File

@@ -145,21 +145,21 @@ export default function AgentTypesPage() {
<div>
{/* Page Title */}
<div className="mb-6">
<h1 className="text-2xl font-bold text-gray-900">Agent Types</h1>
<p className="text-gray-500">Manage agent categories and types</p>
<h1 className="text-2xl font-bold text-[#00293d] font-fractul">Agent Types</h1>
<p className="text-[#666666] font-serif">Manage agent categories and types</p>
</div>
{/* Agent Types Table */}
<div className="bg-white rounded-lg shadow">
<div className="px-6 py-4 border-b border-gray-200">
<div className="bg-white rounded-xl shadow-sm border border-[#e5e7eb]">
<div className="px-6 py-4 border-b border-[#e5e7eb]">
<div className="flex justify-between items-center">
<div className="flex items-center space-x-4">
<label className="flex items-center space-x-2 text-sm text-gray-600">
<label className="flex items-center space-x-2 text-sm text-[#666666] font-serif">
<input
type="checkbox"
checked={showInactive}
onChange={(e) => setShowInactive(e.target.checked)}
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
className="rounded border-[#e5e7eb] text-[#f5a623] focus:ring-[#f5a623]"
/>
<span>Show Inactive</span>
</label>
@@ -167,7 +167,7 @@ export default function AgentTypesPage() {
<div className="flex space-x-3">
<button
onClick={fetchAgentTypes}
className="px-4 py-2 border border-gray-300 hover:bg-gray-50 text-gray-700 text-sm font-medium rounded-lg transition-colors flex items-center"
className="px-4 py-2 border border-[#e5e7eb] hover:bg-[#f5f9f8] text-[#00293d] text-sm font-medium rounded-lg transition-colors flex items-center"
>
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
@@ -176,7 +176,7 @@ export default function AgentTypesPage() {
</button>
<button
onClick={openCreateModal}
className="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium rounded-lg transition-colors flex items-center"
className="px-4 py-2 bg-[#f5a623] hover:bg-[#e09620] text-white text-sm font-medium rounded-lg transition-colors flex items-center"
>
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
@@ -196,64 +196,64 @@ export default function AgentTypesPage() {
<div className="overflow-x-auto">
{isLoading ? (
<div className="flex items-center justify-center py-12">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-[#f5a623]"></div>
</div>
) : agentTypes.length === 0 ? (
<div className="text-center py-12">
<svg className="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<svg className="mx-auto h-12 w-12 text-[#9ca3af]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
</svg>
<p className="mt-2 text-gray-500">No agent types found</p>
<p className="mt-2 text-[#666666]">No agent types found</p>
<button
onClick={openCreateModal}
className="mt-4 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium rounded-lg transition-colors"
className="mt-4 px-4 py-2 bg-[#f5a623] hover:bg-[#e09620] text-white text-sm font-medium rounded-lg transition-colors"
>
Create First Agent Type
</button>
</div>
) : (
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<table className="min-w-full divide-y divide-[#e5e7eb]">
<thead className="bg-[#f5f9f8]">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<th className="px-6 py-3 text-left text-xs font-medium text-[#666666] uppercase tracking-wider">
Name
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<th className="px-6 py-3 text-left text-xs font-medium text-[#666666] uppercase tracking-wider">
Description
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<th className="px-6 py-3 text-left text-xs font-medium text-[#666666] uppercase tracking-wider">
Icon
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<th className="px-6 py-3 text-left text-xs font-medium text-[#666666] uppercase tracking-wider">
Sort Order
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<th className="px-6 py-3 text-left text-xs font-medium text-[#666666] uppercase tracking-wider">
Status
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<th className="px-6 py-3 text-left text-xs font-medium text-[#666666] uppercase tracking-wider">
Agents
</th>
<th className="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
<th className="px-6 py-3 text-right text-xs font-medium text-[#666666] uppercase tracking-wider">
Actions
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
<tbody className="bg-white divide-y divide-[#e5e7eb]">
{agentTypes.map((agentType) => (
<tr key={agentType.id} className="hover:bg-gray-50">
<tr key={agentType.id} className="hover:bg-[#f5f9f8]">
<td className="px-6 py-4 whitespace-nowrap">
<div className="text-sm font-medium text-gray-900">{agentType.name}</div>
<div className="text-sm font-medium text-[#00293d]">{agentType.name}</div>
</td>
<td className="px-6 py-4">
<div className="text-sm text-gray-500 max-w-xs truncate">
<div className="text-sm text-[#666666] max-w-xs truncate">
{agentType.description || '-'}
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<div className="text-sm text-gray-500">{agentType.icon || '-'}</div>
<div className="text-sm text-[#666666]">{agentType.icon || '-'}</div>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<div className="text-sm text-gray-500">{agentType.sortOrder}</div>
<div className="text-sm text-[#666666]">{agentType.sortOrder}</div>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<button
@@ -268,18 +268,18 @@ export default function AgentTypesPage() {
</button>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<div className="text-sm text-gray-500">{agentType._count?.agents || 0}</div>
<div className="text-sm text-[#666666]">{agentType._count?.agents || 0}</div>
</td>
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<button
onClick={() => router.push(`/dashboard/agent-types/${agentType.id}/sections`)}
className="text-purple-600 hover:text-purple-900 mr-4"
className="text-[#5ba4a4] hover:text-[#5ba4a4]/80 mr-4"
>
Sections
</button>
<button
onClick={() => openEditModal(agentType)}
className="text-blue-600 hover:text-blue-900 mr-4"
className="text-[#f5a623] hover:text-[#e09620] mr-4"
>
Edit
</button>
@@ -300,10 +300,10 @@ export default function AgentTypesPage() {
{/* Create/Edit Modal */}
{(modalType === 'create' || modalType === 'edit') && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="bg-white rounded-lg shadow-xl max-w-md w-full mx-4">
<div className="px-6 py-4 border-b border-gray-200">
<h3 className="text-lg font-semibold text-gray-900">
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
<div className="bg-white rounded-xl shadow-xl max-w-md w-full mx-4">
<div className="px-6 py-4 border-b border-[#e5e7eb]">
<h3 className="text-lg font-semibold text-[#00293d]">
{modalType === 'create' ? 'Create Agent Type' : 'Edit Agent Type'}
</h3>
</div>
@@ -315,42 +315,42 @@ export default function AgentTypesPage() {
</div>
)}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
<label className="block text-sm font-medium text-[#00293d] mb-1">
Name <span className="text-red-500">*</span>
</label>
<input
type="text"
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-900 bg-white"
className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:ring-2 focus:ring-[#f5a623] focus:border-[#f5a623] text-[#00293d] bg-white"
required
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
<label className="block text-sm font-medium text-[#00293d] mb-1">
Description
</label>
<textarea
value={formData.description}
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
rows={3}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-900 bg-white"
className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:ring-2 focus:ring-[#f5a623] focus:border-[#f5a623] text-[#00293d] bg-white"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
<label className="block text-sm font-medium text-[#00293d] mb-1">
Icon (emoji or icon name)
</label>
<input
type="text"
value={formData.icon}
onChange={(e) => setFormData({ ...formData, icon: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-900 bg-white placeholder:text-gray-500"
className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:ring-2 focus:ring-[#f5a623] focus:border-[#f5a623] text-[#00293d] bg-white placeholder:text-[#666666]"
placeholder="e.g. house, building, key"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
<label className="block text-sm font-medium text-[#00293d] mb-1">
Sort Order
</label>
<input
@@ -360,7 +360,7 @@ export default function AgentTypesPage() {
onFocus={(e) => e.target.select()}
placeholder="0"
min={0}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-900 bg-white placeholder:text-gray-500"
className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:ring-2 focus:ring-[#f5a623] focus:border-[#f5a623] text-[#00293d] bg-white placeholder:text-[#666666]"
/>
</div>
<div className="flex items-center">
@@ -369,25 +369,25 @@ export default function AgentTypesPage() {
id="isActive"
checked={formData.isActive}
onChange={(e) => setFormData({ ...formData, isActive: e.target.checked })}
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
className="rounded border-[#e5e7eb] text-[#f5a623] focus:ring-[#f5a623]"
/>
<label htmlFor="isActive" className="ml-2 text-sm text-gray-700">
<label htmlFor="isActive" className="ml-2 text-sm text-[#00293d] font-serif">
Active
</label>
</div>
</div>
<div className="px-6 py-4 border-t border-gray-200 flex justify-end space-x-3">
<div className="px-6 py-4 border-t border-[#e5e7eb] flex justify-end space-x-3">
<button
type="button"
onClick={closeModal}
className="px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
className="px-4 py-2 border border-[#e5e7eb] text-[#00293d] rounded-xl hover:bg-[#f5f9f8] transition-colors"
>
Cancel
</button>
<button
type="submit"
disabled={isSubmitting}
className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50"
className="px-4 py-2 bg-[#f5a623] text-white rounded-xl hover:bg-[#e09620] transition-colors disabled:opacity-50"
>
{isSubmitting ? 'Saving...' : modalType === 'create' ? 'Create' : 'Save Changes'}
</button>
@@ -399,10 +399,10 @@ export default function AgentTypesPage() {
{/* Delete Confirmation Modal */}
{modalType === 'delete' && selectedType && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="bg-white rounded-lg shadow-xl max-w-md w-full mx-4">
<div className="px-6 py-4 border-b border-gray-200">
<h3 className="text-lg font-semibold text-gray-900">Delete Agent Type</h3>
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
<div className="bg-white rounded-xl shadow-xl max-w-md w-full mx-4">
<div className="px-6 py-4 border-b border-[#e5e7eb]">
<h3 className="text-lg font-semibold text-[#00293d]">Delete Agent Type</h3>
</div>
<div className="px-6 py-4">
{modalError && (
@@ -410,7 +410,7 @@ export default function AgentTypesPage() {
<p className="text-red-700 text-sm">{modalError}</p>
</div>
)}
<p className="text-gray-600">
<p className="text-[#666666] font-serif">
Are you sure you want to delete <span className="font-semibold">{selectedType.name}</span>?
{selectedType._count?.agents && selectedType._count.agents > 0 && (
<span className="block mt-2 text-red-600 text-sm">
@@ -419,10 +419,10 @@ export default function AgentTypesPage() {
)}
</p>
</div>
<div className="px-6 py-4 border-t border-gray-200 flex justify-end space-x-3">
<div className="px-6 py-4 border-t border-[#e5e7eb] flex justify-end space-x-3">
<button
onClick={closeModal}
className="px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
className="px-4 py-2 border border-[#e5e7eb] text-[#00293d] rounded-xl hover:bg-[#f5f9f8] transition-colors"
>
Cancel
</button>

View File

@@ -38,8 +38,8 @@ export default function DashboardLayout({
if (isLoading) {
return (
<div className="min-h-screen flex items-center justify-center bg-gray-100">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
<div className="min-h-screen flex items-center justify-center bg-[#f5f9f8]">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-[#f5a623]"></div>
</div>
);
}
@@ -49,35 +49,35 @@ export default function DashboardLayout({
}
return (
<div className="min-h-screen bg-gray-50">
<div className="min-h-screen bg-[#f5f9f8]">
{/* Sidebar */}
<Sidebar />
{/* Main Content */}
<div className="ml-64">
{/* Top Header */}
<header className="bg-white border-b border-gray-200 sticky top-0 z-40">
<header className="bg-white border-b border-[#e5e7eb] sticky top-0 z-40">
<div className="px-8 py-3">
<div className="flex justify-end items-center">
{/* User Dropdown */}
<div className="relative" ref={dropdownRef}>
<button
onClick={() => setIsDropdownOpen(!isDropdownOpen)}
className="flex items-center gap-3 py-2 px-3 rounded-xl hover:bg-gray-50 transition-all duration-200"
className="flex items-center gap-3 py-2 px-3 rounded-xl hover:bg-[#f5f9f8] transition-all duration-200"
>
<div className="w-9 h-9 bg-gradient-to-br from-blue-500 to-blue-600 rounded-full flex items-center justify-center shadow-sm">
<span className="text-white font-semibold text-sm">
<div className="w-9 h-9 bg-[#00293d] rounded-full flex items-center justify-center shadow-sm">
<span className="text-white font-semibold text-sm font-fractul">
{user?.firstName?.[0] || user?.email?.[0]?.toUpperCase() || 'A'}
</span>
</div>
<div className="text-left hidden sm:block">
<p className="text-sm font-semibold text-gray-800">
<p className="text-sm font-semibold text-[#00293d] font-fractul">
{user?.firstName || 'Admin'} {user?.lastName || ''}
</p>
<p className="text-xs text-gray-500">{user?.email}</p>
<p className="text-xs text-[#666666] font-serif">{user?.email}</p>
</div>
<svg
className={`w-4 h-4 text-gray-400 transition-transform duration-200 ${isDropdownOpen ? 'rotate-180' : ''}`}
className={`w-4 h-4 text-[#9ca3af] transition-transform duration-200 ${isDropdownOpen ? 'rotate-180' : ''}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
@@ -88,19 +88,19 @@ export default function DashboardLayout({
{/* Dropdown Menu */}
{isDropdownOpen && (
<div className="absolute right-0 mt-2 w-56 bg-white rounded-xl shadow-lg border border-gray-100 overflow-hidden z-50">
<div className="px-4 py-4 bg-gradient-to-br from-gray-50 to-white border-b border-gray-100">
<div className="absolute right-0 mt-2 w-56 bg-white rounded-xl shadow-lg border border-[#e5e7eb] overflow-hidden z-50">
<div className="px-4 py-4 bg-[#f5f9f8] border-b border-[#e5e7eb]">
<div className="flex items-center gap-3">
<div className="w-10 h-10 bg-gradient-to-br from-blue-500 to-blue-600 rounded-full flex items-center justify-center shadow-sm">
<span className="text-white font-semibold">
<div className="w-10 h-10 bg-[#00293d] rounded-full flex items-center justify-center shadow-sm">
<span className="text-white font-semibold font-fractul">
{user?.firstName?.[0] || user?.email?.[0]?.toUpperCase() || 'A'}
</span>
</div>
<div>
<p className="text-sm font-semibold text-gray-800">
<p className="text-sm font-semibold text-[#00293d] font-fractul">
{user?.firstName || 'Admin'} {user?.lastName || ''}
</p>
<p className="text-xs text-gray-500 truncate">{user?.email}</p>
<p className="text-xs text-[#666666] truncate font-serif">{user?.email}</p>
</div>
</div>
</div>

View File

@@ -57,7 +57,7 @@ export default function DashboardPage() {
if (isLoading) {
return (
<div className="flex items-center justify-center py-12">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-[#f5a623]"></div>
</div>
);
}
@@ -66,17 +66,17 @@ export default function DashboardPage() {
<div>
{/* Page Title */}
<div className="mb-6">
<h1 className="text-2xl font-bold text-gray-900">Dashboard</h1>
<p className="text-gray-500">Welcome to Re-Quest Admin Panel</p>
<h1 className="text-2xl font-bold text-[#00293d] font-fractul">Dashboard</h1>
<p className="text-[#666666] font-serif">Welcome to Re-Quest Admin Panel</p>
</div>
{/* Stats Cards */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-6 mb-8">
<div className="bg-white rounded-lg shadow p-6">
<div className="bg-white rounded-xl shadow-sm border border-[#e5e7eb] p-6 card-hover">
<div className="flex items-center">
<div className="p-3 bg-blue-100 rounded-lg">
<div className="p-3 bg-[#e8f0ee] rounded-xl">
<svg
className="w-6 h-6 text-blue-600"
className="w-6 h-6 text-[#5ba4a4]"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
@@ -90,14 +90,14 @@ export default function DashboardPage() {
</svg>
</div>
<div className="ml-4">
<p className="text-sm font-medium text-gray-500">Total Users</p>
<p className="text-2xl font-bold text-gray-900">{stats.totalUsers}</p>
<p className="text-sm font-medium text-[#666666] font-serif">Total Users</p>
<p className="text-2xl font-bold text-[#00293d] font-fractul">{stats.totalUsers}</p>
</div>
</div>
</div>
<div className="bg-white rounded-lg shadow p-6">
<div className="bg-white rounded-xl shadow-sm border border-[#e5e7eb] p-6 card-hover">
<div className="flex items-center">
<div className="p-3 bg-green-100 rounded-lg">
<div className="p-3 bg-green-50 rounded-xl">
<svg
className="w-6 h-6 text-green-600"
fill="none"
@@ -113,16 +113,16 @@ export default function DashboardPage() {
</svg>
</div>
<div className="ml-4">
<p className="text-sm font-medium text-gray-500">Active</p>
<p className="text-2xl font-bold text-gray-900">{stats.activeUsers}</p>
<p className="text-sm font-medium text-[#666666] font-serif">Active</p>
<p className="text-2xl font-bold text-[#00293d] font-fractul">{stats.activeUsers}</p>
</div>
</div>
</div>
<div className="bg-white rounded-lg shadow p-6">
<div className="bg-white rounded-xl shadow-sm border border-[#e5e7eb] p-6 card-hover">
<div className="flex items-center">
<div className="p-3 bg-purple-100 rounded-lg">
<div className="p-3 bg-[#fff7ed] rounded-xl">
<svg
className="w-6 h-6 text-purple-600"
className="w-6 h-6 text-[#f5a623]"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
@@ -136,14 +136,14 @@ export default function DashboardPage() {
</svg>
</div>
<div className="ml-4">
<p className="text-sm font-medium text-gray-500">Agents</p>
<p className="text-2xl font-bold text-gray-900">{stats.agents}</p>
<p className="text-sm font-medium text-[#666666] font-serif">Agents</p>
<p className="text-2xl font-bold text-[#00293d] font-fractul">{stats.agents}</p>
</div>
</div>
</div>
<div className="bg-white rounded-lg shadow p-6">
<div className="bg-white rounded-xl shadow-sm border border-[#e5e7eb] p-6 card-hover">
<div className="flex items-center">
<div className="p-3 bg-yellow-100 rounded-lg">
<div className="p-3 bg-yellow-50 rounded-xl">
<svg
className="w-6 h-6 text-yellow-600"
fill="none"
@@ -159,21 +159,21 @@ export default function DashboardPage() {
</svg>
</div>
<div className="ml-4">
<p className="text-sm font-medium text-gray-500">Pending</p>
<p className="text-2xl font-bold text-gray-900">{stats.pendingUsers}</p>
<p className="text-sm font-medium text-[#666666] font-serif">Pending</p>
<p className="text-2xl font-bold text-[#00293d] font-fractul">{stats.pendingUsers}</p>
</div>
</div>
</div>
</div>
{/* Recent Users */}
<div className="bg-white rounded-lg shadow">
<div className="px-6 py-4 border-b border-gray-200">
<div className="bg-white rounded-xl shadow-sm border border-[#e5e7eb]">
<div className="px-6 py-4 border-b border-[#e5e7eb]">
<div className="flex justify-between items-center">
<h2 className="text-lg font-semibold text-gray-900">Recent Users</h2>
<h2 className="text-lg font-semibold text-[#00293d] font-fractul">Recent Users</h2>
<Link
href="/dashboard/users"
className="text-blue-600 hover:text-blue-700 text-sm font-medium"
className="text-[#f5a623] hover:text-[#e09620] text-sm font-medium font-serif transition-colors"
>
View All
</Link>
@@ -183,7 +183,7 @@ export default function DashboardPage() {
{recentUsers.length === 0 ? (
<div className="text-center py-12">
<svg
className="mx-auto h-12 w-12 text-gray-400"
className="mx-auto h-12 w-12 text-[#9ca3af]"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
@@ -195,29 +195,29 @@ export default function DashboardPage() {
d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"
/>
</svg>
<p className="mt-2 text-gray-500">No users found</p>
<p className="mt-2 text-[#666666] font-serif">No users found</p>
</div>
) : (
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<table className="min-w-full divide-y divide-[#e5e7eb]">
<thead className="bg-[#f5f9f8]">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<th className="px-6 py-3 text-left text-xs font-medium text-[#666666] uppercase tracking-wider font-serif">
User
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<th className="px-6 py-3 text-left text-xs font-medium text-[#666666] uppercase tracking-wider font-serif">
Role
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<th className="px-6 py-3 text-left text-xs font-medium text-[#666666] uppercase tracking-wider font-serif">
Status
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<th className="px-6 py-3 text-left text-xs font-medium text-[#666666] uppercase tracking-wider font-serif">
Joined
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
<tbody className="bg-white divide-y divide-[#e5e7eb]">
{recentUsers.map((user) => (
<tr key={user.id} className="hover:bg-gray-50">
<tr key={user.id} className="hover:bg-[#f5f9f8] transition-colors">
<td className="px-6 py-4 whitespace-nowrap">
<div className="flex items-center">
<div className="flex-shrink-0 h-10 w-10">
@@ -228,18 +228,18 @@ export default function DashboardPage() {
alt=""
/>
) : (
<div className="h-10 w-10 rounded-full bg-gray-200 flex items-center justify-center">
<span className="text-gray-500 font-medium text-sm">
<div className="h-10 w-10 rounded-full bg-[#e8f0ee] flex items-center justify-center">
<span className="text-[#00293d] font-medium text-sm font-fractul">
{user.profile?.firstName?.[0] || user.email[0].toUpperCase()}
</span>
</div>
)}
</div>
<div className="ml-4">
<div className="text-sm font-medium text-gray-900">
<div className="text-sm font-medium text-[#00293d] font-serif">
{user.profile?.firstName} {user.profile?.lastName}
</div>
<div className="text-sm text-gray-500">{user.email}</div>
<div className="text-sm text-[#666666]">{user.email}</div>
</div>
</div>
</td>
@@ -249,8 +249,8 @@ export default function DashboardPage() {
user.role === 'ADMIN'
? 'bg-red-100 text-red-800'
: user.role === 'AGENT'
? 'bg-purple-100 text-purple-800'
: 'bg-blue-100 text-blue-800'
? 'bg-[#fff7ed] text-[#f5a623]'
: 'bg-[#e8f0ee] text-[#5ba4a4]'
}`}
>
{user.role}
@@ -271,7 +271,7 @@ export default function DashboardPage() {
{user.status === 'PENDING_VERIFICATION' ? 'PENDING' : user.status}
</span>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<td className="px-6 py-4 whitespace-nowrap text-sm text-[#666666] font-serif">
{new Date(user.createdAt).toLocaleDateString()}
</td>
</tr>

View File

@@ -299,7 +299,7 @@ export default function SectionDetailPage() {
if (isLoading) {
return (
<div className="flex items-center justify-center py-12">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-[#f5a623]"></div>
</div>
);
}
@@ -307,8 +307,8 @@ export default function SectionDetailPage() {
if (!section) {
return (
<div className="text-center py-12">
<p className="text-gray-500">Section not found</p>
<Link href="/dashboard/profile-sections" className="mt-4 text-blue-600 hover:underline">
<p className="text-[#666666] font-serif">Section not found</p>
<Link href="/dashboard/profile-sections" className="mt-4 text-[#f5a623] hover:underline">
Back to sections
</Link>
</div>
@@ -323,7 +323,7 @@ export default function SectionDetailPage() {
<div>
{/* Breadcrumb */}
<div className="mb-4">
<Link href="/dashboard/profile-sections" className="text-blue-600 hover:underline text-sm">
<Link href="/dashboard/profile-sections" className="text-[#f5a623] hover:underline text-sm">
&larr; Back to Profile Sections
</Link>
</div>
@@ -333,24 +333,24 @@ export default function SectionDetailPage() {
<div>
<div className="flex items-center">
{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-[#00293d] font-fractul">{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 && (
<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-[#fff7ed] text-[#f5a623]">
Global
</span>
)}
</div>
<p className="text-gray-500 mt-1">{section.description || 'No description'}</p>
<p className="text-[#666666] font-serif mt-1">{section.description || 'No description'}</p>
</div>
<div className="flex space-x-3">
<button
onClick={fetchData}
className="px-4 py-2 border border-gray-300 hover:bg-gray-50 text-gray-700 text-sm font-medium rounded-lg transition-colors"
className="px-4 py-2 border border-[#e5e7eb] hover:bg-[#f5f9f8] text-[#00293d] text-sm font-medium rounded-lg transition-colors"
>
Refresh
</button>
@@ -366,12 +366,12 @@ export default function SectionDetailPage() {
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Fields Section */}
<div className="lg:col-span-2">
<div className="bg-white rounded-lg shadow">
<div className="px-6 py-4 border-b border-gray-200 flex justify-between items-center">
<h2 className="text-lg font-semibold text-gray-900">Fields ({fields.length})</h2>
<div className="bg-white rounded-xl shadow-sm border border-[#e5e7eb]">
<div className="px-6 py-4 border-b border-[#e5e7eb] flex justify-between items-center">
<h2 className="text-lg font-semibold text-[#00293d] font-fractul">Fields ({fields.length})</h2>
<button
onClick={openCreateFieldModal}
className="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium rounded-lg transition-colors flex items-center"
className="px-4 py-2 bg-[#f5a623] hover:bg-[#e09620] text-white text-sm font-medium rounded-lg transition-colors flex items-center"
>
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
@@ -380,28 +380,28 @@ export default function SectionDetailPage() {
</button>
</div>
<div className="divide-y divide-gray-200">
<div className="divide-y divide-[#e5e7eb]">
{fields.length === 0 ? (
<div className="text-center py-12">
<svg className="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<svg className="mx-auto h-12 w-12 text-[#9ca3af]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
</svg>
<p className="mt-2 text-gray-500">No fields defined yet</p>
<p className="mt-2 text-[#666666] font-serif">No fields defined yet</p>
<button
onClick={openCreateFieldModal}
className="mt-4 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium rounded-lg transition-colors"
className="mt-4 px-4 py-2 bg-[#f5a623] hover:bg-[#e09620] text-white text-sm font-medium rounded-lg transition-colors"
>
Add First Field
</button>
</div>
) : (
fields.map((field, index) => (
<div key={field.id} className="px-6 py-4 hover:bg-gray-50">
<div key={field.id} className="px-6 py-4 hover:bg-[#f5f9f8]">
<div className="flex justify-between items-start">
<div className="flex-1">
<div className="flex items-center flex-wrap gap-2">
<span className="text-gray-400 mr-1 text-sm">#{index + 1}</span>
<span className="font-medium text-gray-900">{field.name}</span>
<span className="text-[#9ca3af] mr-1 text-sm">#{index + 1}</span>
<span className="font-medium text-[#00293d]">{field.name}</span>
{field.isRequired && (
<span className="text-red-500 text-xs">*required</span>
)}
@@ -412,12 +412,12 @@ export default function SectionDetailPage() {
)}
</div>
<div className="mt-1 flex items-center space-x-3 text-sm">
<span className="px-2 py-0.5 bg-gray-100 text-gray-600 rounded">
<span className="px-2 py-0.5 bg-[#e8f0ee] text-[#666666] rounded">
{getFieldTypeLabel(field.fieldType)}
</span>
<span className="text-gray-400">{field.slug}</span>
<span className="text-[#9ca3af]">{field.slug}</span>
{field.placeholder && (
<span className="text-gray-400 truncate max-w-xs">
<span className="text-[#9ca3af] truncate max-w-xs">
placeholder: {field.placeholder}
</span>
)}
@@ -425,12 +425,12 @@ export default function SectionDetailPage() {
{field.options && field.options.length > 0 && (
<div className="mt-2 flex flex-wrap gap-1">
{(field.options as FieldOption[]).slice(0, 5).map((opt, i) => (
<span key={i} className="px-2 py-0.5 bg-blue-50 text-blue-700 text-xs rounded">
<span key={i} className="px-2 py-0.5 bg-[#e8f0ee] text-[#5ba4a4] text-xs rounded">
{opt.label}
</span>
))}
{field.options.length > 5 && (
<span className="px-2 py-0.5 bg-gray-100 text-gray-500 text-xs rounded">
<span className="px-2 py-0.5 bg-[#e8f0ee] text-[#666666] text-xs rounded">
+{field.options.length - 5} more
</span>
)}
@@ -450,7 +450,7 @@ export default function SectionDetailPage() {
</button>
<button
onClick={() => openEditFieldModal(field)}
className="p-1 text-gray-400 hover:text-blue-600"
className="p-1 text-[#9ca3af] hover:text-[#f5a623]"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
@@ -458,7 +458,7 @@ export default function SectionDetailPage() {
</button>
<button
onClick={() => openDeleteFieldModal(field)}
className="p-1 text-gray-400 hover:text-red-600"
className="p-1 text-[#9ca3af] hover:text-red-600"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
@@ -475,13 +475,13 @@ export default function SectionDetailPage() {
{/* Agent Type Assignments */}
<div className="lg:col-span-1">
<div className="bg-white rounded-lg shadow">
<div className="px-6 py-4 border-b border-gray-200 flex justify-between items-center">
<h2 className="text-lg font-semibold text-gray-900">Agent Types</h2>
<div className="bg-white rounded-xl shadow-sm border border-[#e5e7eb]">
<div className="px-6 py-4 border-b border-[#e5e7eb] flex justify-between items-center">
<h2 className="text-lg font-semibold text-[#00293d] font-fractul">Agent Types</h2>
{availableAgentTypes.length > 0 && (
<button
onClick={openAssignModal}
className="px-3 py-1.5 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium rounded-lg transition-colors"
className="px-3 py-1.5 bg-[#f5a623] hover:bg-[#e09620] text-white text-sm font-medium rounded-lg transition-colors"
>
Assign
</button>
@@ -489,10 +489,10 @@ export default function SectionDetailPage() {
</div>
<div className="p-6">
{/* Global toggle */}
<div className="flex items-center justify-between p-3 bg-gray-50 rounded-lg mb-4">
<div className="flex items-center justify-between p-3 bg-[#f5f9f8] rounded-xl mb-4">
<div>
<p className="font-medium text-gray-900">Available to all agent types</p>
<p className="text-xs text-gray-500">
<p className="font-medium text-[#00293d]">Available to all agent types</p>
<p className="text-xs text-[#666666]">
{section.isGlobal
? 'This section appears for all agent types by default'
: 'Only assigned agent types will see this section'}
@@ -500,8 +500,8 @@ export default function SectionDetailPage() {
</div>
<button
onClick={() => handleToggleGlobal(!section.isGlobal)}
className={`relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-offset-2 ${
section.isGlobal ? 'bg-purple-600' : 'bg-gray-200'
className={`relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-[#5ba4a4] focus:ring-offset-2 ${
section.isGlobal ? 'bg-[#5ba4a4]' : 'bg-[#e8f0ee]'
}`}
role="switch"
aria-checked={section.isGlobal}
@@ -517,7 +517,7 @@ export default function SectionDetailPage() {
{/* Specific agent type assignments */}
{section.agentTypeSections && section.agentTypeSections.length > 0 ? (
<div className="space-y-3">
<p className="text-xs text-gray-500 font-medium uppercase tracking-wide mb-2">
<p className="text-xs text-[#666666] font-medium uppercase tracking-wide mb-2">
Specific Assignments
</p>
{section.agentTypeSections.map((ats) => {
@@ -525,17 +525,17 @@ export default function SectionDetailPage() {
return (
<div
key={ats.id}
className="flex items-center justify-between p-3 bg-gray-50 rounded-lg"
className="flex items-center justify-between p-3 bg-[#f5f9f8] rounded-xl"
>
<div>
<p className="font-medium text-gray-900">{agentType?.name || 'Unknown'}</p>
<p className="text-xs text-gray-500">
<p className="font-medium text-[#00293d]">{agentType?.name || 'Unknown'}</p>
<p className="text-xs text-[#666666]">
{ats.isRequired ? 'Required' : 'Optional'} &bull; Order: {ats.sortOrder}
</p>
</div>
<button
onClick={() => handleRemoveAssignment(ats.agentTypeId)}
className="p-1 text-gray-400 hover:text-red-600"
className="p-1 text-[#9ca3af] hover:text-red-600"
title="Remove assignment"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -548,7 +548,7 @@ export default function SectionDetailPage() {
</div>
) : (
<div className="text-center py-4">
<p className="text-gray-500 text-sm">
<p className="text-[#666666] text-sm font-serif">
{section.isGlobal
? 'No specific agent type configurations yet'
: 'Not assigned to any agent type'}
@@ -556,7 +556,7 @@ export default function SectionDetailPage() {
{availableAgentTypes.length > 0 && (
<button
onClick={openAssignModal}
className="mt-3 text-blue-600 hover:underline text-sm"
className="mt-3 text-[#f5a623] hover:underline text-sm"
>
{section.isGlobal ? 'Add specific configuration' : 'Assign to agent type'}
</button>
@@ -571,9 +571,9 @@ export default function SectionDetailPage() {
{/* Field Create/Edit Modal */}
{(fieldModalType === 'create' || fieldModalType === 'edit') && (
<div className="fixed inset-0 bg-black/30 flex items-center justify-center z-50 overflow-y-auto">
<div className="bg-white rounded-lg shadow-xl max-w-2xl w-full mx-4 my-8">
<div className="px-6 py-4 border-b border-gray-200">
<h3 className="text-lg font-semibold text-gray-900">
<div className="bg-white rounded-xl shadow-sm border border-[#e5e7eb]-xl max-w-2xl w-full mx-4 my-8">
<div className="px-6 py-4 border-b border-[#e5e7eb]">
<h3 className="text-lg font-semibold text-[#00293d] font-fractul">
{fieldModalType === 'create' ? 'Create Field' : 'Edit Field'}
</h3>
</div>
@@ -587,25 +587,25 @@ export default function SectionDetailPage() {
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
<label className="block text-sm font-medium text-[#00293d] mb-1">
Name <span className="text-red-500">*</span>
</label>
<input
type="text"
value={fieldForm.name}
onChange={(e) => setFieldForm({ ...fieldForm, name: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-900 bg-white"
className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:ring-2 focus:ring-[#f5a623] focus:border-[#f5a623] text-[#00293d] bg-white"
required
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
<label className="block text-sm font-medium text-[#00293d] mb-1">
Field Type <span className="text-red-500">*</span>
</label>
<select
value={fieldForm.fieldType}
onChange={(e) => setFieldForm({ ...fieldForm, fieldType: e.target.value as FieldType })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-900 bg-white"
className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:ring-2 focus:ring-[#f5a623] focus:border-[#f5a623] text-[#00293d] bg-white"
>
{FIELD_TYPES.map((ft) => (
<option key={ft.value} value={ft.value}>
@@ -617,46 +617,46 @@ export default function SectionDetailPage() {
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
<label className="block text-sm font-medium text-[#00293d] mb-1">
Description
</label>
<input
type="text"
value={fieldForm.description}
onChange={(e) => setFieldForm({ ...fieldForm, description: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-900 bg-white placeholder:text-gray-500"
className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:ring-2 focus:ring-[#f5a623] focus:border-[#f5a623] text-[#00293d] bg-white placeholder:text-[#666666]"
placeholder="Help text for this field"
/>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
<label className="block text-sm font-medium text-[#00293d] mb-1">
Placeholder
</label>
<input
type="text"
value={fieldForm.placeholder}
onChange={(e) => setFieldForm({ ...fieldForm, placeholder: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-900 bg-white"
className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:ring-2 focus:ring-[#f5a623] focus:border-[#f5a623] text-[#00293d] bg-white"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
<label className="block text-sm font-medium text-[#00293d] mb-1">
Default Value
</label>
<input
type="text"
value={fieldForm.defaultValue}
onChange={(e) => setFieldForm({ ...fieldForm, defaultValue: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-900 bg-white"
className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:ring-2 focus:ring-[#f5a623] focus:border-[#f5a623] text-[#00293d] bg-white"
/>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
<label className="block text-sm font-medium text-[#00293d] mb-1">
Sort Order
</label>
<input
@@ -666,7 +666,7 @@ export default function SectionDetailPage() {
onFocus={(e) => e.target.select()}
placeholder="0"
min={0}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-900 bg-white placeholder:text-gray-500"
className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:ring-2 focus:ring-[#f5a623] focus:border-[#f5a623] text-[#00293d] bg-white placeholder:text-[#666666]"
/>
</div>
<div className="flex items-center space-x-6 pt-6">
@@ -676,9 +676,9 @@ export default function SectionDetailPage() {
id="fieldIsActive"
checked={fieldForm.isActive}
onChange={(e) => setFieldForm({ ...fieldForm, isActive: e.target.checked })}
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
className="rounded border-[#e5e7eb] text-[#f5a623] focus:ring-[#f5a623]"
/>
<label htmlFor="fieldIsActive" className="ml-2 text-sm text-gray-700">
<label htmlFor="fieldIsActive" className="ml-2 text-sm text-[#00293d] font-serif">
Active
</label>
</div>
@@ -688,9 +688,9 @@ export default function SectionDetailPage() {
id="fieldIsRequired"
checked={fieldForm.isRequired}
onChange={(e) => setFieldForm({ ...fieldForm, isRequired: e.target.checked })}
className="rounded border-gray-300 text-red-600 focus:ring-red-500"
className="rounded border-[#e5e7eb] text-red-600 focus:ring-red-500"
/>
<label htmlFor="fieldIsRequired" className="ml-2 text-sm text-gray-700">
<label htmlFor="fieldIsRequired" className="ml-2 text-sm text-[#00293d] font-serif">
Required
</label>
</div>
@@ -701,10 +701,10 @@ export default function SectionDetailPage() {
<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">
<label htmlFor="fieldIsSearchableOnly" className="text-sm font-medium text-[#00293d]">
Search Only (Hidden from Public Profile)
</label>
<p className="text-xs text-gray-500 mt-1">
<p className="text-xs text-[#666666] font-serif 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>
@@ -714,15 +714,15 @@ export default function SectionDetailPage() {
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"
className="mt-1 rounded border-[#e5e7eb] text-amber-600 focus:ring-amber-500"
/>
</div>
</div>
{/* Options Editor for SELECT, RADIO, MULTI_SELECT, CHECKBOX_GROUP */}
{REQUIRES_OPTIONS.includes(fieldForm.fieldType) && (
<div className="border border-gray-200 rounded-lg p-4">
<label className="block text-sm font-medium text-gray-700 mb-3">
<div className="border border-[#e5e7eb] rounded-xl p-4">
<label className="block text-sm font-medium text-[#00293d] mb-3">
Options
</label>
<div className="flex space-x-2 mb-3">
@@ -731,19 +731,19 @@ export default function SectionDetailPage() {
value={optionInput.value}
onChange={(e) => setOptionInput({ ...optionInput, value: e.target.value })}
placeholder="Value"
className="flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm text-gray-900 bg-white placeholder:text-gray-500"
className="flex-1 px-3 py-2 border border-[#e5e7eb] rounded-xl focus:ring-2 focus:ring-[#f5a623] focus:border-[#f5a623] text-sm text-[#00293d] bg-white placeholder:text-[#666666]"
/>
<input
type="text"
value={optionInput.label}
onChange={(e) => setOptionInput({ ...optionInput, label: e.target.value })}
placeholder="Label"
className="flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm text-gray-900 bg-white placeholder:text-gray-500"
className="flex-1 px-3 py-2 border border-[#e5e7eb] rounded-xl focus:ring-2 focus:ring-[#f5a623] focus:border-[#f5a623] text-sm text-[#00293d] bg-white placeholder:text-[#666666]"
/>
<button
type="button"
onClick={addOption}
className="px-4 py-2 bg-gray-100 hover:bg-gray-200 text-gray-700 text-sm font-medium rounded-lg transition-colors"
className="px-4 py-2 bg-[#e8f0ee] hover:bg-[#d5e5e1] text-[#00293d] text-sm font-medium rounded-lg transition-colors"
>
Add
</button>
@@ -753,11 +753,11 @@ export default function SectionDetailPage() {
{fieldForm.options.map((opt, index) => (
<div
key={index}
className="flex items-center justify-between px-3 py-2 bg-gray-50 rounded"
className="flex items-center justify-between px-3 py-2 bg-[#f5f9f8] rounded"
>
<span className="text-sm">
<span className="font-medium text-gray-900">{opt.label}</span>
<span className="text-xs text-gray-400 ml-2">({opt.value})</span>
<span className="font-medium text-[#00293d]">{opt.label}</span>
<span className="text-xs text-[#9ca3af] ml-2">({opt.value})</span>
</span>
<button
type="button"
@@ -777,68 +777,68 @@ export default function SectionDetailPage() {
{/* Range Config for RANGE type */}
{fieldForm.fieldType === 'RANGE' && (
<div className="border border-gray-200 rounded-lg p-4">
<label className="block text-sm font-medium text-gray-700 mb-3">
<div className="border border-[#e5e7eb] rounded-xl p-4">
<label className="block text-sm font-medium text-[#00293d] mb-3">
Range Configuration
</label>
<div className="grid grid-cols-4 gap-3">
<div>
<label className="block text-xs text-gray-500 mb-1">Min</label>
<label className="block text-xs text-[#666666] mb-1">Min</label>
<input
type="number"
value={fieldForm.rangeConfig?.min || 0}
onChange={(e) => updateRangeConfig('min', e.target.value)}
onFocus={(e) => e.target.select()}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm text-gray-900 bg-white"
className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:ring-2 focus:ring-[#f5a623] focus:border-[#f5a623] text-sm text-[#00293d] bg-white"
/>
</div>
<div>
<label className="block text-xs text-gray-500 mb-1">Max</label>
<label className="block text-xs text-[#666666] mb-1">Max</label>
<input
type="number"
value={fieldForm.rangeConfig?.max || 100}
onChange={(e) => updateRangeConfig('max', e.target.value)}
onFocus={(e) => e.target.select()}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm text-gray-900 bg-white"
className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:ring-2 focus:ring-[#f5a623] focus:border-[#f5a623] text-sm text-[#00293d] bg-white"
/>
</div>
<div>
<label className="block text-xs text-gray-500 mb-1">Step</label>
<label className="block text-xs text-[#666666] mb-1">Step</label>
<input
type="number"
value={fieldForm.rangeConfig?.step || 1}
onChange={(e) => updateRangeConfig('step', e.target.value)}
onFocus={(e) => e.target.select()}
min={1}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm text-gray-900 bg-white"
className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:ring-2 focus:ring-[#f5a623] focus:border-[#f5a623] text-sm text-[#00293d] bg-white"
/>
</div>
<div>
<label className="block text-xs text-gray-500 mb-1">Unit</label>
<label className="block text-xs text-[#666666] mb-1">Unit</label>
<input
type="text"
value={fieldForm.rangeConfig?.unit || ''}
onChange={(e) => updateRangeConfig('unit', e.target.value)}
placeholder="e.g., years"
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm text-gray-900 bg-white placeholder:text-gray-500"
className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:ring-2 focus:ring-[#f5a623] focus:border-[#f5a623] text-sm text-[#00293d] bg-white placeholder:text-[#666666]"
/>
</div>
</div>
</div>
)}
</div>
<div className="px-6 py-4 border-t border-gray-200 flex justify-end space-x-3">
<div className="px-6 py-4 border-t border-[#e5e7eb] flex justify-end space-x-3">
<button
type="button"
onClick={closeFieldModal}
className="px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
className="px-4 py-2 border border-[#e5e7eb] text-[#00293d] rounded-xl hover:bg-[#f5f9f8] transition-colors"
>
Cancel
</button>
<button
type="submit"
disabled={isSubmitting}
className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50"
className="px-4 py-2 bg-[#f5a623] text-white rounded-xl hover:bg-[#e09620] transition-colors disabled:opacity-50"
>
{isSubmitting ? 'Saving...' : fieldModalType === 'create' ? 'Create Field' : 'Save Changes'}
</button>
@@ -851,9 +851,9 @@ export default function SectionDetailPage() {
{/* Field Delete Modal */}
{fieldModalType === 'delete' && selectedField && (
<div className="fixed inset-0 bg-black/30 flex items-center justify-center z-50">
<div className="bg-white rounded-lg shadow-xl max-w-md w-full mx-4">
<div className="px-6 py-4 border-b border-gray-200">
<h3 className="text-lg font-semibold text-gray-900">Delete Field</h3>
<div className="bg-white rounded-xl shadow-sm border border-[#e5e7eb]-xl max-w-md w-full mx-4">
<div className="px-6 py-4 border-b border-[#e5e7eb]">
<h3 className="text-lg font-semibold text-[#00293d] font-fractul">Delete Field</h3>
</div>
<div className="px-6 py-4">
{modalError && (
@@ -861,14 +861,14 @@ export default function SectionDetailPage() {
<p className="text-red-700 text-sm">{modalError}</p>
</div>
)}
<p className="text-gray-600">
<p className="text-[#666666] font-serif">
Are you sure you want to delete the field <span className="font-semibold">{selectedField.name}</span>?
</p>
</div>
<div className="px-6 py-4 border-t border-gray-200 flex justify-end space-x-3">
<div className="px-6 py-4 border-t border-[#e5e7eb] flex justify-end space-x-3">
<button
onClick={closeFieldModal}
className="px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
className="px-4 py-2 border border-[#e5e7eb] text-[#00293d] rounded-xl hover:bg-[#f5f9f8] transition-colors"
>
Cancel
</button>
@@ -887,9 +887,9 @@ export default function SectionDetailPage() {
{/* Assign to Agent Type Modal */}
{assignModalType === 'assign' && (
<div className="fixed inset-0 bg-black/30 flex items-center justify-center z-50">
<div className="bg-white rounded-lg shadow-xl max-w-md w-full mx-4">
<div className="px-6 py-4 border-b border-gray-200">
<h3 className="text-lg font-semibold text-gray-900">Assign to Agent Type</h3>
<div className="bg-white rounded-xl shadow-sm border border-[#e5e7eb]-xl max-w-md w-full mx-4">
<div className="px-6 py-4 border-b border-[#e5e7eb]">
<h3 className="text-lg font-semibold text-[#00293d] font-fractul">Assign to Agent Type</h3>
</div>
<form onSubmit={handleAssign}>
<div className="px-6 py-4 space-y-4">
@@ -899,13 +899,13 @@ export default function SectionDetailPage() {
</div>
)}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
<label className="block text-sm font-medium text-[#00293d] mb-1">
Agent Type <span className="text-red-500">*</span>
</label>
<select
value={assignForm.agentTypeId}
onChange={(e) => setAssignForm({ ...assignForm, agentTypeId: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-900 bg-white"
className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:ring-2 focus:ring-[#f5a623] focus:border-[#f5a623] text-[#00293d] bg-white"
required
>
<option value="">Select agent type...</option>
@@ -917,7 +917,7 @@ export default function SectionDetailPage() {
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
<label className="block text-sm font-medium text-[#00293d] mb-1">
Sort Order
</label>
<input
@@ -927,7 +927,7 @@ export default function SectionDetailPage() {
onFocus={(e) => e.target.select()}
placeholder="0"
min={0}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-900 bg-white placeholder:text-gray-500"
className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:ring-2 focus:ring-[#f5a623] focus:border-[#f5a623] text-[#00293d] bg-white placeholder:text-[#666666]"
/>
</div>
<div className="flex items-center">
@@ -936,25 +936,25 @@ export default function SectionDetailPage() {
id="assignIsRequired"
checked={assignForm.isRequired}
onChange={(e) => setAssignForm({ ...assignForm, isRequired: e.target.checked })}
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
className="rounded border-[#e5e7eb] text-[#f5a623] focus:ring-[#f5a623]"
/>
<label htmlFor="assignIsRequired" className="ml-2 text-sm text-gray-700">
<label htmlFor="assignIsRequired" className="ml-2 text-sm text-[#00293d] font-serif">
Required section for this agent type
</label>
</div>
</div>
<div className="px-6 py-4 border-t border-gray-200 flex justify-end space-x-3">
<div className="px-6 py-4 border-t border-[#e5e7eb] flex justify-end space-x-3">
<button
type="button"
onClick={closeAssignModal}
className="px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
className="px-4 py-2 border border-[#e5e7eb] text-[#00293d] rounded-xl hover:bg-[#f5f9f8] transition-colors"
>
Cancel
</button>
<button
type="submit"
disabled={isSubmitting || !assignForm.agentTypeId}
className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50"
className="px-4 py-2 bg-[#f5a623] text-white rounded-xl hover:bg-[#e09620] transition-colors disabled:opacity-50"
>
{isSubmitting ? 'Assigning...' : 'Assign'}
</button>

View File

@@ -159,21 +159,21 @@ export default function ProfileSectionsPage() {
<div>
{/* Page Title */}
<div className="mb-6">
<h1 className="text-2xl font-bold text-gray-900">Profile Sections</h1>
<p className="text-gray-500">Manage profile sections and their fields for agent types</p>
<h1 className="text-2xl font-bold text-[#00293d] font-fractul">Profile Sections</h1>
<p className="text-[#666666] font-serif">Manage profile sections and their fields for agent types</p>
</div>
{/* Sections Table */}
<div className="bg-white rounded-lg shadow">
<div className="px-6 py-4 border-b border-gray-200">
<div className="bg-white rounded-xl shadow-sm border border-[#e5e7eb]">
<div className="px-6 py-4 border-b border-[#e5e7eb]">
<div className="flex justify-between items-center">
<div className="flex items-center space-x-4">
<label className="flex items-center space-x-2 text-sm text-gray-600">
<label className="flex items-center space-x-2 text-sm text-[#666666] font-serif">
<input
type="checkbox"
checked={showInactive}
onChange={(e) => setShowInactive(e.target.checked)}
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
className="rounded border-[#e5e7eb] text-[#f5a623] focus:ring-[#f5a623]"
/>
<span>Show Inactive</span>
</label>
@@ -181,7 +181,7 @@ export default function ProfileSectionsPage() {
<div className="flex space-x-3">
<button
onClick={fetchSections}
className="px-4 py-2 border border-gray-300 hover:bg-gray-50 text-gray-700 text-sm font-medium rounded-lg transition-colors flex items-center"
className="px-4 py-2 border border-[#e5e7eb] hover:bg-[#f5f9f8] text-[#00293d] text-sm font-medium rounded-lg transition-colors flex items-center"
>
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
@@ -190,7 +190,7 @@ export default function ProfileSectionsPage() {
</button>
<button
onClick={openCreateModal}
className="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium rounded-lg transition-colors flex items-center"
className="px-4 py-2 bg-[#f5a623] hover:bg-[#e09620] text-white text-sm font-medium rounded-lg transition-colors flex items-center"
>
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
@@ -210,73 +210,73 @@ export default function ProfileSectionsPage() {
<div className="overflow-x-auto">
{isLoading ? (
<div className="flex items-center justify-center py-12">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-[#f5a623]"></div>
</div>
) : sections.length === 0 ? (
<div className="text-center py-12">
<svg className="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<svg className="mx-auto h-12 w-12 text-[#9ca3af]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 10h16M4 14h16M4 18h16" />
</svg>
<p className="mt-2 text-gray-500">No profile sections found</p>
<p className="mt-2 text-[#666666] font-serif">No profile sections found</p>
<button
onClick={openCreateModal}
className="mt-4 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium rounded-lg transition-colors"
className="mt-4 px-4 py-2 bg-[#f5a623] hover:bg-[#e09620] text-white text-sm font-medium rounded-lg transition-colors"
>
Create First Section
</button>
</div>
) : (
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<table className="min-w-full divide-y divide-[#e5e7eb]">
<thead className="bg-[#f5f9f8]">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<th className="px-6 py-3 text-left text-xs font-medium text-[#666666] uppercase tracking-wider">
Section
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<th className="px-6 py-3 text-left text-xs font-medium text-[#666666] uppercase tracking-wider">
Description
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<th className="px-6 py-3 text-left text-xs font-medium text-[#666666] uppercase tracking-wider">
Fields
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<th className="px-6 py-3 text-left text-xs font-medium text-[#666666] uppercase tracking-wider">
Agent Types
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<th className="px-6 py-3 text-left text-xs font-medium text-[#666666] uppercase tracking-wider">
Global
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<th className="px-6 py-3 text-left text-xs font-medium text-[#666666] uppercase tracking-wider">
Status
</th>
<th className="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
<th className="px-6 py-3 text-right text-xs font-medium text-[#666666] uppercase tracking-wider">
Actions
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
<tbody className="bg-white divide-y divide-[#e5e7eb]">
{sections.map((section) => (
<tr key={section.id} className="hover:bg-gray-50">
<tr key={section.id} className="hover:bg-[#f5f9f8]">
<td className="px-6 py-4 whitespace-nowrap">
<div className="flex items-center">
{section.icon && (
<span className="mr-3 text-gray-500">
<span className="mr-3 text-[#666666]">
<SectionIcon name={section.icon} className="w-5 h-5" />
</span>
)}
<div>
<div className="text-sm font-medium text-gray-900">{section.name}</div>
<div className="text-xs text-gray-500">{section.slug}</div>
<div className="text-sm font-medium text-[#00293d]">{section.name}</div>
<div className="text-xs text-[#666666]">{section.slug}</div>
</div>
</div>
</td>
<td className="px-6 py-4">
<div className="text-sm text-gray-500 max-w-xs truncate">
<div className="text-sm text-[#666666] max-w-xs truncate">
{section.description || '-'}
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<Link
href={`/dashboard/profile-sections/${section.id}`}
className="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-800 hover:bg-blue-200"
className="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium bg-[#e8f0ee] text-[#5ba4a4] hover:bg-[#d5e5e1]"
>
{section._count?.fields || 0} fields
<svg className="ml-1 w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -285,9 +285,9 @@ export default function ProfileSectionsPage() {
</Link>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<div className="text-sm text-gray-500">
<div className="text-sm text-[#666666]">
{section.isGlobal ? (
<span className="text-purple-600 font-medium">All (Global)</span>
<span className="text-[#5ba4a4] font-medium">All (Global)</span>
) : (
`${section._count?.agentTypeSections || 0} assigned`
)}
@@ -296,15 +296,15 @@ export default function ProfileSectionsPage() {
<td className="px-6 py-4 whitespace-nowrap">
<div className="flex items-center space-x-2">
{section.isSystem && (
<span className="inline-flex px-2 py-1 text-xs font-semibold rounded-full bg-amber-100 text-amber-800">
<span className="inline-flex px-2 py-1 text-xs font-semibold rounded-full bg-[#fff7ed] text-[#f5a623]">
System
</span>
)}
<span
className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${
section.isGlobal
? 'bg-purple-100 text-purple-800'
: 'bg-gray-100 text-gray-600'
? 'bg-[#fff7ed] text-[#f5a623]'
: 'bg-[#e8f0ee] text-[#666666]'
}`}
>
{section.isGlobal ? 'Global' : 'Specific'}
@@ -331,19 +331,19 @@ export default function ProfileSectionsPage() {
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<Link
href={`/dashboard/profile-sections/${section.id}`}
className="text-blue-600 hover:text-blue-900 mr-4"
className="text-[#f5a623] hover:text-[#e09620] mr-4"
>
Manage
</Link>
<button
onClick={() => openEditModal(section)}
className="text-gray-600 hover:text-gray-900 mr-4"
className="text-[#5ba4a4] hover:text-[#5ba4a4]/80 mr-4"
>
Edit
</button>
{section.isSystem ? (
<span
className="text-gray-400 cursor-not-allowed"
className="text-[#9ca3af] cursor-not-allowed"
title="System sections cannot be deleted"
>
Delete
@@ -367,10 +367,10 @@ export default function ProfileSectionsPage() {
{/* Create/Edit Modal */}
{(modalType === 'create' || modalType === 'edit') && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="bg-white rounded-lg shadow-xl max-w-md w-full mx-4">
<div className="px-6 py-4 border-b border-gray-200">
<h3 className="text-lg font-semibold text-gray-900">
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
<div className="bg-white rounded-xl shadow-xl max-w-md w-full mx-4">
<div className="px-6 py-4 border-b border-[#e5e7eb]">
<h3 className="text-lg font-semibold text-[#00293d] font-fractul">
{modalType === 'create' ? 'Create Profile Section' : 'Edit Profile Section'}
</h3>
</div>
@@ -382,44 +382,44 @@ export default function ProfileSectionsPage() {
</div>
)}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
<label className="block text-sm font-medium text-[#00293d] mb-1">
Name <span className="text-red-500">*</span>
</label>
<input
type="text"
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-900 bg-white placeholder:text-gray-500"
className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:ring-2 focus:ring-[#f5a623] focus:border-[#f5a623] text-[#00293d] bg-white placeholder:text-[#666666]"
placeholder="e.g., Location, Experience, Education"
required
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
<label className="block text-sm font-medium text-[#00293d] mb-1">
Description
</label>
<textarea
value={formData.description}
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
rows={3}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-900 bg-white placeholder:text-gray-500"
className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:ring-2 focus:ring-[#f5a623] focus:border-[#f5a623] text-[#00293d] bg-white placeholder:text-[#666666]"
placeholder="Brief description of this section"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
<label className="block text-sm font-medium text-[#00293d] mb-1">
Icon (emoji)
</label>
<input
type="text"
value={formData.icon}
onChange={(e) => setFormData({ ...formData, icon: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-900 bg-white placeholder:text-gray-500"
className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:ring-2 focus:ring-[#f5a623] focus:border-[#f5a623] text-[#00293d] bg-white placeholder:text-[#666666]"
placeholder="e.g., location_on"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
<label className="block text-sm font-medium text-[#00293d] mb-1">
Sort Order
</label>
<input
@@ -429,7 +429,7 @@ export default function ProfileSectionsPage() {
onFocus={(e) => e.target.select()}
placeholder="0"
min={0}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-900 bg-white placeholder:text-gray-500"
className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:ring-2 focus:ring-[#f5a623] focus:border-[#f5a623] text-[#00293d] bg-white placeholder:text-[#666666]"
/>
</div>
<div className="flex items-center space-x-6">
@@ -439,9 +439,9 @@ export default function ProfileSectionsPage() {
id="isActive"
checked={formData.isActive}
onChange={(e) => setFormData({ ...formData, isActive: e.target.checked })}
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
className="rounded border-[#e5e7eb] text-[#f5a623] focus:ring-[#f5a623]"
/>
<label htmlFor="isActive" className="ml-2 text-sm text-gray-700">
<label htmlFor="isActive" className="ml-2 text-sm text-[#00293d] font-serif">
Active
</label>
</div>
@@ -451,9 +451,9 @@ export default function ProfileSectionsPage() {
id="isGlobal"
checked={formData.isGlobal}
onChange={(e) => setFormData({ ...formData, isGlobal: e.target.checked })}
className="rounded border-gray-300 text-purple-600 focus:ring-purple-500"
className="rounded border-[#e5e7eb] text-[#5ba4a4] focus:ring-[#5ba4a4]"
/>
<label htmlFor="isGlobal" className="ml-2 text-sm text-gray-700">
<label htmlFor="isGlobal" className="ml-2 text-sm text-[#00293d] font-serif">
Global (all agent types)
</label>
</div>
@@ -464,25 +464,25 @@ export default function ProfileSectionsPage() {
id="isRepeatable"
checked={formData.isRepeatable}
onChange={(e) => setFormData({ ...formData, isRepeatable: e.target.checked })}
className="rounded border-gray-300 text-orange-600 focus:ring-orange-500"
className="rounded border-[#e5e7eb] text-[#f5a623] focus:ring-[#f5a623]"
/>
<label htmlFor="isRepeatable" className="ml-2 text-sm text-gray-700">
<label htmlFor="isRepeatable" className="ml-2 text-sm text-[#00293d] font-serif">
Allow Multiple Entries (e.g., certifications, education)
</label>
</div>
</div>
<div className="px-6 py-4 border-t border-gray-200 flex justify-end space-x-3">
<div className="px-6 py-4 border-t border-[#e5e7eb] flex justify-end space-x-3">
<button
type="button"
onClick={closeModal}
className="px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
className="px-4 py-2 border border-[#e5e7eb] text-[#00293d] rounded-xl hover:bg-[#f5f9f8] transition-colors"
>
Cancel
</button>
<button
type="submit"
disabled={isSubmitting}
className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50"
className="px-4 py-2 bg-[#f5a623] text-white rounded-xl hover:bg-[#e09620] transition-colors disabled:opacity-50"
>
{isSubmitting ? 'Saving...' : modalType === 'create' ? 'Create' : 'Save Changes'}
</button>
@@ -494,10 +494,10 @@ export default function ProfileSectionsPage() {
{/* Delete Confirmation Modal */}
{modalType === 'delete' && selectedSection && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="bg-white rounded-lg shadow-xl max-w-md w-full mx-4">
<div className="px-6 py-4 border-b border-gray-200">
<h3 className="text-lg font-semibold text-gray-900">Delete Profile Section</h3>
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
<div className="bg-white rounded-xl shadow-xl max-w-md w-full mx-4">
<div className="px-6 py-4 border-b border-[#e5e7eb]">
<h3 className="text-lg font-semibold text-[#00293d] font-fractul">Delete Profile Section</h3>
</div>
<div className="px-6 py-4">
{modalError && (
@@ -505,7 +505,7 @@ export default function ProfileSectionsPage() {
<p className="text-red-700 text-sm">{modalError}</p>
</div>
)}
<p className="text-gray-600">
<p className="text-[#666666] font-serif">
Are you sure you want to delete <span className="font-semibold">{selectedSection.name}</span>?
</p>
{(selectedSection._count?.fields && selectedSection._count.fields > 0) && (
@@ -514,10 +514,10 @@ export default function ProfileSectionsPage() {
</p>
)}
</div>
<div className="px-6 py-4 border-t border-gray-200 flex justify-end space-x-3">
<div className="px-6 py-4 border-t border-[#e5e7eb] flex justify-end space-x-3">
<button
onClick={closeModal}
className="px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
className="px-4 py-2 border border-[#e5e7eb] text-[#00293d] rounded-xl hover:bg-[#f5f9f8] transition-colors"
>
Cancel
</button>

View File

@@ -234,7 +234,7 @@ export default function UserDetailPage() {
if (isLoading) {
return (
<div className="flex items-center justify-center py-12">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-[#f5a623]"></div>
</div>
);
}
@@ -247,7 +247,7 @@ export default function UserDetailPage() {
</div>
<button
onClick={() => router.push('/dashboard/users')}
className="mt-4 px-4 py-2 text-blue-600 hover:text-blue-800"
className="mt-4 px-4 py-2 text-[#f5a623] hover:text-[#e09620]"
>
Back to Users
</button>
@@ -258,10 +258,10 @@ export default function UserDetailPage() {
if (!user) {
return (
<div className="p-6">
<p className="text-gray-500">User not found</p>
<p className="text-[#666666] font-serif">User not found</p>
<button
onClick={() => router.push('/dashboard/users')}
className="mt-4 px-4 py-2 text-blue-600 hover:text-blue-800"
className="mt-4 px-4 py-2 text-[#f5a623] hover:text-[#e09620]"
>
Back to Users
</button>
@@ -274,7 +274,7 @@ export default function UserDetailPage() {
{/* Back Button */}
<button
onClick={() => router.push('/dashboard/users')}
className="mb-6 flex items-center text-gray-600 hover:text-gray-900"
className="mb-6 flex items-center text-[#666666] hover:text-[#00293d]"
>
<svg className="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
@@ -295,7 +295,7 @@ export default function UserDetailPage() {
)}
{/* User Header */}
<div className="bg-white rounded-lg shadow mb-6">
<div className="bg-white rounded-xl shadow-sm border border-[#e5e7eb] mb-6">
<div className="p-6">
<div className="flex items-center">
{/* Avatar */}
@@ -303,8 +303,8 @@ export default function UserDetailPage() {
{avatarUrl ? (
<img className="h-20 w-20 rounded-full object-cover" src={avatarUrl} alt="" />
) : (
<div className="h-20 w-20 rounded-full bg-gray-200 flex items-center justify-center">
<span className="text-gray-500 font-medium text-2xl">
<div className="h-20 w-20 rounded-full bg-[#e8f0ee] flex items-center justify-center">
<span className="text-[#666666] font-medium text-2xl">
{user.profile?.firstName?.[0] || user.email[0].toUpperCase()}
</span>
</div>
@@ -313,10 +313,10 @@ export default function UserDetailPage() {
{/* Name & Email */}
<div className="ml-6 flex-1">
<h1 className="text-2xl font-bold text-gray-900">
<h1 className="text-2xl font-bold text-[#00293d] font-fractul">
{user.profile?.firstName} {user.profile?.lastName}
</h1>
<p className="text-gray-500">{user.email}</p>
<p className="text-[#666666] font-serif">{user.email}</p>
</div>
{/* Role & Status Badges */}
@@ -326,8 +326,8 @@ export default function UserDetailPage() {
user.role === 'ADMIN'
? 'bg-red-100 text-red-800'
: user.role === 'AGENT'
? 'bg-purple-100 text-purple-800'
: 'bg-blue-100 text-blue-800'
? 'bg-[#fff7ed] text-[#f5a623]'
: 'bg-[#e8f0ee] text-[#5ba4a4]'
}`}
>
{user.role}
@@ -349,20 +349,20 @@ export default function UserDetailPage() {
</div>
{/* Basic Information */}
<div className="bg-white rounded-lg shadow mb-6">
<div className="px-6 py-4 border-b border-gray-200">
<h2 className="text-lg font-semibold text-gray-900">Basic Information</h2>
<div className="bg-white rounded-xl shadow-sm border border-[#e5e7eb] mb-6">
<div className="px-6 py-4 border-b border-[#e5e7eb]">
<h2 className="text-lg font-semibold text-[#00293d] font-fractul">Basic Information</h2>
</div>
<div className="p-6">
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
<div>
<p className="text-sm text-gray-500">Provider</p>
<p className="text-sm font-medium text-gray-900">
<p className="text-sm text-[#666666]">Provider</p>
<p className="text-sm font-medium text-[#00293d]">
{user.authProvider === 'LOCAL' ? 'Email' : user.authProvider}
</p>
</div>
<div>
<p className="text-sm text-gray-500">Email Verified</p>
<p className="text-sm text-[#666666]">Email Verified</p>
<div className="flex items-center">
{user.emailVerified ? (
<>
@@ -377,25 +377,25 @@ export default function UserDetailPage() {
</>
) : (
<>
<svg className="w-5 h-5 text-gray-400 mr-1" fill="currentColor" viewBox="0 0 20 20">
<svg className="w-5 h-5 text-[#9ca3af] mr-1" fill="currentColor" viewBox="0 0 20 20">
<path
fillRule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
clipRule="evenodd"
/>
</svg>
<span className="text-sm font-medium text-gray-500">Not Verified</span>
<span className="text-sm font-medium text-[#666666]">Not Verified</span>
</>
)}
</div>
</div>
<div>
<p className="text-sm text-gray-500">Joined</p>
<p className="text-sm font-medium text-gray-900">{formatDate(user.createdAt)}</p>
<p className="text-sm text-[#666666]">Joined</p>
<p className="text-sm font-medium text-[#00293d]">{formatDate(user.createdAt)}</p>
</div>
<div>
<p className="text-sm text-gray-500">Last Login</p>
<p className="text-sm font-medium text-gray-900">
<p className="text-sm text-[#666666]">Last Login</p>
<p className="text-sm font-medium text-[#00293d]">
{user.lastLoginAt ? formatDate(user.lastLoginAt) : 'Never'}
</p>
</div>
@@ -404,33 +404,33 @@ export default function UserDetailPage() {
</div>
{/* Contact Information */}
<div className="bg-white rounded-lg shadow mb-6">
<div className="px-6 py-4 border-b border-gray-200">
<h2 className="text-lg font-semibold text-gray-900">Contact Information</h2>
<div className="bg-white rounded-xl shadow-sm border border-[#e5e7eb] mb-6">
<div className="px-6 py-4 border-b border-[#e5e7eb]">
<h2 className="text-lg font-semibold text-[#00293d] font-fractul">Contact Information</h2>
</div>
<div className="p-6">
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
<div>
<p className="text-sm text-gray-500">Phone</p>
<p className="text-sm font-medium text-gray-900">
<p className="text-sm text-[#666666]">Phone</p>
<p className="text-sm font-medium text-[#00293d]">
{user.profile?.phone || 'Not provided'}
</p>
</div>
<div>
<p className="text-sm text-gray-500">City</p>
<p className="text-sm font-medium text-gray-900">
<p className="text-sm text-[#666666]">City</p>
<p className="text-sm font-medium text-[#00293d]">
{user.profile?.city || 'Not provided'}
</p>
</div>
<div>
<p className="text-sm text-gray-500">State</p>
<p className="text-sm font-medium text-gray-900">
<p className="text-sm text-[#666666]">State</p>
<p className="text-sm font-medium text-[#00293d]">
{user.profile?.state || 'Not provided'}
</p>
</div>
<div>
<p className="text-sm text-gray-500">Country</p>
<p className="text-sm font-medium text-gray-900">
<p className="text-sm text-[#666666]">Country</p>
<p className="text-sm font-medium text-[#00293d]">
{user.profile?.country || 'Not provided'}
</p>
</div>
@@ -440,21 +440,21 @@ export default function UserDetailPage() {
{/* Agent Details (only for AGENT role) */}
{user.role === 'AGENT' && user.agentProfile && (
<div className="bg-white rounded-lg shadow mb-6">
<div className="px-6 py-4 border-b border-gray-200">
<h2 className="text-lg font-semibold text-gray-900">Agent Details</h2>
<div className="bg-white rounded-xl shadow-sm border border-[#e5e7eb] mb-6">
<div className="px-6 py-4 border-b border-[#e5e7eb]">
<h2 className="text-lg font-semibold text-[#00293d] font-fractul">Agent Details</h2>
</div>
<div className="p-6">
{/* Agent Details Grid */}
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
{/* Agent Type - Editable */}
<div>
<p className="text-sm text-gray-500 mb-1">Agent Type</p>
<p className="text-sm text-[#666666] mb-1">Agent Type</p>
<div className="flex items-center space-x-2">
<select
value={selectedAgentTypeId}
onChange={(e) => setSelectedAgentTypeId(e.target.value)}
className="w-full max-w-[160px] px-2 py-1 text-sm border border-gray-300 rounded focus:ring-1 focus:ring-blue-500 focus:border-blue-500 text-gray-900 bg-white"
className="w-full max-w-[160px] px-2 py-1 text-sm border border-[#e5e7eb] rounded-lg focus:ring-1 focus:ring-[#f5a623] focus:border-[#f5a623] text-[#00293d] bg-white"
>
<option value="">Select...</option>
{agentTypes.map((type) => (
@@ -467,7 +467,7 @@ export default function UserDetailPage() {
<button
onClick={handleUpdateAgentType}
disabled={isUpdatingAgentType}
className="px-2 py-1 bg-blue-600 hover:bg-blue-700 text-white text-xs font-medium rounded transition-colors disabled:opacity-50"
className="px-2 py-1 bg-[#f5a623] hover:bg-[#e09620] text-white text-xs font-medium rounded transition-colors disabled:opacity-50"
>
{isUpdatingAgentType ? '...' : 'Save'}
</button>
@@ -475,32 +475,32 @@ export default function UserDetailPage() {
</div>
</div>
<div>
<p className="text-sm text-gray-500">Slug</p>
<p className="text-sm font-medium text-gray-900">
<p className="text-sm text-[#666666]">Slug</p>
<p className="text-sm font-medium text-[#00293d]">
{user.agentProfile.slug || 'Not set'}
</p>
</div>
<div>
<p className="text-sm text-gray-500">Company</p>
<p className="text-sm font-medium text-gray-900">
<p className="text-sm text-[#666666]">Company</p>
<p className="text-sm font-medium text-[#00293d]">
{user.agentProfile.companyName || 'Not provided'}
</p>
</div>
<div>
<p className="text-sm text-gray-500">License Number</p>
<p className="text-sm font-medium text-gray-900">
<p className="text-sm text-[#666666]">License Number</p>
<p className="text-sm font-medium text-[#00293d]">
{user.agentProfile.licenseNumber || 'Not provided'}
</p>
</div>
<div>
<p className="text-sm text-gray-500">Years of Experience</p>
<p className="text-sm font-medium text-gray-900">
<p className="text-sm text-[#666666]">Years of Experience</p>
<p className="text-sm font-medium text-[#00293d]">
{user.agentProfile.yearsOfExperience ?? 'Not provided'}
</p>
</div>
<div>
<p className="text-sm text-gray-500">Profile Status</p>
<p className="text-sm font-medium text-gray-900">
<p className="text-sm text-[#666666]">Profile Status</p>
<p className="text-sm font-medium text-[#00293d]">
{user.agentProfile.profileCompleteness}%{' '}
<span
className={`ml-1 inline-flex px-2 py-0.5 text-xs font-semibold rounded-full ${
@@ -517,19 +517,19 @@ export default function UserDetailPage() {
{/* Headline & Bio */}
{(user.agentProfile.headline || user.agentProfile.bio) && (
<div className="mt-6 pt-6 border-t border-gray-200">
<div className="mt-6 pt-6 border-t border-[#e5e7eb]">
{user.agentProfile.headline && (
<div className="mb-4">
<p className="text-sm text-gray-500 mb-1">Headline</p>
<p className="text-sm font-medium text-gray-900">
<p className="text-sm text-[#666666] mb-1">Headline</p>
<p className="text-sm font-medium text-[#00293d]">
{user.agentProfile.headline}
</p>
</div>
)}
{user.agentProfile.bio && (
<div>
<p className="text-sm text-gray-500 mb-1">Bio</p>
<p className="text-sm text-gray-700 whitespace-pre-wrap">
<p className="text-sm text-[#666666] mb-1">Bio</p>
<p className="text-sm text-[#666666] whitespace-pre-wrap">
{user.agentProfile.bio}
</p>
</div>
@@ -542,10 +542,10 @@ export default function UserDetailPage() {
{/* Verification Section (only for AGENT role) */}
{user.role === 'AGENT' && user.agentProfile && (
<div className="bg-white rounded-lg shadow mb-6">
<div className="px-6 py-4 border-b border-gray-200">
<div className="bg-white rounded-xl shadow-sm border border-[#e5e7eb] mb-6">
<div className="px-6 py-4 border-b border-[#e5e7eb]">
<div className="flex items-center justify-between">
<h2 className="text-lg font-semibold text-gray-900">Verification Status</h2>
<h2 className="text-lg font-semibold text-[#00293d] font-fractul">Verification Status</h2>
{getVerificationStatusBadge(user.agentProfile.verificationStatus as VerificationStatus)}
</div>
</div>
@@ -568,21 +568,21 @@ export default function UserDetailPage() {
{/* Uploaded Documents */}
<div className="mb-6">
<h3 className="text-sm font-medium text-gray-900 mb-3">Uploaded Documents</h3>
<h3 className="text-sm font-medium text-[#00293d] mb-3">Uploaded Documents</h3>
{isLoadingDocuments ? (
<div className="flex items-center justify-center py-4">
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-blue-600"></div>
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-[#f5a623]"></div>
</div>
) : verificationDocuments.length > 0 ? (
<div className="space-y-2">
{verificationDocuments.map((doc, index) => (
<div
key={index}
className="flex items-center justify-between p-3 bg-gray-50 border border-gray-200 rounded-lg"
className="flex items-center justify-between p-3 bg-[#f5f9f8] border border-[#e5e7eb] rounded-xl"
>
<div className="flex items-center">
<svg
className="w-8 h-8 text-gray-400 mr-3"
className="w-8 h-8 text-[#9ca3af] mr-3"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
@@ -595,8 +595,8 @@ export default function UserDetailPage() {
/>
</svg>
<div>
<p className="text-sm font-medium text-gray-900">{doc.name}</p>
<p className="text-xs text-gray-500">{formatFileSize(doc.size)}</p>
<p className="text-sm font-medium text-[#00293d]">{doc.name}</p>
<p className="text-xs text-[#666666]">{formatFileSize(doc.size)}</p>
</div>
</div>
{doc.url && (
@@ -604,7 +604,7 @@ export default function UserDetailPage() {
href={doc.url}
target="_blank"
rel="noopener noreferrer"
className="px-3 py-1 text-sm text-blue-600 hover:text-blue-800 hover:bg-blue-50 rounded transition-colors"
className="px-3 py-1 text-sm text-[#f5a623] hover:text-[#e09620] hover:bg-[#fff7ed] rounded transition-colors"
>
Download
</a>
@@ -613,7 +613,7 @@ export default function UserDetailPage() {
))}
</div>
) : (
<p className="text-sm text-gray-500 py-4 text-center">
<p className="text-sm text-[#666666] py-4 text-center">
No documents uploaded yet
</p>
)}
@@ -621,19 +621,19 @@ export default function UserDetailPage() {
{/* Admin Actions */}
{user.agentProfile.verificationStatus !== 'APPROVED' && (
<div className="border-t border-gray-200 pt-6">
<h3 className="text-sm font-medium text-gray-900 mb-3">Admin Actions</h3>
<div className="border-t border-[#e5e7eb] pt-6">
<h3 className="text-sm font-medium text-[#00293d] mb-3">Admin Actions</h3>
{/* Note Input */}
<div className="mb-4">
<label className="block text-sm text-gray-500 mb-1">
<label className="block text-sm text-[#666666] mb-1">
Note (optional, required for rejection)
</label>
<textarea
value={verificationNote}
onChange={(e) => setVerificationNote(e.target.value)}
rows={2}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-900 bg-white placeholder:text-gray-500"
className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:ring-2 focus:ring-[#f5a623] focus:border-[#f5a623] text-[#00293d] bg-white placeholder:text-[#666666]"
placeholder="Enter a note for the agent (e.g., rejection reason)"
/>
</div>
@@ -736,7 +736,7 @@ export default function UserDetailPage() {
</button>
</div>
{!verificationNote.trim() && (
<p className="mt-2 text-xs text-gray-500">
<p className="mt-2 text-xs text-[#666666]">
* A note is required to reject verification
</p>
)}
@@ -750,7 +750,7 @@ export default function UserDetailPage() {
{/* Already Approved Message */}
{user.agentProfile.verificationStatus === 'APPROVED' && (
<div className="border-t border-gray-200 pt-6">
<div className="border-t border-[#e5e7eb] pt-6">
<div className="flex items-center text-green-600">
<svg className="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path

View File

@@ -96,24 +96,24 @@ export default function UsersPage() {
<div>
{/* Page Title */}
<div className="mb-6">
<h1 className="text-2xl font-bold text-gray-900">Users</h1>
<p className="text-gray-500">Manage all registered users</p>
<h1 className="text-2xl font-bold text-[#00293d] font-fractul">Users</h1>
<p className="text-[#666666] font-serif">Manage all registered users</p>
</div>
{/* Users Table */}
<div className="bg-white rounded-lg shadow">
<div className="px-6 py-4 border-b border-gray-200">
<div className="bg-white rounded-xl shadow-sm border border-[#e5e7eb]">
<div className="px-6 py-4 border-b border-[#e5e7eb]">
<div className="flex justify-between items-center">
{/* Filter Tabs */}
<div className="flex space-x-1 bg-gray-100 rounded-lg p-1">
<div className="flex space-x-1 bg-[#f5f9f8] rounded-lg p-1">
{roleFilters.map((filter) => (
<button
key={filter.value}
onClick={() => handleFilterChange(filter.value)}
className={`px-4 py-2 text-sm font-medium rounded-md transition-colors ${
activeFilter === filter.value
? 'bg-white text-gray-900 shadow-sm'
: 'text-gray-600 hover:text-gray-900'
? 'bg-white text-[#00293d] shadow-sm'
: 'text-[#666666] hover:text-[#00293d]'
}`}
>
{filter.label}
@@ -122,7 +122,7 @@ export default function UsersPage() {
</div>
<button
onClick={fetchUsers}
className="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium rounded-lg transition-colors flex items-center"
className="px-4 py-2 bg-[#f5a623] hover:bg-[#e09620] text-white text-sm font-medium rounded-lg transition-colors flex items-center"
>
<svg
className="w-4 h-4 mr-2"
@@ -151,12 +151,12 @@ export default function UsersPage() {
<div className="overflow-x-auto">
{isLoading ? (
<div className="flex items-center justify-center py-12">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-[#f5a623]"></div>
</div>
) : users.length === 0 ? (
<div className="text-center py-12">
<svg
className="mx-auto h-12 w-12 text-gray-400"
className="mx-auto h-12 w-12 text-[#9ca3af]"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
@@ -168,40 +168,40 @@ export default function UsersPage() {
d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"
/>
</svg>
<p className="mt-2 text-gray-500">No users found</p>
<p className="mt-2 text-[#666666]">No users found</p>
</div>
) : (
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<table className="min-w-full divide-y divide-[#e5e7eb]">
<thead className="bg-[#f5f9f8]">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<th className="px-6 py-3 text-left text-xs font-medium text-[#666666] uppercase tracking-wider">
User
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<th className="px-6 py-3 text-left text-xs font-medium text-[#666666] uppercase tracking-wider">
Role
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<th className="px-6 py-3 text-left text-xs font-medium text-[#666666] uppercase tracking-wider">
Status
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<th className="px-6 py-3 text-left text-xs font-medium text-[#666666] uppercase tracking-wider">
Provider
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<th className="px-6 py-3 text-left text-xs font-medium text-[#666666] uppercase tracking-wider">
Verified
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<th className="px-6 py-3 text-left text-xs font-medium text-[#666666] uppercase tracking-wider">
Joined
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<th className="px-6 py-3 text-left text-xs font-medium text-[#666666] uppercase tracking-wider">
Last Login
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
<tbody className="bg-white divide-y divide-[#e5e7eb]">
{users.map((user) => (
<tr
key={user.id}
className="hover:bg-gray-50 cursor-pointer"
className="hover:bg-[#f5f9f8] cursor-pointer"
onClick={() => router.push(`/dashboard/users/${user.id}`)}
>
<td className="px-6 py-4 whitespace-nowrap">
@@ -214,18 +214,18 @@ export default function UsersPage() {
alt=""
/>
) : (
<div className="h-10 w-10 rounded-full bg-gray-200 flex items-center justify-center">
<span className="text-gray-500 font-medium text-sm">
<div className="h-10 w-10 rounded-full bg-[#e8f0ee] flex items-center justify-center">
<span className="text-[#666666] font-medium text-sm">
{user.profile?.firstName?.[0] || user.email[0].toUpperCase()}
</span>
</div>
)}
</div>
<div className="ml-4">
<div className="text-sm font-medium text-gray-900">
<div className="text-sm font-medium text-[#00293d]">
{user.profile?.firstName} {user.profile?.lastName}
</div>
<div className="text-sm text-gray-500">{user.email}</div>
<div className="text-sm text-[#666666]">{user.email}</div>
</div>
</div>
</td>
@@ -235,8 +235,8 @@ export default function UsersPage() {
user.role === 'ADMIN'
? 'bg-red-100 text-red-800'
: user.role === 'AGENT'
? 'bg-purple-100 text-purple-800'
: 'bg-blue-100 text-blue-800'
? 'bg-[#fff7ed] text-[#f5a623]'
: 'bg-[#e8f0ee] text-[#5ba4a4]'
}`}
>
{user.role}
@@ -257,7 +257,7 @@ export default function UsersPage() {
{user.status === 'PENDING_VERIFICATION' ? 'PENDING' : user.status}
</span>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<td className="px-6 py-4 whitespace-nowrap text-sm text-[#666666]">
{user.authProvider === 'LOCAL' ? 'Email' : user.authProvider}
</td>
<td className="px-6 py-4 whitespace-nowrap">
@@ -275,7 +275,7 @@ export default function UsersPage() {
</svg>
) : (
<svg
className="w-5 h-5 text-gray-400"
className="w-5 h-5 text-[#9ca3af]"
fill="currentColor"
viewBox="0 0 20 20"
>
@@ -287,10 +287,10 @@ export default function UsersPage() {
</svg>
)}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<td className="px-6 py-4 whitespace-nowrap text-sm text-[#666666]">
{new Date(user.createdAt).toLocaleDateString()}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<td className="px-6 py-4 whitespace-nowrap text-sm text-[#666666]">
{user.lastLoginAt
? new Date(user.lastLoginAt).toLocaleDateString()
: 'Never'}
@@ -304,8 +304,8 @@ export default function UsersPage() {
{/* Pagination */}
{totalPages > 1 && (
<div className="px-6 py-4 border-t border-gray-200 flex items-center justify-between">
<p className="text-sm text-gray-500">
<div className="px-6 py-4 border-t border-[#e5e7eb] flex items-center justify-between">
<p className="text-sm text-[#666666]">
Showing {(currentPage - 1) * limit + 1} to{' '}
{Math.min(currentPage * limit, totalUsers)} of {totalUsers} users
</p>
@@ -313,14 +313,14 @@ export default function UsersPage() {
<button
onClick={() => setCurrentPage((p) => Math.max(1, p - 1))}
disabled={currentPage === 1}
className="px-3 py-1 border border-gray-300 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
className="px-3 py-1 border border-[#e5e7eb] rounded-md text-sm font-medium text-[#00293d] hover:bg-[#f5f9f8] disabled:opacity-50 disabled:cursor-not-allowed"
>
Previous
</button>
<button
onClick={() => setCurrentPage((p) => Math.min(totalPages, p + 1))}
disabled={currentPage === totalPages}
className="px-3 py-1 border border-gray-300 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
className="px-3 py-1 border border-[#e5e7eb] rounded-md text-sm font-medium text-[#00293d] hover:bg-[#f5f9f8] disabled:opacity-50 disabled:cursor-not-allowed"
>
Next
</button>

View File

@@ -1,19 +1,70 @@
@import "tailwindcss";
:root {
--background: #ffffff;
--foreground: #171717;
/* RE-Quest Admin Design Tokens (matching web app) */
--color-primary-dark: #00293d;
--color-primary-light: #c4d9d4;
--color-accent-orange: #f5a623;
--color-accent-orange-hover: #e09620;
--color-accent-teal: #5ba4a4;
--color-background: #ffffff;
--color-background-alt: #f5f9f8;
--color-foreground: #171717;
--color-text-gray: #666666;
--color-text-muted: #9ca3af;
--color-border: #e5e7eb;
/* Spacing */
--spacing-section: 6rem;
--spacing-section-sm: 4rem;
/* Border Radius */
--radius-sm: 0.375rem;
--radius-md: 0.5rem;
--radius-lg: 1rem;
--radius-xl: 1.5rem;
--radius-2xl: 2rem;
--radius-full: 9999px;
/* Shadows */
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-background: #ffffff;
--color-foreground: #171717;
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
--font-serif: var(--font-source-serif-4);
--font-fractul: var(--font-fractul);
}
body {
background: var(--background);
color: var(--foreground);
font-family: Arial, Helvetica, sans-serif;
background: var(--color-background);
color: var(--color-foreground);
font-family: var(--font-source-serif-4, Arial, Helvetica, sans-serif);
}
/* Selection styling to match web */
::selection {
background-color: var(--color-primary-light);
color: var(--color-primary-dark);
}
/* Smooth scrolling */
html {
scroll-behavior: smooth;
}
/* Card hover effects */
.card-hover {
transition: all 0.3s ease;
}
.card-hover:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-lg);
}

View File

@@ -1,5 +1,6 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import localFont from "next/font/local";
import "./globals.css";
import { AuthProvider } from "@/context/AuthContext";
@@ -13,6 +14,110 @@ const geistMono = Geist_Mono({
subsets: ["latin"],
});
// Local Fractul font (matching web app)
const fractul = localFont({
src: [
{
path: "../fonts/fractul/Fractul-Regular.ttf",
weight: "400",
style: "normal",
},
{
path: "../fonts/fractul/Fractul-Italic.ttf",
weight: "400",
style: "italic",
},
{
path: "../fonts/fractul/Fractul-Medium.ttf",
weight: "500",
style: "normal",
},
{
path: "../fonts/fractul/Fractul-MediumItalic.ttf",
weight: "500",
style: "italic",
},
{
path: "../fonts/fractul/Fractul-SemiBold.ttf",
weight: "600",
style: "normal",
},
{
path: "../fonts/fractul/Fractul-SemiBoldItalic.ttf",
weight: "600",
style: "italic",
},
{
path: "../fonts/fractul/Fractul-Bold.ttf",
weight: "700",
style: "normal",
},
{
path: "../fonts/fractul/Fractul-BoldItalic.ttf",
weight: "700",
style: "italic",
},
{
path: "../fonts/fractul/Fractul-ExtraBold.ttf",
weight: "800",
style: "normal",
},
{
path: "../fonts/fractul/Fractul-ExtraBoldItalic.ttf",
weight: "800",
style: "italic",
},
],
variable: "--font-fractul",
});
// Local Source Serif 4 font (matching web app)
const sourceSerif4 = localFont({
src: [
{
path: "../fonts/Source_Serif_4/static/SourceSerif4-Regular.ttf",
weight: "400",
style: "normal",
},
{
path: "../fonts/Source_Serif_4/static/SourceSerif4-Italic.ttf",
weight: "400",
style: "italic",
},
{
path: "../fonts/Source_Serif_4/static/SourceSerif4-Medium.ttf",
weight: "500",
style: "normal",
},
{
path: "../fonts/Source_Serif_4/static/SourceSerif4-MediumItalic.ttf",
weight: "500",
style: "italic",
},
{
path: "../fonts/Source_Serif_4/static/SourceSerif4-SemiBold.ttf",
weight: "600",
style: "normal",
},
{
path: "../fonts/Source_Serif_4/static/SourceSerif4-SemiBoldItalic.ttf",
weight: "600",
style: "italic",
},
{
path: "../fonts/Source_Serif_4/static/SourceSerif4-Bold.ttf",
weight: "700",
style: "normal",
},
{
path: "../fonts/Source_Serif_4/static/SourceSerif4-BoldItalic.ttf",
weight: "700",
style: "italic",
},
],
variable: "--font-source-serif-4",
});
export const metadata: Metadata = {
title: "Re-Quest Admin",
description: "Admin dashboard for Re-Quest Platform",
@@ -26,7 +131,7 @@ export default function RootLayout({
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
className={`${geistSans.variable} ${geistMono.variable} ${sourceSerif4.variable} ${fractul.variable} antialiased`}
>
<AuthProvider>{children}</AuthProvider>
</body>

View File

@@ -35,21 +35,21 @@ export default function LoginPage() {
if (isLoading) {
return (
<div className="min-h-screen flex items-center justify-center bg-gray-100">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
<div className="min-h-screen flex items-center justify-center bg-[#f5f9f8]">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-[#f5a623]"></div>
</div>
);
}
return (
<div className="min-h-screen flex items-center justify-center bg-gray-100">
<div className="min-h-screen flex items-center justify-center bg-[#f5f9f8]">
<div className="max-w-md w-full mx-4">
<div className="bg-white rounded-lg shadow-lg p-8">
<div className="bg-white rounded-2xl shadow-lg p-8 border border-[#e5e7eb]">
{/* Header */}
<div className="text-center mb-8">
<div className="mx-auto w-16 h-16 bg-blue-600 rounded-full flex items-center justify-center mb-4">
<div className="mx-auto w-16 h-16 bg-[#00293d] rounded-2xl flex items-center justify-center mb-4 shadow-md">
<svg
className="w-8 h-8 text-white"
className="w-8 h-8 text-[#f5a623]"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
@@ -58,17 +58,17 @@ export default function LoginPage() {
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"
d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4"
/>
</svg>
</div>
<h1 className="text-2xl font-bold text-gray-900">Re-Quest Admin</h1>
<p className="text-gray-500 mt-2">Re-Quest Platform Administration</p>
<h1 className="text-2xl font-bold text-[#00293d] font-fractul">Re-Quest Admin</h1>
<p className="text-[#666666] mt-2 font-serif">Platform Administration</p>
</div>
{/* Error Alert */}
{error && (
<div className="mb-6 p-4 bg-red-50 border border-red-200 rounded-lg">
<div className="mb-6 p-4 bg-red-50 border border-red-200 rounded-xl">
<div className="flex items-center">
<svg
className="w-5 h-5 text-red-500 mr-2"
@@ -91,7 +91,7 @@ export default function LoginPage() {
<div>
<label
htmlFor="email"
className="block text-sm font-medium text-gray-700 mb-2"
className="block text-sm font-medium text-[#00293d] mb-2 font-serif"
>
Email Address
</label>
@@ -101,7 +101,7 @@ export default function LoginPage() {
value={email}
onChange={(e) => setEmail(e.target.value)}
required
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors text-gray-900 placeholder:text-gray-400 bg-white"
className="w-full px-4 py-3 border border-[#e5e7eb] rounded-xl focus:ring-2 focus:ring-[#f5a623] focus:border-[#f5a623] transition-colors text-gray-900 placeholder:text-gray-400 bg-white"
placeholder="admin@realestate.com"
/>
</div>
@@ -109,7 +109,7 @@ export default function LoginPage() {
<div>
<label
htmlFor="password"
className="block text-sm font-medium text-gray-700 mb-2"
className="block text-sm font-medium text-[#00293d] mb-2 font-serif"
>
Password
</label>
@@ -119,7 +119,7 @@ export default function LoginPage() {
value={password}
onChange={(e) => setPassword(e.target.value)}
required
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors text-gray-900 placeholder:text-gray-400 bg-white"
className="w-full px-4 py-3 border border-[#e5e7eb] rounded-xl focus:ring-2 focus:ring-[#f5a623] focus:border-[#f5a623] transition-colors text-gray-900 placeholder:text-gray-400 bg-white"
placeholder="Enter your password"
/>
</div>
@@ -127,7 +127,7 @@ export default function LoginPage() {
<button
type="submit"
disabled={isSubmitting}
className="w-full py-3 px-4 bg-blue-600 hover:bg-blue-700 disabled:bg-blue-400 text-white font-medium rounded-lg transition-colors flex items-center justify-center"
className="w-full py-3 px-4 bg-[#f5a623] hover:bg-[#e09620] disabled:bg-[#f5a623]/60 text-white font-semibold rounded-xl transition-all duration-200 flex items-center justify-center shadow-sm hover:shadow-md font-fractul"
>
{isSubmitting ? (
<>
@@ -160,7 +160,7 @@ export default function LoginPage() {
{/* Footer */}
<div className="mt-6 text-center">
<p className="text-xs text-gray-500">
<p className="text-xs text-[#9ca3af] font-serif">
Only administrators can access this portal
</p>
</div>

View File

@@ -19,8 +19,8 @@ export default function Home() {
}, [isAuthenticated, isLoading, router]);
return (
<div className="min-h-screen flex items-center justify-center bg-gray-100">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
<div className="min-h-screen flex items-center justify-center bg-[#f5f9f8]">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-[#f5a623]"></div>
</div>
);
}

View File

@@ -66,11 +66,11 @@ export default function Sidebar() {
const pathname = usePathname();
return (
<aside className="w-64 bg-gray-900 min-h-screen fixed left-0 top-0">
<aside className="w-64 bg-[#00293d] min-h-screen fixed left-0 top-0">
{/* Logo */}
<div className="p-6 border-b border-gray-800">
<div className="p-6 border-b border-[#003a57]">
<div className="flex items-center">
<div className="w-10 h-10 bg-blue-600 rounded-lg flex items-center justify-center mr-3">
<div className="w-10 h-10 bg-[#f5a623] rounded-lg flex items-center justify-center mr-3 shadow-sm">
<svg
className="w-6 h-6 text-white"
fill="none"
@@ -86,15 +86,15 @@ export default function Sidebar() {
</svg>
</div>
<div>
<h1 className="text-lg font-bold text-white">Re-Quest</h1>
<p className="text-xs text-gray-400">Admin Panel</p>
<h1 className="text-lg font-bold text-white font-fractul">Re-Quest</h1>
<p className="text-xs text-[#e8f0ee]">Admin Panel</p>
</div>
</div>
</div>
{/* Navigation */}
<nav className="p-4">
<ul className="space-y-2">
<ul className="space-y-1">
{menuItems.map((item) => {
const isActive = pathname === item.href ||
(item.href !== '/dashboard' && pathname.startsWith(item.href));
@@ -103,14 +103,14 @@ export default function Sidebar() {
<li key={item.name}>
<Link
href={item.href}
className={`flex items-center px-4 py-3 rounded-lg transition-colors ${
className={`flex items-center px-4 py-3 rounded-lg transition-all duration-200 ${
isActive
? 'bg-blue-600 text-white'
: 'text-gray-300 hover:bg-gray-800 hover:text-white'
? 'bg-[#f5a623] text-white shadow-sm'
: 'text-[#c4d9d4] hover:bg-[#003a57] hover:text-white'
}`}
>
{item.icon}
<span className="ml-3 font-medium">{item.name}</span>
<span className="ml-3 font-medium font-serif text-sm">{item.name}</span>
</Link>
</li>
);

View File

@@ -0,0 +1,91 @@
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@@ -0,0 +1,128 @@
Source Serif 4 Variable Font
============================
This download contains Source Serif 4 as both variable fonts and static fonts.
Source Serif 4 is a variable font with these axes:
opsz
wght
This means all the styles are contained in these files:
SourceSerif4-VariableFont_opsz,wght.ttf
SourceSerif4-Italic-VariableFont_opsz,wght.ttf
If your app fully supports variable fonts, you can now pick intermediate styles
that arent available as static fonts. Not all apps support variable fonts, and
in those cases you can use the static font files for Source Serif 4:
static/SourceSerif4-ExtraLight.ttf
static/SourceSerif4-Light.ttf
static/SourceSerif4-Regular.ttf
static/SourceSerif4-Medium.ttf
static/SourceSerif4-SemiBold.ttf
static/SourceSerif4-Bold.ttf
static/SourceSerif4-ExtraBold.ttf
static/SourceSerif4-Black.ttf
static/SourceSerif4_18pt-ExtraLight.ttf
static/SourceSerif4_18pt-Light.ttf
static/SourceSerif4_18pt-Regular.ttf
static/SourceSerif4_18pt-Medium.ttf
static/SourceSerif4_18pt-SemiBold.ttf
static/SourceSerif4_18pt-Bold.ttf
static/SourceSerif4_18pt-ExtraBold.ttf
static/SourceSerif4_18pt-Black.ttf
static/SourceSerif4_36pt-ExtraLight.ttf
static/SourceSerif4_36pt-Light.ttf
static/SourceSerif4_36pt-Regular.ttf
static/SourceSerif4_36pt-Medium.ttf
static/SourceSerif4_36pt-SemiBold.ttf
static/SourceSerif4_36pt-Bold.ttf
static/SourceSerif4_36pt-ExtraBold.ttf
static/SourceSerif4_36pt-Black.ttf
static/SourceSerif4_48pt-ExtraLight.ttf
static/SourceSerif4_48pt-Light.ttf
static/SourceSerif4_48pt-Regular.ttf
static/SourceSerif4_48pt-Medium.ttf
static/SourceSerif4_48pt-SemiBold.ttf
static/SourceSerif4_48pt-Bold.ttf
static/SourceSerif4_48pt-ExtraBold.ttf
static/SourceSerif4_48pt-Black.ttf
static/SourceSerif4-ExtraLightItalic.ttf
static/SourceSerif4-LightItalic.ttf
static/SourceSerif4-Italic.ttf
static/SourceSerif4-MediumItalic.ttf
static/SourceSerif4-SemiBoldItalic.ttf
static/SourceSerif4-BoldItalic.ttf
static/SourceSerif4-ExtraBoldItalic.ttf
static/SourceSerif4-BlackItalic.ttf
static/SourceSerif4_18pt-ExtraLightItalic.ttf
static/SourceSerif4_18pt-LightItalic.ttf
static/SourceSerif4_18pt-Italic.ttf
static/SourceSerif4_18pt-MediumItalic.ttf
static/SourceSerif4_18pt-SemiBoldItalic.ttf
static/SourceSerif4_18pt-BoldItalic.ttf
static/SourceSerif4_18pt-ExtraBoldItalic.ttf
static/SourceSerif4_18pt-BlackItalic.ttf
static/SourceSerif4_36pt-ExtraLightItalic.ttf
static/SourceSerif4_36pt-LightItalic.ttf
static/SourceSerif4_36pt-Italic.ttf
static/SourceSerif4_36pt-MediumItalic.ttf
static/SourceSerif4_36pt-SemiBoldItalic.ttf
static/SourceSerif4_36pt-BoldItalic.ttf
static/SourceSerif4_36pt-ExtraBoldItalic.ttf
static/SourceSerif4_36pt-BlackItalic.ttf
static/SourceSerif4_48pt-ExtraLightItalic.ttf
static/SourceSerif4_48pt-LightItalic.ttf
static/SourceSerif4_48pt-Italic.ttf
static/SourceSerif4_48pt-MediumItalic.ttf
static/SourceSerif4_48pt-SemiBoldItalic.ttf
static/SourceSerif4_48pt-BoldItalic.ttf
static/SourceSerif4_48pt-ExtraBoldItalic.ttf
static/SourceSerif4_48pt-BlackItalic.ttf
Get started
-----------
1. Install the font files you want to use
2. Use your app's font picker to view the font family and all the
available styles
Learn more about variable fonts
-------------------------------
https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts
https://variablefonts.typenetwork.com
https://medium.com/variable-fonts
In desktop apps
https://theblog.adobe.com/can-variable-fonts-illustrator-cc
https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts
Online
https://developers.google.com/fonts/docs/getting_started
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide
https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts
Installing fonts
MacOS: https://support.apple.com/en-us/HT201749
Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux
Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows
Android Apps
https://developers.google.com/fonts/docs/android
https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts
License
-------
Please read the full license text (OFL.txt) to understand the permissions,
restrictions and requirements for usage, redistribution, and modification.
You can use them in your products & projects print or digital,
commercial or otherwise.
This isn't legal advice, please consider consulting a lawyer and see the full
license for all details.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.