diff --git a/src/JobsJobsJobs/Client/App.razor b/src/JobsJobsJobs/Client/App.razor index 6f67a6e..2d39d60 100644 --- a/src/JobsJobsJobs/Client/App.razor +++ b/src/JobsJobsJobs/Client/App.razor @@ -1,10 +1,10 @@ - - - - - -

Sorry, there's nothing at this address.

-
-
+ + + + + +

Sorry, there's nothing at this address.

+
+
diff --git a/src/JobsJobsJobs/Client/Pages/Citizen/Profile.razor b/src/JobsJobsJobs/Client/Pages/Citizen/Profile.razor index 1cabc54..b45220f 100644 --- a/src/JobsJobsJobs/Client/Pages/Citizen/Profile.razor +++ b/src/JobsJobsJobs/Client/Pages/Citizen/Profile.razor @@ -13,42 +13,78 @@ else if (profileForm != null) { - - -
- -
- -
-
- -

- -

+
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+
+ + + + @foreach (var (id, name) in continents) + { + + } + + +
+
+
+
+ + + +
+
+
+
+
+
+ + + +
+
+
+
+
+ + +
+
+
+
+ +
+
} @@ -58,10 +94,22 @@ else if (profileForm != null) public ProfileForm? profileForm = null; + private IEnumerable continents = Enumerable.Empty(); + public string errorMessage = ""; protected override async Task OnInitializedAsync() { + var continentResult = await ServerApi.AllContinents(http, state); + if (continentResult.IsOk) + { + continents = continentResult.Ok; + } + else + { + errorMessage = continentResult.Error; + } + var result = await ServerApi.RetrieveProfile(http, state); if (result.IsOk) { diff --git a/src/JobsJobsJobs/Client/ServerApi.cs b/src/JobsJobsJobs/Client/ServerApi.cs index 008f296..b548138 100644 --- a/src/JobsJobsJobs/Client/ServerApi.cs +++ b/src/JobsJobsJobs/Client/ServerApi.cs @@ -77,5 +77,22 @@ namespace JobsJobsJobs.Client _ => Result.AsError(await res.Content.ReadAsStringAsync()), }; } + + /// + /// Retrieve all continents + /// + /// The HTTP client to use for server communication + /// The current application state + /// The continents, or an error message if one occurs + public static async Task>> AllContinents(HttpClient http, AppState state) + { + var req = WithHeader(state, "continent/all"); + var res = await http.SendAsync(req); + if (res.IsSuccessStatusCode) { + var continents = await res.Content.ReadFromJsonAsync>(); + return Result>.AsOk(continents ?? Enumerable.Empty()); + } + return Result>.AsError(await res.Content.ReadAsStringAsync()); + } } } diff --git a/src/JobsJobsJobs/Server/Areas/Api/Controllers/CitizenController.cs b/src/JobsJobsJobs/Server/Areas/Api/Controllers/CitizenController.cs index b79380e..22aab08 100644 --- a/src/JobsJobsJobs/Server/Areas/Api/Controllers/CitizenController.cs +++ b/src/JobsJobsJobs/Server/Areas/Api/Controllers/CitizenController.cs @@ -9,16 +9,34 @@ using System.Threading.Tasks; namespace JobsJobsJobs.Server.Areas.Api.Controllers { + /// + /// API controller for citizen information + /// [Route("api/[controller]")] [ApiController] public class CitizenController : ControllerBase { + /// + /// Authorization configuration section + /// private readonly IConfigurationSection _config; + /// + /// NodaTime clock + /// private readonly IClock _clock; + /// + /// The data connection to use for this request + /// private readonly NpgsqlConnection _db; + /// + /// Constructor + /// + /// The authorization configuration section + /// The NodaTime clock instance + /// The data connection to use for this request public CitizenController(IConfiguration config, IClock clock, NpgsqlConnection db) { _config = config.GetSection("Auth"); diff --git a/src/JobsJobsJobs/Server/Areas/Api/Controllers/ContinentController.cs b/src/JobsJobsJobs/Server/Areas/Api/Controllers/ContinentController.cs new file mode 100644 index 0000000..8e0ce3d --- /dev/null +++ b/src/JobsJobsJobs/Server/Areas/Api/Controllers/ContinentController.cs @@ -0,0 +1,38 @@ +using JobsJobsJobs.Server.Data; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Npgsql; +using System.Threading.Tasks; + +namespace JobsJobsJobs.Server.Areas.Api.Controllers +{ + /// + /// API endpoint for continent information + /// + [Route("api/[controller]")] + [Authorize] + [ApiController] + public class ContinentController : ControllerBase + { + /// + /// The database connection to use for this request + /// + private readonly NpgsqlConnection _db; + + /// + /// Constructor + /// + /// The database connection to use for this request + public ContinentController(NpgsqlConnection db) + { + _db = db; + } + + [HttpGet("all")] + public async Task All() + { + await _db.OpenAsync(); + return Ok(await _db.AllContinents()); + } + } +} diff --git a/src/JobsJobsJobs/Server/Areas/Api/Controllers/ProfileController.cs b/src/JobsJobsJobs/Server/Areas/Api/Controllers/ProfileController.cs index 33e9b6f..dde9e4d 100644 --- a/src/JobsJobsJobs/Server/Areas/Api/Controllers/ProfileController.cs +++ b/src/JobsJobsJobs/Server/Areas/Api/Controllers/ProfileController.cs @@ -13,6 +13,9 @@ using System.Threading.Tasks; namespace JobsJobsJobs.Server.Areas.Api.Controllers { + /// + /// API controller for employment profile information + /// [Route("api/[controller]")] [ApiController] public class ProfileController : ControllerBase @@ -20,19 +23,23 @@ namespace JobsJobsJobs.Server.Areas.Api.Controllers /// /// The database connection /// - private readonly NpgsqlConnection db; + private readonly NpgsqlConnection _db; - public ProfileController(NpgsqlConnection dbConn) + /// + /// Constructor + /// + /// The database connection to use for this request + public ProfileController(NpgsqlConnection db) { - db = dbConn; + _db = db; } [Authorize] [HttpGet("")] public async Task Get() { - await db.OpenAsync(); - var profile = await db.FindProfileByCitizen( + await _db.OpenAsync(); + var profile = await _db.FindProfileByCitizen( CitizenId.Parse(User.FindFirst(ClaimTypes.NameIdentifier)!.Value)); return profile == null ? NoContent() : Ok(profile); } diff --git a/src/JobsJobsJobs/Server/Data/ContinentExtensions.cs b/src/JobsJobsJobs/Server/Data/ContinentExtensions.cs new file mode 100644 index 0000000..3aba495 --- /dev/null +++ b/src/JobsJobsJobs/Server/Data/ContinentExtensions.cs @@ -0,0 +1,40 @@ +using JobsJobsJobs.Shared; +using Npgsql; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace JobsJobsJobs.Server.Data +{ + /// + /// Data extensions for manipulation of continent objects + /// + public static class ContinentExtensions + { + /// + /// Create a continent from the current row in the data reader + /// + /// The data reader + /// The current row's values as a continent object + private static Continent ToContinent(NpgsqlDataReader rdr) => + new Continent(ContinentId.Parse(rdr.GetString("id")), rdr.GetString("name")); + + /// + /// Retrieve all continents + /// + /// All continents + public static async Task> AllContinents(this NpgsqlConnection conn) + { + using var cmd = conn.CreateCommand(); + cmd.CommandText = "SELECT * FROM continent ORDER BY name"; + + using var rdr = await cmd.ExecuteReaderAsync().ConfigureAwait(false); + var continents = new List(); + while (await rdr.ReadAsync()) + { + continents.Add(ToContinent(rdr)); + } + + return continents; + } + } +} diff --git a/src/JobsJobsJobs/Server/JobsJobsJobs.Server.csproj.user b/src/JobsJobsJobs/Server/JobsJobsJobs.Server.csproj.user index 2103251..a97ca33 100644 --- a/src/JobsJobsJobs/Server/JobsJobsJobs.Server.csproj.user +++ b/src/JobsJobsJobs/Server/JobsJobsJobs.Server.csproj.user @@ -4,8 +4,8 @@ RazorPageScaffolder root/Common/RazorPage JobsJobsJobs.Server - ApiControllerEmptyScaffolder - root/Common/Api + MvcControllerEmptyScaffolder + root/Common/MVC/Controller ProjectDebugger diff --git a/src/database/tables.sql b/src/database/tables.sql index c660257..fbb6635 100644 --- a/src/database/tables.sql +++ b/src/database/tables.sql @@ -36,6 +36,14 @@ COMMENT ON COLUMN jjj.continent.id COMMENT ON COLUMN jjj.continent.name IS 'The name of the continent'; +INSERT INTO jjj.continent VALUES ('QbpCTd3mDbTU', 'Africa'); +INSERT INTO jjj.continent VALUES ('yHGzQvD80uS9', 'Antarctica'); +INSERT INTO jjj.continent VALUES ('jEvaGTKwnDB8', 'Asia'); +INSERT INTO jjj.continent VALUES ('mUN8AOddmx2i', 'Australia'); +INSERT INTO jjj.continent VALUES ('-TXN-0Cvn_RT', 'Europe'); +INSERT INTO jjj.continent VALUES ('2ELDbrTvVSPO', 'North America'); +INSERT INTO jjj.continent VALUES ('XxnCcCHu8wqL', 'South America'); + CREATE TABLE jjj.profile ( citizen_id VARCHAR(12) NOT NULL, seeking_employment BOOLEAN NOT NULL,