feat: add auto furniture interaction detection
CI / check (push) Successful in 25s
CI / release (push) Skipped
CI / deploy (push) Successful in 59s

- New auto-interaction.ts: detects canSit/canLay/canStand from classname/name keywords
- Maps keywords to interaction types (bench, chair, bed, table, etc.)
- Returns clickLimit and interactionModesCount for proper click/interaction limits
- Nitro editor: added auto-detect button with preview of detected settings
- furni-import.ts: auto-sets interaction_modes_count during import
- upload-import.ts: auto-detects for direct insert and SQL migrations
- clone-import.ts: auto-detects when cloning from other hotels
This commit is contained in:
openhands committed 2026-08-18 22:33:40 +02:00
1 parent 0b4fc4559e
commit dee843106f
8 files changed
+596 -25

No files matched your search

@@ -32,9 +32,14 @@ import { Switch } from "@/components/ui/switch";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Textarea } from "@/components/ui/textarea";
import { adminFetch } from "@/lib/admin-fetch";
import {
autoDetectInteraction,
getInteractionTypeLabel,
} from "@/lib/furni/auto-interaction";
interface NitroEditorDialogProps {
classname: string;
name?: string;
open: boolean;
onOpenChange: (open: boolean) => void;
}
@@ -122,6 +127,7 @@ function CopyClassname({ value }: { value: string }) {
export function NitroEditorDialog({
classname,
name = "",
open,
onOpenChange,
}: NitroEditorDialogProps) {
@@ -142,8 +148,22 @@ export function NitroEditorDialog({
cansiton: false,
canlayon: false,
});
const [autoDetectResult, setAutoDetectResult] = useState<ReturnType<
typeof autoDetectInteraction
> | null>(null);
const [jsonError, setJsonError] = useState<string | null>(null);
const handleAutoDetect = () => {
const result = autoDetectInteraction(classname, name);
setAutoDetectResult(result);
setFlags({
canstandon: result.canStand,
cansiton: result.canSit,
canlayon: result.canLay,
});
toast.success(`Auto-detected: ${result.reason}`);
};
const patchMeta = (path: string, value: unknown) => {
setMetadata((prev) =>
prev == null ? prev : updateNested(prev, path, value),
@@ -497,6 +517,53 @@ export function NitroEditorDialog({
/>
</div>
</div>
<div className="mt-4 flex flex-col gap-2">
<Button
variant="outline"
size="sm"
onClick={handleAutoDetect}
className="gap-1.5 w-full"
>
<Sparkles className="w-3.5 h-3.5" />
Auto-detect from name
</Button>
{autoDetectResult && (
<div className="rounded-md bg-muted/50 p-3 text-xs space-y-1">
<div className="flex items-center gap-1.5 flex-wrap">
<Badge
variant={
autoDetectResult.canLay ? "default" : "secondary"
}
className="gap-1"
>
{autoDetectResult.canLay ? (
<Layers className="w-2.5 h-2.5" />
) : autoDetectResult.canSit ? (
<MousePointerClick className="w-2.5 h-2.5" />
) : (
<MousePointerClick className="w-2.5 h-2.5" />
)}
{getInteractionTypeLabel(
autoDetectResult.interactionType,
)}
</Badge>
{autoDetectResult.clickLimit > 0 && (
<Badge variant="outline" className="gap-1">
Click limit: {autoDetectResult.clickLimit}
</Badge>
)}
{autoDetectResult.interactionModesCount > 0 && (
<Badge variant="outline" className="gap-1">
Modes: {autoDetectResult.interactionModesCount}
</Badge>
)}
</div>
<p className="text-muted-foreground">
{autoDetectResult.reason}
</p>
</div>
)}
</div>
</section>
</div>