feat: add manual recent furni resync action
This commit is contained in:
1 parent
88ac5ac1ba
commit
bac2f653af
1 file changed
+120
@@ -6,6 +6,7 @@ import {
|
|||||||
BarChart3,
|
BarChart3,
|
||||||
Check,
|
Check,
|
||||||
CheckCheck,
|
CheckCheck,
|
||||||
|
DatabaseZap,
|
||||||
Download,
|
Download,
|
||||||
ExternalLink,
|
ExternalLink,
|
||||||
FileWarning,
|
FileWarning,
|
||||||
@@ -53,6 +54,10 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
|
import {
|
||||||
|
type RecentFurniResyncResult,
|
||||||
|
requestRecentFurniResync,
|
||||||
|
} from "@/lib/admin/recent-furni-resync";
|
||||||
import { adminFetch } from "@/lib/admin-fetch";
|
import { adminFetch } from "@/lib/admin-fetch";
|
||||||
import type { FurniImportSource } from "@/lib/habbo-gamedata-hotel";
|
import type { FurniImportSource } from "@/lib/habbo-gamedata-hotel";
|
||||||
import { NitroEditorDialog } from "./nitro-editor-dialog";
|
import { NitroEditorDialog } from "./nitro-editor-dialog";
|
||||||
@@ -211,6 +216,10 @@ export function ImportFurniClient({ source }: { source: FurniImportSource }) {
|
|||||||
|
|
||||||
// Reorganize catalog
|
// Reorganize catalog
|
||||||
const [reorganizing, setReorganizing] = useState(false);
|
const [reorganizing, setReorganizing] = useState(false);
|
||||||
|
const [confirmRecentResync, setConfirmRecentResync] = useState(false);
|
||||||
|
const [recentResyncing, setRecentResyncing] = useState(false);
|
||||||
|
const [recentResyncResult, setRecentResyncResult] =
|
||||||
|
useState<RecentFurniResyncResult | null>(null);
|
||||||
const searchRef = useRef<HTMLInputElement>(null);
|
const searchRef = useRef<HTMLInputElement>(null);
|
||||||
const batchAbortRef = useRef<AbortController | null>(null);
|
const batchAbortRef = useRef<AbortController | null>(null);
|
||||||
|
|
||||||
@@ -792,6 +801,30 @@ export function ImportFurniClient({ source }: { source: FurniImportSource }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function resyncRecentImports() {
|
||||||
|
setConfirmRecentResync(false);
|
||||||
|
setRecentResyncing(true);
|
||||||
|
setRecentResyncResult(null);
|
||||||
|
try {
|
||||||
|
const result = await requestRecentFurniResync(adminFetch);
|
||||||
|
setRecentResyncResult(result);
|
||||||
|
if (result.examined === 0) {
|
||||||
|
toast.info("No imported furni found in the last 7 days");
|
||||||
|
} else if (result.failed > 0 || !result.rconOk) {
|
||||||
|
toast.warning("Furni resync completed with warnings");
|
||||||
|
} else {
|
||||||
|
toast.success(`Updated ${result.resynced} imported furni`);
|
||||||
|
}
|
||||||
|
fetchStats();
|
||||||
|
} catch (error) {
|
||||||
|
toast.error(
|
||||||
|
error instanceof Error ? error.message : "Furni resync failed",
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
setRecentResyncing(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function applyReorganize() {
|
async function applyReorganize() {
|
||||||
setReorgPreview(null);
|
setReorgPreview(null);
|
||||||
setReorganizing(true);
|
setReorganizing(true);
|
||||||
@@ -1038,6 +1071,56 @@ export function ImportFurniClient({ source }: { source: FurniImportSource }) {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{recentResyncResult && (
|
||||||
|
<div className="rounded-lg border bg-card p-4 space-y-3">
|
||||||
|
<div className="flex items-center justify-between gap-3">
|
||||||
|
<h3 className="text-sm font-semibold">Imported Furni Update</h3>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => setRecentResyncResult(null)}
|
||||||
|
>
|
||||||
|
Dismiss
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-wrap gap-4 text-sm">
|
||||||
|
<span>{recentResyncResult.examined} examined</span>
|
||||||
|
<span className="text-[var(--admin-success)]">
|
||||||
|
{recentResyncResult.resynced} updated
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className={recentResyncResult.failed ? "text-destructive" : ""}
|
||||||
|
>
|
||||||
|
{recentResyncResult.failed} failed
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className={
|
||||||
|
recentResyncResult.rconOk
|
||||||
|
? "text-[var(--admin-success)]"
|
||||||
|
: "text-[var(--admin-warning)]"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
RCON {recentResyncResult.rconOk ? "refreshed" : "not refreshed"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{recentResyncResult.errors.length > 0 && (
|
||||||
|
<details className="text-xs">
|
||||||
|
<summary className="cursor-pointer text-muted-foreground">
|
||||||
|
View errors
|
||||||
|
</summary>
|
||||||
|
<div className="mt-2 max-h-48 space-y-1 overflow-y-auto">
|
||||||
|
{recentResyncResult.errors.map((error) => (
|
||||||
|
<p key={`${error.classname}:${error.message}`}>
|
||||||
|
<span className="font-mono">{error.classname}</span>:{" "}
|
||||||
|
{error.message}
|
||||||
|
</p>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Batch Summary */}
|
{/* Batch Summary */}
|
||||||
{batchSummary && (
|
{batchSummary && (
|
||||||
<div className="rounded-lg border p-4 space-y-3 bg-card">
|
<div className="rounded-lg border p-4 space-y-3 bg-card">
|
||||||
@@ -1535,6 +1618,19 @@ export function ImportFurniClient({ source }: { source: FurniImportSource }) {
|
|||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() => setConfirmRecentResync(true)}
|
||||||
|
disabled={recentResyncing}
|
||||||
|
>
|
||||||
|
{recentResyncing ? (
|
||||||
|
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||||
|
) : (
|
||||||
|
<DatabaseZap className="mr-2 h-4 w-4" />
|
||||||
|
)}
|
||||||
|
{recentResyncing
|
||||||
|
? "Updating imported furni..."
|
||||||
|
: "Update imported furni"}
|
||||||
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem onClick={startReorganize} disabled={reorganizing}>
|
<DropdownMenuItem onClick={startReorganize} disabled={reorganizing}>
|
||||||
<FolderSync className="w-4 h-4 mr-2" />
|
<FolderSync className="w-4 h-4 mr-2" />
|
||||||
{reorganizing ? "Reorganizing..." : "Reorganize Catalog"}
|
{reorganizing ? "Reorganizing..." : "Reorganize Catalog"}
|
||||||
@@ -1936,6 +2032,30 @@ export function ImportFurniClient({ source }: { source: FurniImportSource }) {
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
|
<Dialog open={confirmRecentResync} onOpenChange={setConfirmRecentResync}>
|
||||||
|
<DialogContent className="max-w-sm">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Update imported furni?</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
This updates FurnitureData for furni imported during the last 7
|
||||||
|
days, then refreshes the emulator catalog and item caches. No
|
||||||
|
database rows or asset files are deleted.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => setConfirmRecentResync(false)}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button onClick={resyncRecentImports} disabled={recentResyncing}>
|
||||||
|
Update last 7 days
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
{/* Delete Confirmation */}
|
{/* Delete Confirmation */}
|
||||||
<Dialog
|
<Dialog
|
||||||
open={!!confirmDelete}
|
open={!!confirmDelete}
|
||||||
|
|||||||
Reference in new issue
Block a user