feat(inventory): show spool ID in edit modal and AMS hover card (#1385) (#1402)

feat(inventory): show spool ID in edit modal and AMS hover card (#1385)
This commit is contained in:
Chanakyan
2026-05-18 12:34:42 +02:00
committed by GitHub
parent ae0f485a84
commit ed5af66839
4 changed files with 91 additions and 6 deletions
@@ -310,6 +310,21 @@ describe('FilamentHoverCard', () => {
});
expect(screen.queryAllByTitle('Open in Inventory')).toHaveLength(1);
});
it('shows the spool ID in the assigned-spool block', async () => {
renderWithHover(
<FilamentHoverCard
data={baseFilamentData}
inventory={{ assignedSpool: inventorySpool }}
>
<div>trigger</div>
</FilamentHoverCard>
);
vi.advanceTimersByTime(100);
await waitFor(() => {
expect(screen.getByText('#42')).toBeInTheDocument();
});
});
});
});
@@ -1117,3 +1117,67 @@ describe('SpoolFormModal copy mode', () => {
expect((payload as Record<string, unknown>).weight_used).toBe(0);
});
});
// The "#<id>" affordance in the modal header (#1385) is only meaningful when
// editing an existing spool — there's no ID yet on create, and the copy path
// is producing a new spool too. Guard all three cases so a future refactor
// can't quietly start leaking the source spool's ID into the Copy modal.
describe('SpoolFormModal header spool ID (#1385)', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('shows #<id> next to the title when editing an existing spool', async () => {
render(
<SpoolFormModal
isOpen={true}
onClose={vi.fn()}
spool={existingSpool}
mode="edit"
currencySymbol="$"
/>
);
await waitFor(() => {
expect(screen.getByText('Edit Spool')).toBeInTheDocument();
});
// existingSpool.id is 1; render as "#1" in the modal header.
expect(screen.getByText('#1')).toBeInTheDocument();
});
it('does not show an ID when creating a new spool', async () => {
render(
<SpoolFormModal
isOpen={true}
onClose={vi.fn()}
currencySymbol="$"
/>
);
await waitFor(() => {
expect(screen.getByRole('heading', { name: 'Add Spool' })).toBeInTheDocument();
});
// No spool exists yet → header carries no "#..." token.
expect(screen.queryByText(/^#\d+$/)).not.toBeInTheDocument();
});
it('does not leak the source spool ID when copying', async () => {
// Copying produces a fresh spool — surfacing the source ID in the
// "Copy Spool" header would mislead the user into thinking the new
// spool inherits it.
render(
<SpoolFormModal
isOpen={true}
onClose={vi.fn()}
spool={existingSpool}
mode="copy"
currencySymbol="$"
/>
);
await waitFor(() => {
expect(screen.getByRole('heading', { name: 'Copy Spool' })).toBeInTheDocument();
});
expect(screen.queryByText(/^#\d+$/)).not.toBeInTheDocument();
});
});
@@ -366,11 +366,14 @@ export function FilamentHoverCard({ data, children, disabled, className = '', sp
{t('inventory.assigned')}
</span>
</div>
<p className="text-xs text-white truncate">
{inventory.assignedSpool.brand ? `${inventory.assignedSpool.brand} ` : ''}
{inventory.assignedSpool.material}
{inventory.assignedSpool.color_name ? ` - ${inventory.assignedSpool.color_name}` : ''}
</p>
<div className="flex items-baseline gap-1.5 min-w-0 mb-1">
<p className="text-xs text-white truncate">
{inventory.assignedSpool.brand ? `${inventory.assignedSpool.brand} ` : ''}
{inventory.assignedSpool.material}
{inventory.assignedSpool.color_name ? ` - ${inventory.assignedSpool.color_name}` : ''}
</p>
<span className="text-[10px] font-mono text-bambu-gray shrink-0">#{inventory.assignedSpool.id}</span>
</div>
{(!spoolman?.linkedSpoolId || inventory.assignedSpool!.id !== spoolman.linkedSpoolId) && (
<button
onClick={(e) => {
+4 -1
View File
@@ -745,8 +745,11 @@ export function SpoolFormModal({
<div className="relative w-full max-w-xl mx-4 bg-bambu-dark-secondary border border-bambu-dark-tertiary rounded-xl shadow-2xl max-h-[90vh] flex flex-col">
{/* Header */}
<div className="flex items-center justify-between p-4 border-b border-bambu-dark-tertiary flex-shrink-0">
<h2 className="text-lg font-semibold text-white">
<h2 className="text-lg font-semibold text-white flex items-baseline gap-2">
{isEditing ? t('inventory.editSpool') : isCopying ? t('inventory.copySpool') : t('inventory.addSpool')}
{isEditing && spool && (
<span className="text-sm font-mono text-bambu-gray">#{spool.id}</span>
)}
</h2>
<button
onClick={onClose}