Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 33 additions & 69 deletions src/components/advanced/AdvancedTools.tsx
Original file line number Diff line number Diff line change
@@ -1,87 +1,51 @@
/**
* Advanced Analysis tools container.
* Single-column, all-expanded layout — every tool visible at full width.
*/

import { useState } from 'react';
import { TreatmentDetection } from './TreatmentDetection';
import { OriginCharacteristics } from './OriginCharacteristics';
import { ProportionAnalyzer } from './ProportionAnalyzer';
import { CalculatorCard } from '../calculator/CalculatorCard';
import { ToolSection } from '../ui/ToolSection';

type AdvancedId = 'treatment' | 'origin' | 'proportion';

interface AdvancedConfig {
id: AdvancedId;
label: string;
iconPath: string;
description: string;
component: React.ReactNode;
}

const ICON_PATHS: Record<string, string> = {
const ICON_PATHS = {
treatment: 'M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z',
globe: 'M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z',
ruler: 'M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01',
};

const ADVANCED_TOOLS: AdvancedConfig[] = [
{
id: 'treatment',
label: 'Treatment Detection',
iconPath: ICON_PATHS.treatment,
description: 'Visual & instrumental indicators for gem treatments',
component: <TreatmentDetection />,
},
{
id: 'origin',
label: 'Origin Characteristics',
iconPath: ICON_PATHS.globe,
description: 'Geographic origin determination features',
component: <OriginCharacteristics />,
},
{
id: 'proportion',
label: 'Proportion Analyzer',
iconPath: ICON_PATHS.ruler,
description: 'Cut quality analysis from proportion measurements',
component: <ProportionAnalyzer />,
},
];

export function AdvancedTools({ defaultExpanded = 'treatment' }: { defaultExpanded?: AdvancedId | null }) {
const [expandedTool, setExpandedTool] = useState<AdvancedId | null>(defaultExpanded);

const handleToggle = (id: AdvancedId) => {
setExpandedTool(prev => (prev === id ? null : id));
};

export function AdvancedTools() {
return (
<div className="space-y-6">
<div className="text-sm text-slate-600 mb-4">
<p>
Advanced gemmological analysis tools including treatment detection, origin determination characteristics, and cut proportion evaluation.
</p>
</div>

<div className="grid gap-4 md:grid-cols-2">
{ADVANCED_TOOLS.map(tool => (
<CalculatorCard
key={tool.id}
id={tool.id}
title={tool.label}
description={tool.description}
iconPath={tool.iconPath}
expanded={expandedTool === tool.id}
onToggle={() => handleToggle(tool.id)}
>
{tool.component}
</CalculatorCard>
))}
</div>

<p className="text-xs text-slate-500 text-center">
Tip: Click on any tool header to expand or collapse it.
</p>
<ToolSection
id="treatment"
title="Treatment Detection"
description="Visual & instrumental indicators for common gem treatments"
iconPath={ICON_PATHS.treatment}
accent="cyan"
>
<TreatmentDetection />
</ToolSection>

<ToolSection
id="origin"
title="Origin Characteristics"
description="Geographic origin features for major gem species"
iconPath={ICON_PATHS.globe}
accent="cyan"
>
<OriginCharacteristics />
</ToolSection>

<ToolSection
id="proportion"
title="Proportion Analyzer"
description="Evaluate cut quality from proportion measurements"
iconPath={ICON_PATHS.ruler}
accent="cyan"
>
<ProportionAnalyzer />
</ToolSection>
</div>
);
}
153 changes: 77 additions & 76 deletions src/components/advanced/TreatmentDetection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,15 +161,16 @@ export function TreatmentDetection() {
});

return (
<div className="space-y-6">
<div className="space-y-5">
{/* Treatment types overview — responsive grid */}
<div>
<h4 className="text-sm font-semibold text-slate-900 mb-3">Common Treatment Types</h4>
<div className="grid gap-3 sm:grid-cols-2">
<h4 className="text-xs font-semibold text-slate-600 uppercase tracking-wider mb-2">Common Treatment Types</h4>
<div className="grid sm:grid-cols-2 lg:grid-cols-4 gap-2">
{TREATMENT_TYPES.map(type => (
<div key={type.name} className="p-3 rounded-lg border border-slate-200 bg-slate-50">
<div className="flex items-start justify-between mb-1">
<h5 className="font-semibold text-slate-900 text-sm">{type.name}</h5>
<span className={`text-xs px-2 py-0.5 rounded font-medium ${
<div className="flex items-start justify-between gap-1 mb-1">
<h5 className="font-semibold text-slate-900 text-xs">{type.name}</h5>
<span className={`shrink-0 text-xs px-1.5 py-0.5 rounded font-medium ${
type.detectability === 'Easy' ? 'bg-green-100 text-green-700' :
type.detectability === 'Moderate' ? 'bg-blue-100 text-blue-700' :
type.detectability === 'Difficult' ? 'bg-amber-100 text-amber-700' :
Expand All @@ -178,87 +179,87 @@ export function TreatmentDetection() {
{type.detectability}
</span>
</div>
<p className="text-xs text-slate-700 mb-1">{type.description}</p>
<p className="text-xs text-slate-600">
<strong>Purpose:</strong> {type.purpose}
</p>
<p className="text-xs text-slate-600">{type.purpose}</p>
</div>
))}
</div>
</div>

<div className="border-t border-slate-200 pt-6">
<h4 className="text-sm font-semibold text-slate-900 mb-3">Detection Indicators</h4>
{/* Detection indicators — filter bar + results table */}
<div>
<h4 className="text-xs font-semibold text-slate-600 uppercase tracking-wider mb-2">Detection Indicators</h4>

<div className="flex flex-col sm:flex-row gap-3 mb-4">
<input
type="text"
placeholder="Search treatment or gem..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="flex-1 px-3 py-2 text-sm border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-crystal-500"
/>
<select
value={selectedGem}
onChange={(e) => setSelectedGem(e.target.value)}
className="px-3 py-2 text-sm border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-crystal-500"
>
{uniqueGems.map(gem => (
<option key={gem} value={gem}>
{gem === 'all' ? 'All Gems' : gem}
</option>
))}
</select>
<div className="flex flex-wrap gap-2 items-end mb-3">
<div className="flex-1 min-w-[180px]">
<input
type="text"
placeholder="Search treatment or gem..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="w-full px-3 py-1.5 text-sm border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-400 focus:border-cyan-400"
/>
</div>
<div className="w-40 shrink-0">
<select
value={selectedGem}
onChange={(e) => setSelectedGem(e.target.value)}
className="w-full px-3 py-1.5 text-sm border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-400 focus:border-cyan-400"
>
{uniqueGems.map(gem => (
<option key={gem} value={gem}>
{gem === 'all' ? 'All Gems' : gem}
</option>
))}
</select>
</div>
<span className="text-xs text-slate-400 shrink-0 pb-0.5">{filteredTreatments.length} treatment{filteredTreatments.length !== 1 ? 's' : ''}</span>
</div>

<div className="space-y-3">
{filteredTreatments.map((treatment, idx) => (
<div key={idx} className="p-4 rounded-lg border border-slate-200 bg-white hover:border-crystal-300 transition-colors">
<div className="flex items-start justify-between mb-2">
<div>
<h5 className="font-semibold text-slate-900">{treatment.treatment}</h5>
<p className="text-xs text-slate-500">{treatment.gems}</p>
</div>
<span className={`text-xs px-2 py-0.5 rounded font-medium ${
treatment.permanence.startsWith('Permanent') ? 'bg-green-100 text-green-700' :
'bg-amber-100 text-amber-700'
}`}>
{treatment.permanence.startsWith('Permanent') ? 'Permanent' : 'Temporary'}
</span>
</div>

<div className="grid gap-2 text-xs">
<div>
<strong className="text-slate-700">Visual Indicators:</strong>
<p className="text-slate-600 mt-0.5">{treatment.visualIndicators}</p>
</div>
<div>
<strong className="text-slate-700">Instrumental Tests:</strong>
<p className="text-slate-600 mt-0.5">{treatment.instrumentalTests}</p>
</div>
<div>
<strong className="text-slate-700">Disclosure:</strong>
<p className="text-slate-600 mt-0.5">{treatment.disclosure}</p>
</div>
</div>
</div>
))}
<div className="rounded-lg border border-slate-200 overflow-hidden">
<table className="w-full text-sm">
<thead>
<tr className="bg-slate-50 border-b border-slate-200">
<th className="px-3 py-2 text-left text-xs font-semibold text-slate-600 uppercase tracking-wider">Treatment</th>
<th className="px-3 py-2 text-left text-xs font-semibold text-slate-600 uppercase tracking-wider">Visual</th>
<th className="px-3 py-2 text-left text-xs font-semibold text-slate-600 uppercase tracking-wider hidden lg:table-cell">Instrumental</th>
<th className="px-3 py-2 text-center text-xs font-semibold text-slate-600 uppercase tracking-wider">Status</th>
</tr>
</thead>
<tbody>
{filteredTreatments.map((treatment, idx) => (
<tr key={idx} className={`border-b border-slate-100 last:border-0 ${idx % 2 === 0 ? '' : 'bg-slate-50/50'}`}>
<td className="px-3 py-2">
<p className="font-medium text-slate-900 text-xs">{treatment.treatment}</p>
<p className="text-xs text-slate-500">{treatment.gems}</p>
</td>
<td className="px-3 py-2 text-xs text-slate-600">{treatment.visualIndicators}</td>
<td className="px-3 py-2 text-xs text-slate-600 hidden lg:table-cell">{treatment.instrumentalTests}</td>
<td className="px-3 py-2 text-center">
<span className={`inline-block px-2 py-0.5 rounded text-xs font-medium ${
treatment.permanence.startsWith('Permanent') ? 'bg-green-100 text-green-700' :
'bg-amber-100 text-amber-700'
}`}>
{treatment.permanence.startsWith('Permanent') ? 'Permanent' : 'Temporary'}
</span>
</td>
</tr>
))}
</tbody>
</table>
{filteredTreatments.length === 0 && (
<p className="text-center text-slate-500 text-sm py-4">No treatments found matching your criteria.</p>
)}
</div>

{filteredTreatments.length === 0 && (
<p className="text-center text-slate-500 text-sm py-4">No treatments found matching your criteria.</p>
)}
</div>

<div className="bg-red-50 border border-red-200 rounded-lg p-4">
<h4 className="text-sm font-semibold text-red-900 mb-2">⚠️ Important Disclosure Requirements</h4>
<ul className="text-sm text-red-800 space-y-1">
<li>• All treatments must be disclosed to buyers in most markets</li>
<li>• Undisclosed treatments can result in legal liability</li>
<li>• Some treatments (fracture filling, dyeing) drastically reduce value</li>
<li>• Standard treatments (heat in corundum) may not require disclosure in some markets</li>
<li>• When in doubt, always disclose - transparency builds trust</li>
</ul>
{/* Disclosure requirements — inline warning */}
<div className="flex flex-wrap gap-x-5 gap-y-1 text-xs text-red-700 bg-red-50 border border-red-200 rounded-lg px-4 py-2.5">
<span className="font-semibold">Disclosure:</span>
<span>All treatments must be disclosed to buyers</span>
<span>•</span>
<span>Fracture filling &amp; dyeing drastically reduce value</span>
<span>•</span>
<span>When in doubt, always disclose</span>
</div>
</div>
);
Expand Down
Loading
Loading