using Microsoft.AspNetCore.Mvc;
namespace Portfolio.Web.Controllers;
public sealed class HomeController(
IPortfolioContent content) : Controller
{
public IActionResult Index()
{
var model = new PortfolioViewModel(
content.Profile,
content.Projects,
content.Experience,
content.Skills);
return View(model);
}
[Route("not-found")]
public IActionResult NotFoundPage()
=> View("NotFound", content.Profile);
}
using Microsoft.AspNetCore.Mvc;
namespace Portfolio.Web.Controllers;
public sealed class HomeController(
IPortfolioContent content) : Controller
{
public IActionResult Index()
{
var model = new PortfolioViewModel(
content.Profile,
content.Projects,
content.Experience,
content.Skills);
return View(model);
}
[Route("not-found")]
public IActionResult NotFoundPage()
=> View("NotFound", content.Profile);
}
public sealed record GetAccountsQuery(Guid UserId)
: IRequest<IReadOnlyList<AccountDto>>;
public sealed class GetAccountsHandler(AppDbContext db)
: IRequestHandler<GetAccountsQuery,
IReadOnlyList<AccountDto>>
{
public async Task<IReadOnlyList<AccountDto>> Handle(
GetAccountsQuery request, CancellationToken ct)
{
return await db.Accounts
.Where(a => a.UserId == request.UserId)
.OrderBy(a => a.Name)
.Select(a => new AccountDto(
a.Id, a.Name, a.Balance))
.ToListAsync(ct);
}
}
public sealed record GetAccountsQuery(Guid UserId)
: IRequest<IReadOnlyList<AccountDto>>;
public sealed class GetAccountsHandler(AppDbContext db)
: IRequestHandler<GetAccountsQuery,
IReadOnlyList<AccountDto>>
{
public async Task<IReadOnlyList<AccountDto>> Handle(
GetAccountsQuery request, CancellationToken ct)
{
return await db.Accounts
.Where(a => a.UserId == request.UserId)
.OrderBy(a => a.Name)
.Select(a => new AccountDto(
a.Id, a.Name, a.Balance))
.ToListAsync(ct);
}
}
@model IReadOnlyList<Project>
<section id="projects" class="section">
<h2 class="section-title">Selected projects</h2>
@foreach (var project in Model)
{
<partial name="_ProjectCard"
model="project" />
}
</section>
@inject IPortfolioContent Content
<footer class="site-footer">
© @DateTime.UtcNow.Year
@Content.Profile.Name
</footer>
@model IReadOnlyList<Project>
<section id="projects" class="section">
<h2 class="section-title">Selected projects</h2>
@foreach (var project in Model)
{
<partial name="_ProjectCard"
model="project" />
}
</section>
@inject IPortfolioContent Content
<footer class="site-footer">
© @DateTime.UtcNow.Year
@Content.Profile.Name
</footer>
<script setup>
import { ref, computed } from 'vue'
const bookings = ref([])
const open = computed(() =>
bookings.value.filter(b => !b.cancelled)
)
</script>
<template>
<ul class="bookings">
<li v-for="b in open" :key="b.id">
{{ b.reference }} - {{ b.status }}
</li>
</ul>
</template>
<script setup>
import { ref, computed } from 'vue'
const bookings = ref([])
const open = computed(() =>
bookings.value.filter(b => !b.cancelled)
)
</script>
<template>
<ul class="bookings">
<li v-for="b in open" :key="b.id">
{{ b.reference }} - {{ b.status }}
</li>
</ul>
</template>
SELECT TOP (50)
m.MemberId,
m.PlanCode,
e.EffectiveDate
FROM dbo.Member AS m
INNER JOIN dbo.Enrollment AS e
ON e.MemberId = m.MemberId
WHERE e.Status = 'Active'
AND e.EffectiveDate >= @Since
ORDER BY e.EffectiveDate DESC;
UPDATE dbo.Availability
SET Remaining = Remaining - 1
WHERE RoomId = @RoomId
AND Remaining > 0;
SELECT TOP (50)
m.MemberId,
m.PlanCode,
e.EffectiveDate
FROM dbo.Member AS m
INNER JOIN dbo.Enrollment AS e
ON e.MemberId = m.MemberId
WHERE e.Status = 'Active'
AND e.EffectiveDate >= @Since
ORDER BY e.EffectiveDate DESC;
UPDATE dbo.Availability
SET Remaining = Remaining - 1
WHERE RoomId = @RoomId
AND Remaining > 0;
[Fact]
public async Task Export_fails_loudly_when_a_page_fails()
{
using var broken = new HttpClient(
new StatusHandler(HttpStatusCode.InternalServerError));
var error = await Assert.ThrowsAsync<
InvalidOperationException>(() =>
StaticExporter.ExportAsync(
broken, webRoot, output));
Assert.Contains("500", error.Message);
}
const { data } = useQuery({
queryKey: ['accounts'],
queryFn: fetchAccounts,
})
[Fact]
public async Task Export_fails_loudly_when_a_page_fails()
{
using var broken = new HttpClient(
new StatusHandler(HttpStatusCode.InternalServerError));
var error = await Assert.ThrowsAsync<
InvalidOperationException>(() =>
StaticExporter.ExportAsync(
broken, webRoot, output));
Assert.Contains("500", error.Message);
}
const { data } = useQuery({
queryKey: ['accounts'],
queryFn: fetchAccounts,
})
@inherits UmbracoViewPage<HomePage>
@foreach (var block in
Model.Content.Children<ContentBlock>())
{
@await Html.PartialAsync(
"Blocks/" + block.ContentType.Alias,
block)
}
const nonce = document
.querySelector('meta[name="csp-nonce"]')
.content
const script = document.createElement('script')
script.nonce = nonce
@inherits UmbracoViewPage<HomePage>
@foreach (var block in
Model.Content.Children<ContentBlock>())
{
@await Html.PartialAsync(
"Blocks/" + block.ContentType.Alias,
block)
}
const nonce = document
.querySelector('meta[name="csp-nonce"]')
.content
const script = document.createElement('script')
script.nonce = nonce
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY . .
RUN dotnet test Portfolio.slnx
RUN dotnet run --project src/Portfolio.Web \
-- --export /out
pipeline {
agent any
stages {
stage('Build') {
steps { sh 'dotnet build' }
}
stage('Test') {
steps { sh 'dotnet test' }
}
}
}
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY . .
RUN dotnet test Portfolio.slnx
RUN dotnet run --project src/Portfolio.Web \
-- --export /out
pipeline {
agent any
stages {
stage('Build') {
steps { sh 'dotnet build' }
}
stage('Test') {
steps { sh 'dotnet test' }
}
}
}