Can show an asset
This commit is contained in:
@@ -24,6 +24,7 @@ public class WebController {
|
||||
private static final String DESCRIPTORS = "descriptors";
|
||||
/// The name of the model attribute that holds the asset descriptor for the current view.
|
||||
private static final String DESCRIPTOR = "descriptor";
|
||||
private static final String GENERIC_DESCRIPTOR = "generic";
|
||||
|
||||
private final AssetService assetService;
|
||||
|
||||
@@ -37,6 +38,23 @@ public class WebController {
|
||||
return "index";
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the view of an asset by its QR code.
|
||||
* If the asset does not exist, it redirects to the index page.
|
||||
*
|
||||
* @param qr The QR code of the asset to view.
|
||||
*/
|
||||
@GetMapping("/view/{qr}")
|
||||
public String view(Model model, @PathVariable long qr) {
|
||||
var asset = assetService.getAssetByQr(qr);
|
||||
if (asset == null) {
|
||||
return "redirect:/";
|
||||
}
|
||||
model.addAttribute("asset", asset);
|
||||
model.addAttribute(DESCRIPTORS, assetService.getAssetDescriptorTree(asset.getAsset().getType()));
|
||||
return "view";
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a view where the user can create the type of asset to create.
|
||||
*/
|
||||
|
||||
@@ -46,7 +46,7 @@ public class AssetDescriptor {
|
||||
.displayName(assetInfo.displayName())
|
||||
.visible(assetInfo.isVisible())
|
||||
.properties(Arrays.stream(assetType.getDeclaredFields())
|
||||
.map(AssetProperty::loadFrom)
|
||||
.map(field -> AssetProperty.loadFrom(field, assetInfo.type()))
|
||||
.filter(Objects::nonNull)
|
||||
.toList());
|
||||
if (Asset.class.isAssignableFrom(assetType)) {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package be.seeseepuff.pcinv.meta;
|
||||
|
||||
import be.seeseepuff.pcinv.models.Asset;
|
||||
import be.seeseepuff.pcinv.models.AssetCondition;
|
||||
import be.seeseepuff.pcinv.models.GenericAsset;
|
||||
import jakarta.annotation.Nonnull;
|
||||
import jakarta.annotation.Nullable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
@@ -10,6 +13,7 @@ import lombok.Singular;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Represents a property of an asset, such as its name or type.
|
||||
@@ -35,6 +39,8 @@ public class AssetProperty {
|
||||
private final boolean capacityAsIEC;
|
||||
/// A setter function that can be used to set the value of the property on an asset.
|
||||
private final BiConsumer<Object, Object> setter;
|
||||
/// A getter function that can be used to get the value of the property from an asset.
|
||||
private final Function<Object, Object> getter;
|
||||
|
||||
/**
|
||||
* Enum representing the possible types of asset properties.
|
||||
@@ -66,7 +72,7 @@ public class AssetProperty {
|
||||
* @return An AssetProperty instance with the name, display name, and type determined from the field.
|
||||
*/
|
||||
@Nullable
|
||||
public static AssetProperty loadFrom(Field property) {
|
||||
public static AssetProperty loadFrom(Field property, @Nonnull String assetType) {
|
||||
var annotation = property.getAnnotation(Property.class);
|
||||
if (annotation == null) {
|
||||
return null;
|
||||
@@ -85,6 +91,17 @@ public class AssetProperty {
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
})
|
||||
.getter(obj -> {
|
||||
try {
|
||||
if (assetType.equals(GenericAsset.TYPE) && obj instanceof Asset asset) {
|
||||
obj = asset.getAsset();
|
||||
}
|
||||
property.setAccessible(true);
|
||||
return property.get(obj);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
|
||||
if (type.isEnum) {
|
||||
@@ -144,6 +161,39 @@ public class AssetProperty {
|
||||
setter.accept(asset, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the property from the given asset.
|
||||
*
|
||||
* @param asset The asset to get the property value from.
|
||||
* @return The value of the property.
|
||||
*/
|
||||
public Object getValue(Object asset) {
|
||||
return getter.apply(asset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the value of the property as a string.
|
||||
*
|
||||
* @return The rendered value as a string.
|
||||
*/
|
||||
public String renderValue(Object asset) {
|
||||
var value = getValue(asset);
|
||||
if (value == null) {
|
||||
return "Unknown";
|
||||
} else if (type == Type.INTEGER || type == Type.STRING) {
|
||||
return value.toString();
|
||||
} else if (type == Type.CAPACITY) {
|
||||
return String.format("%s bytes", value);
|
||||
} else if (type.isEnum) {
|
||||
if (value instanceof AssetEnum assetEnum) {
|
||||
return assetEnum.getDisplayName();
|
||||
}
|
||||
throw new IllegalArgumentException("Expected value to be an instance of AssetEnum, but got: " + value.getClass().getName());
|
||||
} else {
|
||||
return value.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
var enumOptions = "";
|
||||
|
||||
@@ -23,6 +23,8 @@ import lombok.Setter;
|
||||
@Table(name = "assets")
|
||||
public class GenericAsset
|
||||
{
|
||||
public static final String TYPE = "asset";
|
||||
|
||||
@Id @GeneratedValue
|
||||
private long id;
|
||||
|
||||
@@ -30,6 +32,9 @@ public class GenericAsset
|
||||
@Property(value = "QR", required = true)
|
||||
private long qr;
|
||||
|
||||
/// The type of asset
|
||||
private String type;
|
||||
|
||||
/// The brand of the asset.
|
||||
@Property("Brand")
|
||||
private String brand;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package be.seeseepuff.pcinv.repositories;
|
||||
|
||||
import be.seeseepuff.pcinv.models.Asset;
|
||||
import be.seeseepuff.pcinv.models.GenericAsset;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
@@ -16,6 +17,8 @@ public interface AssetRepository<T extends Asset> {
|
||||
}
|
||||
}
|
||||
|
||||
T findByAsset(GenericAsset asset);
|
||||
|
||||
Class<T> getAssetType();
|
||||
|
||||
long count();
|
||||
|
||||
@@ -5,4 +5,5 @@ import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public interface GenericAssetRepository extends JpaRepository<GenericAsset, Long> {
|
||||
GenericAsset findByQr(long qr);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,21 @@ public class AssetService {
|
||||
return genericRepository.count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an asset by its QR code.
|
||||
*
|
||||
* @param qr the QR code of the asset to retrieve
|
||||
* @return the Asset associated with the given QR code
|
||||
* @throws IllegalArgumentException if no asset is found with the given QR code
|
||||
*/
|
||||
public Asset getAssetByQr(long qr) {
|
||||
var genericAsset = genericRepository.findByQr(qr);
|
||||
if (genericAsset == null) {
|
||||
throw new IllegalArgumentException("No asset found with QR code: " + qr);
|
||||
}
|
||||
return getRepositoryFor(genericAsset.getType()).findByAsset(genericAsset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the global asset descriptors for all asset types.
|
||||
*
|
||||
@@ -69,10 +84,10 @@ public class AssetService {
|
||||
* @return a list of AssetDescriptors for the specified type
|
||||
*/
|
||||
public List<AssetDescriptor> getAssetDescriptorTree(String type) {
|
||||
if (type.equals("asset")) {
|
||||
return List.of(getAssetDescriptor("asset"));
|
||||
if (type.equals(GenericAsset.TYPE)) {
|
||||
return List.of(getAssetDescriptor(GenericAsset.TYPE));
|
||||
}
|
||||
return List.of(getAssetDescriptor("asset"), getAssetDescriptor(type));
|
||||
return List.of(getAssetDescriptor(GenericAsset.TYPE), getAssetDescriptor(type));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,10 +99,11 @@ public class AssetService {
|
||||
*/
|
||||
@Transactional
|
||||
public Asset createAsset(String type, Map<String, String> formData) {
|
||||
var genericDescriptor = getAssetDescriptor("asset");
|
||||
var genericDescriptor = getAssetDescriptor(GenericAsset.TYPE);
|
||||
var assetDescriptor = getAssetDescriptor(type);
|
||||
|
||||
var genericAsset = new GenericAsset();
|
||||
genericAsset.setType(type);
|
||||
fillIn(genericAsset, genericDescriptor, formData);
|
||||
|
||||
var asset = assetDescriptor.newInstance();
|
||||
|
||||
@@ -4,7 +4,12 @@
|
||||
<title>PC Inventory</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>PC Inventory - <span th:text="${title}"></span></h1>
|
||||
<h1>PC Inventory - <span th:text="${title}"></span>
|
||||
</h1>
|
||||
<hr>
|
||||
<a href="/">Home</a>
|
||||
<a href="/browse">Browse</a>
|
||||
<a href="/create">Create</a>
|
||||
<hr>
|
||||
<div th:replace="${content}">
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<div th:replace="fragments :: base(title='Home', content=~{::content})">
|
||||
<div th:fragment="content">
|
||||
<a href="/create">Create a new device</a>
|
||||
<p>This system holds <span th:text="${asset_count}">5</span> assets.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
14
src/main/resources/templates/view.html
Normal file
14
src/main/resources/templates/view.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<body th:replace="~{fragments :: base(title='Select type to create', content=~{::content})}">
|
||||
<div th:fragment="content">
|
||||
View device details
|
||||
<div th:each="d : ${descriptors}">
|
||||
<h2 th:text="${d.displayName}"></h2>
|
||||
<table border="1">
|
||||
<tr th:each="p : ${d.properties}">
|
||||
<td><b th:text="${p.displayName}"></b></td>
|
||||
<td th:text="${p.renderValue(asset)}"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
Reference in New Issue
Block a user