Can show an asset
This commit is contained in:
@@ -24,6 +24,7 @@ public class WebController {
|
|||||||
private static final String DESCRIPTORS = "descriptors";
|
private static final String DESCRIPTORS = "descriptors";
|
||||||
/// The name of the model attribute that holds the asset descriptor for the current view.
|
/// 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 DESCRIPTOR = "descriptor";
|
||||||
|
private static final String GENERIC_DESCRIPTOR = "generic";
|
||||||
|
|
||||||
private final AssetService assetService;
|
private final AssetService assetService;
|
||||||
|
|
||||||
@@ -37,6 +38,23 @@ public class WebController {
|
|||||||
return "index";
|
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.
|
* Shows a view where the user can create the type of asset to create.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ public class AssetDescriptor {
|
|||||||
.displayName(assetInfo.displayName())
|
.displayName(assetInfo.displayName())
|
||||||
.visible(assetInfo.isVisible())
|
.visible(assetInfo.isVisible())
|
||||||
.properties(Arrays.stream(assetType.getDeclaredFields())
|
.properties(Arrays.stream(assetType.getDeclaredFields())
|
||||||
.map(AssetProperty::loadFrom)
|
.map(field -> AssetProperty.loadFrom(field, assetInfo.type()))
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.toList());
|
.toList());
|
||||||
if (Asset.class.isAssignableFrom(assetType)) {
|
if (Asset.class.isAssignableFrom(assetType)) {
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package be.seeseepuff.pcinv.meta;
|
package be.seeseepuff.pcinv.meta;
|
||||||
|
|
||||||
|
import be.seeseepuff.pcinv.models.Asset;
|
||||||
import be.seeseepuff.pcinv.models.AssetCondition;
|
import be.seeseepuff.pcinv.models.AssetCondition;
|
||||||
|
import be.seeseepuff.pcinv.models.GenericAsset;
|
||||||
|
import jakarta.annotation.Nonnull;
|
||||||
import jakarta.annotation.Nullable;
|
import jakarta.annotation.Nullable;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
@@ -10,6 +13,7 @@ import lombok.Singular;
|
|||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a property of an asset, such as its name or type.
|
* Represents a property of an asset, such as its name or type.
|
||||||
@@ -35,6 +39,8 @@ public class AssetProperty {
|
|||||||
private final boolean capacityAsIEC;
|
private final boolean capacityAsIEC;
|
||||||
/// A setter function that can be used to set the value of the property on an asset.
|
/// A setter function that can be used to set the value of the property on an asset.
|
||||||
private final BiConsumer<Object, Object> setter;
|
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.
|
* 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.
|
* @return An AssetProperty instance with the name, display name, and type determined from the field.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public static AssetProperty loadFrom(Field property) {
|
public static AssetProperty loadFrom(Field property, @Nonnull String assetType) {
|
||||||
var annotation = property.getAnnotation(Property.class);
|
var annotation = property.getAnnotation(Property.class);
|
||||||
if (annotation == null) {
|
if (annotation == null) {
|
||||||
return null;
|
return null;
|
||||||
@@ -85,6 +91,17 @@ public class AssetProperty {
|
|||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
throw new RuntimeException(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) {
|
if (type.isEnum) {
|
||||||
@@ -144,6 +161,39 @@ public class AssetProperty {
|
|||||||
setter.accept(asset, value);
|
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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
var enumOptions = "";
|
var enumOptions = "";
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ import lombok.Setter;
|
|||||||
@Table(name = "assets")
|
@Table(name = "assets")
|
||||||
public class GenericAsset
|
public class GenericAsset
|
||||||
{
|
{
|
||||||
|
public static final String TYPE = "asset";
|
||||||
|
|
||||||
@Id @GeneratedValue
|
@Id @GeneratedValue
|
||||||
private long id;
|
private long id;
|
||||||
|
|
||||||
@@ -30,6 +32,9 @@ public class GenericAsset
|
|||||||
@Property(value = "QR", required = true)
|
@Property(value = "QR", required = true)
|
||||||
private long qr;
|
private long qr;
|
||||||
|
|
||||||
|
/// The type of asset
|
||||||
|
private String type;
|
||||||
|
|
||||||
/// The brand of the asset.
|
/// The brand of the asset.
|
||||||
@Property("Brand")
|
@Property("Brand")
|
||||||
private String brand;
|
private String brand;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package be.seeseepuff.pcinv.repositories;
|
package be.seeseepuff.pcinv.repositories;
|
||||||
|
|
||||||
import be.seeseepuff.pcinv.models.Asset;
|
import be.seeseepuff.pcinv.models.Asset;
|
||||||
|
import be.seeseepuff.pcinv.models.GenericAsset;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
@@ -16,6 +17,8 @@ public interface AssetRepository<T extends Asset> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
T findByAsset(GenericAsset asset);
|
||||||
|
|
||||||
Class<T> getAssetType();
|
Class<T> getAssetType();
|
||||||
|
|
||||||
long count();
|
long count();
|
||||||
|
|||||||
@@ -5,4 +5,5 @@ import org.springframework.data.jpa.repository.JpaRepository;
|
|||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public interface GenericAssetRepository extends JpaRepository<GenericAsset, Long> {
|
public interface GenericAssetRepository extends JpaRepository<GenericAsset, Long> {
|
||||||
|
GenericAsset findByQr(long qr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,21 @@ public class AssetService {
|
|||||||
return genericRepository.count();
|
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.
|
* 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
|
* @return a list of AssetDescriptors for the specified type
|
||||||
*/
|
*/
|
||||||
public List<AssetDescriptor> getAssetDescriptorTree(String type) {
|
public List<AssetDescriptor> getAssetDescriptorTree(String type) {
|
||||||
if (type.equals("asset")) {
|
if (type.equals(GenericAsset.TYPE)) {
|
||||||
return List.of(getAssetDescriptor("asset"));
|
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
|
@Transactional
|
||||||
public Asset createAsset(String type, Map<String, String> formData) {
|
public Asset createAsset(String type, Map<String, String> formData) {
|
||||||
var genericDescriptor = getAssetDescriptor("asset");
|
var genericDescriptor = getAssetDescriptor(GenericAsset.TYPE);
|
||||||
var assetDescriptor = getAssetDescriptor(type);
|
var assetDescriptor = getAssetDescriptor(type);
|
||||||
|
|
||||||
var genericAsset = new GenericAsset();
|
var genericAsset = new GenericAsset();
|
||||||
|
genericAsset.setType(type);
|
||||||
fillIn(genericAsset, genericDescriptor, formData);
|
fillIn(genericAsset, genericDescriptor, formData);
|
||||||
|
|
||||||
var asset = assetDescriptor.newInstance();
|
var asset = assetDescriptor.newInstance();
|
||||||
|
|||||||
@@ -4,7 +4,12 @@
|
|||||||
<title>PC Inventory</title>
|
<title>PC Inventory</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<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>
|
<hr>
|
||||||
<div th:replace="${content}">
|
<div th:replace="${content}">
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<div th:replace="fragments :: base(title='Home', content=~{::content})">
|
<div th:replace="fragments :: base(title='Home', content=~{::content})">
|
||||||
<div th:fragment="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>
|
<p>This system holds <span th:text="${asset_count}">5</span> assets.</p>
|
||||||
</div>
|
</div>
|
||||||
</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