Implement async manual CSV ingest, unified UI pagination/filters, and serial placeholder strategy

This commit is contained in:
2026-02-21 22:14:04 +03:00
parent ca762a658b
commit c84102d2f1
44 changed files with 3314 additions and 342 deletions

View File

@@ -27,16 +27,10 @@ func (r *AssetRepository) Create(ctx context.Context, asset domain.Asset) (domai
return domain.Asset{}, err
}
var projectID any
if strings.TrimSpace(asset.ProjectID) != "" {
projectID = asset.ProjectID
}
_, err = r.db.ExecContext(ctx,
`INSERT INTO machines (id, project_id, name, vendor, model, vendor_serial, machine_tag)
VALUES (?, ?, ?, ?, ?, ?, ?)`,
`INSERT INTO machines (id, name, vendor, model, vendor_serial, machine_tag)
VALUES (?, ?, ?, ?, ?, ?)`,
id,
projectID,
asset.Name,
asset.Vendor,
asset.Model,
@@ -52,26 +46,21 @@ func (r *AssetRepository) Create(ctx context.Context, asset domain.Asset) (domai
func (r *AssetRepository) Get(ctx context.Context, id string) (domain.Asset, error) {
var asset domain.Asset
var projectID sql.NullString
var vendor sql.NullString
var model sql.NullString
var machineTag sql.NullString
row := r.db.QueryRowContext(ctx,
`SELECT id, project_id, name, vendor, model, vendor_serial, machine_tag, created_at, updated_at
`SELECT id, name, vendor, model, vendor_serial, machine_tag, created_at, updated_at
FROM machines WHERE id = ?`,
id,
)
if err := row.Scan(&asset.ID, &projectID, &asset.Name, &vendor, &model, &asset.VendorSerial, &machineTag, &asset.CreatedAt, &asset.UpdatedAt); err != nil {
if err := row.Scan(&asset.ID, &asset.Name, &vendor, &model, &asset.VendorSerial, &machineTag, &asset.CreatedAt, &asset.UpdatedAt); err != nil {
if err == sql.ErrNoRows {
return domain.Asset{}, ErrNotFound
}
return domain.Asset{}, err
}
if projectID.Valid {
asset.ProjectID = projectID.String
}
asset.Vendor = nullStringToPtr(vendor)
asset.Model = nullStringToPtr(model)
asset.MachineTag = nullStringToPtr(machineTag)
@@ -81,7 +70,7 @@ func (r *AssetRepository) Get(ctx context.Context, id string) (domain.Asset, err
func (r *AssetRepository) List(ctx context.Context) ([]domain.Asset, error) {
rows, err := r.db.QueryContext(ctx,
`SELECT id, project_id, name, vendor, model, vendor_serial, machine_tag, created_at, updated_at
`SELECT id, name, vendor, model, vendor_serial, machine_tag, created_at, updated_at
FROM machines ORDER BY created_at DESC`,
)
if err != nil {
@@ -92,18 +81,13 @@ func (r *AssetRepository) List(ctx context.Context) ([]domain.Asset, error) {
machines := make([]domain.Asset, 0)
for rows.Next() {
var asset domain.Asset
var projectID sql.NullString
var vendor sql.NullString
var model sql.NullString
var machineTag sql.NullString
if err := rows.Scan(&asset.ID, &projectID, &asset.Name, &vendor, &model, &asset.VendorSerial, &machineTag, &asset.CreatedAt, &asset.UpdatedAt); err != nil {
if err := rows.Scan(&asset.ID, &asset.Name, &vendor, &model, &asset.VendorSerial, &machineTag, &asset.CreatedAt, &asset.UpdatedAt); err != nil {
return nil, err
}
if projectID.Valid {
asset.ProjectID = projectID.String
}
asset.Vendor = nullStringToPtr(vendor)
asset.Model = nullStringToPtr(model)
asset.MachineTag = nullStringToPtr(machineTag)
@@ -116,6 +100,31 @@ func (r *AssetRepository) List(ctx context.Context) ([]domain.Asset, error) {
return machines, nil
}
func (r *AssetRepository) Update(ctx context.Context, asset domain.Asset) (domain.Asset, error) {
result, err := r.db.ExecContext(ctx,
`UPDATE machines
SET name = ?, vendor = ?, model = ?, vendor_serial = ?, machine_tag = ?
WHERE id = ?`,
strings.TrimSpace(asset.Name),
asset.Vendor,
asset.Model,
strings.TrimSpace(asset.VendorSerial),
asset.MachineTag,
asset.ID,
)
if err != nil {
return domain.Asset{}, classifyError(err)
}
affected, err := result.RowsAffected()
if err != nil {
return domain.Asset{}, err
}
if affected == 0 {
return domain.Asset{}, ErrNotFound
}
return r.Get(ctx, asset.ID)
}
type AssetDeleteResult struct {
DeletedParts int
}