Rewrite with React after AI got stuck in some obscure state errors on SolidJS
This commit is contained in:
@@ -71,5 +71,69 @@ public static class LokEndpoints
|
||||
httpContext.Response.StatusCode = StatusCodes.Status204NoContent;
|
||||
})
|
||||
.WithName("DeleteLokOpenHours");
|
||||
|
||||
app.MapPut("/lok/open-hours/{id:long}", async (HttpContext httpContext, long id) =>
|
||||
{
|
||||
var lokService = httpContext.RequestServices.GetRequiredService<LokService>();
|
||||
var openHours = await httpContext.Request.ReadFromJsonAsync<LokOpenHours>();
|
||||
|
||||
if (openHours is null)
|
||||
{
|
||||
httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
|
||||
await httpContext.Response.WriteAsJsonAsync(new
|
||||
{
|
||||
Message = "Request body is required."
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(openHours.Name))
|
||||
{
|
||||
httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
|
||||
await httpContext.Response.WriteAsJsonAsync(new
|
||||
{
|
||||
Message = "Open hours version name is required."
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var updatedOpenHours = await lokService.UpdateOpenHours(id, openHours);
|
||||
|
||||
if (updatedOpenHours is null)
|
||||
{
|
||||
httpContext.Response.StatusCode = StatusCodes.Status404NotFound;
|
||||
await httpContext.Response.WriteAsJsonAsync(new
|
||||
{
|
||||
Message = "Open hours version not found."
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await httpContext.Response.WriteAsJsonAsync(updatedOpenHours);
|
||||
})
|
||||
.WithName("UpdateLokOpenHours");
|
||||
|
||||
app.MapPut("/lok/open-hours/{id:long}/active", async (HttpContext httpContext, long id) =>
|
||||
{
|
||||
var lokService = httpContext.RequestServices.GetRequiredService<LokService>();
|
||||
var activated = await lokService.SetActiveOpenHours(id);
|
||||
|
||||
if (!activated)
|
||||
{
|
||||
httpContext.Response.StatusCode = StatusCodes.Status404NotFound;
|
||||
await httpContext.Response.WriteAsJsonAsync(new
|
||||
{
|
||||
Message = "Open hours version not found."
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await httpContext.Response.WriteAsJsonAsync(new
|
||||
{
|
||||
Id = id,
|
||||
IsActive = true
|
||||
});
|
||||
})
|
||||
.WithName("SetActiveLokOpenHours");
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ public class LokOpenHours
|
||||
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
public bool IsActive { get; set; }
|
||||
|
||||
public DateTime Version { get; set; }
|
||||
|
||||
public string Paragraph1 { get; set; } = string.Empty;
|
||||
|
||||
@@ -21,6 +21,20 @@ public class Program
|
||||
|
||||
builder.Services.AddScoped(_ => new SqliteConnection(resolvedConnectionString));
|
||||
builder.Services.AddScoped<LokService>();
|
||||
builder.Services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy("UiCors", policy =>
|
||||
{
|
||||
policy
|
||||
.WithOrigins(
|
||||
"http://localhost:5173",
|
||||
"http://127.0.0.1:5173",
|
||||
"http://localhost:4173",
|
||||
"http://127.0.0.1:4173")
|
||||
.AllowAnyHeader()
|
||||
.AllowAnyMethod();
|
||||
});
|
||||
});
|
||||
|
||||
builder.Services.AddOpenApi();
|
||||
|
||||
@@ -64,6 +78,57 @@ public class Program
|
||||
command.CommandText = "ALTER TABLE LokOpenHours ADD COLUMN name TEXT NOT NULL DEFAULT '';";
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
command.CommandText = "SELECT COUNT(*) FROM pragma_table_info('LokOpenHours') WHERE name = 'isActive';";
|
||||
var hasIsActiveColumn = Convert.ToInt32(command.ExecuteScalar()) > 0;
|
||||
|
||||
if (!hasIsActiveColumn)
|
||||
{
|
||||
command.CommandText = "ALTER TABLE LokOpenHours ADD COLUMN isActive INTEGER NOT NULL DEFAULT 0;";
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
command.CommandText = "SELECT COUNT(*) FROM LokOpenHours WHERE isActive = 1;";
|
||||
var activeCount = Convert.ToInt32(command.ExecuteScalar());
|
||||
|
||||
if (activeCount == 0)
|
||||
{
|
||||
command.CommandText = @"
|
||||
UPDATE LokOpenHours
|
||||
SET isActive = 1
|
||||
WHERE id = (
|
||||
SELECT id
|
||||
FROM LokOpenHours
|
||||
ORDER BY datetime(version) DESC, id DESC
|
||||
LIMIT 1
|
||||
);";
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
else if (activeCount > 1)
|
||||
{
|
||||
command.CommandText = @"
|
||||
WITH selected_active AS (
|
||||
SELECT id
|
||||
FROM LokOpenHours
|
||||
WHERE isActive = 1
|
||||
ORDER BY datetime(version) DESC, id DESC
|
||||
LIMIT 1
|
||||
)
|
||||
UPDATE LokOpenHours
|
||||
SET isActive = CASE
|
||||
WHEN id = (SELECT id FROM selected_active) THEN 1
|
||||
ELSE 0
|
||||
END
|
||||
WHERE isActive = 1
|
||||
OR id = (SELECT id FROM selected_active);";
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
command.CommandText = @"
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS IX_LokOpenHours_OneActive
|
||||
ON LokOpenHours(isActive)
|
||||
WHERE isActive = 1;";
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +137,12 @@ public class Program
|
||||
app.MapOpenApi();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
app.UseCors("UiCors");
|
||||
|
||||
if (!app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseHttpsRedirection();
|
||||
}
|
||||
|
||||
SystemEndpoints.MapSystemEndpoints(app);
|
||||
LokEndpoints.MapLokEndpoints(app);
|
||||
|
||||
@@ -19,7 +19,7 @@ public class LokService
|
||||
|
||||
await using var command = _connection.CreateCommand();
|
||||
command.CommandText = @"
|
||||
SELECT id, name, version, paragraph1, paragraph2, paragraph3, paragraph4, kitchenNotice
|
||||
SELECT id, name, isActive, version, paragraph1, paragraph2, paragraph3, paragraph4, kitchenNotice
|
||||
FROM LokOpenHours
|
||||
ORDER BY datetime(version) DESC, id DESC
|
||||
LIMIT 5";
|
||||
@@ -34,6 +34,7 @@ public class LokService
|
||||
{
|
||||
Id = reader["id"] is long id ? id : Convert.ToInt64(reader["id"]),
|
||||
Name = reader["name"]?.ToString() ?? string.Empty,
|
||||
IsActive = ParseBoolean(reader["isActive"]),
|
||||
Version = ParseVersion(reader["version"]?.ToString()),
|
||||
Paragraph1 = reader["paragraph1"]?.ToString() ?? string.Empty,
|
||||
Paragraph2 = reader["paragraph2"]?.ToString() ?? string.Empty,
|
||||
@@ -55,13 +56,22 @@ public class LokService
|
||||
|
||||
var version = DateTime.UtcNow;
|
||||
|
||||
using var transaction = _connection.BeginTransaction();
|
||||
|
||||
await using var resetCommand = _connection.CreateCommand();
|
||||
resetCommand.Transaction = transaction;
|
||||
resetCommand.CommandText = "UPDATE LokOpenHours SET isActive = 0 WHERE isActive = 1;";
|
||||
await resetCommand.ExecuteNonQueryAsync();
|
||||
|
||||
await using var command = _connection.CreateCommand();
|
||||
command.Transaction = transaction;
|
||||
command.CommandText = @"
|
||||
INSERT INTO LokOpenHours (name, version, paragraph1, paragraph2, paragraph3, paragraph4, kitchenNotice)
|
||||
VALUES (@name, @version, @paragraph1, @paragraph2, @paragraph3, @paragraph4, @kitchenNotice);
|
||||
INSERT INTO LokOpenHours (name, isActive, version, paragraph1, paragraph2, paragraph3, paragraph4, kitchenNotice)
|
||||
VALUES (@name, @isActive, @version, @paragraph1, @paragraph2, @paragraph3, @paragraph4, @kitchenNotice);
|
||||
SELECT last_insert_rowid();";
|
||||
|
||||
command.Parameters.AddWithValue("@name", openHours.Name ?? string.Empty);
|
||||
command.Parameters.AddWithValue("@isActive", 1);
|
||||
command.Parameters.AddWithValue("@version", version.ToString("O"));
|
||||
command.Parameters.AddWithValue("@paragraph1", openHours.Paragraph1 ?? string.Empty);
|
||||
command.Parameters.AddWithValue("@paragraph2", openHours.Paragraph2 ?? string.Empty);
|
||||
@@ -70,11 +80,15 @@ public class LokService
|
||||
command.Parameters.AddWithValue("@kitchenNotice", openHours.KitchenNotice ?? string.Empty);
|
||||
|
||||
var insertedId = await command.ExecuteScalarAsync();
|
||||
var insertedIdValue = Convert.ToInt64(insertedId);
|
||||
|
||||
transaction.Commit();
|
||||
|
||||
return new LokOpenHours
|
||||
{
|
||||
Id = Convert.ToInt64(insertedId),
|
||||
Id = insertedIdValue,
|
||||
Name = openHours.Name ?? string.Empty,
|
||||
IsActive = true,
|
||||
Version = version,
|
||||
Paragraph1 = openHours.Paragraph1 ?? string.Empty,
|
||||
Paragraph2 = openHours.Paragraph2 ?? string.Empty,
|
||||
@@ -91,6 +105,16 @@ public class LokService
|
||||
await _connection.OpenAsync();
|
||||
}
|
||||
|
||||
await using var activeCommand = _connection.CreateCommand();
|
||||
activeCommand.CommandText = "SELECT isActive FROM LokOpenHours WHERE id = @id;";
|
||||
activeCommand.Parameters.AddWithValue("@id", id);
|
||||
var activeValue = await activeCommand.ExecuteScalarAsync();
|
||||
|
||||
if (activeValue is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
await using var command = _connection.CreateCommand();
|
||||
command.CommandText = @"
|
||||
DELETE FROM LokOpenHours
|
||||
@@ -99,9 +123,115 @@ public class LokService
|
||||
command.Parameters.AddWithValue("@id", id);
|
||||
|
||||
var affectedRows = await command.ExecuteNonQueryAsync();
|
||||
|
||||
if (affectedRows > 0)
|
||||
{
|
||||
await EnsureSingleActiveInvariant();
|
||||
}
|
||||
|
||||
return affectedRows > 0;
|
||||
}
|
||||
|
||||
public async Task<LokOpenHours?> UpdateOpenHours(long id, LokOpenHours openHours)
|
||||
{
|
||||
if (_connection.State != ConnectionState.Open)
|
||||
{
|
||||
await _connection.OpenAsync();
|
||||
}
|
||||
|
||||
var version = DateTime.UtcNow;
|
||||
|
||||
await using var activeCommand = _connection.CreateCommand();
|
||||
activeCommand.CommandText = "SELECT isActive FROM LokOpenHours WHERE id = @id;";
|
||||
activeCommand.Parameters.AddWithValue("@id", id);
|
||||
var activeValue = await activeCommand.ExecuteScalarAsync();
|
||||
|
||||
if (activeValue is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var isActive = ParseBoolean(activeValue);
|
||||
|
||||
await using var command = _connection.CreateCommand();
|
||||
command.CommandText = @"
|
||||
UPDATE LokOpenHours
|
||||
SET
|
||||
name = @name,
|
||||
version = @version,
|
||||
paragraph1 = @paragraph1,
|
||||
paragraph2 = @paragraph2,
|
||||
paragraph3 = @paragraph3,
|
||||
paragraph4 = @paragraph4,
|
||||
kitchenNotice = @kitchenNotice
|
||||
WHERE id = @id;";
|
||||
|
||||
command.Parameters.AddWithValue("@id", id);
|
||||
command.Parameters.AddWithValue("@name", openHours.Name ?? string.Empty);
|
||||
command.Parameters.AddWithValue("@version", version.ToString("O"));
|
||||
command.Parameters.AddWithValue("@paragraph1", openHours.Paragraph1 ?? string.Empty);
|
||||
command.Parameters.AddWithValue("@paragraph2", openHours.Paragraph2 ?? string.Empty);
|
||||
command.Parameters.AddWithValue("@paragraph3", openHours.Paragraph3 ?? string.Empty);
|
||||
command.Parameters.AddWithValue("@paragraph4", openHours.Paragraph4 ?? string.Empty);
|
||||
command.Parameters.AddWithValue("@kitchenNotice", openHours.KitchenNotice ?? string.Empty);
|
||||
|
||||
var affectedRows = await command.ExecuteNonQueryAsync();
|
||||
|
||||
if (affectedRows == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new LokOpenHours
|
||||
{
|
||||
Id = id,
|
||||
Name = openHours.Name ?? string.Empty,
|
||||
IsActive = isActive,
|
||||
Version = version,
|
||||
Paragraph1 = openHours.Paragraph1 ?? string.Empty,
|
||||
Paragraph2 = openHours.Paragraph2 ?? string.Empty,
|
||||
Paragraph3 = openHours.Paragraph3 ?? string.Empty,
|
||||
Paragraph4 = openHours.Paragraph4 ?? string.Empty,
|
||||
KitchenNotice = openHours.KitchenNotice ?? string.Empty
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<bool> SetActiveOpenHours(long id)
|
||||
{
|
||||
if (_connection.State != ConnectionState.Open)
|
||||
{
|
||||
await _connection.OpenAsync();
|
||||
}
|
||||
|
||||
using var transaction = _connection.BeginTransaction();
|
||||
|
||||
await using var existsCommand = _connection.CreateCommand();
|
||||
existsCommand.Transaction = transaction;
|
||||
existsCommand.CommandText = "SELECT COUNT(*) FROM LokOpenHours WHERE id = @id;";
|
||||
existsCommand.Parameters.AddWithValue("@id", id);
|
||||
var exists = Convert.ToInt32(await existsCommand.ExecuteScalarAsync()) > 0;
|
||||
|
||||
if (!exists)
|
||||
{
|
||||
transaction.Rollback();
|
||||
return false;
|
||||
}
|
||||
|
||||
await using var resetCommand = _connection.CreateCommand();
|
||||
resetCommand.Transaction = transaction;
|
||||
resetCommand.CommandText = "UPDATE LokOpenHours SET isActive = 0 WHERE isActive = 1;";
|
||||
await resetCommand.ExecuteNonQueryAsync();
|
||||
|
||||
await using var activateCommand = _connection.CreateCommand();
|
||||
activateCommand.Transaction = transaction;
|
||||
activateCommand.CommandText = "UPDATE LokOpenHours SET isActive = 1 WHERE id = @id;";
|
||||
activateCommand.Parameters.AddWithValue("@id", id);
|
||||
await activateCommand.ExecuteNonQueryAsync();
|
||||
|
||||
transaction.Commit();
|
||||
return true;
|
||||
}
|
||||
|
||||
private static DateTime ParseVersion(string? value)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(value) && DateTime.TryParse(value, out var parsed))
|
||||
@@ -111,4 +241,61 @@ public class LokService
|
||||
|
||||
return DateTime.MinValue;
|
||||
}
|
||||
|
||||
private static bool ParseBoolean(object? value)
|
||||
{
|
||||
return value switch
|
||||
{
|
||||
bool boolValue => boolValue,
|
||||
long longValue => longValue == 1,
|
||||
int intValue => intValue == 1,
|
||||
string stringValue when int.TryParse(stringValue, out var parsedInt) => parsedInt == 1,
|
||||
string stringValue when bool.TryParse(stringValue, out var parsedBool) => parsedBool,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
private async Task EnsureSingleActiveInvariant()
|
||||
{
|
||||
await using var countCommand = _connection.CreateCommand();
|
||||
countCommand.CommandText = "SELECT COUNT(*) FROM LokOpenHours WHERE isActive = 1;";
|
||||
var activeCount = Convert.ToInt32(await countCommand.ExecuteScalarAsync());
|
||||
|
||||
if (activeCount == 0)
|
||||
{
|
||||
await using var promoteCommand = _connection.CreateCommand();
|
||||
promoteCommand.CommandText = @"
|
||||
UPDATE LokOpenHours
|
||||
SET isActive = 1
|
||||
WHERE id = (
|
||||
SELECT id
|
||||
FROM LokOpenHours
|
||||
ORDER BY datetime(version) DESC, id DESC
|
||||
LIMIT 1
|
||||
);";
|
||||
await promoteCommand.ExecuteNonQueryAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
if (activeCount > 1)
|
||||
{
|
||||
await using var normalizeCommand = _connection.CreateCommand();
|
||||
normalizeCommand.CommandText = @"
|
||||
WITH selected_active AS (
|
||||
SELECT id
|
||||
FROM LokOpenHours
|
||||
WHERE isActive = 1
|
||||
ORDER BY datetime(version) DESC, id DESC
|
||||
LIMIT 1
|
||||
)
|
||||
UPDATE LokOpenHours
|
||||
SET isActive = CASE
|
||||
WHEN id = (SELECT id FROM selected_active) THEN 1
|
||||
ELSE 0
|
||||
END
|
||||
WHERE isActive = 1
|
||||
OR id = (SELECT id FROM selected_active);";
|
||||
await normalizeCommand.ExecuteNonQueryAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user