feat(inventory): remove the requiredLevel equip gate from the detail panel
The API can no longer emit ITEM_LEVEL_REQUIREMENT_NOT_MET (Task 8 removed the level gate server-side) and InventoryItem.item no longer carries requiredLevel (Task 12). Drop the characterLevel input, the meetsLevelRequirement computed, the associated template branch and copy, and the dead German error-message mapping. Update fixtures across the inventory specs to the real InventoryItem/CharacterResponse shapes.
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
<h2 class="inventory-detail__name" data-detail-name>{{ item.item.name }}</h2>
|
||||
<p class="inventory-detail__rarity" data-detail-rarity>{{ rarityLabel() }}</p>
|
||||
@if (slotLabel(); as slot) {
|
||||
<p class="inventory-detail__meta">{{ slot }} · Stufe {{ item.item.requiredLevel }}</p>
|
||||
<p class="inventory-detail__meta">{{ slot }}</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
@@ -38,10 +38,6 @@
|
||||
<span class="inventory-detail__equipped" data-detail-equipped>Ausgerüstet</span>
|
||||
} @else if (!isEquippable()) {
|
||||
<span class="inventory-detail__note">Nicht ausrüstbar</span>
|
||||
} @else if (!meetsLevelRequirement()) {
|
||||
<button type="button" class="inventory-detail__equip" data-detail-equip disabled>
|
||||
Benötigt Stufe {{ item.item.requiredLevel }}
|
||||
</button>
|
||||
} @else {
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -12,7 +12,6 @@ const wornSword: InventoryItem = {
|
||||
name: 'Abgenutztes Kurzschwert',
|
||||
rarity: 'COMMON',
|
||||
equipmentSlot: 'WEAPON',
|
||||
requiredLevel: 1,
|
||||
weaponDamage: 8,
|
||||
bonusAttack: 0,
|
||||
bonusHp: 0,
|
||||
@@ -30,7 +29,6 @@ const banditBlade: InventoryItem = {
|
||||
name: 'Räuberklinge',
|
||||
rarity: 'COMMON',
|
||||
equipmentSlot: 'WEAPON',
|
||||
requiredLevel: 1,
|
||||
weaponDamage: 11,
|
||||
bonusAttack: 1,
|
||||
bonusHp: 0,
|
||||
@@ -42,7 +40,6 @@ const banditBlade: InventoryItem = {
|
||||
async function setup(overrides: {
|
||||
item?: InventoryItem | null;
|
||||
equippedItemInSlot?: InventoryItem | null;
|
||||
characterLevel?: number;
|
||||
busy?: boolean;
|
||||
}) {
|
||||
TestBed.resetTestingModule();
|
||||
@@ -50,7 +47,6 @@ async function setup(overrides: {
|
||||
const fixture = TestBed.createComponent(InventoryDetailPanelComponent);
|
||||
fixture.componentRef.setInput('item', overrides.item ?? null);
|
||||
fixture.componentRef.setInput('equippedItemInSlot', overrides.equippedItemInSlot ?? null);
|
||||
fixture.componentRef.setInput('characterLevel', overrides.characterLevel ?? 1);
|
||||
fixture.componentRef.setInput('busy', overrides.busy ?? false);
|
||||
fixture.detectChanges();
|
||||
return fixture;
|
||||
@@ -79,12 +75,13 @@ describe('InventoryDetailPanelComponent', () => {
|
||||
expect(text).toContain('+1');
|
||||
});
|
||||
|
||||
it('shows a disabled Benötigt Stufe X button when the level requirement is not met', async () => {
|
||||
const fixture = await setup({ item: { ...banditBlade, item: { ...banditBlade.item, requiredLevel: 5 } }, characterLevel: 1 });
|
||||
const button = (fixture.nativeElement as HTMLElement).querySelector<HTMLButtonElement>('[data-detail-equip]');
|
||||
it('always shows the plain equip button for an owned equippable item', async () => {
|
||||
const fixture = await setup({ item: banditBlade });
|
||||
const element = fixture.nativeElement as HTMLElement;
|
||||
|
||||
expect(button?.textContent).toContain('Benötigt Stufe 5');
|
||||
expect(button?.disabled).toBe(true);
|
||||
const button = element.querySelector<HTMLButtonElement>('[data-detail-equip]');
|
||||
expect(button?.disabled).toBe(false);
|
||||
expect(button?.textContent?.trim()).toBe('Ausrüsten');
|
||||
});
|
||||
|
||||
it('emits equip with the characterItemId when Ausrüsten is clicked', async () => {
|
||||
|
||||
@@ -35,7 +35,6 @@ const STAT_LABELS: ReadonlyArray<{ label: string; key: StatKey }> = [
|
||||
export class InventoryDetailPanelComponent {
|
||||
readonly item = input<InventoryItem | null>(null);
|
||||
readonly equippedItemInSlot = input<InventoryItem | null>(null);
|
||||
readonly characterLevel = input(1);
|
||||
readonly busy = input(false);
|
||||
readonly equip = output<string>();
|
||||
|
||||
@@ -66,11 +65,6 @@ export class InventoryDetailPanelComponent {
|
||||
|
||||
protected readonly isEquippable = computed(() => !!this.item()?.item.equipmentSlot);
|
||||
|
||||
protected readonly meetsLevelRequirement = computed(() => {
|
||||
const item = this.item();
|
||||
return item ? item.item.requiredLevel <= this.characterLevel() : true;
|
||||
});
|
||||
|
||||
protected onEquip(): void {
|
||||
const item = this.item();
|
||||
if (item) {
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
<app-inventory-detail-panel
|
||||
[item]="inventoryStore.selectedItem()"
|
||||
[equippedItemInSlot]="equippedItemInSelectedSlot()"
|
||||
[characterLevel]="characterLevel()"
|
||||
[busy]="inventoryStore.equipping()"
|
||||
(equip)="equipSelected($event)"
|
||||
/>
|
||||
|
||||
@@ -19,7 +19,6 @@ const inventory: InventoryResponse = {
|
||||
name: 'Abgenutztes Kurzschwert',
|
||||
rarity: 'COMMON',
|
||||
equipmentSlot: 'WEAPON',
|
||||
requiredLevel: 1,
|
||||
weaponDamage: 8,
|
||||
bonusAttack: 0,
|
||||
bonusHp: 0,
|
||||
@@ -36,7 +35,6 @@ const inventory: InventoryResponse = {
|
||||
name: 'Räuberklinge',
|
||||
rarity: 'COMMON',
|
||||
equipmentSlot: 'WEAPON',
|
||||
requiredLevel: 1,
|
||||
weaponDamage: 11,
|
||||
bonusAttack: 1,
|
||||
bonusHp: 0,
|
||||
@@ -63,8 +61,7 @@ const equipment: EquipmentResponse = {
|
||||
const character: CharacterResponse = {
|
||||
id: 'character-1',
|
||||
name: 'Aric Duskwalker',
|
||||
level: 1,
|
||||
experience: 0,
|
||||
renown: 0,
|
||||
silver: 0,
|
||||
currentHp: 100,
|
||||
maxHp: 100,
|
||||
@@ -178,7 +175,6 @@ describe('InventoryPageComponent', () => {
|
||||
name: 'Eiserner Helm',
|
||||
rarity: 'COMMON',
|
||||
equipmentSlot: 'HEAD',
|
||||
requiredLevel: 1,
|
||||
weaponDamage: 0,
|
||||
bonusAttack: 0,
|
||||
bonusHp: 5,
|
||||
|
||||
@@ -37,8 +37,6 @@ export class InventoryPageComponent implements OnInit {
|
||||
protected readonly slotOrder = SLOT_ORDER;
|
||||
protected readonly slotLabels = SLOT_LABELS;
|
||||
|
||||
protected readonly characterLevel = computed(() => this.worldStore.character()?.level ?? 1);
|
||||
|
||||
protected readonly equippedItemInSelectedSlot = computed(() => {
|
||||
const selected = this.inventoryStore.selectedItem();
|
||||
if (!selected?.item.equipmentSlot) {
|
||||
|
||||
@@ -18,7 +18,6 @@ const inventory: InventoryResponse = {
|
||||
name: 'Abgenutztes Kurzschwert',
|
||||
rarity: 'COMMON',
|
||||
equipmentSlot: 'WEAPON',
|
||||
requiredLevel: 1,
|
||||
weaponDamage: 8,
|
||||
bonusAttack: 0,
|
||||
bonusHp: 0,
|
||||
@@ -35,7 +34,6 @@ const inventory: InventoryResponse = {
|
||||
name: 'Räuberklinge',
|
||||
rarity: 'COMMON',
|
||||
equipmentSlot: 'WEAPON',
|
||||
requiredLevel: 1,
|
||||
weaponDamage: 11,
|
||||
bonusAttack: 1,
|
||||
bonusHp: 0,
|
||||
@@ -135,11 +133,11 @@ describe('InventoryStore', () => {
|
||||
it('surfaces a German message for a known equip error', async () => {
|
||||
await store.load();
|
||||
api.equipItem.mockReturnValue(
|
||||
throwError(() => new HttpErrorResponse({ error: { code: 'ITEM_LEVEL_REQUIREMENT_NOT_MET' }, status: 400 })),
|
||||
throwError(() => new HttpErrorResponse({ error: { code: 'ITEM_NOT_EQUIPPABLE' }, status: 400 })),
|
||||
);
|
||||
|
||||
await store.equip('item-blade');
|
||||
|
||||
expect(store.error()).toBe('Du erfüllst die Stufenanforderung nicht.');
|
||||
expect(store.error()).toBe('Dieser Gegenstand kann nicht ausgerüstet werden.');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,7 +12,6 @@ const EQUIPMENT_ERROR_MESSAGES: Readonly<Record<string, string>> = {
|
||||
CHARACTER_ITEM_NOT_FOUND: 'Dieser Gegenstand konnte nicht gefunden werden.',
|
||||
ITEM_NOT_OWNED: 'Dieser Gegenstand gehört dir nicht.',
|
||||
ITEM_NOT_EQUIPPABLE: 'Dieser Gegenstand kann nicht ausgerüstet werden.',
|
||||
ITEM_LEVEL_REQUIREMENT_NOT_MET: 'Du erfüllst die Stufenanforderung nicht.',
|
||||
INVALID_EQUIPMENT_SLOT: 'Dieser Ausrüstungsplatz ist ungültig.',
|
||||
CHARACTER_IN_COMBAT: 'Ausrüstung kann während eines Kampfes nicht geändert werden.',
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user