feat(clone): add hotel field to clone source presets
CI / check (push) Successful in 26s
CI / release (push) Skipped
CI / deploy (push) Successful in 47s

- CloneSource interface now has a hotel field (maps to habbo_gamedata_hotel)
- DEFAULT_SOURCES presets include their associated hotel (it/com)
- When cloning from a source with a hotel configured, habbo_gamedata_hotel
  is set automatically so official furnidata enrichment uses the correct locale
- Clone source form UI now has a hotel select dropdown
- Source list shows the hotel label when configured
- Clone API route passes hotel through to upsertSource
This commit is contained in:
openhands committed 2026-08-05 16:21:45 +02:00
1 parent 79b82e0a31
commit 936053cca6
5 files changed
+53 -1

No files matched your search

@@ -18,7 +18,15 @@ import { toast } from "sonner";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { adminFetch } from "@/lib/admin-fetch";
import { HABBO_GAMEDATA_HOTELS } from "@/lib/habbo-gamedata-hotel";
import { runSseImport } from "@/lib/sse-client";
import { cn } from "@/lib/utils";
@@ -30,6 +38,7 @@ interface CloneSource {
furnidataUrl: string;
nitroBaseUrl: string;
iconBaseUrl: string;
hotel: string;
}
interface FurniItem {
@@ -70,6 +79,7 @@ function SourceForm({ initial, onSave, onCancel, saving }: SourceFormProps) {
const [furnidataUrl, setFurnidataUrl] = useState(initial?.furnidataUrl ?? "");
const [nitroBaseUrl, setNitroBaseUrl] = useState(initial?.nitroBaseUrl ?? "");
const [iconBaseUrl, setIconBaseUrl] = useState(initial?.iconBaseUrl ?? "");
const [hotel, setHotel] = useState(initial?.hotel ?? "");
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
@@ -79,6 +89,7 @@ function SourceForm({ initial, onSave, onCancel, saving }: SourceFormProps) {
furnidataUrl,
nitroBaseUrl,
iconBaseUrl,
hotel,
});
}
@@ -115,6 +126,19 @@ function SourceForm({ initial, onSave, onCancel, saving }: SourceFormProps) {
onChange={(e) => setIconBaseUrl(e.target.value)}
required
/>
<Select value={hotel} onValueChange={setHotel}>
<SelectTrigger>
<SelectValue placeholder="Hotel (optional)" />
</SelectTrigger>
<SelectContent>
<SelectItem value="">None</SelectItem>
{HABBO_GAMEDATA_HOTELS.map((h) => (
<SelectItem key={h.value} value={h.value}>
{h.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="flex gap-2">
<Button type="submit" size="sm" disabled={saving}>
@@ -261,6 +285,12 @@ function SourcesManager({
<div className="text-xs text-muted-foreground truncate">
{src.furnidataUrl}
</div>
{src.hotel ? (
<div className="text-[10px] text-muted-foreground/70">
{HABBO_GAMEDATA_HOTELS.find((h) => h.value === src.hotel)
?.label ?? `habbo.${src.hotel}`}
</div>
) : null}
</div>
</button>
<div className="flex items-center gap-1 shrink-0">
+2 -1
View File
@@ -76,7 +76,7 @@ export const POST = withAdmin(
{ permission: PERMS.ASSETS_IMPORT },
async (request, ctx) => {
const body = await request.json();
const { name, furnidataUrl, nitroBaseUrl, iconBaseUrl, id } =
const { name, furnidataUrl, nitroBaseUrl, iconBaseUrl, hotel, id } =
body as Record<string, string>;
const isHttps = (u: string) => {
@@ -104,6 +104,7 @@ export const POST = withAdmin(
furnidataUrl,
nitroBaseUrl,
iconBaseUrl,
hotel: hotel ?? "",
});
logAudit({