mirror of
https://github.com/PlatypusPus/MushroomEmpire.git
synced 2026-02-07 22:18:59 +00:00
fix:fixed custom selection and interface
This commit is contained in:
@@ -57,6 +57,11 @@ export function CenterPanel({ tab, onAnalyze }: CenterPanelProps) {
|
|||||||
const [piiDetectionResult, setPIIDetectionResult] =
|
const [piiDetectionResult, setPIIDetectionResult] =
|
||||||
useState<DetectPIIResponse | null>(null);
|
useState<DetectPIIResponse | null>(null);
|
||||||
|
|
||||||
|
// Strategy selection state for custom anonymization choices
|
||||||
|
const [selectedStrategies, setSelectedStrategies] = useState<
|
||||||
|
Record<string, { enabled: boolean; strategy: string }>
|
||||||
|
>({});
|
||||||
|
|
||||||
const reset = () => {
|
const reset = () => {
|
||||||
setFileMeta(null);
|
setFileMeta(null);
|
||||||
setUploadedFile(null);
|
setUploadedFile(null);
|
||||||
@@ -103,6 +108,20 @@ export function CenterPanel({ tab, onAnalyze }: CenterPanelProps) {
|
|||||||
try {
|
try {
|
||||||
const result = await detectPII(uploadedFile);
|
const result = await detectPII(uploadedFile);
|
||||||
setPIIDetectionResult(result);
|
setPIIDetectionResult(result);
|
||||||
|
|
||||||
|
// Initialize selected strategies with recommended defaults
|
||||||
|
const initialStrategies: Record<
|
||||||
|
string,
|
||||||
|
{ enabled: boolean; strategy: string }
|
||||||
|
> = {};
|
||||||
|
result.risky_features.forEach((feature) => {
|
||||||
|
initialStrategies[feature.column] = {
|
||||||
|
enabled: true,
|
||||||
|
strategy: feature.recommended_strategy,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
setSelectedStrategies(initialStrategies);
|
||||||
|
|
||||||
setProgressLabel("PII detection complete!");
|
setProgressLabel("PII detection complete!");
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
setError(err.message || "PII detection failed");
|
setError(err.message || "PII detection failed");
|
||||||
@@ -122,7 +141,8 @@ export function CenterPanel({ tab, onAnalyze }: CenterPanelProps) {
|
|||||||
setProgressLabel("Cleaning dataset...");
|
setProgressLabel("Cleaning dataset...");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await cleanDataset(uploadedFile);
|
// Pass the selected strategies to the API
|
||||||
|
const result = await cleanDataset(uploadedFile, selectedStrategies);
|
||||||
setCleanResult(result);
|
setCleanResult(result);
|
||||||
setProgressLabel("Cleaning complete!");
|
setProgressLabel("Cleaning complete!");
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
@@ -2821,6 +2841,81 @@ export function CenterPanel({ tab, onAnalyze }: CenterPanelProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Risky Features List */}
|
{/* Risky Features List */}
|
||||||
|
<div className="space-y-4">
|
||||||
|
{/* Bulk Selection Controls */}
|
||||||
|
<div className="flex items-center justify-between p-3 bg-slate-100 rounded-lg border border-slate-300">
|
||||||
|
<div className="text-sm font-semibold text-slate-700">
|
||||||
|
Bulk Actions:
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
const allEnabled: Record<
|
||||||
|
string,
|
||||||
|
{ enabled: boolean; strategy: string }
|
||||||
|
> = {};
|
||||||
|
piiDetectionResult.risky_features.forEach(
|
||||||
|
(feature) => {
|
||||||
|
allEnabled[feature.column] = {
|
||||||
|
enabled: true,
|
||||||
|
strategy:
|
||||||
|
selectedStrategies[feature.column]
|
||||||
|
?.strategy ?? feature.recommended_strategy,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
setSelectedStrategies(allEnabled);
|
||||||
|
}}
|
||||||
|
className="px-3 py-1 bg-green-600 text-white text-xs font-semibold rounded hover:bg-green-500"
|
||||||
|
>
|
||||||
|
✓ Enable All
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
const allDisabled: Record<
|
||||||
|
string,
|
||||||
|
{ enabled: boolean; strategy: string }
|
||||||
|
> = {};
|
||||||
|
piiDetectionResult.risky_features.forEach(
|
||||||
|
(feature) => {
|
||||||
|
allDisabled[feature.column] = {
|
||||||
|
enabled: false,
|
||||||
|
strategy:
|
||||||
|
selectedStrategies[feature.column]
|
||||||
|
?.strategy ?? feature.recommended_strategy,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
setSelectedStrategies(allDisabled);
|
||||||
|
}}
|
||||||
|
className="px-3 py-1 bg-slate-600 text-white text-xs font-semibold rounded hover:bg-slate-500"
|
||||||
|
>
|
||||||
|
✗ Disable All
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
const reset: Record<
|
||||||
|
string,
|
||||||
|
{ enabled: boolean; strategy: string }
|
||||||
|
> = {};
|
||||||
|
piiDetectionResult.risky_features.forEach(
|
||||||
|
(feature) => {
|
||||||
|
reset[feature.column] = {
|
||||||
|
enabled: true,
|
||||||
|
strategy: feature.recommended_strategy,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
setSelectedStrategies(reset);
|
||||||
|
}}
|
||||||
|
className="px-3 py-1 bg-blue-600 text-white text-xs font-semibold rounded hover:bg-blue-500"
|
||||||
|
>
|
||||||
|
↻ Reset to Recommended
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Individual Feature Cards */}
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{piiDetectionResult.risky_features.map((feature, idx) => {
|
{piiDetectionResult.risky_features.map((feature, idx) => {
|
||||||
const riskColor =
|
const riskColor =
|
||||||
@@ -2841,13 +2936,37 @@ export function CenterPanel({ tab, onAnalyze }: CenterPanelProps) {
|
|||||||
? "bg-yellow-50 border-yellow-300"
|
? "bg-yellow-50 border-yellow-300"
|
||||||
: "bg-gray-50 border-gray-300";
|
: "bg-gray-50 border-gray-300";
|
||||||
|
|
||||||
|
const isEnabled =
|
||||||
|
selectedStrategies[feature.column]?.enabled ?? true;
|
||||||
|
const selectedStrategy =
|
||||||
|
selectedStrategies[feature.column]?.strategy ??
|
||||||
|
feature.recommended_strategy;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={idx}
|
key={idx}
|
||||||
className={`p-5 rounded-xl border-2 ${bgColor}`}
|
className={`p-5 rounded-xl border-2 ${bgColor} ${!isEnabled ? "opacity-60" : ""}`}
|
||||||
>
|
>
|
||||||
{/* Header */}
|
{/* Header with Checkbox */}
|
||||||
<div className="flex items-start justify-between mb-3">
|
<div className="flex items-start justify-between mb-3">
|
||||||
|
<div className="flex items-start gap-3 flex-1">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={isEnabled}
|
||||||
|
onChange={(e) => {
|
||||||
|
setSelectedStrategies((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[feature.column]: {
|
||||||
|
...prev[feature.column],
|
||||||
|
enabled: e.target.checked,
|
||||||
|
strategy:
|
||||||
|
prev[feature.column]?.strategy ??
|
||||||
|
feature.recommended_strategy,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
className="mt-1 w-5 h-5 text-green-600 rounded focus:ring-2 focus:ring-green-500 cursor-pointer"
|
||||||
|
/>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex items-center gap-3 mb-2">
|
<div className="flex items-center gap-3 mb-2">
|
||||||
<span
|
<span
|
||||||
@@ -2860,7 +2979,9 @@ export function CenterPanel({ tab, onAnalyze }: CenterPanelProps) {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm text-slate-700">
|
<div className="text-sm text-slate-700">
|
||||||
<span className="font-semibold">Detected:</span>{" "}
|
<span className="font-semibold">
|
||||||
|
Detected:
|
||||||
|
</span>{" "}
|
||||||
{feature.entity_type}
|
{feature.entity_type}
|
||||||
<span className="mx-2">•</span>
|
<span className="mx-2">•</span>
|
||||||
<span className="font-semibold">
|
<span className="font-semibold">
|
||||||
@@ -2875,6 +2996,7 @@ export function CenterPanel({ tab, onAnalyze }: CenterPanelProps) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Explanation */}
|
{/* Explanation */}
|
||||||
<div className="p-4 bg-white rounded-lg mb-4">
|
<div className="p-4 bg-white rounded-lg mb-4">
|
||||||
@@ -2909,111 +3031,195 @@ export function CenterPanel({ tab, onAnalyze }: CenterPanelProps) {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Recommended Strategy */}
|
{/* Strategy Selection */}
|
||||||
<div className="p-4 bg-white rounded-lg border-2 border-green-300">
|
<div
|
||||||
|
className={`p-4 bg-white rounded-lg border-2 ${isEnabled ? "border-green-300" : "border-slate-300"}`}
|
||||||
|
>
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="text-xs font-semibold text-green-700 mb-1">
|
<div className="text-xs font-semibold text-slate-700 mb-2">
|
||||||
✓ RECOMMENDED STRATEGY
|
{isEnabled
|
||||||
|
? "✓ SELECT ANONYMIZATION STRATEGY"
|
||||||
|
: "⚠️ STRATEGY DISABLED"}
|
||||||
</div>
|
</div>
|
||||||
<div className="font-bold text-lg text-slate-900">
|
<select
|
||||||
{feature.recommended_strategy}
|
value={selectedStrategy}
|
||||||
</div>
|
onChange={(e) => {
|
||||||
<div className="text-sm text-slate-700 mt-1">
|
setSelectedStrategies((prev) => ({
|
||||||
{feature.strategy_description}
|
...prev,
|
||||||
</div>
|
[feature.column]: {
|
||||||
<div className="mt-2 flex gap-4 text-xs text-slate-600">
|
enabled: isEnabled,
|
||||||
<div>
|
strategy: e.target.value,
|
||||||
<strong>Reversible:</strong>{" "}
|
},
|
||||||
{feature.reversible ? "Yes" : "No"}
|
}));
|
||||||
</div>
|
}}
|
||||||
<div>
|
disabled={!isEnabled}
|
||||||
<strong>Use Cases:</strong>{" "}
|
className="w-full px-3 py-2 border-2 border-slate-300 rounded-lg text-sm font-semibold focus:outline-none focus:ring-2 focus:ring-green-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
{feature.use_cases.join(", ")}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
className="px-4 py-2 bg-green-600 text-white text-sm font-semibold rounded-lg hover:bg-green-500"
|
|
||||||
onClick={() =>
|
|
||||||
alert(
|
|
||||||
`Apply ${feature.recommended_strategy} to ${feature.column}`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
Apply
|
{Object.keys(
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Alternative Strategies */}
|
|
||||||
<details className="mt-3">
|
|
||||||
<summary className="text-xs font-semibold text-slate-600 cursor-pointer hover:text-slate-800">
|
|
||||||
View Alternative Strategies
|
|
||||||
</summary>
|
|
||||||
<div className="mt-2 grid grid-cols-1 md:grid-cols-2 gap-2">
|
|
||||||
{Object.entries(
|
|
||||||
piiDetectionResult.available_strategies,
|
piiDetectionResult.available_strategies,
|
||||||
)
|
).map((strategy) => (
|
||||||
.filter(
|
<option key={strategy} value={strategy}>
|
||||||
([strategy]) =>
|
|
||||||
strategy !== feature.recommended_strategy,
|
|
||||||
)
|
|
||||||
.map(([strategy, details]: [string, any]) => (
|
|
||||||
<div
|
|
||||||
key={strategy}
|
|
||||||
className="p-3 bg-white rounded border border-slate-200 hover:border-slate-400"
|
|
||||||
>
|
|
||||||
<div className="font-semibold text-sm text-slate-800">
|
|
||||||
{strategy}
|
{strategy}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
{/* Show selected strategy description */}
|
||||||
|
{isEnabled &&
|
||||||
|
piiDetectionResult.available_strategies[
|
||||||
|
selectedStrategy
|
||||||
|
] && (
|
||||||
|
<div className="mt-3 p-3 bg-slate-50 rounded-lg border border-slate-200">
|
||||||
|
<div className="text-sm text-slate-700 mb-2">
|
||||||
|
{
|
||||||
|
piiDetectionResult
|
||||||
|
.available_strategies[
|
||||||
|
selectedStrategy
|
||||||
|
].description
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-slate-600 mt-1">
|
<div className="flex gap-4 text-xs text-slate-600">
|
||||||
{details.description}
|
<div>
|
||||||
</div>
|
<strong>Risk Level:</strong>{" "}
|
||||||
<div className="mt-2 flex items-center justify-between">
|
|
||||||
<span
|
<span
|
||||||
className={`px-2 py-0.5 text-xs rounded ${
|
className={`px-2 py-0.5 rounded ${
|
||||||
details.risk_level === "HIGH"
|
piiDetectionResult
|
||||||
|
.available_strategies[
|
||||||
|
selectedStrategy
|
||||||
|
].risk_level === "HIGH"
|
||||||
? "bg-red-100 text-red-800"
|
? "bg-red-100 text-red-800"
|
||||||
: details.risk_level === "MEDIUM"
|
: piiDetectionResult
|
||||||
|
.available_strategies[
|
||||||
|
selectedStrategy
|
||||||
|
].risk_level === "MEDIUM"
|
||||||
? "bg-orange-100 text-orange-800"
|
? "bg-orange-100 text-orange-800"
|
||||||
: "bg-yellow-100 text-yellow-800"
|
: "bg-yellow-100 text-yellow-800"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{details.risk_level} Risk
|
{
|
||||||
</span>
|
piiDetectionResult
|
||||||
<button
|
.available_strategies[
|
||||||
className="px-2 py-1 bg-blue-600 text-white text-xs rounded hover:bg-blue-500"
|
selectedStrategy
|
||||||
onClick={() =>
|
].risk_level
|
||||||
alert(
|
|
||||||
`Apply ${strategy} to ${feature.column}`,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
>
|
</span>
|
||||||
Use This
|
</div>
|
||||||
</button>
|
<div>
|
||||||
|
<strong>Reversible:</strong>{" "}
|
||||||
|
{piiDetectionResult
|
||||||
|
.available_strategies[
|
||||||
|
selectedStrategy
|
||||||
|
].reversible
|
||||||
|
? "Yes"
|
||||||
|
: "No"}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-2 text-xs text-slate-600">
|
||||||
|
<strong>Use Cases:</strong>{" "}
|
||||||
|
{piiDetectionResult.available_strategies[
|
||||||
|
selectedStrategy
|
||||||
|
].use_cases.join(", ")}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
</details>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Apply All Button */}
|
{/* Apply All Button with Strategy Summary */}
|
||||||
<div className="sticky bottom-0 p-4 bg-gradient-to-t from-white via-white to-transparent">
|
<div className="sticky bottom-0 p-4 bg-gradient-to-t from-white via-white to-transparent">
|
||||||
<button
|
{/* Strategy Summary */}
|
||||||
className="w-full py-3 bg-green-600 text-white font-bold rounded-lg hover:bg-green-500 shadow-lg"
|
<div className="mb-4 p-4 bg-blue-50 rounded-lg border-2 border-blue-200">
|
||||||
onClick={() =>
|
<div className="text-sm font-semibold text-blue-900 mb-2">
|
||||||
alert(
|
📋 Selected Strategies Summary
|
||||||
"Apply all recommended strategies and clean dataset",
|
</div>
|
||||||
)
|
<div className="grid grid-cols-2 md:grid-cols-3 gap-2 text-xs">
|
||||||
|
<div className="p-2 bg-white rounded border border-blue-200">
|
||||||
|
<div className="font-semibold text-slate-700">
|
||||||
|
Total Features:
|
||||||
|
</div>
|
||||||
|
<div className="text-2xl font-bold text-blue-900">
|
||||||
|
{piiDetectionResult.risky_features.length}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="p-2 bg-white rounded border border-green-200">
|
||||||
|
<div className="font-semibold text-slate-700">
|
||||||
|
Enabled:
|
||||||
|
</div>
|
||||||
|
<div className="text-2xl font-bold text-green-900">
|
||||||
|
{
|
||||||
|
Object.values(selectedStrategies).filter(
|
||||||
|
(s) => s.enabled,
|
||||||
|
).length
|
||||||
}
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="p-2 bg-white rounded border border-slate-200">
|
||||||
|
<div className="font-semibold text-slate-700">
|
||||||
|
Disabled:
|
||||||
|
</div>
|
||||||
|
<div className="text-2xl font-bold text-slate-900">
|
||||||
|
{
|
||||||
|
Object.values(selectedStrategies).filter(
|
||||||
|
(s) => !s.enabled,
|
||||||
|
).length
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-3 p-3 bg-white rounded-lg border border-blue-200">
|
||||||
|
<div className="text-xs font-semibold text-slate-600 mb-2">
|
||||||
|
Enabled Strategies:
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
{Object.entries(selectedStrategies)
|
||||||
|
.filter(([_, config]) => config.enabled)
|
||||||
|
.map(([column, config]) => (
|
||||||
|
<div
|
||||||
|
key={column}
|
||||||
|
className="px-2 py-1 bg-blue-100 text-blue-900 rounded text-xs font-medium border border-blue-300"
|
||||||
>
|
>
|
||||||
✓ Apply All Recommended Strategies & Clean Dataset
|
<strong>{column}:</strong> {config.strategy}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{Object.values(selectedStrategies).filter(
|
||||||
|
(s) => s.enabled,
|
||||||
|
).length === 0 && (
|
||||||
|
<div className="text-xs text-slate-500 italic">
|
||||||
|
No strategies enabled
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className="w-full py-3 bg-green-600 text-white font-bold rounded-lg hover:bg-green-500 shadow-lg disabled:opacity-50 disabled:cursor-not-allowed transition-all"
|
||||||
|
disabled={
|
||||||
|
isProcessing ||
|
||||||
|
!uploadedFile ||
|
||||||
|
Object.values(selectedStrategies).filter((s) => s.enabled)
|
||||||
|
.length === 0
|
||||||
|
}
|
||||||
|
onClick={handleClean}
|
||||||
|
>
|
||||||
|
{isProcessing
|
||||||
|
? "⏳ Processing..."
|
||||||
|
: Object.values(selectedStrategies).filter(
|
||||||
|
(s) => s.enabled,
|
||||||
|
).length === 0
|
||||||
|
? "⚠️ Enable at least one strategy to clean"
|
||||||
|
: `✓ Apply ${Object.values(selectedStrategies).filter((s) => s.enabled).length} Selected Strategies & Clean Dataset`}
|
||||||
</button>
|
</button>
|
||||||
|
<div className="mt-2 text-xs text-center text-slate-500">
|
||||||
|
Note: Only enabled features will be anonymized
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
Reference in New Issue
Block a user