Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
name: Build and Deploy DevEats
name: Build and Deploy DevEats in Prod

on:
push:
branches: [ "main", "issues/9" ]
branches: [ "main"]
# pull_request:
# branches: [ "issues/*" ]

Expand Down Expand Up @@ -39,7 +39,6 @@ jobs:
deploy:
runs-on: ubuntu-latest
needs: build-and-test
# if: github.ref == 'refs/heads/main' && github.event_name == 'push'

steps:
- name: Download artifact from build job
Expand All @@ -58,16 +57,17 @@ jobs:
--name deveats-wpc2025-test \
--resource-group deveats-wpc2025-rg \
--settings \
ConnectionStrings__DefaultConnection="${{ secrets.CONNECTION_STRING_TEST }}" \
ConnectionStrings__DefaultConnection="${{ secrets.CONNECTION_STRING_PROD }}" \
ASPNETCORE_ENVIRONMENT="Production"

- name: Deploy to Azure Web App
uses: azure/webapps-deploy@v3
with:
app-name: 'deveats-wpc2025-test'
publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }}
publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE_PROD }}
package: .

- name: Azure Logout
run: az logout
if: always()
if: always()

71 changes: 71 additions & 0 deletions .github/workflows/deploy-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Build and Deploy DevEats in Test

on:
pull_request:


jobs:
build-and-test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x

- name: Restore dependencies
run: dotnet restore

- name: Build
run: dotnet build --no-restore --configuration Release

- name: Run Tests
run: dotnet test --no-build --verbosity normal --configuration Release

- name: Publish
run: dotnet publish src/DevEats.Web/DevEats.Web.csproj -c Release -o ./publish

- name: Upload artifact for deployment
uses: actions/upload-artifact@v4
with:
name: deveats-app
path: ./publish

deploy:
runs-on: ubuntu-latest
needs: build-and-test

steps:
- name: Download artifact from build job
uses: actions/download-artifact@v4
with:
name: deveats-app

- name: Azure Login
uses: azure/login@v2
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}

- name: Set Azure Web App Settings
run: |
az webapp config appsettings set \
--name deveats-wpc2025-test \
--resource-group deveats-wpc2025-rg \
--settings \
ConnectionStrings__DefaultConnection="${{ secrets.CONNECTION_STRING_TEST }}" \
ASPNETCORE_ENVIRONMENT="Production"

- name: Deploy to Azure Web App
uses: azure/webapps-deploy@v3
with:
app-name: 'deveats-wpc2025-test'
publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE_TEST }}
package: .

- name: Azure Logout
run: az logout
if: always()

2 changes: 2 additions & 0 deletions note.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
az ad sp create-for-rbac --name "deveats-wpc2025" --sdk-auth --role contributor \
--scopes /subscriptions/403624a6-e899-4d2b-95df-ec6353be64a8/resourceGroups/deveats-wpc2025-rg
1 change: 1 addition & 0 deletions src/DevEats.Core/Interfaces/IRestaurantService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ public interface IRestaurantService
{
Task<Restaurant?> GetByIdAsync(int id);
Task<IEnumerable<Restaurant>> GetAllAsync();
Task<PagedResult<Restaurant>> GetRestaurantsPagedAsync(int pageNumber, int pageSize);
Task<Restaurant> AddAsync(Restaurant restaurant);
Task UpdateAsync(Restaurant restaurant);
Task DeleteAsync(int id);
Expand Down
25 changes: 25 additions & 0 deletions src/DevEats.Infrastructure/Services/RestaurantService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,31 @@ public async Task<IEnumerable<Restaurant>> GetAllAsync()
.ToListAsync();
}

public async Task<PagedResult<Restaurant>> GetRestaurantsPagedAsync(int pageNumber, int pageSize)
{
if (pageNumber < 1) pageNumber = 1;
if (pageSize < 1) pageSize = 1;

var query = _context.Restaurants
.Include(r => r.Reviews)
.OrderByDescending(r => r.AverageRating);

var totalCount = await query.CountAsync();

var items = await query
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize)
.ToListAsync();

return new PagedResult<Restaurant>
{
Items = items,
TotalCount = totalCount,
PageNumber = pageNumber,
PageSize = pageSize
};
}

public async Task<Restaurant> AddAsync(Restaurant restaurant)
{
_context.Restaurants.Add(restaurant);
Expand Down
191 changes: 184 additions & 7 deletions src/DevEats.Web/Pages/Index.razor
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
@page "/"
@using DevEats.Core.Models
@using DevEats.Infrastructure.Data
@using Microsoft.EntityFrameworkCore
@inject DevEatsDbContext DbContext
@using DevEats.Core.Interfaces
@inject IRestaurantService RestaurantService
@inject NavigationManager Navigation

<PageTitle>DevEats - Perché il refactoring a stomaco vuoto è pericoloso</PageTitle>
Expand Down Expand Up @@ -108,6 +107,37 @@
</div>
}
</div>

@if (totalPages > 1)
{
<nav class="pagination-controls"
role="navigation"
aria-label="Navigazione ristoranti paginata">
<button class="pagination-btn"
@onclick="() => ChangePage(currentPage - 1)"
disabled="@(!pagedRestaurants.HasPreviousPage)"
aria-label="Vai alla pagina precedente"
aria-disabled="@(!pagedRestaurants.HasPreviousPage ? "true" : "false")">
<i class="bi bi-chevron-left" aria-hidden="true"></i>
Precedente
</button>
<span class="pagination-info"
role="status"
aria-live="polite"
aria-atomic="true"
aria-label="Pagina corrente">
Pagina @currentPage di @totalPages
</span>
<button class="pagination-btn"
@onclick="() => ChangePage(currentPage + 1)"
disabled="@(!pagedRestaurants.HasNextPage)"
aria-label="Vai alla pagina successiva"
aria-disabled="@(!pagedRestaurants.HasNextPage ? "true" : "false")">
Successivo
<i class="bi bi-chevron-right" aria-hidden="true"></i>
</button>
</nav>
}
}
</div>

Expand Down Expand Up @@ -434,6 +464,102 @@
}
}

/* Pagination Controls */
.pagination-controls {
display: flex;
justify-content: center;
align-items: center;
gap: 2rem;
margin-top: 4rem;
padding-top: 2rem;
}

.pagination-btn {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.75rem 1.5rem;
background: linear-gradient(135deg, #ff6b6b, #ff8e53);
color: white;
border: none;
border-radius: 12px;
font-weight: 600;
font-size: 1rem;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 12px rgba(255, 107, 107, 0.3);
position: relative;
}

.pagination-btn:hover:not([disabled]) {
background: linear-gradient(135deg, #ff8e53, #feca57);
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(255, 107, 107, 0.4);
}

/* Enhanced focus indicator for keyboard navigation - WCAG 2.1 AA compliant */
.pagination-btn:focus {
outline: none;
box-shadow: 0 0 0 3px rgba(255, 107, 107, 0.4), 0 0 0 5px rgba(255, 255, 255, 1);
}

.pagination-btn:focus-visible {
outline: 3px solid #ff6b6b;
outline-offset: 2px;
box-shadow: 0 0 0 3px rgba(255, 107, 107, 0.4);
}

.pagination-btn[disabled] {
background: #cbd5e0;
color: #a0aec0;
cursor: not-allowed;
box-shadow: none;
opacity: 0.6;
}

/* Improved disabled state contrast - WCAG 2.1 AA minimum 3:1 */
.pagination-btn[disabled]:focus {
outline: 2px solid #718096;
outline-offset: 2px;
box-shadow: 0 0 0 3px rgba(113, 128, 150, 0.3);
}

.pagination-btn i {
font-size: 1rem;
}

.pagination-info {
color: #4a5568;
font-weight: 600;
font-size: 1rem;
min-width: 140px;
text-align: center;
padding: 0.5rem;
}

/* High contrast mode support */
@@media (prefers-contrast: high) {
.pagination-btn {
border: 2px solid currentColor;
}

.pagination-btn:focus {
outline: 4px solid currentColor;
outline-offset: 3px;
}
}

/* Reduced motion support */
@@media (prefers-reduced-motion: reduce) {
.pagination-btn {
transition: none;
}

.pagination-btn:hover:not([disabled]) {
transform: none;
}
}

/* Responsive */
@@media (max-width: 768px) {
.hero-title {
Expand All @@ -451,17 +577,68 @@
.section-title {
font-size: 2rem;
}

/* Mobile pagination adjustments */
.pagination-controls {
gap: 1rem;
flex-wrap: wrap;
}

.pagination-btn {
padding: 0.625rem 1rem;
font-size: 0.9rem;
min-width: 44px; /* WCAG 2.1 AA minimum touch target size */
min-height: 44px;
}

.pagination-info {
min-width: 100px;
font-size: 0.9rem;
}
}
</style>

@code {
[SupplyParameterFromQuery(Name = "page")]
public int PageQuery { get; set; } = 1;

private List<Restaurant>? restaurants;
private PagedResult<Restaurant> pagedRestaurants = new();

private int currentPage = 1;
private const int pageSize = 3;
private int totalPages => pagedRestaurants.TotalPages;

protected override async Task OnInitializedAsync()
{
restaurants = await DbContext.Restaurants
.Include(r => r.Reviews)
.OrderByDescending(r => r.AverageRating)
.ToListAsync();
currentPage = PageQuery > 0 ? PageQuery : 1;
await LoadRestaurants();
}

protected override async Task OnParametersSetAsync()
{
if (PageQuery != currentPage && PageQuery > 0)
{
currentPage = PageQuery;
await LoadRestaurants();
}
}

private async Task LoadRestaurants()
{
pagedRestaurants = await RestaurantService.GetRestaurantsPagedAsync(currentPage, pageSize);
restaurants = pagedRestaurants.Items.ToList();
}

private async Task ChangePage(int newPage)
{
if (newPage < 1 || newPage > totalPages || newPage == currentPage)
{
return;
}

currentPage = newPage;
Navigation.NavigateTo($"/?page={currentPage}");
await LoadRestaurants();
}
}
1 change: 1 addition & 0 deletions src/DevEats.Web/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

var builder = WebApplication.CreateBuilder(args);

// Explicitly configure appsettings.json files
builder.Configuration
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
Expand Down
Loading