From b05cbe61e89d96a8515e6ad290fd8c244d3762b0 Mon Sep 17 00:00:00 2001 From: "MASTO-15\\Admin" Date: Sun, 8 Nov 2026 20:30:40 +0100 Subject: [PATCH] v1.0.0 --- .dockerignore | 30 ++ .gitignore | 30 ++ HA_Shopping_List.Server/Components/App.razor | 17 + .../Components/Layout/MainLayout.razor | 10 + .../Components/Layout/MainLayout.razor.css | 18 + .../Components/Pages/Error.razor | 36 ++ .../Components/Pages/Home.razor | 445 ++++++++++++++++++ .../Components/Routes.razor | 6 + .../Components/_Imports.razor | 10 + HA_Shopping_List.Server/Data/AppDbContext.cs | 36 ++ HA_Shopping_List.Server/Data/ShoppingItem.cs | 46 ++ HA_Shopping_List.Server/Dockerfile | 23 + .../Enum/ShoppingItemStatus.cs | 23 + .../HA_Shopping_List.Server.csproj | 19 + HA_Shopping_List.Server/Program.cs | 59 +++ .../Properties/launchSettings.json | 39 ++ .../appsettings.Development.json | 8 + HA_Shopping_List.Server/appsettings.json | 9 + HA_Shopping_List.Server/wwwroot/app.css | 29 ++ HA_Shopping_List.Server/wwwroot/favicon.ico | Bin 0 -> 270398 bytes HA_Shopping_List.slnx | 7 + docker_launch.ps1 | 44 ++ publish.ps1 | 39 ++ 23 files changed, 983 insertions(+) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 HA_Shopping_List.Server/Components/App.razor create mode 100644 HA_Shopping_List.Server/Components/Layout/MainLayout.razor create mode 100644 HA_Shopping_List.Server/Components/Layout/MainLayout.razor.css create mode 100644 HA_Shopping_List.Server/Components/Pages/Error.razor create mode 100644 HA_Shopping_List.Server/Components/Pages/Home.razor create mode 100644 HA_Shopping_List.Server/Components/Routes.razor create mode 100644 HA_Shopping_List.Server/Components/_Imports.razor create mode 100644 HA_Shopping_List.Server/Data/AppDbContext.cs create mode 100644 HA_Shopping_List.Server/Data/ShoppingItem.cs create mode 100644 HA_Shopping_List.Server/Dockerfile create mode 100644 HA_Shopping_List.Server/Enum/ShoppingItemStatus.cs create mode 100644 HA_Shopping_List.Server/HA_Shopping_List.Server.csproj create mode 100644 HA_Shopping_List.Server/Program.cs create mode 100644 HA_Shopping_List.Server/Properties/launchSettings.json create mode 100644 HA_Shopping_List.Server/appsettings.Development.json create mode 100644 HA_Shopping_List.Server/appsettings.json create mode 100644 HA_Shopping_List.Server/wwwroot/app.css create mode 100644 HA_Shopping_List.Server/wwwroot/favicon.ico create mode 100644 HA_Shopping_List.slnx create mode 100644 docker_launch.ps1 create mode 100644 publish.ps1 diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..fe1152b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,30 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md +!**/.gitignore +!.git/HEAD +!.git/config +!.git/packed-refs +!.git/refs/heads/** \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..67babe0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +## ========================================== +## 1. Fichiers de compilation & Cache .NET +## ========================================== +**/bin/ +**/obj/ +**/out/ + +## ========================================== +## 2. Visual Studio & IDE (VS Code, JetBrains) +## ========================================== +.vs/ +.vscode/ +*.suo +*.user +*.userosscache +*.sln.docstates + +## ========================================== +## 3. Packages NuGet temporaires +## ========================================== +nupkgs/ +*.nupkg +*.snupkg + +## ========================================== +## 4. Fichiers système +## ========================================== +Thumbs.db +[Dd]esktop.ini +.DS_Store \ No newline at end of file diff --git a/HA_Shopping_List.Server/Components/App.razor b/HA_Shopping_List.Server/Components/App.razor new file mode 100644 index 0000000..b90968a --- /dev/null +++ b/HA_Shopping_List.Server/Components/App.razor @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/HA_Shopping_List.Server/Components/Layout/MainLayout.razor b/HA_Shopping_List.Server/Components/Layout/MainLayout.razor new file mode 100644 index 0000000..f7226b6 --- /dev/null +++ b/HA_Shopping_List.Server/Components/Layout/MainLayout.razor @@ -0,0 +1,10 @@ +@inherits LayoutComponentBase + +
+ @Body +
+ + \ No newline at end of file diff --git a/HA_Shopping_List.Server/Components/Layout/MainLayout.razor.css b/HA_Shopping_List.Server/Components/Layout/MainLayout.razor.css new file mode 100644 index 0000000..df8c10f --- /dev/null +++ b/HA_Shopping_List.Server/Components/Layout/MainLayout.razor.css @@ -0,0 +1,18 @@ +#blazor-error-ui { + background: lightyellow; + bottom: 0; + box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); + display: none; + left: 0; + padding: 0.6rem 1.25rem 0.7rem 1.25rem; + position: fixed; + width: 100%; + z-index: 1000; +} + + #blazor-error-ui .dismiss { + cursor: pointer; + position: absolute; + right: 0.75rem; + top: 0.5rem; + } diff --git a/HA_Shopping_List.Server/Components/Pages/Error.razor b/HA_Shopping_List.Server/Components/Pages/Error.razor new file mode 100644 index 0000000..576cc2d --- /dev/null +++ b/HA_Shopping_List.Server/Components/Pages/Error.razor @@ -0,0 +1,36 @@ +@page "/Error" +@using System.Diagnostics + +Error + +

Error.

+

An error occurred while processing your request.

+ +@if (ShowRequestId) +{ +

+ Request ID: @RequestId +

+} + +

Development Mode

+

+ Swapping to Development environment will display more detailed information about the error that occurred. +

+

+ The Development environment shouldn't be enabled for deployed applications. + It can result in displaying sensitive information from exceptions to end users. + For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development + and restarting the app. +

+ +@code{ + [CascadingParameter] + private HttpContext? HttpContext { get; set; } + + private string? RequestId { get; set; } + private bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + + protected override void OnInitialized() => + RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; +} diff --git a/HA_Shopping_List.Server/Components/Pages/Home.razor b/HA_Shopping_List.Server/Components/Pages/Home.razor new file mode 100644 index 0000000..3822bee --- /dev/null +++ b/HA_Shopping_List.Server/Components/Pages/Home.razor @@ -0,0 +1,445 @@ +@page "/" +@rendermode InteractiveServer +@using HA_Shopping_List.Server.Data +@using HA_Shopping_List.Server.Enum +@using Microsoft.EntityFrameworkCore +@inject IDbContextFactory DbFactory + +Liste de Courses + +
+ +

+ 🛒 Courses de la Maison +

+ + +
+ + + + + +
+ + + + + @if (currentTab == Tab.ShoppingList) + { + +
+
+ + + + @foreach (var suggestion in historySuggestions) + { + + } + + + + + + +
+
+ + +
+ @if (!activeItems.Any()) + { +
+ La liste est vide ! Utilisez le champ ci-dessus ou piochez dans l'historique. +
+ } + else + { + @foreach (var item in activeItems) + { +
+
+ @item.Name + @if (item.Quantity > 1) + { + + x@(item.Quantity) + + } +
+ +
+ +
+ + + @item.Quantity + + +
+ + + + + + +
+
+ } + } +
+ } + + + + + @if (currentTab == Tab.Cart) + { +
+
+

Articles actuellement dans le panier

+ @if (cartItems.Any()) + { + + } +
+ + @if (!cartItems.Any()) + { +
+ Votre chariot est vide pour l'instant. +
+ } + else + { + @foreach (var item in cartItems) + { +
+
+ @item.Name + @if (item.Quantity > 1) + { + (x@(item.Quantity)) + } +
+ +
+ } + } +
+ } + + + + + @if (currentTab == Tab.History) + { +
+ +
+ +
+ + @{ + var filteredHistory = string.IsNullOrWhiteSpace(searchQuery) + ? historyItems + : historyItems.Where(h => h.Name.Contains(searchQuery, StringComparison.OrdinalIgnoreCase)).ToList(); + } + + @if (!filteredHistory.Any()) + { +
+ Aucun article trouvé dans l'historique. +
+ } + else + { + @foreach (var item in filteredHistory) + { +
+
+ @item.Name +
+ Dernier achat : @item.LastPurchasedAt.ToString("dd/MM/yyyy") +
+
+ +
+ + +
+
+ } + } +
+ } +
+ +@code { + private enum Tab { ShoppingList, Cart, History } + private Tab currentTab = Tab.ShoppingList; + + private List activeItems = new(); + private List cartItems = new(); + private List historyItems = new(); + private List historySuggestions = new(); + + private string newItemName = string.Empty; + private int newItemQuantity = 1; + private string searchQuery = string.Empty; + + protected override async Task OnInitializedAsync() + { + await LoadAllData(); + } + + private async Task LoadAllData() + { + using var db = await DbFactory.CreateDbContextAsync(); + + var all = await db.Items.ToListAsync(); + + activeItems = all.Where(i => i.Status == ShoppingItemStatus.Active) + .OrderBy(i => i.Name) + .ToList(); + + cartItems = all.Where(i => i.Status == ShoppingItemStatus.InCart) + .OrderBy(i => i.Name) + .ToList(); + + // L'historique regroupe les articles par nom distinct pour l'affichage et l'autocomplétion + historyItems = all.GroupBy(i => i.Name.Trim().ToLowerInvariant()) + .Select(g => g.OrderByDescending(x => x.LastPurchasedAt).First()) + .OrderBy(i => i.Name) + .ToList(); + + historySuggestions = historyItems.Select(h => h.Name).ToList(); + StateHasChanged(); + } + + private async Task AddItem() + { + if (string.IsNullOrWhiteSpace(newItemName)) return; + + using var db = await DbFactory.CreateDbContextAsync(); + var normalizedName = newItemName.Trim(); + + // Vérifie si l'article est déjà présent dans la liste active + var existingActive = await db.Items.FirstOrDefaultAsync(i => + i.Status == ShoppingItemStatus.Active && + i.Name.ToLower() == normalizedName.ToLower()); + + if (existingActive != null) + { + existingActive.Quantity += newItemQuantity; + } + else + { + db.Items.Add(new ShoppingItem + { + Name = normalizedName, + Quantity = newItemQuantity, + Status = ShoppingItemStatus.Active, + LastPurchasedAt = DateTime.Now + }); + } + + await db.SaveChangesAsync(); + + newItemName = string.Empty; + newItemQuantity = 1; + await LoadAllData(); + } + + private async Task MoveToCart(ShoppingItem item) + { + using var db = await DbFactory.CreateDbContextAsync(); + var entity = await db.Items.FindAsync(item.Id); + if (entity != null) + { + entity.Status = ShoppingItemStatus.InCart; + await db.SaveChangesAsync(); + } + await LoadAllData(); + } + + private async Task ReturnToList(ShoppingItem item) + { + using var db = await DbFactory.CreateDbContextAsync(); + var entity = await db.Items.FindAsync(item.Id); + if (entity != null) + { + entity.Status = ShoppingItemStatus.Active; + await db.SaveChangesAsync(); + } + await LoadAllData(); + } + + private async Task RemoveActiveItem(ShoppingItem item) + { + using var db = await DbFactory.CreateDbContextAsync(); + var entity = await db.Items.FindAsync(item.Id); + if (entity != null) + { + // Passe en statut Purchased pour rester consultable dans l'historique + entity.Status = ShoppingItemStatus.Purchased; + entity.LastPurchasedAt = DateTime.Now; + await db.SaveChangesAsync(); + } + await LoadAllData(); + } + + private async Task ClearCartAndArchive() + { + using var db = await DbFactory.CreateDbContextAsync(); + var inCart = await db.Items.Where(i => i.Status == ShoppingItemStatus.InCart).ToListAsync(); + + foreach (var item in inCart) + { + item.Status = ShoppingItemStatus.Purchased; + item.LastPurchasedAt = DateTime.Now; + } + + await db.SaveChangesAsync(); + await LoadAllData(); + } + + private async Task ReAddFromHistory(ShoppingItem item) + { + using var db = await DbFactory.CreateDbContextAsync(); + var existingActive = await db.Items.FirstOrDefaultAsync(i => + i.Status == ShoppingItemStatus.Active && + i.Name.ToLower() == item.Name.ToLower()); + + if (existingActive != null) + { + existingActive.Quantity += 1; + } + else + { + db.Items.Add(new ShoppingItem + { + Name = item.Name, + Quantity = 1, + Status = ShoppingItemStatus.Active, + LastPurchasedAt = DateTime.Now + }); + } + + await db.SaveChangesAsync(); + await LoadAllData(); + } + + private async Task DeleteFromHistory(int id) + { + using var db = await DbFactory.CreateDbContextAsync(); + var entity = await db.Items.FindAsync(id); + if (entity != null) + { + db.Items.Remove(entity); + await db.SaveChangesAsync(); + } + await LoadAllData(); + } + + private async Task IncreaseQuantity(ShoppingItem item) + { + using var db = await DbFactory.CreateDbContextAsync(); + var entity = await db.Items.FindAsync(item.Id); + if (entity != null) + { + entity.Quantity++; + await db.SaveChangesAsync(); + } + await LoadAllData(); + } + + private async Task DecreaseQuantity(ShoppingItem item) + { + using var db = await DbFactory.CreateDbContextAsync(); + var entity = await db.Items.FindAsync(item.Id); + if (entity != null) + { + if (entity.Quantity > 1) + { + entity.Quantity--; + await db.SaveChangesAsync(); + } + else + { + // Si la quantité passe en dessous de 1, l'article est retiré de la liste active vers l'historique + await RemoveActiveItem(item); + return; + } + } + await LoadAllData(); + } +} \ No newline at end of file diff --git a/HA_Shopping_List.Server/Components/Routes.razor b/HA_Shopping_List.Server/Components/Routes.razor new file mode 100644 index 0000000..f756e19 --- /dev/null +++ b/HA_Shopping_List.Server/Components/Routes.razor @@ -0,0 +1,6 @@ + + + + + + diff --git a/HA_Shopping_List.Server/Components/_Imports.razor b/HA_Shopping_List.Server/Components/_Imports.razor new file mode 100644 index 0000000..f2b8813 --- /dev/null +++ b/HA_Shopping_List.Server/Components/_Imports.razor @@ -0,0 +1,10 @@ +@using System.Net.Http +@using System.Net.Http.Json +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using static Microsoft.AspNetCore.Components.Web.RenderMode +@using Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.JSInterop +@using HA_Shopping_List.Server +@using HA_Shopping_List.Server.Components diff --git a/HA_Shopping_List.Server/Data/AppDbContext.cs b/HA_Shopping_List.Server/Data/AppDbContext.cs new file mode 100644 index 0000000..93bc60d --- /dev/null +++ b/HA_Shopping_List.Server/Data/AppDbContext.cs @@ -0,0 +1,36 @@ +using Microsoft.EntityFrameworkCore; +using System.Reflection.Emit; + +namespace HA_Shopping_List.Server.Data +{ + /// + /// Classe AppDbContext. + /// Implémente le + /// + /// + public class AppDbContext : DbContext + { + /// + /// Initialise une nouvelle instance de la classe . + /// + /// L'options. + public AppDbContext(DbContextOptions options) : base(options) { } + + /// + /// Obtient le items. + /// + /// La valeur "items" + public DbSet Items => Set(); + + /// + /// Appelé lorsque [model creating]. + /// + /// Le model builder. + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + modelBuilder.Entity() + .HasIndex(i => i.Name); + } + } +} diff --git a/HA_Shopping_List.Server/Data/ShoppingItem.cs b/HA_Shopping_List.Server/Data/ShoppingItem.cs new file mode 100644 index 0000000..fa46a9e --- /dev/null +++ b/HA_Shopping_List.Server/Data/ShoppingItem.cs @@ -0,0 +1,46 @@ +using HA_Shopping_List.Server.Enum; + +namespace HA_Shopping_List.Server.Data +{ + /// + /// Classe ShoppingItem. + /// + public class ShoppingItem + { + /// + /// Obtient ou définit le identifier. + /// + /// La valeur "identifier" + public int Id { get; set; } + + /// + /// Obtient ou définit le name. + /// + /// La valeur "name" + public string Name { get; set; } = string.Empty; + + /// + /// Obtient ou définit le quantity. + /// + /// La valeur "quantity" + public int Quantity { get; set; } = 1; + + /// + /// Obtient ou définit le unit. + /// + /// La valeur "unit" + public string? Unit { get; set; } // Ex: kg, pack, bouteille, ou null + + /// + /// Obtient ou définit le status. + /// + /// La valeur "status" + public ShoppingItemStatus Status { get; set; } = ShoppingItemStatus.Active; + + /// + /// Obtient ou définit le last purchased at. + /// + /// La valeur "last purchased at" + public DateTime LastPurchasedAt { get; set; } = DateTime.Now; + } +} diff --git a/HA_Shopping_List.Server/Dockerfile b/HA_Shopping_List.Server/Dockerfile new file mode 100644 index 0000000..4c39c1f --- /dev/null +++ b/HA_Shopping_List.Server/Dockerfile @@ -0,0 +1,23 @@ +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +WORKDIR /app +RUN mkdir -p /data && chmod 777 /data +EXPOSE 8099 +ENV ASPNETCORE_URLS=http://+:8099 + +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +ARG BUILD_CONFIGURATION=Release +WORKDIR /src +COPY ["HA_Shopping_List.Server/HA_Shopping_List.Server.csproj", "HA_Shopping_List.Server/"] +RUN dotnet restore "./HA_Shopping_List.Server/HA_Shopping_List.Server.csproj" +COPY . . +WORKDIR "/src/HA_Shopping_List.Server" +RUN dotnet build "./HA_Shopping_List.Server.csproj" -c $BUILD_CONFIGURATION -o /app/build + +FROM build AS publish +ARG BUILD_CONFIGURATION=Release +RUN dotnet publish "./HA_Shopping_List.Server.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "HA_Shopping_List.Server.dll"] \ No newline at end of file diff --git a/HA_Shopping_List.Server/Enum/ShoppingItemStatus.cs b/HA_Shopping_List.Server/Enum/ShoppingItemStatus.cs new file mode 100644 index 0000000..722e24b --- /dev/null +++ b/HA_Shopping_List.Server/Enum/ShoppingItemStatus.cs @@ -0,0 +1,23 @@ +namespace HA_Shopping_List.Server.Enum +{ + /// + /// Énumération ShoppingItemStatus + /// + public enum ShoppingItemStatus + { + /// + /// Le Active. + /// + Active = 0, // Dans la liste de courses + + /// + /// Le In cart. + /// + InCart = 1, // Dans le chariot + + /// + /// Le Purchased. + /// + Purchased = 2 // Acheté / uniquement dans l'historique + } +} diff --git a/HA_Shopping_List.Server/HA_Shopping_List.Server.csproj b/HA_Shopping_List.Server/HA_Shopping_List.Server.csproj new file mode 100644 index 0000000..3bfdfe3 --- /dev/null +++ b/HA_Shopping_List.Server/HA_Shopping_List.Server.csproj @@ -0,0 +1,19 @@ + + + + net8.0 + enable + enable + Linux + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + diff --git a/HA_Shopping_List.Server/Program.cs b/HA_Shopping_List.Server/Program.cs new file mode 100644 index 0000000..1a2b354 --- /dev/null +++ b/HA_Shopping_List.Server/Program.cs @@ -0,0 +1,59 @@ +using HA_Shopping_List.Server.Components; +using HA_Shopping_List.Server.Data; +using Microsoft.EntityFrameworkCore; + +var builder = WebApplication.CreateBuilder(args); + +// Blazor Server interactif +builder.Services.AddRazorComponents() + .AddInteractiveServerComponents(options => options.DetailedErrors = true); + +// Persistance dans /data (Home Assistant) ou dans le dossier local en Dev +var dbFolder = Directory.Exists("/data") ? "/data" : AppContext.BaseDirectory; +Directory.CreateDirectory(dbFolder); +var dbPath = Path.Combine(dbFolder, "shopping.db"); + +builder.Services.AddDbContextFactory(options => + options.UseSqlite($"Data Source={dbPath}")); + +var app = builder.Build(); + +// Création automatique de la base SQLite au démarrage +using (var scope = app.Services.CreateScope()) +{ + var factory = scope.ServiceProvider.GetRequiredService>(); + using var db = factory.CreateDbContext(); + db.Database.EnsureCreated(); +} + +#if DEBUG +app.Use(async (context, next) => +{ + context.Response.Headers.Append("Permissions-Policy", "unload=(self)"); + await next(); +}); +#endif + +// Gestion du préfixe dynamique Ingress de Home Assistant +app.Use(async (context, next) => +{ + if (context.Request.Headers.TryGetValue("X-Ingress-Path", out var ingressPath)) + { + context.Request.PathBase = ingressPath.ToString(); + } + await next(); +}); + +if (!app.Environment.IsDevelopment()) +{ + app.UseExceptionHandler("/Error", createScopeForErrors: true); +} + +app.UseStaticFiles(); +app.UseAntiforgery(); + +app.MapRazorComponents() + .AddInteractiveServerRenderMode(); + +// Port Ingress standard +app.Run("http://0.0.0.0:8099"); \ No newline at end of file diff --git a/HA_Shopping_List.Server/Properties/launchSettings.json b/HA_Shopping_List.Server/Properties/launchSettings.json new file mode 100644 index 0000000..b279b86 --- /dev/null +++ b/HA_Shopping_List.Server/Properties/launchSettings.json @@ -0,0 +1,39 @@ +{ + "profiles": { + "http": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:8099" + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Container (Dockerfile)": { + "commandName": "Docker", + "launchBrowser": true, + "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", + "environmentVariables": { + "ASPNETCORE_HTTP_PORTS": "8099" + }, + "publishAllPorts": true, + "useSSL": false + } + }, + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:33927", + "sslPort": 0 + } + } +} \ No newline at end of file diff --git a/HA_Shopping_List.Server/appsettings.Development.json b/HA_Shopping_List.Server/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/HA_Shopping_List.Server/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/HA_Shopping_List.Server/appsettings.json b/HA_Shopping_List.Server/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/HA_Shopping_List.Server/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/HA_Shopping_List.Server/wwwroot/app.css b/HA_Shopping_List.Server/wwwroot/app.css new file mode 100644 index 0000000..e398853 --- /dev/null +++ b/HA_Shopping_List.Server/wwwroot/app.css @@ -0,0 +1,29 @@ +h1:focus { + outline: none; +} + +.valid.modified:not([type=checkbox]) { + outline: 1px solid #26b050; +} + +.invalid { + outline: 1px solid #e50000; +} + +.validation-message { + color: #e50000; +} + +.blazor-error-boundary { + background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; + padding: 1rem 1rem 1rem 3.7rem; + color: white; +} + + .blazor-error-boundary::after { + content: "An error has occurred." + } + +.darker-border-checkbox.form-check-input { + border-color: #929292; +} diff --git a/HA_Shopping_List.Server/wwwroot/favicon.ico b/HA_Shopping_List.Server/wwwroot/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..70167247969e75467c95a7fe26d4a6e0d8d8ae41 GIT binary patch literal 270398 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 literal 0 HcmV?d00001 diff --git a/HA_Shopping_List.slnx b/HA_Shopping_List.slnx new file mode 100644 index 0000000..8f6c5ab --- /dev/null +++ b/HA_Shopping_List.slnx @@ -0,0 +1,7 @@ + + + + + + + diff --git a/docker_launch.ps1 b/docker_launch.ps1 new file mode 100644 index 0000000..4b653fb --- /dev/null +++ b/docker_launch.ps1 @@ -0,0 +1,44 @@ +function Start-DockerDesktop { + [CmdletBinding()] + param( + [Parameter()] + [string]$Path = "$env:ProgramFiles\Docker\Docker\Docker Desktop.exe", + + [Parameter()] + [int]$TimeoutSeconds = 120 + ) + + if (-not (Test-Path $Path)) { + Write-Error "Docker Desktop est introuvable au chemin : $Path" + return $false + } + + $dockerProcess = Get-Process -Name "Docker Desktop" -ErrorAction SilentlyContinue + if ($null -eq $dockerProcess) { + Write-Host "Démarrage de Docker Desktop..." -ForegroundColor Cyan + Start-Process -FilePath $Path + } else { + Write-Host "Le processus Docker Desktop est déjà en cours." -ForegroundColor Yellow + } + + Write-Host "En attente du moteur Docker..." -NoNewline + $elapsed = 0 + $interval = 2 + + while ($elapsed -lt $TimeoutSeconds) { + cmd.exe /c "docker info >nul 2>&1" + if ($LASTEXITCODE -eq 0) { + Write-Host " Prêt !" -ForegroundColor Green + docker --version + return $true + } + + Start-Sleep -Seconds $interval + Write-Host "." -NoNewline + $elapsed += $interval + } + + Write-Host "" + Write-Warning "Le moteur Docker n'a pas répondu dans le délai imparti ($TimeoutSeconds s)." + return $false +} \ No newline at end of file diff --git a/publish.ps1 b/publish.ps1 new file mode 100644 index 0000000..e14ea46 --- /dev/null +++ b/publish.ps1 @@ -0,0 +1,39 @@ +# S'assurer que le script s'arrête immédiatement en cas d'erreur +$ErrorActionPreference = "Stop" + +. "$PSScriptRoot\docker_launch.ps1" + +Write-Host "=== 1/5. Demarage de docker ===" -ForegroundColor Cyan +Start-DockerDesktop + +$RegistryUrl = "gitea.mragot1408.synology.me" +$User = "admin_gitea" +$Pass = "Z7Q2kKELozwno0Eve8b4FYnlCw" +$ImageName = "hashoppinglist" +$VersionNumber = "1.0.0" +$Version = "latest" + + +Write-Host "`n=== 2/5. Nettoyage et Compilation de la Solution (Release | Any CPU) ===" -ForegroundColor Cyan +dotnet restore +docker login https://$RegistryUrl -u $User -p $Pass + +Write-Host "`n=== 3/5. Build de Blazor / Docker ===" -ForegroundColor Cyan +dotnet build ./HA_Shopping_List.Server/HA_Shopping_List.Server.csproj --configuration Release -p:Platform="Any CPU" + +# Format obligatoire pour Gitea : Domaine/Utilisateur/NomImage (en minuscules) +$FullImage = "gitea.mragot1408.synology.me/$($User.ToLower())/$($ImageName.ToLower())" + +Write-Host "`n=== 4/5. Publication du Blazor / Docker ===" -ForegroundColor Cyan +docker build --no-cache ` + -t "${FullImage}:${VersionNumber}" ` + -t "${FullImage}:${Version}" ` + -f ./HA_Shopping_List.Server/Dockerfile . + +Write-Host "`n=== 5/5. Push du Blazor ===" -ForegroundColor Cyan +docker push "${FullImage}:${VersionNumber}" +docker push "${FullImage}:${Version}" + +Write-Host "`n==========================================" -ForegroundColor Green +Write-Host " Terminé avec succès !" -ForegroundColor Green +Write-Host "==========================================" -ForegroundColor Green \ No newline at end of file