revert fb8e77bb68
Local Build and Deploy / deploy (push) Successful in 1m11s
Local Build and Deploy / deploy (push) Successful in 1m11s
revert style: clean up code with prettier and eslint
This commit is contained in:
1 parent
27de078f54
commit
e85e4d74ea
378 files changed
+20710
-22428
No files matched your search
@@ -1,4 +1,4 @@
|
||||
"use client";
|
||||
'use client'
|
||||
|
||||
import {
|
||||
AlertTriangle,
|
||||
@@ -11,39 +11,46 @@ import {
|
||||
Save,
|
||||
ShoppingCart,
|
||||
Sparkles,
|
||||
} from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { translateCatalogItems } from "@/actions/catalog-items";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { useServerAction } from "@/hooks/use-server-action";
|
||||
import { FurniIcon } from "./catalog-items-table";
|
||||
} from 'lucide-react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
import { translateCatalogItems } from '@/actions/catalog-items'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Checkbox } from '@/components/ui/checkbox'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from '@/components/ui/table'
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
|
||||
import { useServerAction } from '@/hooks/use-server-action'
|
||||
import { FurniIcon } from './catalog-items-table'
|
||||
|
||||
interface BaseItemForTranslation {
|
||||
id: number;
|
||||
publicName: string;
|
||||
itemName: string;
|
||||
spriteId: number;
|
||||
type: string;
|
||||
id: number
|
||||
publicName: string
|
||||
itemName: string
|
||||
spriteId: number
|
||||
type: string
|
||||
}
|
||||
|
||||
interface CatalogTranslateTabProps {
|
||||
baseItems: BaseItemForTranslation[];
|
||||
catalogNameMap: Record<number, string>;
|
||||
canEdit: boolean;
|
||||
furniDataIdList: number[];
|
||||
furniDescriptionMap: Record<number, string>;
|
||||
furniRevisionMap?: Record<number, { classname: string; revision: number }>;
|
||||
baseItems: BaseItemForTranslation[]
|
||||
catalogNameMap: Record<number, string>
|
||||
canEdit: boolean
|
||||
furniDataIdList: number[]
|
||||
furniDescriptionMap: Record<number, string>
|
||||
furniRevisionMap?: Record<number, { classname: string; revision: number }>
|
||||
}
|
||||
|
||||
type FilterMode = "all" | "missing" | "modified";
|
||||
type FilterMode = 'all' | 'missing' | 'modified'
|
||||
|
||||
export function CatalogTranslateTab({
|
||||
baseItems,
|
||||
@@ -53,164 +60,164 @@ export function CatalogTranslateTab({
|
||||
furniDescriptionMap,
|
||||
furniRevisionMap = {},
|
||||
}: CatalogTranslateTabProps) {
|
||||
const router = useRouter();
|
||||
const { run, isPending } = useServerAction();
|
||||
const [isSuggesting, setIsSuggesting] = useState(false);
|
||||
const [filterMode, setFilterMode] = useState<FilterMode>("all");
|
||||
const router = useRouter()
|
||||
const { run, isPending } = useServerAction()
|
||||
const [isSuggesting, setIsSuggesting] = useState(false)
|
||||
const [filterMode, setFilterMode] = useState<FilterMode>('all')
|
||||
|
||||
const furniDataIdSet = useMemo(() => new Set(furniDataIdList), [furniDataIdList]);
|
||||
const [selected, setSelected] = useState<Set<number>>(new Set());
|
||||
const furniDataIdSet = useMemo(() => new Set(furniDataIdList), [furniDataIdList])
|
||||
const [selected, setSelected] = useState<Set<number>>(new Set())
|
||||
|
||||
// ── Editable state: names + descriptions ─────────────────────────
|
||||
const [editedNames, setEditedNames] = useState<Record<number, string>>(
|
||||
Object.fromEntries(baseItems.map((item) => [item.id, item.publicName])),
|
||||
);
|
||||
)
|
||||
|
||||
const [editedDescs, setEditedDescs] = useState<Record<number, string>>(
|
||||
Object.fromEntries(baseItems.map((item) => [item.id, furniDescriptionMap[item.id] ?? ""])),
|
||||
);
|
||||
Object.fromEntries(baseItems.map((item) => [item.id, furniDescriptionMap[item.id] ?? ''])),
|
||||
)
|
||||
|
||||
// Track which items have name or description changes
|
||||
const isNameChanged = useCallback(
|
||||
(item: BaseItemForTranslation) => editedNames[item.id] !== item.publicName,
|
||||
[editedNames],
|
||||
);
|
||||
)
|
||||
|
||||
const isDescChanged = useCallback(
|
||||
(item: BaseItemForTranslation) => editedDescs[item.id] !== (furniDescriptionMap[item.id] ?? ""),
|
||||
(item: BaseItemForTranslation) => editedDescs[item.id] !== (furniDescriptionMap[item.id] ?? ''),
|
||||
[editedDescs, furniDescriptionMap],
|
||||
);
|
||||
)
|
||||
|
||||
const changedItems = useMemo(
|
||||
() => baseItems.filter((item) => isNameChanged(item) || isDescChanged(item)),
|
||||
[baseItems, isNameChanged, isDescChanged],
|
||||
);
|
||||
)
|
||||
|
||||
const missingItems = useMemo(
|
||||
() => baseItems.filter((item) => !furniDataIdSet.has(item.id)),
|
||||
[baseItems, furniDataIdSet],
|
||||
);
|
||||
)
|
||||
|
||||
const filteredItems = useMemo(() => {
|
||||
switch (filterMode) {
|
||||
case "missing":
|
||||
return baseItems.filter((item) => !furniDataIdSet.has(item.id));
|
||||
case "modified":
|
||||
return baseItems.filter((item) => isNameChanged(item) || isDescChanged(item));
|
||||
case 'missing':
|
||||
return baseItems.filter((item) => !furniDataIdSet.has(item.id))
|
||||
case 'modified':
|
||||
return baseItems.filter((item) => isNameChanged(item) || isDescChanged(item))
|
||||
default:
|
||||
return baseItems;
|
||||
return baseItems
|
||||
}
|
||||
}, [baseItems, filterMode, furniDataIdSet, isNameChanged, isDescChanged]);
|
||||
}, [baseItems, filterMode, furniDataIdSet, isNameChanged, isDescChanged])
|
||||
|
||||
const toggleOne = useCallback((id: number) => {
|
||||
setSelected((prev) => {
|
||||
const next = new Set(prev);
|
||||
if (next.has(id)) next.delete(id);
|
||||
else next.add(id);
|
||||
return next;
|
||||
});
|
||||
}, []);
|
||||
const next = new Set(prev)
|
||||
if (next.has(id)) next.delete(id)
|
||||
else next.add(id)
|
||||
return next
|
||||
})
|
||||
}, [])
|
||||
|
||||
const toggleAll = useCallback(() => {
|
||||
setSelected((prev) =>
|
||||
prev.size === filteredItems.length ? new Set() : new Set(filteredItems.map((i) => i.id)),
|
||||
);
|
||||
}, [filteredItems]);
|
||||
)
|
||||
}, [filteredItems])
|
||||
|
||||
// Items to act on: selected ones if any, otherwise all
|
||||
const targetItems = useMemo(() => {
|
||||
if (selected.size === 0) return baseItems;
|
||||
return baseItems.filter((i) => selected.has(i.id));
|
||||
}, [baseItems, selected]);
|
||||
if (selected.size === 0) return baseItems
|
||||
return baseItems.filter((i) => selected.has(i.id))
|
||||
}, [baseItems, selected])
|
||||
|
||||
function handleNameChange(id: number, value: string) {
|
||||
setEditedNames((prev) => ({ ...prev, [id]: value }));
|
||||
setEditedNames((prev) => ({ ...prev, [id]: value }))
|
||||
}
|
||||
|
||||
function handleDescChange(id: number, value: string) {
|
||||
setEditedDescs((prev) => ({ ...prev, [id]: value }));
|
||||
setEditedDescs((prev) => ({ ...prev, [id]: value }))
|
||||
}
|
||||
|
||||
function handleReset() {
|
||||
setEditedNames(Object.fromEntries(baseItems.map((item) => [item.id, item.publicName])));
|
||||
setEditedNames(Object.fromEntries(baseItems.map((item) => [item.id, item.publicName])))
|
||||
setEditedDescs(
|
||||
Object.fromEntries(baseItems.map((item) => [item.id, furniDescriptionMap[item.id] ?? ""])),
|
||||
);
|
||||
Object.fromEntries(baseItems.map((item) => [item.id, furniDescriptionMap[item.id] ?? ''])),
|
||||
)
|
||||
}
|
||||
|
||||
async function handleSuggestFromHabboFurni() {
|
||||
if (targetItems.length === 0) return;
|
||||
setIsSuggesting(true);
|
||||
if (targetItems.length === 0) return
|
||||
setIsSuggesting(true)
|
||||
|
||||
try {
|
||||
// Collect classnames from selected items (or all if none selected)
|
||||
const classnames = targetItems.map((item) => item.itemName).filter(Boolean);
|
||||
const classnames = targetItems.map((item) => item.itemName).filter(Boolean)
|
||||
if (classnames.length === 0) {
|
||||
toast.info("No classnames found to look up.");
|
||||
setIsSuggesting(false);
|
||||
return;
|
||||
toast.info('No classnames found to look up.')
|
||||
setIsSuggesting(false)
|
||||
return
|
||||
}
|
||||
|
||||
// Batch in chunks of 50 to avoid too-long URLs
|
||||
const CHUNK_SIZE = 50;
|
||||
const allSuggestions: Record<string, { name: string; description: string }> = {};
|
||||
const CHUNK_SIZE = 50
|
||||
const allSuggestions: Record<string, { name: string; description: string }> = {}
|
||||
|
||||
for (let i = 0; i < classnames.length; i += CHUNK_SIZE) {
|
||||
const chunk = classnames.slice(i, i + CHUNK_SIZE);
|
||||
const chunk = classnames.slice(i, i + CHUNK_SIZE)
|
||||
const res = await fetch(
|
||||
`/api/admin/catalog/suggest?classnames=${encodeURIComponent(chunk.join(","))}`,
|
||||
);
|
||||
`/api/admin/catalog/suggest?classnames=${encodeURIComponent(chunk.join(','))}`,
|
||||
)
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
Object.assign(allSuggestions, data.suggestions || {});
|
||||
const data = await res.json()
|
||||
Object.assign(allSuggestions, data.suggestions || {})
|
||||
}
|
||||
}
|
||||
|
||||
// Apply suggestions to items that have a match (names + descriptions)
|
||||
let nameCount = 0;
|
||||
let descCount = 0;
|
||||
const newNames = { ...editedNames };
|
||||
const newDescs = { ...editedDescs };
|
||||
let nameCount = 0
|
||||
let descCount = 0
|
||||
const newNames = { ...editedNames }
|
||||
const newDescs = { ...editedDescs }
|
||||
|
||||
for (const item of targetItems) {
|
||||
const suggestion = allSuggestions[item.itemName];
|
||||
if (!suggestion) continue;
|
||||
const suggestion = allSuggestions[item.itemName]
|
||||
if (!suggestion) continue
|
||||
|
||||
if (suggestion.name && suggestion.name !== item.publicName) {
|
||||
newNames[item.id] = suggestion.name;
|
||||
nameCount++;
|
||||
newNames[item.id] = suggestion.name
|
||||
nameCount++
|
||||
}
|
||||
|
||||
const currentDesc = furniDescriptionMap[item.id] ?? "";
|
||||
const currentDesc = furniDescriptionMap[item.id] ?? ''
|
||||
if (suggestion.description && suggestion.description !== currentDesc) {
|
||||
newDescs[item.id] = suggestion.description;
|
||||
descCount++;
|
||||
newDescs[item.id] = suggestion.description
|
||||
descCount++
|
||||
}
|
||||
}
|
||||
|
||||
setEditedNames(newNames);
|
||||
setEditedDescs(newDescs);
|
||||
setEditedNames(newNames)
|
||||
setEditedDescs(newDescs)
|
||||
|
||||
const total = nameCount + descCount;
|
||||
const total = nameCount + descCount
|
||||
if (total > 0) {
|
||||
const parts = [];
|
||||
if (nameCount > 0) parts.push(`${nameCount} name(s)`);
|
||||
if (descCount > 0) parts.push(`${descCount} description(s)`);
|
||||
toast.success(`HabboFurni: ${parts.join(" + ")} suggested from habbo.it. Review and save.`);
|
||||
const parts = []
|
||||
if (nameCount > 0) parts.push(`${nameCount} name(s)`)
|
||||
if (descCount > 0) parts.push(`${descCount} description(s)`)
|
||||
toast.success(`HabboFurni: ${parts.join(' + ')} suggested from habbo.it. Review and save.`)
|
||||
} else {
|
||||
toast.info(
|
||||
`HabboFurni: No new suggestions found (${Object.keys(allSuggestions).length} looked up, all already match).`,
|
||||
);
|
||||
)
|
||||
}
|
||||
} catch (err) {
|
||||
toast.error(`HabboFurni error: ${(err as Error).message}`);
|
||||
toast.error(`HabboFurni error: ${(err as Error).message}`)
|
||||
} finally {
|
||||
setIsSuggesting(false);
|
||||
setIsSuggesting(false)
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSaveAll() {
|
||||
if (changedItems.length === 0) return;
|
||||
if (changedItems.length === 0) return
|
||||
|
||||
run(
|
||||
() =>
|
||||
@@ -222,26 +229,26 @@ export function CatalogTranslateTab({
|
||||
})),
|
||||
}),
|
||||
{
|
||||
successMessage: "Translations saved.",
|
||||
errorMessage: "Failed to save translations.",
|
||||
successMessage: 'Translations saved.',
|
||||
errorMessage: 'Failed to save translations.',
|
||||
onSuccess: (data) => {
|
||||
const d = data as Record<string, number | boolean>;
|
||||
const parts = [];
|
||||
const d = data as Record<string, number | boolean>
|
||||
const parts = []
|
||||
if (d.namesUpdated && (d.namesUpdated as number) > 0)
|
||||
parts.push(`${d.namesUpdated} name(s) updated`);
|
||||
parts.push(`${d.namesUpdated} name(s) updated`)
|
||||
if (d.descriptionsUpdated && (d.descriptionsUpdated as number) > 0)
|
||||
parts.push(`${d.descriptionsUpdated} description(s) updated`);
|
||||
if (parts.length === 0) parts.push(`${d.updated ?? 0} item(s) processed`);
|
||||
if (d.furniDataUpdated) parts.push("FurnitureData.json synced");
|
||||
parts.push(`${d.descriptionsUpdated} description(s) updated`)
|
||||
if (parts.length === 0) parts.push(`${d.updated ?? 0} item(s) processed`)
|
||||
if (d.furniDataUpdated) parts.push('FurnitureData.json synced')
|
||||
if (d.furniDataInserted && (d.furniDataInserted as number) > 0) {
|
||||
parts.push(`${d.furniDataInserted} new item(s) added to FurnitureData.json`);
|
||||
parts.push(`${d.furniDataInserted} new item(s) added to FurnitureData.json`)
|
||||
}
|
||||
parts.push("emulator cache refreshed");
|
||||
toast.success(`${parts.join(" · ")}.`);
|
||||
router.refresh();
|
||||
parts.push('emulator cache refreshed')
|
||||
toast.success(`${parts.join(' · ')}.`)
|
||||
router.refresh()
|
||||
},
|
||||
},
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -267,7 +274,8 @@ export function CatalogTranslateTab({
|
||||
<div className="flex items-center gap-1.5 mt-2 text-xs text-[var(--admin-warning)] dark:text-[var(--admin-warning)]">
|
||||
<AlertTriangle className="h-3.5 w-3.5" />
|
||||
<span>
|
||||
{missingItems.length} item(s) missing from FurnitureData.json — will be auto-inserted on save
|
||||
{missingItems.length} item(s) missing from FurnitureData.json — will be
|
||||
auto-inserted on save
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
@@ -286,7 +294,7 @@ export function CatalogTranslateTab({
|
||||
) : (
|
||||
<Sparkles className="mr-2 h-4 w-4" />
|
||||
)}
|
||||
Suggest IT{selected.size > 0 ? ` (${selected.size})` : ""}
|
||||
Suggest IT{selected.size > 0 ? ` (${selected.size})` : ''}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
@@ -297,7 +305,11 @@ export function CatalogTranslateTab({
|
||||
<RotateCcw className="mr-2 h-4 w-4" />
|
||||
Reset
|
||||
</Button>
|
||||
<Button size="sm" onClick={handleSaveAll} disabled={isPending || changedItems.length === 0}>
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={handleSaveAll}
|
||||
disabled={isPending || changedItems.length === 0}
|
||||
>
|
||||
{isPending ? (
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
) : (
|
||||
@@ -315,28 +327,28 @@ export function CatalogTranslateTab({
|
||||
<Filter className="h-4 w-4 text-muted-foreground" />
|
||||
<div className="flex gap-1">
|
||||
<Button
|
||||
variant={filterMode === "all" ? "default" : "outline"}
|
||||
variant={filterMode === 'all' ? 'default' : 'outline'}
|
||||
size="sm"
|
||||
className="h-7 text-xs"
|
||||
onClick={() => setFilterMode("all")}
|
||||
onClick={() => setFilterMode('all')}
|
||||
>
|
||||
All ({baseItems.length})
|
||||
</Button>
|
||||
<Button
|
||||
variant={filterMode === "missing" ? "default" : "outline"}
|
||||
variant={filterMode === 'missing' ? 'default' : 'outline'}
|
||||
size="sm"
|
||||
className="h-7 text-xs"
|
||||
onClick={() => setFilterMode("missing")}
|
||||
onClick={() => setFilterMode('missing')}
|
||||
disabled={missingItems.length === 0}
|
||||
>
|
||||
<AlertTriangle className="mr-1 h-3 w-3" />
|
||||
Missing ({missingItems.length})
|
||||
</Button>
|
||||
<Button
|
||||
variant={filterMode === "modified" ? "default" : "outline"}
|
||||
variant={filterMode === 'modified' ? 'default' : 'outline'}
|
||||
size="sm"
|
||||
className="h-7 text-xs"
|
||||
onClick={() => setFilterMode("modified")}
|
||||
onClick={() => setFilterMode('modified')}
|
||||
disabled={changedItems.length === 0}
|
||||
>
|
||||
Modified ({changedItems.length})
|
||||
@@ -350,7 +362,9 @@ export function CatalogTranslateTab({
|
||||
No items to translate in this page.
|
||||
</p>
|
||||
) : filteredItems.length === 0 ? (
|
||||
<p className="text-sm text-muted-foreground text-center py-8">No items match the current filter.</p>
|
||||
<p className="text-sm text-muted-foreground text-center py-8">
|
||||
No items match the current filter.
|
||||
</p>
|
||||
) : (
|
||||
<TooltipProvider>
|
||||
<div className="rounded-md border overflow-auto max-h-[600px]">
|
||||
@@ -375,19 +389,19 @@ export function CatalogTranslateTab({
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{filteredItems.map((item) => {
|
||||
const nameChanged = isNameChanged(item);
|
||||
const descChanged = isDescChanged(item);
|
||||
const isChanged = nameChanged || descChanged;
|
||||
const isMissing = !furniDataIdSet.has(item.id);
|
||||
const currentCatalogName = catalogNameMap[item.id];
|
||||
const nameChanged = isNameChanged(item)
|
||||
const descChanged = isDescChanged(item)
|
||||
const isChanged = nameChanged || descChanged
|
||||
const isMissing = !furniDataIdSet.has(item.id)
|
||||
const currentCatalogName = catalogNameMap[item.id]
|
||||
return (
|
||||
<TableRow
|
||||
key={item.id}
|
||||
className={
|
||||
isChanged
|
||||
? "bg-[var(--admin-warning-subtle)] dark:bg-[var(--admin-warning-subtle)]"
|
||||
? 'bg-[var(--admin-warning-subtle)] dark:bg-[var(--admin-warning-subtle)]'
|
||||
: isMissing
|
||||
? "bg-[var(--admin-warning-subtle)] dark:bg-[var(--admin-warning-subtle)]"
|
||||
? 'bg-[var(--admin-warning-subtle)] dark:bg-[var(--admin-warning-subtle)]'
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
@@ -400,18 +414,24 @@ export function CatalogTranslateTab({
|
||||
</TableCell>
|
||||
<TableCell className="px-1">
|
||||
{(() => {
|
||||
const revInfo = furniRevisionMap[item.id];
|
||||
const revInfo = furniRevisionMap[item.id]
|
||||
return revInfo ? (
|
||||
<FurniIcon classname={revInfo.classname} revision={revInfo.revision} />
|
||||
<FurniIcon
|
||||
classname={revInfo.classname}
|
||||
revision={revInfo.revision}
|
||||
/>
|
||||
) : (
|
||||
<FurniIcon classname={item.itemName} revision={0} />
|
||||
);
|
||||
)
|
||||
})()}
|
||||
</TableCell>
|
||||
<TableCell className="font-mono text-xs">{item.id}</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant={item.type === "s" ? "default" : "secondary"} className="text-xs">
|
||||
{item.type === "s" ? "Floor" : item.type === "i" ? "Wall" : item.type}
|
||||
<Badge
|
||||
variant={item.type === 's' ? 'default' : 'secondary'}
|
||||
className="text-xs"
|
||||
>
|
||||
{item.type === 's' ? 'Floor' : item.type === 'i' ? 'Wall' : item.type}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell className="text-xs text-muted-foreground font-mono">
|
||||
@@ -419,10 +439,10 @@ export function CatalogTranslateTab({
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Input
|
||||
value={editedNames[item.id] || ""}
|
||||
value={editedNames[item.id] || ''}
|
||||
onChange={(e) => handleNameChange(item.id, e.target.value)}
|
||||
disabled={!canEdit}
|
||||
className={`h-8 text-sm ${nameChanged ? "border-[var(--admin-warning-border)] dark:border-[var(--admin-warning-border)]" : ""}`}
|
||||
className={`h-8 text-sm ${nameChanged ? 'border-[var(--admin-warning-border)] dark:border-[var(--admin-warning-border)]' : ''}`}
|
||||
/>
|
||||
{currentCatalogName && currentCatalogName !== item.publicName && (
|
||||
<p className="text-[11px] text-muted-foreground mt-0.5 truncate">
|
||||
@@ -432,11 +452,11 @@ export function CatalogTranslateTab({
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Input
|
||||
value={editedDescs[item.id] || ""}
|
||||
value={editedDescs[item.id] || ''}
|
||||
onChange={(e) => handleDescChange(item.id, e.target.value)}
|
||||
disabled={!canEdit}
|
||||
placeholder="No description"
|
||||
className={`h-8 text-sm ${descChanged ? "border-[var(--admin-warning-border)] dark:border-[var(--admin-warning-border)]" : ""}`}
|
||||
className={`h-8 text-sm ${descChanged ? 'border-[var(--admin-warning-border)] dark:border-[var(--admin-warning-border)]' : ''}`}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
@@ -444,7 +464,7 @@ export function CatalogTranslateTab({
|
||||
{isChanged ? (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="text-xs bg-[var(--admin-warning-subtle)] text-[var(--admin-warning)]"
|
||||
className="text-xs bg-[var(--admin-warning-subtle)] text-[var(--admin-warning)]"
|
||||
>
|
||||
Modified
|
||||
</Badge>
|
||||
@@ -474,7 +494,7 @@ export function CatalogTranslateTab({
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
)
|
||||
})}
|
||||
</TableBody>
|
||||
</Table>
|
||||
@@ -483,5 +503,5 @@ export function CatalogTranslateTab({
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user