Add support for hiding properties in asset overview and enhance input list handling
This commit is contained in:
@@ -35,6 +35,8 @@ public class WebController {
|
||||
private static final String ACTION = "action";
|
||||
/// The name of the model attribute that holds the current time in milliseconds.
|
||||
private static final String TIME = "time";
|
||||
/// The name of the model attribute that holds the input lists for creating or editing assets.
|
||||
private static final String INPUT_LIST = "inputLists";
|
||||
|
||||
private final AssetService assetService;
|
||||
|
||||
@@ -157,6 +159,7 @@ public class WebController {
|
||||
model.addAttribute(ACTION, "create");
|
||||
model.addAttribute(DESCRIPTORS, assetService.getAssetDescriptorTree(type));
|
||||
model.addAttribute(DESCRIPTOR, assetService.getAssetDescriptor(type));
|
||||
model.addAttribute(INPUT_LIST, assetService.getInputList(type));
|
||||
return "create_asset";
|
||||
}
|
||||
|
||||
@@ -177,6 +180,7 @@ public class WebController {
|
||||
model.addAttribute(ASSET, asset);
|
||||
model.addAttribute(DESCRIPTORS, assetService.getAssetDescriptorTree(assetType));
|
||||
model.addAttribute(DESCRIPTOR, assetService.getAssetDescriptor(assetType));
|
||||
model.addAttribute(INPUT_LIST, assetService.getInputList(assetType));
|
||||
return "create_asset";
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,8 @@ public class AssetProperty {
|
||||
private final Function<Object, Object> getter;
|
||||
/// Whether the property is an input list.
|
||||
private final boolean inputList;
|
||||
/// Whether the property should be hidden in the overview.
|
||||
private final boolean hideInOverview;
|
||||
|
||||
/**
|
||||
* Enum representing the possible types of asset properties.
|
||||
@@ -87,6 +89,7 @@ public class AssetProperty {
|
||||
.type(type)
|
||||
.required(annotation.required())
|
||||
.inputList(property.isAnnotationPresent(InputList.class))
|
||||
.hideInOverview(property.isAnnotationPresent(HideInOverview.class))
|
||||
.setter((obj, value) -> {
|
||||
try {
|
||||
property.setAccessible(true);
|
||||
|
||||
14
src/main/java/be/seeseepuff/pcinv/meta/HideInOverview.java
Normal file
14
src/main/java/be/seeseepuff/pcinv/meta/HideInOverview.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package be.seeseepuff.pcinv.meta;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Indicates that the field should not be included in the overview of an asset.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface HideInOverview {
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package be.seeseepuff.pcinv.models;
|
||||
|
||||
import be.seeseepuff.pcinv.meta.AssetInfo;
|
||||
import be.seeseepuff.pcinv.meta.InputList;
|
||||
import be.seeseepuff.pcinv.meta.HideInOverview;
|
||||
import be.seeseepuff.pcinv.meta.Property;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Getter;
|
||||
@@ -52,6 +53,7 @@ public class GenericAsset
|
||||
|
||||
/// A description of the asset, providing additional details.
|
||||
@Property("Description")
|
||||
@HideInOverview
|
||||
private String description;
|
||||
|
||||
/// The state of the asset, indicating its condition.
|
||||
|
||||
@@ -2,6 +2,7 @@ package be.seeseepuff.pcinv.models;
|
||||
|
||||
import be.seeseepuff.pcinv.meta.AssetInfo;
|
||||
import be.seeseepuff.pcinv.meta.Capacity;
|
||||
import be.seeseepuff.pcinv.meta.InputList;
|
||||
import be.seeseepuff.pcinv.meta.Property;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Getter;
|
||||
@@ -36,9 +37,11 @@ public class HddAsset implements Asset
|
||||
|
||||
/// The drive's interface type, such as SATA, IDE, ISA-16, ...
|
||||
@Property("Interface Type")
|
||||
@InputList
|
||||
private String interfaceType;
|
||||
|
||||
/// The drive's form factor, such as 2.5", 3.5", etc.
|
||||
@Property("Form Factor")
|
||||
@InputList
|
||||
private String formFactor;
|
||||
}
|
||||
|
||||
@@ -12,9 +12,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Service for managing assets in the repository.
|
||||
@@ -173,6 +171,12 @@ public class AssetService {
|
||||
private void fillIn(Object asset, AssetDescriptor assetDescriptor, Map<String, String> formData) {
|
||||
for (var property : assetDescriptor.getProperties()) {
|
||||
var value = parseValue(assetDescriptor, property, formData);
|
||||
if (property.isInputList()) {
|
||||
var selectedItem = formData.get(assetDescriptor.asString(property) + "-list");
|
||||
if (selectedItem != null && !selectedItem.isBlank() && !selectedItem.equals("__new__")) {
|
||||
value = selectedItem;
|
||||
}
|
||||
}
|
||||
if (value == null && property.isRequired()) {
|
||||
throw new IllegalArgumentException("Property '" + property.getName() + "' is required but not provided.");
|
||||
}
|
||||
@@ -237,4 +241,50 @@ public class AssetService {
|
||||
genericRepository.delete(genericAsset);
|
||||
genericRepository.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the input list mapping for a specific asset type.
|
||||
*
|
||||
* @param type the type of asset to retrieve the input list for
|
||||
* @return a map of input names to their corresponding list
|
||||
*/
|
||||
public Map<String, Set<String>> getInputList(String type) {
|
||||
var map = new HashMap<String, Set<String>>();
|
||||
var tree = getAssetDescriptorTree(type);
|
||||
for (var descriptor : tree) {
|
||||
for (var property : descriptor.getProperties()) {
|
||||
if (property.isInputList()) {
|
||||
var inputList = getInputList(descriptor, property);
|
||||
map.put(descriptor.asString(property), inputList);
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the input list for a specific asset descriptor and property.
|
||||
*
|
||||
* @param descriptor the asset descriptor containing the property
|
||||
* @param property the asset property to retrieve the input list for
|
||||
* @return a set of input values for the specified property
|
||||
*/
|
||||
private Set<String> getInputList(AssetDescriptor descriptor, AssetProperty property) {
|
||||
List<?> entries;
|
||||
if (descriptor.getType().equals(GenericAsset.TYPE)) {
|
||||
entries = genericRepository.findAll();
|
||||
} else {
|
||||
var repository = getRepositoryFor(descriptor.getType());
|
||||
entries = repository.findAll();
|
||||
}
|
||||
|
||||
Set<String> inputList = new TreeSet<>();
|
||||
for (var entry : entries) {
|
||||
var value = property.getValue(entry);
|
||||
if (value != null) {
|
||||
inputList.add(value.toString());
|
||||
}
|
||||
}
|
||||
return inputList;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
There are <span th:text="${assets.size()}"></span> <span th:text="${descriptor.pluralName}"></span> in the database.
|
||||
<table border="1" cellpadding="4">
|
||||
<tr bgcolor="#d3d3d3">
|
||||
<th th:each="p : ${properties}" th:text="${p.displayName}"></th>
|
||||
<th th:each="p : ${properties}" th:if="${!p.hideInOverview}" th:text="${p.displayName}"></th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
<tr th:each="a : ${assets}">
|
||||
<td th:each="p : ${properties}">
|
||||
<td th:each="p : ${properties}" th:if="${!p.hideInOverview}">
|
||||
<a th:if="${p.name == 'qr'}" th:href="'/view/'+${a.getQr()}" th:text="${p.renderValue(a)}"></a>
|
||||
<span th:if="${p.name != 'qr'}" th:text="${p.renderValue(a)}"></span>
|
||||
</td>
|
||||
|
||||
@@ -8,6 +8,14 @@
|
||||
<tr th:each="p : ${d.getProperties()}">
|
||||
<td bgcolor="#d3d3d3"><b><label th:text="${p.displayName}" th:for="${d.asString(p)}"></label></b></td>
|
||||
<td th:switch="${p.type.nameOrEnum()}">
|
||||
<span th:case="STRING">
|
||||
<select th:if="${p.inputList}" th:id="${d.asString(p)+'-list'}" th:name="${d.asString(p)+'-list'}">
|
||||
<option value="__new__">New...</option>
|
||||
<option th:each="e : ${inputLists.get(d.asString(p))}" th:text="${e}" th:value="${e}"></option>
|
||||
</select>
|
||||
<span th:if="${p.inputList}"> or </span>
|
||||
<input type="text" th:id="${d.asString(p)}" th:name="${d.asString(p)}" th:value="${p.getValue(asset)}" th:placeholder="${p.displayName}" th:required="${p.required}"/>
|
||||
</span>
|
||||
<input th:case="STRING" type="text" th:id="${d.asString(p)}" th:name="${d.asString(p)}" th:value="${p.getValue(asset)}" th:placeholder="${p.displayName}" th:required="${p.required}"/>
|
||||
<input th:case="INTEGER" type="number" th:id="${d.asString(p)}" th:name="${d.asString(p)}" th:value="${p.getValue(asset)}" th:required="${p.required}"/>
|
||||
<select th:case="enum" th:id="${d.asString(p)}" th:name="${d.asString(p)}">
|
||||
|
||||
Reference in New Issue
Block a user