Close to the same feature set as the Go interface
Some checks failed
Build / build (push) Failing after 19s

This commit is contained in:
2025-06-08 07:02:13 +02:00
parent d4718d15c3
commit 3a70613023
12 changed files with 87 additions and 6 deletions

View File

@@ -24,7 +24,10 @@ 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";
/// The name of the model attribute that holds the list of assets.
private static final String ASSETS = "assets";
/// The name of the model attribute that holds a list of all properties of all descriptors.
private static final String PROPERTIES = "properties";
private final AssetService assetService;
@@ -38,6 +41,33 @@ public class WebController {
return "index";
}
/**
* Shows a view where a user can select the type of asset to browse.
*/
@GetMapping("/browse")
public String browse(Model model) {
model.addAttribute(DESCRIPTORS, assetService.getAssetDescriptors());
return "browse";
}
/**
* Handles the browsing of assets by type.
* Displays the asset descriptor tree and the specific descriptor for the given type.
*
* @param model The model to add attributes to.
* @param type The type of asset to browse.
* @return The view name for browsing assets by type.
*/
@GetMapping("/browse/{type}")
public String browseType(Model model, @PathVariable String type) {
var tree = assetService.getAssetDescriptorTree(type);
model.addAttribute(DESCRIPTOR, assetService.getAssetDescriptor(type));
model.addAttribute(DESCRIPTORS, tree);
model.addAttribute(PROPERTIES, tree.stream().flatMap(d -> d.getProperties().stream()).toList());
model.addAttribute(ASSETS, assetService.getAssetsByType(type));
return "browse_type";
}
/**
* Handles the view of an asset by its QR code.
* If the asset does not exist, it redirects to the index page.

View File

@@ -19,9 +19,12 @@ public class AssetDescriptor {
/// The type of property, e.g.: ram, asset, etc...
private final String type;
/// The displayable name of the property, e.g.: "Random Access memory"
/// The displayable name of the property, e.g.: "Random Access Memory"
private final String displayName;
/// The plural name of the property, e.g.: "Random Access Memories"
private final String pluralName;
/// Whether the asset is visible in the user interface.
private final boolean visible;
@@ -44,6 +47,7 @@ public class AssetDescriptor {
var builder = AssetDescriptor.builder()
.type(assetInfo.type())
.displayName(assetInfo.displayName())
.pluralName(assetInfo.pluralName())
.visible(assetInfo.isVisible())
.properties(Arrays.stream(assetType.getDeclaredFields())
.map(field -> AssetProperty.loadFrom(field, assetInfo.type()))

View File

@@ -16,14 +16,21 @@ public @interface AssetInfo {
*
* @return the display name of the asset type
*/
String displayName() default "";
String displayName();
/**
* The plural name of the asset type, used for display purposes.
*
* @return the plural name of the asset type
*/
String pluralName();
/**
* The type of the asset, which can be a string or an integer.
*
* @return the type of the asset
*/
String type() default "";
String type();
/**
* Indicates whether the asset type should be visible in the UI.

View File

@@ -17,6 +17,7 @@ import lombok.Setter;
@Entity
@AssetInfo(
displayName = "Asset",
pluralName = "Assets",
type = "asset",
isVisible = false
)

View File

@@ -15,6 +15,7 @@ import lombok.Setter;
@Entity
@AssetInfo(
displayName = "Hard Drive",
pluralName = "Hard Drives",
type = "HDD"
)
@Table(name = "hdd_assets")

View File

@@ -15,6 +15,7 @@ import lombok.Setter;
@Entity
@AssetInfo(
displayName = "Random Access Memory",
pluralName = "Random Access Memories",
type = "RAM"
)
@Table(name = "ram_assets")

View File

@@ -4,6 +4,8 @@ import be.seeseepuff.pcinv.models.Asset;
import be.seeseepuff.pcinv.models.GenericAsset;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface AssetRepository<T extends Asset> {
T saveAndFlush(T entity);
@@ -19,6 +21,8 @@ public interface AssetRepository<T extends Asset> {
T findByAsset(GenericAsset asset);
List<T> findAll();
Class<T> getAssetType();
long count();

View File

@@ -50,6 +50,16 @@ public class AssetService {
return getRepositoryFor(genericAsset.getType()).findByAsset(genericAsset);
}
/**
* Retrieves all assets of a specific type.
*
* @param type the type of asset to retrieve
* @return a list of assets of the specified type
*/
public List<? extends Asset> getAssetsByType(String type) {
return getRepositoryFor(type).findAll();
}
/**
* Retrieves the global asset descriptors for all asset types.
*

View File

@@ -0,0 +1,10 @@
<body th:replace="~{fragments :: base(title='Browse assets', content=~{::content})}">
<div th:fragment="content">
View device details
<ul>
<li th:each="d : ${descriptors.getAssets()}" th:if="${d.visible}">
<a th:href="'/browse/'+${d.getType()}" th:text="${d.pluralName}"></a>
</li>
</ul>
</div>
</body>

View File

@@ -0,0 +1,13 @@
<body th:replace="~{fragments :: base(title='View '+${descriptor.pluralName}, content=~{::content})}">
<div th:fragment="content">
There are <span th:text="${assets.size()}"></span> <span th:text="${descriptor.pluralName}"></span> in the database.
<table border="1">
<tr>
<th th:each="p : ${properties}" th:text="${p.displayName}" bgcolor="#d3d3d3"></th>
</tr>
<tr th:each="a : ${assets}">
<td th:each="p : ${properties}"><a th:href="'/view/'+${a.getQr()}" th:text="${p.renderValue(a)}"></a></td>
</tr>
</table>
</div>
</body>

View File

@@ -1,4 +1,4 @@
<body th:replace="~{fragments :: base(title='Select type to create', content=~{::content})}">
<body th:replace="~{fragments :: base(title='Create '+${descriptor.displayName}, content=~{::content})}">
<div th:fragment="content">
Create a <span th:text="${descriptor.displayName}"></span>
<form th:action="'/create/'+${descriptor.getType()}" method="post">

View File

@@ -1,4 +1,4 @@
<body th:replace="~{fragments :: base(title='Select type to create', content=~{::content})}">
<body th:replace="~{fragments :: base(title='View asset information', content=~{::content})}">
<div th:fragment="content">
View device details
<div th:each="d : ${descriptors}">