mirror of
https://github.com/jjbrunton/Sauci.git
synced 2026-11-02 10:19:43 +00:00
feat(admin): add NSFW flag to packs, remove intensity
Re-add is_explicit toggle on pack and dare pack forms labeled as "NSFW". Show NSFW badge on pack cards and user enabled packs. Remove all intensity/comfort level references from admin UI including form fields, analytics charts, review dialog columns, AI generation prompts/scoring, cherry-pick balancing, and settings toggles. The database columns remain unchanged.
This commit is contained in:
1 parent
14b0199625
commit
e380c0a31c
32 files changed
+948
-1075
No files matched your search
@@ -10,10 +10,9 @@ interface AIPolishButtonProps {
|
||||
onPolished: (newText: string) => void;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
explicit?: boolean;
|
||||
}
|
||||
|
||||
export function AIPolishButton({ text, type, onPolished, className, disabled, explicit = false }: AIPolishButtonProps) {
|
||||
export function AIPolishButton({ text, type, onPolished, className, disabled }: AIPolishButtonProps) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handlePolish = async (e: React.MouseEvent) => {
|
||||
@@ -25,7 +24,7 @@ export function AIPolishButton({ text, type, onPolished, className, disabled, ex
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
const polished = await polishContent(text, type, explicit);
|
||||
const polished = await polishContent(text, type, false);
|
||||
onPolished(polished);
|
||||
toast.success('Text polished!');
|
||||
} catch (error) {
|
||||
|
||||
@@ -114,8 +114,6 @@ export function AiGeneratorDialog({
|
||||
context={context}
|
||||
config={generation.config}
|
||||
onCountChange={generation.setCount}
|
||||
onExplicitChange={generation.setExplicit}
|
||||
onCrudeLangChange={generation.setCrudeLang}
|
||||
onInspirationChange={generation.setInspiration}
|
||||
/>
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import type { GeneratorType, GeneratorContext, GenerationConfig } from './hooks/useAiGeneration';
|
||||
|
||||
// =============================================================================
|
||||
@@ -13,8 +12,6 @@ interface GenerationFormProps {
|
||||
context?: GeneratorContext;
|
||||
config: GenerationConfig;
|
||||
onCountChange: (count: number) => void;
|
||||
onExplicitChange: (isExplicit: boolean) => void;
|
||||
onCrudeLangChange: (crudeLang: boolean) => void;
|
||||
onInspirationChange: (inspiration: string) => void;
|
||||
}
|
||||
|
||||
@@ -27,8 +24,6 @@ export function GenerationForm({
|
||||
context,
|
||||
config,
|
||||
onCountChange,
|
||||
onExplicitChange,
|
||||
onCrudeLangChange,
|
||||
onInspirationChange,
|
||||
}: GenerationFormProps) {
|
||||
return (
|
||||
@@ -49,29 +44,6 @@ export function GenerationForm({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Explicit Toggle */}
|
||||
<div className="flex items-center space-x-2">
|
||||
<Switch
|
||||
id="explicit-mode"
|
||||
checked={config.isExplicit}
|
||||
onCheckedChange={onExplicitChange}
|
||||
/>
|
||||
<Label htmlFor="explicit-mode">Explicit Content</Label>
|
||||
</div>
|
||||
|
||||
{/* Crude Language Override Toggle */}
|
||||
<div className="flex items-center space-x-2">
|
||||
<Switch
|
||||
id="crude-lang"
|
||||
checked={config.crudeLang}
|
||||
onCheckedChange={onCrudeLangChange}
|
||||
/>
|
||||
<Label htmlFor="crude-lang">Crude Language Override</Label>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
(use "fuck", "suck cock" etc. instead of tasteful phrasing)
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Inspiration/Suggestions Textarea */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="inspiration">Inspiration / Suggestions (optional)</Label>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { useState, useCallback } from 'react';
|
||||
import {
|
||||
generatePack,
|
||||
generateQuestionsWithCouncil,
|
||||
@@ -32,13 +32,10 @@ export interface GeneratorContext {
|
||||
existingCategories?: string[];
|
||||
existingPacks?: string[];
|
||||
existingQuestions?: string[];
|
||||
isExplicit?: boolean;
|
||||
}
|
||||
|
||||
export interface GenerationConfig {
|
||||
count: number;
|
||||
isExplicit: boolean;
|
||||
crudeLang: boolean;
|
||||
inspiration: string;
|
||||
}
|
||||
|
||||
@@ -76,8 +73,6 @@ export function useAiGeneration(
|
||||
// Config state
|
||||
const [config, setConfig] = useState<GenerationConfig>({
|
||||
count: 10,
|
||||
isExplicit: Boolean(context?.isExplicit),
|
||||
crudeLang: false,
|
||||
inspiration: '',
|
||||
});
|
||||
|
||||
@@ -117,27 +112,11 @@ export function useAiGeneration(
|
||||
// Council config
|
||||
const councilConfig = getCouncilConfig();
|
||||
|
||||
// Update explicit flag when context changes
|
||||
useEffect(() => {
|
||||
setConfig(prev => ({
|
||||
...prev,
|
||||
isExplicit: Boolean(context?.isExplicit),
|
||||
}));
|
||||
}, [context?.isExplicit, type]);
|
||||
|
||||
// Config setters
|
||||
const setCount = useCallback((count: number) => {
|
||||
setConfig(prev => ({ ...prev, count }));
|
||||
}, []);
|
||||
|
||||
const setExplicit = useCallback((isExplicit: boolean) => {
|
||||
setConfig(prev => ({ ...prev, isExplicit }));
|
||||
}, []);
|
||||
|
||||
const setCrudeLang = useCallback((crudeLang: boolean) => {
|
||||
setConfig(prev => ({ ...prev, crudeLang }));
|
||||
}, []);
|
||||
|
||||
const setInspiration = useCallback((inspiration: string) => {
|
||||
setConfig(prev => ({ ...prev, inspiration }));
|
||||
}, []);
|
||||
@@ -182,7 +161,7 @@ export function useAiGeneration(
|
||||
const reset = useCallback(() => {
|
||||
setResults({ pack: null, questions: [], categoryIdeas: [], packIdeas: [] });
|
||||
setSelectedIndices(new Set());
|
||||
setConfig(prev => ({ ...prev, crudeLang: false, inspiration: '' }));
|
||||
setConfig(prev => ({ ...prev, inspiration: '' }));
|
||||
setReviewState({
|
||||
reviews: [],
|
||||
summary: null,
|
||||
@@ -216,7 +195,8 @@ export function useAiGeneration(
|
||||
setFilter('all');
|
||||
setSourceModel(null);
|
||||
|
||||
const isExplicit = config.isExplicit;
|
||||
const isExplicit = false;
|
||||
const crudeLang = false;
|
||||
|
||||
try {
|
||||
if (type === 'pack') {
|
||||
@@ -232,7 +212,7 @@ export function useAiGeneration(
|
||||
const result = await generatePack(
|
||||
context?.categoryName,
|
||||
isExplicit,
|
||||
config.crudeLang,
|
||||
crudeLang,
|
||||
config.inspiration || undefined,
|
||||
avoidNames
|
||||
);
|
||||
@@ -249,11 +229,10 @@ export function useAiGeneration(
|
||||
const result = await generateQuestionsWithCouncil(
|
||||
context.packName,
|
||||
config.count,
|
||||
undefined,
|
||||
isExplicit ? 5 : 2,
|
||||
2,
|
||||
context.packDescription || undefined,
|
||||
context.existingQuestions,
|
||||
config.crudeLang,
|
||||
crudeLang,
|
||||
config.inspiration || undefined,
|
||||
setProgress
|
||||
);
|
||||
@@ -309,7 +288,7 @@ export function useAiGeneration(
|
||||
const result = await suggestCategories(
|
||||
context?.existingCategories || [],
|
||||
isExplicit,
|
||||
config.crudeLang,
|
||||
crudeLang,
|
||||
config.inspiration || undefined,
|
||||
avoidNames
|
||||
);
|
||||
@@ -335,7 +314,7 @@ export function useAiGeneration(
|
||||
context.categoryName,
|
||||
context?.existingPacks || [],
|
||||
isExplicit,
|
||||
config.crudeLang,
|
||||
crudeLang,
|
||||
config.inspiration || undefined,
|
||||
avoidNames
|
||||
);
|
||||
@@ -366,8 +345,6 @@ export function useAiGeneration(
|
||||
// Config
|
||||
config,
|
||||
setCount,
|
||||
setExplicit,
|
||||
setCrudeLang,
|
||||
setInspiration,
|
||||
|
||||
// Results
|
||||
|
||||
@@ -260,10 +260,10 @@ export function PackFormDialog({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Explicit Toggle */}
|
||||
{/* NSFW Toggle */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="space-y-0.5">
|
||||
<Label>Explicit Content</Label>
|
||||
<Label>NSFW</Label>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Contains adult or mature content
|
||||
</p>
|
||||
|
||||
@@ -11,7 +11,7 @@ import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
|
||||
import { Loader2, Sparkles, ArrowRight, Check, Wand2, ShieldCheck, Gauge, Trash2, Package } from 'lucide-react';
|
||||
import { Loader2, Sparkles, ArrowRight, Check, Wand2, ShieldCheck, Trash2, Package } from 'lucide-react';
|
||||
import {
|
||||
analyzeQuestionText,
|
||||
analyzeQuestionTargets,
|
||||
@@ -51,7 +51,6 @@ interface ReviewQuestionsDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
questions: Question[];
|
||||
isExplicit?: boolean;
|
||||
onUpdated: () => void;
|
||||
}
|
||||
|
||||
@@ -64,14 +63,13 @@ interface CombinedSuggestion {
|
||||
deleteSuggestion?: DeletionAnalysis;
|
||||
}
|
||||
|
||||
export function ReviewQuestionsDialog({ open, onOpenChange, questions, isExplicit = false, onUpdated }: ReviewQuestionsDialogProps) {
|
||||
export function ReviewQuestionsDialog({ open, onOpenChange, questions, onUpdated }: ReviewQuestionsDialogProps) {
|
||||
const [analyzing, setAnalyzing] = useState(false);
|
||||
const [suggestions, setSuggestions] = useState<CombinedSuggestion[]>([]);
|
||||
const [existingProps, setExistingProps] = useState<string[]>([]);
|
||||
const [selectedTextIds, setSelectedTextIds] = useState<Set<string>>(new Set());
|
||||
const [selectedTargetIds, setSelectedTargetIds] = useState<Set<string>>(new Set());
|
||||
const [selectedPropsIds, setSelectedPropsIds] = useState<Set<string>>(new Set());
|
||||
const [selectedIntensityIds, setSelectedIntensityIds] = useState<Set<string>>(new Set());
|
||||
const [selectedDeleteIds, setSelectedDeleteIds] = useState<Set<string>>(new Set());
|
||||
const [step, setStep] = useState<'initial' | 'review'>('initial');
|
||||
const [applying, setApplying] = useState(false);
|
||||
@@ -134,10 +132,10 @@ export function ReviewQuestionsDialog({ open, onOpenChange, questions, isExplici
|
||||
const [textResults, targetResults, deleteResults, propsResults] = await Promise.all([
|
||||
analyzeQuestionText(
|
||||
questions.map(q => ({ ...q, intensity: q.intensity })),
|
||||
isExplicit
|
||||
false
|
||||
),
|
||||
analyzeQuestionTargets(questions),
|
||||
analyzeQuestionDeletions(questions, isExplicit),
|
||||
analyzeQuestionDeletions(questions, false),
|
||||
analyzeQuestionProps(
|
||||
questions.map(q => ({
|
||||
id: q.id,
|
||||
@@ -192,10 +190,6 @@ export function ReviewQuestionsDialog({ open, onOpenChange, questions, isExplici
|
||||
setSelectedTargetIds(new Set(meaningfulTargetResults.map(r => r.id)));
|
||||
setSelectedPropsIds(new Set(meaningfulPropsResults.map(r => r.id)));
|
||||
setSelectedDeleteIds(new Set(deleteResults.map(r => r.id)));
|
||||
// Select intensity suggestions by default
|
||||
setSelectedIntensityIds(new Set(
|
||||
textResults.filter(r => r.suggested_intensity != null).map(r => r.id)
|
||||
));
|
||||
setStep('review');
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
@@ -211,17 +205,15 @@ export function ReviewQuestionsDialog({ open, onOpenChange, questions, isExplici
|
||||
const updates: { id: string; data: Record<string, unknown> }[] = [];
|
||||
const deleteIds = Array.from(selectedDeleteIds);
|
||||
|
||||
// Build update objects combining text, target, and intensity changes
|
||||
// Build update objects combining text, target, and props changes
|
||||
suggestions.forEach(suggestion => {
|
||||
if (selectedDeleteIds.has(suggestion.questionId)) return;
|
||||
|
||||
const textSelected = selectedTextIds.has(suggestion.questionId) && suggestion.textSuggestion;
|
||||
const targetSelected = selectedTargetIds.has(suggestion.questionId) && suggestion.targetSuggestion;
|
||||
const propsSelected = selectedPropsIds.has(suggestion.questionId) && suggestion.propsSuggestion;
|
||||
const intensitySelected = selectedIntensityIds.has(suggestion.questionId) &&
|
||||
suggestion.textSuggestion?.suggested_intensity != null;
|
||||
|
||||
if (textSelected || targetSelected || propsSelected || intensitySelected) {
|
||||
if (textSelected || targetSelected || propsSelected) {
|
||||
const updateData: Record<string, unknown> = {};
|
||||
|
||||
if (textSelected && suggestion.textSuggestion) {
|
||||
@@ -247,10 +239,6 @@ export function ReviewQuestionsDialog({ open, onOpenChange, questions, isExplici
|
||||
: null;
|
||||
}
|
||||
|
||||
if (intensitySelected && suggestion.textSuggestion?.suggested_intensity != null) {
|
||||
updateData.intensity = suggestion.textSuggestion.suggested_intensity;
|
||||
}
|
||||
|
||||
if (Object.keys(updateData).length > 0) {
|
||||
updates.push({ id: suggestion.questionId, data: updateData });
|
||||
}
|
||||
@@ -301,7 +289,6 @@ export function ReviewQuestionsDialog({ open, onOpenChange, questions, isExplici
|
||||
setSelectedTextIds(new Set());
|
||||
setSelectedTargetIds(new Set());
|
||||
setSelectedPropsIds(new Set());
|
||||
setSelectedIntensityIds(new Set());
|
||||
setSelectedDeleteIds(new Set());
|
||||
}, 300);
|
||||
};
|
||||
@@ -327,13 +314,6 @@ export function ReviewQuestionsDialog({ open, onOpenChange, questions, isExplici
|
||||
setSelectedPropsIds(next);
|
||||
};
|
||||
|
||||
const toggleIntensitySelection = (id: string) => {
|
||||
const next = new Set(selectedIntensityIds);
|
||||
if (next.has(id)) next.delete(id);
|
||||
else next.add(id);
|
||||
setSelectedIntensityIds(next);
|
||||
};
|
||||
|
||||
const toggleDeleteSelection = (id: string) => {
|
||||
const next = new Set(selectedDeleteIds);
|
||||
if (next.has(id)) next.delete(id);
|
||||
@@ -387,14 +367,12 @@ export function ReviewQuestionsDialog({ open, onOpenChange, questions, isExplici
|
||||
const textSuggestionCount = suggestions.filter(s => s.textSuggestion).length;
|
||||
const targetSuggestionCount = suggestions.filter(s => s.targetSuggestion).length;
|
||||
const propsSuggestionCount = suggestions.filter(s => s.propsSuggestion).length;
|
||||
const intensitySuggestionCount = suggestions.filter(s => s.textSuggestion?.suggested_intensity != null).length;
|
||||
const deleteSuggestionCount = suggestions.filter(s => s.deleteSuggestion).length;
|
||||
const effectiveTextCount = Array.from(selectedTextIds).filter(id => !selectedDeleteIds.has(id)).length;
|
||||
const effectiveTargetCount = Array.from(selectedTargetIds).filter(id => !selectedDeleteIds.has(id)).length;
|
||||
const effectivePropsCount = Array.from(selectedPropsIds).filter(id => !selectedDeleteIds.has(id)).length;
|
||||
const effectiveIntensityCount = Array.from(selectedIntensityIds).filter(id => !selectedDeleteIds.has(id)).length;
|
||||
const totalSelectedCount = effectiveTextCount + effectiveTargetCount + effectivePropsCount + effectiveIntensityCount + selectedDeleteIds.size;
|
||||
const hasSuggestions = textSuggestionCount > 0 || targetSuggestionCount > 0 || propsSuggestionCount > 0 || intensitySuggestionCount > 0 || deleteSuggestionCount > 0;
|
||||
const totalSelectedCount = effectiveTextCount + effectiveTargetCount + effectivePropsCount + selectedDeleteIds.size;
|
||||
const hasSuggestions = textSuggestionCount > 0 || targetSuggestionCount > 0 || propsSuggestionCount > 0 || deleteSuggestionCount > 0;
|
||||
|
||||
// Select all/none helpers
|
||||
const selectAllText = () => setSelectedTextIds(new Set(suggestions.filter(s => s.textSuggestion).map(s => s.questionId)));
|
||||
@@ -403,24 +381,18 @@ export function ReviewQuestionsDialog({ open, onOpenChange, questions, isExplici
|
||||
const selectNoneTargets = () => setSelectedTargetIds(new Set());
|
||||
const selectAllProps = () => setSelectedPropsIds(new Set(suggestions.filter(s => s.propsSuggestion).map(s => s.questionId)));
|
||||
const selectNoneProps = () => setSelectedPropsIds(new Set());
|
||||
const selectAllIntensity = () => setSelectedIntensityIds(new Set(
|
||||
suggestions.filter(s => s.textSuggestion?.suggested_intensity != null).map(s => s.questionId)
|
||||
));
|
||||
const selectNoneIntensity = () => setSelectedIntensityIds(new Set());
|
||||
const selectAllDeletes = () => setSelectedDeleteIds(new Set(suggestions.filter(s => s.deleteSuggestion).map(s => s.questionId)));
|
||||
const selectNoneDeletes = () => setSelectedDeleteIds(new Set());
|
||||
const selectAll = () => {
|
||||
selectAllText();
|
||||
selectAllTargets();
|
||||
selectAllProps();
|
||||
selectAllIntensity();
|
||||
selectAllDeletes();
|
||||
};
|
||||
const selectNone = () => {
|
||||
selectNoneText();
|
||||
selectNoneTargets();
|
||||
selectNoneProps();
|
||||
selectNoneIntensity();
|
||||
selectNoneDeletes();
|
||||
};
|
||||
|
||||
@@ -462,10 +434,6 @@ export function ReviewQuestionsDialog({ open, onOpenChange, questions, isExplici
|
||||
<Package className="h-5 w-5 text-cyan-400" />
|
||||
<span className="text-sm font-medium text-cyan-300">Required Props</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 bg-amber-500/20 px-4 py-2 rounded-lg">
|
||||
<Gauge className="h-5 w-5 text-amber-400" />
|
||||
<span className="text-sm font-medium text-amber-300">Intensity Level</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 bg-rose-500/20 px-4 py-2 rounded-lg">
|
||||
<Trash2 className="h-5 w-5 text-rose-400" />
|
||||
<span className="text-sm font-medium text-rose-300">Deletion Suggestions</span>
|
||||
@@ -506,10 +474,6 @@ export function ReviewQuestionsDialog({ open, onOpenChange, questions, isExplici
|
||||
<Package className="h-4 w-4 text-cyan-500" />
|
||||
{propsSuggestionCount} props fixes
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<Gauge className="h-4 w-4 text-amber-500" />
|
||||
{intensitySuggestionCount} intensity fixes
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<Trash2 className="h-4 w-4 text-rose-500" />
|
||||
{deleteSuggestionCount} delete suggestions
|
||||
@@ -572,22 +536,6 @@ export function ReviewQuestionsDialog({ open, onOpenChange, questions, isExplici
|
||||
</Button>
|
||||
</div>
|
||||
</TableHead>
|
||||
<TableHead className="w-[10%]">
|
||||
<div className="flex items-center gap-2">
|
||||
<Gauge className="h-4 w-4 text-amber-500" />
|
||||
Intensity
|
||||
{intensitySuggestionCount > 0 && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-6 px-2 text-xs"
|
||||
onClick={() => selectedIntensityIds.size === intensitySuggestionCount ? selectNoneIntensity() : selectAllIntensity()}
|
||||
>
|
||||
{selectedIntensityIds.size === intensitySuggestionCount ? 'None' : 'All'}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</TableHead>
|
||||
<TableHead className="w-[10%]">
|
||||
<div className="flex items-center gap-2">
|
||||
<Trash2 className="h-4 w-4 text-rose-500" />
|
||||
@@ -757,50 +705,6 @@ export function ReviewQuestionsDialog({ open, onOpenChange, questions, isExplici
|
||||
)}
|
||||
</TableCell>
|
||||
|
||||
{/* Intensity Column */}
|
||||
<TableCell className="align-top py-3">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<Badge variant="outline" className="text-xs">
|
||||
Current: {suggestion.question.intensity}
|
||||
</Badge>
|
||||
</div>
|
||||
{suggestion.textSuggestion?.suggested_intensity != null ? (
|
||||
<div
|
||||
className={`p-2 rounded-lg border cursor-pointer transition-colors ${selectedIntensityIds.has(suggestion.questionId)
|
||||
? 'bg-amber-500/20 border-amber-500/50'
|
||||
: 'bg-muted/30 border-transparent hover:bg-muted/50'
|
||||
}`}
|
||||
onClick={() => toggleIntensitySelection(suggestion.questionId)}
|
||||
>
|
||||
<div className="flex items-start gap-2">
|
||||
<Checkbox
|
||||
checked={selectedIntensityIds.has(suggestion.questionId)}
|
||||
onCheckedChange={() => toggleIntensitySelection(suggestion.questionId)}
|
||||
className="mt-0.5"
|
||||
/>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<ArrowRight className="h-3 w-3 text-muted-foreground" />
|
||||
<Badge className="text-xs bg-amber-500/30 text-amber-300 hover:bg-amber-500/30">
|
||||
{suggestion.textSuggestion.suggested_intensity}
|
||||
</Badge>
|
||||
</div>
|
||||
{suggestion.textSuggestion.intensity_reason && (
|
||||
<div className="text-xs text-muted-foreground mt-1">
|
||||
{suggestion.textSuggestion.intensity_reason}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-xs text-muted-foreground">
|
||||
<Check className="h-4 w-4 text-green-500 inline mr-1" />
|
||||
Correct
|
||||
</div>
|
||||
)}
|
||||
</TableCell>
|
||||
|
||||
{/* Delete Column */}
|
||||
<TableCell className="align-top py-3">
|
||||
{suggestion.deleteSuggestion ? (
|
||||
|
||||
@@ -146,26 +146,26 @@ export function Breadcrumbs() {
|
||||
if (breadcrumbs.length === 0 && !loading) return null;
|
||||
|
||||
return (
|
||||
<nav className="flex items-center space-x-1 text-sm">
|
||||
<nav className="flex items-center gap-1 text-sm overflow-x-auto whitespace-nowrap max-w-full pr-2">
|
||||
<Link
|
||||
to="/"
|
||||
className="flex items-center text-muted-foreground hover:text-primary transition-colors p-1 rounded-md hover:bg-white/5"
|
||||
className="flex items-center text-muted-foreground hover:text-primary transition-colors p-1 rounded-md hover:bg-white/5 flex-shrink-0"
|
||||
>
|
||||
<Home className="h-4 w-4" />
|
||||
</Link>
|
||||
|
||||
{breadcrumbs.map((item, index) => (
|
||||
<div key={index} className="flex items-center">
|
||||
<div key={index} className="flex items-center flex-shrink-0">
|
||||
<ChevronRight className="h-4 w-4 mx-1 text-muted-foreground/50" />
|
||||
{item.href ? (
|
||||
<Link
|
||||
to={item.href}
|
||||
className="text-muted-foreground hover:text-primary transition-colors px-2 py-1 rounded-md hover:bg-white/5"
|
||||
className="text-muted-foreground hover:text-primary transition-colors px-2 py-1 rounded-md hover:bg-white/5 max-w-[160px] truncate"
|
||||
>
|
||||
{item.label}
|
||||
</Link>
|
||||
) : (
|
||||
<span className="text-foreground font-medium px-2 py-1">
|
||||
<span className="text-foreground font-medium px-2 py-1 max-w-[180px] truncate">
|
||||
{item.label}
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -11,7 +11,7 @@ const TabsList = React.forwardRef<
|
||||
<TabsPrimitive.List
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
|
||||
"flex w-full flex-wrap items-center justify-start gap-1 rounded-md bg-muted p-1 text-muted-foreground sm:inline-flex sm:w-auto sm:flex-nowrap sm:justify-center",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
Reference in new issue
Block a user