pitié marche
continuous-integration/drone/push Build is failing Details

master
Siwa12100 1 month ago
commit bb722a9be2

@ -0,0 +1,12 @@
kind: pipeline
type: docker
name: default
steps:
- name: deploy-container
image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest
environment:
IMAGENAME: registry.devalada.valorium-mc.fr/portfolio-siwa:latest
CONTAINERNAME: portfolio
COMMAND: create
OVERWRITE: true

@ -0,0 +1,111 @@
kind: pipeline
type: docker
name: build-and-deploy-portfolio-siwa
trigger:
branch:
- main
event:
- push
steps:
- name: "🔍 vérification"
image: mcr.microsoft.com/dotnet/sdk:9.0
commands:
- echo "🔍 [INFO] Vérification du projet .NET..."
- dotnet restore portfolio_siwa/portfolio_siwa.csproj || {
echo "❌ [ERROR] Restauration des dépendances échouée.";
exit 1;
}
- dotnet build portfolio_siwa/portfolio_siwa.csproj -c Release || {
echo "❌ [ERROR] Build échoué.";
exit 1;
}
- echo "✅ [SUCCESS] Vérification réussie."
- name: "📦 tests"
depends_on:
- "🔍 vérification"
image: mcr.microsoft.com/dotnet/sdk:9.0
volumes:
- name: shared-volume
path: /shared
commands:
- echo "🧪 [INFO] Lancement des tests unitaires..."
- mkdir -p /shared/test-results
- dotnet test portfolio_siwa.sln --configuration Release /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:CoverletOutput=/shared/test-results/coverage.opencover.xml --logger:"trx" || { echo "❌ [ERROR] Tests échoués !"; exit 1; }
- echo "📂 [INFO] Fichiers de couverture générés :"
- find /shared/test-results -type f
- name: "🔎 sonar-analyse"
image: mcr.microsoft.com/dotnet/sdk:9.0
depends_on:
- "📦 tests"
environment:
SONAR_HOST_URL: https://sonar.davalada.valorium-mc.fr
SONAR_TOKEN:
from_secret: SONAR_TOKEN
volumes:
- name: shared-volume
path: /shared
commands:
- apt-get update
- apt-get install -y openjdk-17-jre
- export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
- export PATH=$JAVA_HOME/bin:$PATH
- dotnet tool install --global dotnet-sonarscanner
- export PATH="$PATH:/root/.dotnet/tools:$PATH"
- echo "🚀 [INFO] Initialisation Sonar..."
- dotnet-sonarscanner begin /k:portfolio-siwa /d:sonar.host.url=$SONAR_HOST_URL /d:sonar.login=$SONAR_TOKEN /d:sonar.cs.opencover.reportsPaths=/shared/test-results/coverage.opencover.xml
- echo "🧱 [INFO] Build de la solution..."
- dotnet build portfolio_siwa.sln --no-incremental
- echo "✅ [INFO] Fin de lanalyse Sonar..."
- dotnet-sonarscanner end /d:sonar.login=$SONAR_TOKEN
- name: "🐳 build-and-push"
depends_on:
- "📦 tests"
image: plugins/docker
settings:
registry: registry.devalada.valorium-mc.fr
repo: registry.devalada.valorium-mc.fr/portfolio-siwa
username:
from_secret: DOCKER_USERNAME
password:
from_secret: DOCKER_PASSWORD
no_cache: true
tags:
- latest
dockerfile: Dockerfile
- name: "🚀 deploy-vps"
image: appleboy/drone-ssh
depends_on:
- "🐳 build-and-push"
settings:
host: ecirada.valorium-mc.fr
port: 22
username:
from_secret: SSH_USER
ssh_key:
from_secret: SSH_PRIVATE_KEY
script:
- echo "🚀 [INFO] Déploiement de portfolio_siwa en cours..."
- cd deploiements/portfolio-siwa/
- echo "🛑 [INFO] Arrêt de l'ancien conteneur..."
- docker compose down
- echo "📥 [INFO] Pull de la dernière image depuis le registry..."
- docker compose pull
- echo "🚀 [INFO] Démarrage du nouveau conteneur..."
- docker compose up -d
- sleep 5
- docker ps | grep portfolio_siwa || {
echo "❌ [ERROR] Le service ne tourne pas !";
exit 1;
}
- echo "✅ [SUCCESS] Déploiement terminé avec succès."
volumes:
- name: shared-volume
temp: {}

273
.gitignore vendored

@ -0,0 +1,273 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
bin/
obj/
.env
.vscode/
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
project.fragment.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
#*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
*.dll
*.gz
*.scp.css
*.bundle.*

@ -0,0 +1,56 @@
# Étape de base pour exécuter l'application avec .NET 9
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
WORKDIR /app
EXPOSE 8080
# Créer un utilisateur non-root pour l'étape finale
RUN useradd -m appuser
# Étape de construction avec .NET 9 SDK
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
# Copier uniquement les fichiers nécessaires au restore
COPY portfolio_siwa/portfolio_siwa.csproj ./portfolio_siwa/
# Désactiver le cache Docker si la structure change
RUN dotnet restore portfolio_siwa/portfolio_siwa.csproj
# Copier uniquement les fichiers utiles à la compilation (et ignorer les fichiers sensibles)
COPY portfolio_siwa/ ./portfolio_siwa/
COPY tests/ ./tests/
COPY portfolio_siwa.sln ./
WORKDIR /src/portfolio_siwa
# Nettoyer les dossiers avant build pour éviter les problèmes de compression
RUN dotnet clean && rm -rf bin obj
# Construire l'application
RUN dotnet build "portfolio_siwa.csproj" -c $BUILD_CONFIGURATION -o /app/build
# Étape de publication
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
# Nettoyage avant publication
WORKDIR /src/portfolio_siwa
RUN dotnet clean && rm -rf bin obj
# Empêcher la compression des assets Blazor (si bug compression)
RUN dotnet publish "portfolio_siwa.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
# Étape finale : image d'exécution sécurisée
FROM base AS final
WORKDIR /app
# Copier le résultat publié
COPY --from=publish /app/publish .
# Changer les droits et utiliser l'utilisateur non-root
RUN chown -R appuser:appuser /app
USER appuser
# Entrée de l'application
ENTRYPOINT ["dotnet", "portfolio_siwa.dll"]

@ -0,0 +1,7 @@
# Portfolio
Code de mon portfolio, réalisé en .NET 9 Blazor server.
## A faire
* L'analyse sonar fonctionne mais ne prend pas en compte le coverage ---> A corriger...

@ -0,0 +1,15 @@
#!/bin/bash
source ./config.sh
EXISTING_CONTAINER=$(docker ps -a -q --filter name=$CONTAINER_NAME)
if [ ! -z "$EXISTING_CONTAINER" ]; then
echo "Arrêt du conteneur $CONTAINER_NAME..."
docker stop $CONTAINER_NAME > /dev/null 2>&1
echo "Suppression du conteneur $CONTAINER_NAME..."
docker rm $CONTAINER_NAME > /dev/null 2>&1
else
echo "Aucun conteneur trouvé avec le nom $CONTAINER_NAME."
fi

@ -0,0 +1,10 @@
#!/bin/bash
# Configuration du projet
IMAGE_NAME="portfolio-siwa"
CONTAINER_NAME="portfolio-siwa"
PORT="21018"
export IMAGE_NAME
export CONTAINER_NAME
export PORT

@ -0,0 +1,23 @@
version: '3.8'
services:
site_alliance_elendil:
build:
context: .
dockerfile: Dockerfile
ports:
- "38011:8080"
environment:
ASPNETCORE_ENVIRONMENT: Production
VIRTUAL_HOST: jean-marcillac.dev
VIRTUAL_PORT: 8080
LETSENCRYPT_HOST: jean-marcillac.dev
LETSENCRYPT_EMAIL: jean.marcillac12@gmail.com
container_name: portfolio-siwa
restart: unless-stopped
networks:
- nginx-proxy-network
networks:
nginx-proxy-network:
external: true

@ -0,0 +1,40 @@
#!/bin/bash
# === 🎯 USAGE ===
# ./lance-dev.sh <port> [-w]
# Exemple : ./lance-dev.sh 50001 -w
# === ✅ Vérification des arguments ===
if [ -z "$1" ]; then
echo "❌ Utilisation : ./lance-dev.sh <port> [-w]"
echo " Exemple : ./lance-dev.sh 50001 -w"
exit 1
fi
PORT=$1
USE_WATCH=false
if [ "$2" == "-w" ]; then
USE_WATCH=true
fi
# === 📦 Chargement des variables depuis le fichier .env ===
if [ -f .env ]; then
echo "📦 Chargement des variables depuis .env..."
export $(grep -v '^#' .env | xargs)
else
echo "⚠️ Fichier .env non trouvé. Certaines variables peuvent manquer."
fi
# === 🖨 Affichage du port utilisé ===
echo "🚀 Port utilisé = $PORT"
# === 🔥 Lancement de l'application ===
if [ "$USE_WATCH" = true ]; then
echo "👀 Mode surveillance activé avec dotnet watch..."
dotnet watch --project portfolio_siwa run --urls "http://0.0.0.0:$PORT"
else
echo "🏃 Lancement classique avec dotnet run..."
dotnet run --project portfolio_siwa --urls "http://0.0.0.0:$PORT"
fi

@ -0,0 +1,25 @@
#!/bin/bash
source ./config.sh
EXISTING_CONTAINER=$(docker ps -a -q --filter name=$CONTAINER_NAME)
if [ ! -z "$EXISTING_CONTAINER" ]; then
echo "Arrêt et suppression du conteneur existant..."
docker stop $CONTAINER_NAME > /dev/null 2>&1
docker rm $CONTAINER_NAME > /dev/null 2>&1
fi
EXISTING_IMAGE=$(docker images -q $IMAGE_NAME)
if [ -z "$EXISTING_IMAGE" ]; then
echo "Construction de l'image Docker..."
docker build -t $IMAGE_NAME .
else
echo "L'image $IMAGE_NAME existe déjà."
fi
echo "Lancement du conteneur $CONTAINER_NAME sur le port $PORT..."
docker run -d -p $PORT:8080 --name $CONTAINER_NAME $IMAGE_NAME
echo "Le projet est lancé et disponible sur le port $PORT."

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.002.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "portfolio_siwa", "portfolio_siwa\portfolio_siwa.csproj", "{D7CA2422-E100-4706-ACA5-C35F9C22E848}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tests", "tests\tests.csproj", "{11B882A2-09CB-413E-A5EA-BE108B3C50B6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D7CA2422-E100-4706-ACA5-C35F9C22E848}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D7CA2422-E100-4706-ACA5-C35F9C22E848}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D7CA2422-E100-4706-ACA5-C35F9C22E848}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D7CA2422-E100-4706-ACA5-C35F9C22E848}.Release|Any CPU.Build.0 = Release|Any CPU
{11B882A2-09CB-413E-A5EA-BE108B3C50B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{11B882A2-09CB-413E-A5EA-BE108B3C50B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{11B882A2-09CB-413E-A5EA-BE108B3C50B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{11B882A2-09CB-413E-A5EA-BE108B3C50B6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {69BFEBD8-81CA-41AF-8008-8788B13EA2F9}
EndGlobalSection
EndGlobal

@ -0,0 +1,28 @@
@namespace portfolio_siwa
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<link rel="stylesheet" href="@Assets["lib/bootstrap/dist/css/bootstrap.min.css"]" />
<link href="https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,100;0,300;0,400;0,700;0,900;1,100;1,300;1,400;1,700;1,900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="@Assets["css/app.css"]" />
<link rel="stylesheet" href="@Assets["portfolio_siwa.styles.css"]" />
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet" />
<link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
<ImportMap />
<link rel="icon" type="image/gif" href="favicon.gif" />
<HeadOutlet />
</head>
<body>
<Routes @rendermode="RenderMode.InteractiveServer"/>
<script src="_framework/blazor.web.js"></script>
<script src="_content/MudBlazor/MudBlazor.min.js"></script>
<script src="clipboard.js"></script>
</body>
</html>

@ -0,0 +1,24 @@
@namespace portfolio_siwa.Composants.FenetreProjet
<MudDialog FullScreen="true" MaxWidth="MaxWidth.Large" Class="dialog-style">
<MudDialogTitle>@DetailsProjet?.Titre</MudDialogTitle>
<MudDialogContent>
<MudCard Class="image-container">
<MudCardMedia Image="@DetailsProjet?.CheminImage" Class="image-style" />
</MudCard>
<MudCard Class="text-container">
<MudCardContent>
@if (DetailsProjet?.Texte != null)
{
@((MarkupString)DetailsProjet.Texte.ToString())
}
</MudCardContent>
</MudCard>
</MudDialogContent>
<MudDialogActions>
<MudButton OnClick="FermerFenetre" Color="Color.Primary" Variant="Variant.Filled">Fermer</MudButton>
</MudDialogActions>
</MudDialog>

@ -0,0 +1,23 @@
using Microsoft.AspNetCore.Components;
using MudBlazor;
using portfolio_siwa.Modeles;
namespace portfolio_siwa.Composants.FenetreProjet
{
public partial class FenetreProjet
{
[CascadingParameter]
IMudDialogInstance? MudDialog { get; set; }
[Parameter]
public DetailsProjet? DetailsProjet { get; set; }
protected void FermerFenetre()
{
if (MudDialog != null)
{
MudDialog.Close();
}
}
}
}

@ -0,0 +1,84 @@
@using Microsoft.JSInterop
@inject IJSRuntime JSRuntime
@inject NavigationManager NavigationManager
@implements IDisposable
<script>
function BlazorScrollToId(id) {
const element = document.getElementById(id);
if (element instanceof HTMLElement) {
element.scrollIntoView({
behavior: "smooth",
block: "start",
inline: "nearest"
});
}
}
</script>
@code {
protected override void OnInitialized()
{
NavigationManager.LocationChanged += OnLocationChanged;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await ScrollToFragment();
}
public void Dispose()
{
NavigationManager.LocationChanged -= OnLocationChanged;
}
private async void OnLocationChanged(object? sender, LocationChangedEventArgs e)
{
await ScrollToFragment();
}
@* private async Task ScrollToFragment()
{
var uri = new Uri(NavigationManager.Uri, UriKind.Absolute);
var fragment = uri.Fragment;
if (fragment.StartsWith('#'))
{
// Handle text fragment (https://example.org/#test:~:text=foo)
// https://github.com/WICG/scroll-to-text-fragment/
var elementId = fragment.Substring(1);
var index = elementId.IndexOf(":~:", StringComparison.Ordinal);
if (index > 0)
{
elementId = elementId.Substring(0, index);
}
if (!string.IsNullOrEmpty(elementId))
{
await JSRuntime.InvokeVoidAsync("BlazorScrollToId", elementId);
}
}
} *@
private async Task ScrollToFragment()
{
var uri = new Uri(NavigationManager.Uri, UriKind.Absolute);
var fragment = uri.Fragment;
if (fragment.StartsWith('#'))
{
var elementId = fragment.Substring(1); // Enlève le #
var index = elementId.IndexOf(":~:", StringComparison.Ordinal);
if (index > 0)
{
elementId = elementId.Substring(0, index);
}
if (!string.IsNullOrEmpty(elementId))
{
await JSRuntime.InvokeVoidAsync("BlazorScrollToId", elementId); // Passe uniquement 'partie2' et non '#partie2'
}
}
}
}

@ -0,0 +1,31 @@
@namespace portfolio_siwa.Composants.Global.BasPage
@using portfolio_siwa.Composants.Global.Boutons.BoutonLien
<div class="contenu">
<h2>👋 À propos de moi</h2>
<p>Originaire de lAveyron, je suis un développeur curieux
qui aime créer des projets concrets, utiles et porteurs
de sens. À travers mes études, mon engagement associatif
et mes expériences professionnelles, jai appris à allier
technique et gestion de projet. <br><br> Au sein du Valorium,
l'association que je préside, jexplore le développement
dunivers virtuels et la gestion dune communauté
en ligne. <br>Intéressé par de nombreux domaines, comme lhistoire
et la culture, jaime voir comment le numérique
peut servir à transmettre et valoriser ces sujets.
Mon travail à lInstitut occitan de lAveyron ma permis
dexpérimenter cette approche en développant
des outils pour mettre en avant la culture
occitane et soutenir sa transmission auprès des jeunes
générations. <br><br>Je cherche à travailler sur des
projets où la technologie répond à des enjeux
concrets, en apportant des solutions adaptées
aux besoins daujourdhui et de demain.</p>
<div class="reseaux">
<BoutonLien Icone="/Images/logos/logoDiscord.svg" Texte="sioa" ModeCopierColler="true" TypeLien="1"/>
<BoutonLien Icone="/Images/logos/logoMail.svg" Texte="jean.marcillac12@gmail.com" TypeLien="2" ModeCopierColler="true"/>
<BoutonLien Icone="/Images/logos/logoGithub.svg" Lien="https://github.com/Siwa12100" />
</div>
</div>

@ -0,0 +1,7 @@
namespace portfolio_siwa.Composants.Global.BasPage
{
public partial class BasPage
{
}
}

@ -0,0 +1,42 @@
.contenu {
height: auto;
width: 93%;
/* background: linear-gradient(150deg, #2b2e4a, #3e345e, #57386e, #723b78, #903c7a, #af3d73, #cd3f63, #e84545); */
background: var(--gradient-violet-orange);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.5);
border-radius: 30px;
margin-bottom: 2rem;
margin-top: 2rem;
padding: 2rem;
}
h2 {
font-family: var(--police-grand-titre);
font-size: var(--h2-taille-texte);
font-weight: var(--poids-police-grand-titre);
font-style: var(--font-style-grand-titre);
color: var(--couleur-texte);
text-align: center;
margin-top: 1.5rem;
}
p {
font-family: var(--police-texte);
font-size: var(--taille-texte);
font-weight: var(--poids-police-texte);
font-style: var(--font-style-texte);
color: var(--couleur-texte);
text-align: center;
margin-top: 1.5rem;
}
.reseaux {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 1.5rem;
margin-top: 2rem;
}

@ -0,0 +1,37 @@
<div class="contenu">
<div class="contenu-haut">
@if (this.ContenuHaut != null)
{
@this.ContenuHaut
}
</div>
<div class="centre">
<div class="contenu-gauche">
@if (this.ContenuGauche != null)
{
@this.ContenuGauche
}
</div>
<div class="contenu-centre">
@if (this.ContenuCentre != null)
{
@this.ContenuCentre
}
</div>
<div class="contenu-droite">
@if (this.ContenuDroite != null)
{
@this.ContenuDroite
}
</div>
</div>
<div class="contenu-bas">
@if (this.ContenuBas != null)
{
@this.ContenuBas
}
</div>
</div>

@ -0,0 +1,22 @@
using Microsoft.AspNetCore.Components;
namespace portfolio_siwa.Composants.Global.BlocsContenu.Bloc3Contenu
{
public partial class Bloc3Contenu
{
[Parameter]
public RenderFragment? ContenuHaut { get; set; }
[Parameter]
public RenderFragment? ContenuDroite { get; set; }
[Parameter]
public RenderFragment? ContenuCentre { get; set; }
[Parameter]
public RenderFragment? ContenuGauche { get; set; }
[Parameter]
public RenderFragment? ContenuBas { get; set; }
}
}

@ -0,0 +1,61 @@
.contenu {
display: flex;
flex-direction: column;
justify-content: start;
align-items: start;
width: 100%;
}
.centre {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: start;
width: 100%;
}
.contenu-haut {
margin-bottom: 1.3rem;
}
.contenu-droite, .contenu-gauche, .contenu-centre {
height: 100%;
width: 32%;
}
.contenu-haut, .contenu-bas {
width: 100%;
}
.contenu-bas {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
margin-top: 3rem;
}
@media (max-width: 1024px) {
.centre {
flex-direction: column;
align-items: center;
justify-content: start;
}
.contenu-droite, .contenu-gauche, .contenu-centre {
width: 100%;
margin-bottom: 1.7rem;
}
.contenu-droite {
width: 100%;
}
.contenu-bas {
margin-top: 0.6rem;
}
}

@ -0,0 +1,31 @@
<div class="contenu">
<div class="contenu-haut">
@if (this.ContenuHaut != null)
{
@this.ContenuHaut
}
</div>
<div class="centre @(this.CentrageMilieu ? "centrage-milieu" : "centrage-haut-div")">
<div class="contenu-gauche">
@if (this.ContenuGauche != null)
{
@this.ContenuGauche
}
</div>
<div class="contenu-droite">
@if (this.ContenuDroite != null)
{
@this.ContenuDroite
}
</div>
</div>
<div class="contenu-bas">
@if (this.ContenuBas != null)
{
@this.ContenuBas
}
</div>
</div>

@ -0,0 +1,22 @@
using Microsoft.AspNetCore.Components;
namespace portfolio_siwa.Composants.Global.BlocsContenu.BlocContenuGaucheDroite
{
public partial class BlocContenuGaucheDroite
{
[Parameter]
public RenderFragment? ContenuHaut { get; set; }
[Parameter]
public RenderFragment? ContenuDroite { get; set; }
[Parameter]
public RenderFragment? ContenuGauche { get; set; }
[Parameter]
public RenderFragment? ContenuBas { get; set; }
[Parameter]
public bool CentrageMilieu { get; set; } = true;
}
}

@ -0,0 +1,67 @@
.contenu {
display: flex;
flex-direction: column;
justify-content: start;
align-items: start;
width: 100%;
}
.centrage-milieu {
align-items: center;
}
.centrage-haut-div {
align-items: start;
}
.centre {
display: flex;
flex-direction: row;
justify-content: space-between;
width: 100%;
}
.contenu-haut {
margin-bottom: 1.3rem;
}
.contenu-droite, .contenu-gauche {
height: 100%;
width: 50%;
}
.contenu-haut, .contenu-bas {
width: 100%;
}
.contenu-bas {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
margin-top: 3rem;
}
@media (max-width: 1024px) {
.centre {
flex-direction: column;
align-items: center;
justify-content: start;
}
.contenu-droite, .contenu-gauche {
width: 100%;
margin-bottom: 1.7rem;
}
.contenu-droite {
width: 100%;
}
.contenu-bas {
margin-top: 0.6rem;
}
}

@ -0,0 +1,100 @@
@using portfolio_siwa.Composants.Global.BlocsContenu.BlocContenuGaucheDroite
@using portfolio_siwa.Composants.Global.BlocsContenu.Titre1
<div class="contenu">
<BlocContenuGaucheDroite CentrageMilieu="@this.CentrageMilieu">
<ContenuHaut>
@if (this.TexteEnHaut != null)
{
<Titre1
TexteTitre="@this.TexteTitre"
CheminIconeTitre="@this.CheminIconeTitre"
/>
<div class="texte-haut">
<p>@this.TexteEnHaut</p>
</div>
}
</ContenuHaut>
<ContenuGauche>
@if (this.PositionMedia == PositionMedia.Gauche)
{
@if (this.Media != null)
{
<div class="media partie-gauche">
@this.Media
</div>
}
}
else
{
@if (this.Texte != null)
{
@if (this.TexteTitre != null && this.TexteEnHaut == null)
{
<Titre1
TexteTitre="@this.TexteTitre"
CheminIconeTitre="@this.CheminIconeTitre"/>
}
<div class="texte partie-gauche">
<p>@this.Texte</p>
@if (this.SousTexte != null)
{
<div class="sous-texte">@this.SousTexte</div>
}
</div>
}
}
</ContenuGauche>
<ContenuDroite>
@if (this.PositionMedia == PositionMedia.Droite)
{
@if (this.Media != null)
{
<div class="media partie-droite">
@this.Media
</div>
}
}
else
{
@if (this.Media2 != null)
{
<div class="media partie-droite">
@this.Media2
</div>
}
else
{
@if (this.Texte != null)
{
@if (this.TexteTitre != null && this.TexteEnHaut == null)
{
<div class="partie-droite">
<Titre1
TexteTitre="@this.TexteTitre"
CheminIconeTitre="@this.CheminIconeTitre"/>
</div>
}
<div class="texte partie-droite">
<p>@this.Texte</p>
@if (this.SousTexte != null)
{
<div class="sous-texte">@this.SousTexte</div>
}
</div>
}
}
}
</ContenuDroite>
<ContenuBas>
@if (this.BasPage != null)
{
<div class="bas-page">
@this.BasPage
</div>
}
</ContenuBas>
</BlocContenuGaucheDroite>
</div>

@ -0,0 +1,37 @@
using Microsoft.AspNetCore.Components;
namespace portfolio_siwa.Composants.Global.BlocsContenu.BlocTexteMedia
{
public partial class BlocTexteMedia
{
[Parameter]
public string? TexteTitre { get; set; }
[Parameter]
public string? CheminIconeTitre { get; set; }
[Parameter]
public MarkupString? Texte { get; set; }
[Parameter]
public RenderFragment? Media { get; set; }
[Parameter]
public RenderFragment? Media2 { get; set; }
[Parameter]
public RenderFragment? BasPage { get; set; }
[Parameter]
public PositionMedia PositionMedia { get; set; } = PositionMedia.Droite;
[Parameter]
public MarkupString? TexteEnHaut { get; set; }
[Parameter]
public RenderFragment? SousTexte { get; set; }
[Parameter]
public bool CentrageMilieu { get; set; } = true;
}
}

@ -0,0 +1,72 @@
.contenu {
width: 100%;
}
.texte p, .texte-haut p {
word-wrap: break-word;
font-family: var(--police-texte);
font-size: var(--taille-texte);
margin: 0;
padding: 0;
}
.texte {
padding-left : 1rem;
padding-right : 1rem;
}
.sous-texte {
margin-top: 3rem;
width: 100%;
display: flex;
flex-direction: row;
justify-content: start;
align-items: center;
}
.partie-gauche, .partie-droite {
width: 95%;
}
.partie-droite {
margin-left: 2%;
}
.bas-page {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
@media (max-width: 1024px) {
.partie-droite, .partie-gauche {
width: 100%;
}
.partie-droite {
margin-left: 0%;
}
.media {
margin-top: 1rem;
margin-bottom: 1.5rem;
}
.sous-texte {
align-items: center;
justify-content: center;
margin-top: 1.2rem;
}
}
.projet a {
color : red;
border: 3px solid red;
text-decoration: none;
}

@ -0,0 +1,8 @@
namespace portfolio_siwa.Composants.Global.BlocsContenu.BlocTexteMedia
{
public enum PositionMedia
{
Gauche,
Droite
}
}

@ -0,0 +1,12 @@
@namespace portfolio_siwa.Composants.Global.BlocsContenu.Titre1
<div class="icone-et-titre">
@if (this.CheminIconeTitre != null)
{
<img src="@this.CheminIconeTitre" alt="@this.TexteTitre" />
}
<div class="separation-et-titre">
<h3>@this.TexteTitre</h3>
<div class="separateur"></div>
</div>
</div>

@ -0,0 +1,13 @@
using Microsoft.AspNetCore.Components;
namespace portfolio_siwa.Composants.Global.BlocsContenu.Titre1
{
public partial class Titre1
{
[Parameter]
public string? TexteTitre { get; set; }
[Parameter]
public string? CheminIconeTitre { get; set; }
}
}

@ -0,0 +1,61 @@
.icone-et-titre {
display: flex;
flex-direction: row;
align-items: center;
justify-content: start;
height: 6rem;
}
.separateur-et-titre {
display: flex;
flex-direction: column;
align-items: start;
justify-content: start;
}
.separateur {
margin-top : 0.8rem;
height: 0.4rem;
width: 9rem;
border-radius: 50px;
background: var(--gradient-violet-rose-peche);
}
.icone-et-titre img {
height: 80%;
width: auto;
margin-right: 1.3rem;
}
.texte p, .texte-haut p {
word-wrap: break-word;
font-family: var(--police-texte);
font-size: var(--taille-texte);
}
h3 {
font-size: var(--h3-taille-texte);
font-family: var(--police-grand-titre) !important;
font-weight: var(--poids-police-grand-titre) !important;
font-style: var(--font-style-grand-titre) !important;
}
@media (max-width: 1024px) {
.icone-et-titre img {
height: 54%;
width: auto;
margin-right: 1.3rem;
}
.icone-et-titre {
margin-bottom: 0rem;
}
}

@ -0,0 +1,8 @@
@if (this.Texte != null)
{
<button onclick="() => Naviguer()" class="carousel-button">@Texte</button>
}
else
{
<p>Texte du bouton null</p>
}

@ -0,0 +1,23 @@
using Microsoft.AspNetCore.Components;
namespace portfolio_siwa.Composants.Global.Boutons.BoutonBlanc
{
public partial class BoutonBlanc
{
[Parameter]
public string? Texte { get; set; }
[Parameter]
public string? Lien { get; set; }
[Inject]
public NavigationManager? NavigationManager { get; set; }
private void Naviguer()
{
if (NavigationManager != null && !string.IsNullOrWhiteSpace(Lien))
{
NavigationManager.NavigateTo(Lien);
}
}
}
}

@ -0,0 +1,24 @@
.carousel-button {
background-color: white;
color: black;
padding-left: 1.8rem;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
padding-right: 1.8rem;
font-family: var(--police-texte);
font-style: var(--font-style-texte);
font-weight: var(--poids-police-texte);
border: none;
border-radius: 60px;
font-size: 1.2rem;
cursor: pointer;
transition: transform 0.3s ease, background-color 0.3s ease;
height: 3.1rem;
}
.carousel-button:hover {
background-color: rgba(255, 255, 255, 0.9);
color: black;
transform: scale(1.1);
}

@ -0,0 +1,9 @@
@if (this.Texte != null && this.Lien != null && this.CheminIcone != null)
{
<button class="bouton" @onclick="() => Rediriger()">
<img src="@CheminIcone" alt="Bouton Discord">
<p>@Texte</p>
</button>
}

@ -0,0 +1,38 @@
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
namespace portfolio_siwa.Composants.Global.Boutons.BoutonDiscord
{
public partial class BoutonDiscord
{
[Parameter]
public string? Lien { get; set; }
[Parameter]
public string? Texte { get; set; } = "Discord";
[Parameter]
public string? CheminIcone { get; set; } = "/Images/Icones/discordIcone.png";
[Inject]
public NavigationManager? NavigationManager { get; set; }
[Inject]
protected IJSRuntime? JSRuntime { get; set; }
private async Task Rediriger()
{
if (Lien != null && NavigationManager != null)
{
var url = NavigationManager.ToAbsoluteUri(this.Lien).ToString();
if (this.JSRuntime is not null)
{
await this.JSRuntime.InvokeVoidAsync("window.open", url, "_blank");
}
}
else
{
Console.WriteLine("Lien Discord non défini");
}
}
}
}

@ -0,0 +1,59 @@
.bouton {
position: relative;
overflow: hidden;
border-radius: 13px;
font-size: 170%;
font-weight: var(--poids-police-texte);
font-family: var(--police-texte);
font-style: var(--font-style-texte);
color: var(--couleur-texte);
padding-left: 1rem;
padding-right: 1rem;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
}
.bouton img {
width: auto;
object-fit: cover;
height: calc(var(--taille-texte-boutons) * 1.5);
margin-right: 0.7rem;
}
.bouton p {
margin: 0;
font-size: var(--taille-texte-boutons);
font-style: var(--font-style-texte);
font-weight: var(--poids-police-texte);
font-family: var(--police-texte);
}
button {
transition: var(--transition-grossissement);
background: var(--gradient-discord);
}
.bouton::before {
content: '';
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 0;
background-color: white;
opacity: 0.3;
z-index: -1;
border-radius: inherit;
transition: width 0.14s ease-in-out;
}
.bouton:hover::before {
width: 100%;
}
.bouton:hover {
font-weight: 500;
transform: scale(1.04);
}

@ -0,0 +1,15 @@
@if (this.Texte != null)
{
<button class="bouton @(this.FondBlanc ? "style-blanc" : "style-transparent") @(this.EffetHalo ? " halo" : "")" @onclick="() => Rediriger()">
@if (this.Icone != null)
{
<img src="@Icone" alt="@Alt">
}
<p>@Texte</p>
</button>
}
<MudSnackbarProvider />

@ -0,0 +1,109 @@
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using MudBlazor;
using portfolio_siwa.Modeles;
namespace portfolio_siwa.Composants.Global.Boutons.BoutonFondTransparent
{
public partial class BoutonFondTransparent
{
[Parameter]
public string? Texte { get; set; }
[Parameter]
public string? Icone { get; set; }
[Parameter]
public string? CheminRedirection { get; set; }
[Parameter]
public string? Alt { get; set; } = "Bouton";
[Parameter]
public bool OuvreNouvelOnglet { get; set; } = false;
[Parameter]
public bool ModeCopierPressePapier { get; set; } = false;
[Inject]
public NavigationManager? NavigationManager { get; set; }
[Inject]
public IJSRuntime? JSRuntime { get; set; }
[Parameter]
public bool FondBlanc { get; set; } = false;
[Parameter]
public bool EffetHalo { get; set; } = false;
[Inject]
protected ISnackbar? Snackbar { get; set; }
[Parameter]
public DetailsProjet? DetailsProjet { get; set; }
[Inject]
protected IDialogService? DialogService { get; set; }
private async Task Rediriger()
{
if (this.DetailsProjet != null)
{
await OuvrirDetails();
return;
}
if (ModeCopierPressePapier)
{
await CopierContenuCoordonnees();
return;
}
if (CheminRedirection != null && NavigationManager != null)
{
if (OuvreNouvelOnglet)
{
var url = NavigationManager.ToAbsoluteUri(this.CheminRedirection).ToString();
if (this.JSRuntime is not null)
{
await this.JSRuntime.InvokeVoidAsync("window.open", url, "_blank");
}
return;
}
NavigationManager.NavigateTo(CheminRedirection);
}
}
protected async Task OuvrirDetails()
{
if (this.DialogService == null)
{
Console.WriteLine("DialogService is null");
return;
}
var parameters = new DialogParameters { { "DetailsProjet", this.DetailsProjet }};
var options = new DialogOptions { MaxWidth = MaxWidth.Medium, FullWidth = true, CloseButton = true };
var dialogue = this.DialogService.Show<FenetreProjet.FenetreProjet>("Détails du projet", parameters, options);
var result = await dialogue.Result;
}
protected async Task CopierContenuCoordonnees()
{
if (this.JSRuntime is null) return;
await JSRuntime.InvokeVoidAsync("copyToClipboard", this.Texte);
this.AfficherMessage("Ip copiée dans le presse-papier", Severity.Success);
}
private void AfficherMessage(string message, Severity niveau)
{
Snackbar?.Add(message, niveau);
}
}
}

@ -0,0 +1,82 @@
.bouton {
position: relative;
overflow: hidden;
border-radius: 50px;
width: auto;
font-weight: var(--poids-police-texte);
font-family: var(--police-texte);
font-style: var(--font-style-texte);
padding-left: 1.5rem;
padding-right: 1.5rem;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.6);
}
.bouton p {
margin: 0;
font-size: var(--taille-texte-boutons);
}
.style-blanc {
background-color: white;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.6);
}
.style-blanc p {
color: black;
}
.style-transparent p {
color: white;
}
.halo {
box-shadow: 0 0 10px 2px rgba(255, 255, 255, 0.8);
}
.style-transparent {
background-color: transparent;
border: 4px solid white;
}
.bouton img {
width: auto;
object-fit: cover;
height: calc(var(--taille-texte-boutons) * 1.5);
margin-right: 0.7rem;
}
button {
transition: var(--transition-grossissement);
}
.bouton::before {
content: '';
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 0;
background-color: white;
opacity: 0.3;
z-index: -1;
border-radius: inherit;
transition: width 0.27s ease-in-out;
}
.bouton:hover::before {
width: 100%;
}
.bouton:hover {
font-weight: 500;
transform: scale(1.04);
}
@media (max-width: 1024px) {
}

@ -0,0 +1,6 @@
@namespace portfolio_siwa.Composants.Global.Boutons.BoutonDiscord
<button class="btn" @onclick="OuvrirLien">
<img src="Images/logoGithub.png" alt="Logo Github" />
<p>@Texte</p>
</button>

@ -0,0 +1,42 @@
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
namespace portfolio_siwa.Composants.Global.Boutons.BoutonDiscord
{
public partial class BoutonGithub
{
[Parameter]
public string? Lien { get; set; }
[Parameter]
public string? Texte { get; set; }
[Inject]
public NavigationManager? NavigationManager { get; set; }
[Inject]
public IJSRuntime? JSRuntime { get; set; }
private async Task OuvrirLien()
{
if (this.NavigationManager is null) {
Console.WriteLine("NavigationManager est null");
return;
}
if (this.Lien is null) {
Console.WriteLine("Lien est null");
return;
}
var url = NavigationManager.ToAbsoluteUri(this.Lien).ToString();
if (this.JSRuntime is not null)
{
await this.JSRuntime.InvokeVoidAsync("window.open", url, "_blank");
}
else
{
Console.WriteLine("Impossible d'ouvrir le lien");
}
}
}
}

@ -0,0 +1,46 @@
button {
position: relative;
overflow: hidden;
border-radius: 50px;
width: auto;
font-weight: var(--poids-police-texte);
font-family: var(--police-texte);
font-style: var(--font-style-texte);
padding-left: 1.5rem;
padding-right: 1.5rem;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
background-color: black;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.6);
}
button {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
button p {
margin: 0;
font-size: var(--taille-texte-boutons);
color : white;
}
button img {
width: auto;
object-fit: cover;
height: calc(var(--taille-texte-boutons) * 1.5);
margin-right: 0.7rem;
}
button {
transition: var(--transition-grossissement);
}
button:hover {
font-weight: 500;
transform: scale(1.04);
background-color: black;
}

@ -0,0 +1,9 @@
@if (this.Texte != null && this.CheminRedirection != null)
{
<button class="bouton" @onclick="() => Rediriger()">
<img src="@Icone" alt="@Alt">
<p>@Texte</p>
</button>
}

@ -0,0 +1,30 @@
using Microsoft.AspNetCore.Components;
namespace portfolio_siwa.Composants.Global.Boutons.BoutonGradientViolet
{
public partial class BoutonGradientViolet
{
[Parameter]
public string? Texte { get; set; }
[Parameter]
public string? Icone { get; set; }
[Parameter]
public string? CheminRedirection { get; set; }
[Parameter]
public string? Alt { get; set; } = "Bouton";
[Inject]
public NavigationManager? NavigationManager { get; set; }
private void Rediriger()
{
if (CheminRedirection != null && NavigationManager != null)
{
NavigationManager.NavigateTo(CheminRedirection);
}
}
}
}

@ -0,0 +1,59 @@
.bouton {
position: relative;
overflow: hidden;
border-radius: 50px;
width: auto;
font-weight: var(--poids-police-texte);
font-family: var(--police-texte);
font-style: var(--font-style-texte);
color: var(--couleur-texte);
padding-left: 1.5rem;
padding-right: 1.5rem;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.bouton p {
margin: 0;
font-size: var(--taille-texte-boutons);
}
.bouton img {
width: auto;
object-fit: cover;
height: calc(var(--taille-texte-boutons) * 1.5);
margin-right: 0.7rem;
}
button {
transition: var(--transition-grossissement);
background: var(--gradient-violet-rose-peche);
}
.bouton::before {
content: '';
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 0;
background-color: white;
opacity: 0.3;
z-index: -1;
border-radius: inherit;
transition: width 0.14s ease-in-out;
}
.bouton:hover::before {
width: 100%;
}
.bouton:hover {
font-weight: 500;
transform: scale(1.04);
}
@media (max-width: 1024px) {
}

@ -0,0 +1,7 @@
@namespace portfolio_siwa.Composants.Global.Boutons.BoutonLien
<button @onclick="BoutonClique">
<img src="@Icone" />
</button>
<MudSnackbarProvider />

@ -0,0 +1,91 @@
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using MudBlazor;
namespace portfolio_siwa.Composants.Global.Boutons.BoutonLien
{
public partial class BoutonLien
{
[Parameter]
public string? Lien { get; set; }
[Parameter]
public string? Icone { get; set; }
[Parameter]
public bool? ModeCopierColler { get; set; }
[Inject]
public NavigationManager? NavigationManager { get; set; }
[Inject]
public IJSRuntime? JSRuntime { get; set; }
[Parameter]
public string? Texte { get; set; }
[Inject]
protected ISnackbar? Snackbar { get; set; }
[Parameter]
public int TypeLien { get; set; }
protected async Task BoutonClique()
{
Console.WriteLine("Bouton cliqué");
if (this.ModeCopierColler != null && this.ModeCopierColler == true) {
Console.WriteLine("Copie du contenu");
await this.CopierContenuCoordonnees();
} else {
Console.WriteLine("Redirection vers le lien");
await this.RedirigerVersLien();
}
}
protected async Task CopierContenuCoordonnees()
{
if (this.JSRuntime is null) {
Console.WriteLine("JSRuntime est null");
return;
}
await JSRuntime.InvokeVoidAsync("copyToClipboard", this.Texte);
if (this.TypeLien == 1){
this.AfficherMessage("Identifiant Discord copié !", Severity.Success);
}
if (this.TypeLien == 2){
this.AfficherMessage("Adresse mail copiée !", Severity.Success);
}
}
protected async Task RedirigerVersLien()
{
if (this.NavigationManager is null) {
Console.WriteLine("NavigationManager est null");
return;
}
if (this.Lien is null) {
Console.WriteLine("Lien est null");
return;
}
var url = NavigationManager.ToAbsoluteUri(this.Lien).ToString();
if (this.JSRuntime is not null)
{
await this.JSRuntime.InvokeVoidAsync("window.open", url, "_blank");
}
else
{
Console.WriteLine("Impossible d'ouvrir le lien");
}
}
private void AfficherMessage(string message, Severity niveau)
{
Console.WriteLine("Affichage du msg : " + message);
Snackbar?.Add(message, niveau);
}
}
}

@ -0,0 +1,12 @@
img {
height: 5vh;
width: auto;
transition: transform 0.2s ease;
}
img:hover {
transform: scale(1.1);
transform: rotate(17deg);
}

@ -0,0 +1,10 @@
@namespace portfolio_siwa.Composants.Global.Boutons.BoutonValorium
@if (this.Texte != null)
{
<div class="bouton-style">
<span></span>
<a href="#partie2">@Texte</a>
<span></span>
</div>
}

@ -0,0 +1,13 @@
using Microsoft.AspNetCore.Components;
namespace portfolio_siwa.Composants.Global.Boutons.BoutonValorium
{
public partial class BoutonValorium
{
[Parameter]
public string? Texte { get; set; }
[Parameter]
public string? Lien { get; set; }
}
}

@ -0,0 +1,90 @@
.bouton-style {
width: max-content;
padding: 10px 20px;
font-size: 1.6rem;
font-family: Candara;
font-weight: bold;
color: white;
position: relative;
cursor: pointer;
overflow: hidden;
transition: 1s;
}
a {
color : white;
font-size: var(--taille-texte-boutons);
font-family: var(--police-texte);
font-weight: var(--poids-police-texte);
font-style: var(--font-style-texte);
text-decoration: none;
}
.bouton-style::before,
.bouton-style::after {
content: '';
position: absolute;
background-color: white;
transition: all 250ms ease-in-out;
}
.bouton-style::before {
top: 0;
left: 50%;
width: 100%;
height: 2px;
transform: translateX(-50%);
}
.bouton-style::after {
top: 50%;
left: 0;
width: 2px;
height: 100%;
transform: translateY(-50%);
}
.bouton-style:hover::before {
left: -10px;
width: calc(100% + 20px);
}
.bouton-style:hover::after {
top: -10px;
height: calc(100% + 20px);
}
.bouton-style span:first-child::before {
content: '';
position: absolute;
top: 50%;
right: 0;
height: 100%;
width: 2px;
transform: translateY(-50%);
background-color: white;
transition: all 250ms ease-in-out;
}
.bouton-style span:last-child::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
width: 100%;
height: 2px;
transform: translateX(-50%);
background-color: white;
transition: all 250ms ease-in-out;
}
.bouton-style:hover span:first-child::before {
right: 0;
transform: translateY(0%);
}
.bouton-style:hover span:last-child::after {
bottom: 0;
transform: translateY(0%);
}

@ -0,0 +1,18 @@
<div class="background-container">
<div class="background">
@if (BackgroundContent != null)
{
@BackgroundContent
}
else
{
<p>Le contenu de fond est vide.</p>
}
</div>
<div class="foreground">
@if (ChildContent != null)
{
@ChildContent
}
</div>
</div>

@ -0,0 +1,13 @@
using Microsoft.AspNetCore.Components;
namespace portfolio_siwa.Composants.Global.FondMedia
{
public partial class FondMedia
{
[Parameter]
public RenderFragment? BackgroundContent { get; set; }
[Parameter]
public RenderFragment? ChildContent { get; set; }
}
}

@ -0,0 +1,45 @@
.background-container {
position: relative;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
background-color: transparent;
}
.background {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 0;
display: flex;
justify-content: center;
align-items: center;
}
.background img, .background video {
width: 100%;
height: 100%;
object-fit: cover;
}
.foreground {
position: relative;
z-index: 1;
color: white;
text-align: center;
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 2rem;
}

@ -0,0 +1,10 @@
@namespace portfolio_siwa.Composants.Global.Footer
@using portfolio_siwa.Composants.Global.Boutons.BoutonDiscord
@using portfolio_siwa.Composants.Global.Boutons.BoutonFondTransparent
<footer class="contenu">
<p>Portfolio réalisé par mes soins, avec le Framework .NET Blazor Server.</p>
<BoutonGithub Texte="Code du portfolio" Lien="https://github.com/Siwa12100/portfolio" />
</footer>

@ -0,0 +1,7 @@
namespace portfolio_siwa.Composants.Global.Footer
{
public partial class Footer
{
}
}

@ -0,0 +1,50 @@
footer {
margin-top : 7rem;
width: 100%;
/* background-color: #29648A; */
background-color: #464866;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
box-shadow: 0 -10px 15px rgba(0, 0, 0, 0.5);
height: 20vh;
}
.contenu {
padding: 1rem;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 3rem;
height: 100%;
}
.contenu p {
margin: 0;
padding: 0;
font-family: var(--police-texte);
font-size: var(--taille-texte);
color: var(--couleur-texte);
font-weight: var(--poids-police-texte);
}
@media (max-width: 1024px) {
footer {
flex-direction: column;
height: 40vh;
}
.contenu {
flex-direction: column;
gap: 1rem;
}
.contenu p {
text-align: center;
}
}

@ -0,0 +1,29 @@
@namespace portfolio_siwa.Composants.Global.HautPage
@using portfolio_siwa.Composants.Global.BlocsContenu.BlocContenuGaucheDroite
@using portfolio_siwa.Composants.Global.Boutons.BoutonValorium
@using portfolio_siwa.Composants.Global.Boutons.BoutonLien
<BlocContenuGaucheDroite>
<ContenuGauche>
<h1>Jean Marcillac</h1>
<p>En troisième année de Bachelor informatique à Clermont-Ferrand,
jaffine mes compétences en développement
logiciel, administration système et gestion
de projet, avec lobjectif d'intégrer une école
dingénieurs. Curieux et passionné par le numérique,
je cherche continuellement à progresser en travaillant sur
des projets concrets et stimulants.</p>
<BoutonValorium Texte="Mes travaux" Lien="#partie2"/>
</ContenuGauche>
<ContenuDroite>
<div class="image">
<img src="Images/PhotoProfil.webp" alt="Photo de profil" />
<div class="reseaux">
<BoutonLien Icone="/Images/logos/logoDiscord.svg" Texte="sioa" ModeCopierColler="true" TypeLien="1"/>
<BoutonLien Icone="/Images/logos/logoMail.svg" Texte="jean.marcillac12@gmail.com" TypeLien="2" ModeCopierColler="true"/>
<BoutonLien Icone="/Images/logos/logoGithub.svg" Lien="https://github.com/Siwa12100" />
</div>
</div>
</ContenuDroite>
</BlocContenuGaucheDroite>

@ -0,0 +1,7 @@
namespace portfolio_siwa.Composants.Global.HautPage
{
public partial class HautPage
{
}
}

@ -0,0 +1,58 @@
img {
width: auto;
height: 52vh;
object-fit: cover;
object-position: center;
border-radius: 50%;
border: 2px solid #fff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.6);
}
.image {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
p {
font-size: var(--taille-texte);
font-family: var(--police-texte);
font-weight: var(--poids-police-texte);
font-style: var(--font-style-texte);
color: var(--couleur-texte);
text-align: start;
margin-bottom: 2.3rem;
}
h1 {
font-size: var(--h1-taille-texte);
font-family: var(--police-grand-titre);
font-family: var(--police-grand-titre);
font-weight: var(--poids-police-grand-titre);
font-style: var(--font-style-grand-titre);
color: var(--couleur-texte);
text-align: start;
margin-bottom: 1.5rem;
}
.reseaux {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 1.5rem;
margin-top: 2rem;
}
@media (max-width: 1024px) {
img {
height: 30vh;
}
p {
}
}

@ -0,0 +1 @@
<img src="@this.CheminImage" alt="@this.TexteAlternatif" class="@(this.BordureArrondie ? "bordure-arrondie" : "")"/>

@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Components;
namespace portfolio_siwa.Composants.Global.Images.ImageDimensionee
{
public partial class ImageDimensionee
{
[Parameter]
public string? CheminImage { get; set; }
[Parameter]
public string? TexteAlternatif { get; set; } = "Image";
[Parameter]
public Boolean BordureArrondie { get; set; } = false;
}
}

@ -0,0 +1,10 @@
img {
width: 100%;
height: auto;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.6);
}
.bordure-arrondie {
border-radius: 17px;
}

@ -0,0 +1,27 @@
<nav>
<div class="contenu-nav">
<ul class="menu-principal">
<li id="accueil"><NavLink href="#partie1" class="nav-link" ActiveClass="active">Accueil</NavLink></li>
<li id="travaux"><NavLink href="#partie2" class="nav-link" ActiveClass="active">Travaux</NavLink></li>
<li id="propos"><NavLink href="#partie3" class="nav-link" ActiveClass="active">À propos</NavLink></li>
</ul>
<button class="bouton-burger" @onclick="ToggleMenu">☰</button>
<ul class="menu-mobile @(menuVisible ? "ouvert" : "ferme")">
<li @onclick="ToggleMenu" id="accueil-mobile">Accueil</li>
<li @onclick="ToggleMenu" id="travaux-mobile">Travaux</li>
<li @onclick="ToggleMenu" id="propos-mobile">À propos</li>
</ul>
<button class="bouton-cv" @onclick="RedirigerCv">
<img src="Images/logos/logocv.svg" alt="cv">
<p>CV</p>
</button>
</div>
</nav>
@code {
private bool menuVisible = false;
private void ToggleMenu()
{
menuVisible = !menuVisible;
}
}

@ -0,0 +1,50 @@
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
namespace portfolio_siwa.Composants.Global.NavBar
{
public partial class NavBar
{
[Inject]
public NavigationManager? NavigationManager { get; set; }
[Inject]
protected IJSRuntime? JSRuntime { get; set; }
protected string? LienDiscord = "https://discord.gg/hfKf5y2DC9";
protected string? CheminCv = "https://cv.jean-marcillac.dev";
protected async Task Rediriger()
{
if (NavigationManager is null) return;
var url = NavigationManager.ToAbsoluteUri(this.LienDiscord).ToString();
if (this.JSRuntime is not null)
{
await this.JSRuntime.InvokeVoidAsync("window.open", url, "_blank");
}
}
protected void DirigerBalise(int balise)
{
if (NavigationManager is null) return;
string chemin = "";
if (balise == 1) chemin = "#home";
if (balise == 2) chemin = "#travaux";
if (balise == 3) chemin = "#propos";
Console.WriteLine("Redirection vers : " + chemin);
NavigationManager.NavigateTo(chemin);
}
protected async Task RedirigerCv()
{
if (NavigationManager is null) return;
var url = NavigationManager.ToAbsoluteUri(this.CheminCv).ToString();
if (this.JSRuntime is not null)
{
await this.JSRuntime.InvokeVoidAsync("window.open", url, "_blank");
}
return;
}
}
}

@ -0,0 +1,262 @@
nav {
position: fixed;
width: 100%;
z-index: 1000;
height: 4.8rem;
display: flex;
align-items: center;
justify-content: flex-end;
background-color: transparent;
backdrop-filter: blur(13px);
padding-right: 1rem;
}
nav::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: transparent;
opacity: 0.2;
z-index: -1;
border-radius: inherit;
}
.contenu-nav {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 2rem;
width: 55%;
margin-right: 7rem;
}
.menu-principal {
list-style: none;
display: flex;
align-items: center;
justify-content: space-around;
padding: 0;
margin: 0;
}
.menu-principal li {
position: relative;
color: var(--couleur-texte);
font-size: var(--taille-texte);
font-family: var(--police-texte);
padding-left: 2.6rem;
margin-left: 3.3rem;
transition: transform 0.4s ease;
cursor: pointer;
}
.menu-principal li:hover {
transform: scale(1.1);
}
.menu-principal li::before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 1.9rem;
height: 1.9rem;
background-size: contain;
background-repeat: no-repeat;
transition: transform 0.4s ease;
}
#accueil::before {
background-image: url('/Images/logos/logoHome.svg');
}
#travaux::before {
background-image: url('/Images/logos/logobuild.svg');
}
#propos::before {
background-image: url('/Images/logos/logogredon.svg');
}
.menu-principal li:hover::before {
transform: translateY(-50%) rotate(20deg);
}
.menu-principal li::after {
content: '';
position: absolute;
bottom: -4px;
left: 2.1rem;
width: 0%;
height: 2px;
background-color: var(--couleur-texte);
transition: width 0.3s ease;
}
.menu-principal li:hover::after {
width: calc(100% - 2.1rem);
}
.bouton-cv {
color: white;
font-size: var(--taille-texte);
font-family: var(--police-texte);
font-style: var(--font-style-texte);
font-weight: var(--poids-police-texte);
border-radius: 50px;
display: flex;
align-items: center;
justify-content: center;
background: var(--gradient-violet-orange);
border: 1px solid rgba(255, 255, 255, 0.5);
padding: 0.3rem 0.8rem;
transition: transform 0.4s ease, background 1s ease;
cursor: pointer;
margin-left: 2.2rem;
}
.bouton-cv p {
margin: 0;
padding: 0;
font-size: var(--taille-texte-boutons);
font-family: var(--police-texte);
font-style: var(--font-style-texte);
transition: var(--transition-grossissement);
}
.bouton-cv:hover {
transform: scale(1.03);
}
.bouton-cv img {
width: auto;
height: calc(var(--taille-texte-boutons));
margin-right: 0.7rem;
transition: var(--transition-grossissement);
}
.bouton-cv:hover img {
transform: rotate(-12deg);
}
.bouton-burger {
display: none;
font-size: 2rem;
background: none;
border: none;
color: var(--couleur-texte);
cursor: pointer;
transition: transform 0.4s ease;
}
.bouton-burger:hover {
transform: scale(1.2);
}
.menu-mobile {
display: none;
}
@media (max-width: 1024px) {
nav {
padding-right: 0;
}
.contenu-nav {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
margin-right: 0;
}
.menu-principal {
display: none;
}
.bouton-burger {
display: block;
}
.menu-mobile {
list-style: none;
padding: 1rem;
margin: 0;
display: flex;
flex-direction: column;
align-items: center;
position: absolute;
top: calc(4.8rem + 0rem);
background-color: var(--bleu-fonce);
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.5);
transition: opacity 0.3s ease, transform 0.3s ease;
opacity: 0;
pointer-events: none;
}
.menu-mobile.ouvert {
opacity: 1;
pointer-events: auto;
transform: translateY(0);
}
.menu-mobile.ferme {
transform: translateY(-10px);
}
.menu-mobile li {
position: relative;
color: var(--couleur-texte);
font-size: var(--taille-texte);
font-family: var(--police-texte);
padding: 0.5rem 1rem;
transition: transform 0.4s ease;
margin: 0.5rem 0;
}
.menu-mobile li:hover {
transform: scale(1.05);
}
.menu-mobile li::before {
content: '';
display: inline-block;
vertical-align: middle;
margin-right: 0.5rem;
width: 1.5rem;
height: 1.5rem;
background-size: contain;
background-repeat: no-repeat;
}
#accueil-mobile::before {
background-image: url('/Images/logos/logoHome.svg');
}
#travaux-mobile::before {
background-image: url('/Images/logos/logobuild.svg');
}
#propos-mobile::before {
background-image: url('/Images/logos/logogredon.svg');
}
.menu-mobile li::after {
content: '';
display: block;
height: 1px;
width: 100%;
background: var(--couleur-texte);
margin-top: 0.5rem;
opacity: 0.5;
}
.bouton-cv {
margin-left: 1rem;
}
}

@ -0,0 +1,9 @@
@namespace portfolio_siwa.Composants.Global.Techno
<div class="contenu">
<div class="titre">
<img src="@this.Logo" alt="Logo Techno" />
<h3>@this.Nom</h3>
</div>
<p class="description">@this.Description</p>
</div>

@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Components;
namespace portfolio_siwa.Composants.Global.Techno
{
public partial class Techno
{
[Parameter]
public string? Nom { get; set; }
[Parameter]
public string? Logo { get; set; }
[Parameter]
public string? Description { get; set; }
}
}

@ -0,0 +1,46 @@
.contenu {
display: flex;
flex-direction: column;
align-items: center;
justify-content: start;
width: 32vh;
padding-top: 1rem;
padding-left: 0.4rem;
padding-right: 0.4rem;
padding-bottom: 0.4rem;
background-color: #787a9e;
border-radius: 20px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.6);
}
.titre {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
img {
width: auto;
height: 3rem;
object-fit: cover;
margin-right: 1rem;
}
h3 {
font-size: var(--h3-taille-texte);
font-family: var(--police-sous-titre);
font-weight: var(--poids-police-sous-titre);
font-style: var(--font-style-sous-titre);
}
p {
font-size: calc(var(--taille-texte) * 0.8);
font-family: var(--police-texte);
font-weight: var(--poids-police-texte);
font-style: var(--font-style-texte);
text-align: start;
margin-top: 1rem;
}

@ -0,0 +1,22 @@
@inherits LayoutComponentBase
@using portfolio_siwa.Composants.Global.NavBar;
@using portfolio_siwa.Composants.Global.Footer;
@* Required *@
<MudThemeProvider />
<MudPopoverProvider />
@* Needed for dialogs *@
<MudDialogProvider />
@* Needed for snackbars *@
<MudSnackbarProvider />
<MudScrollToTop>
<MudFab Color="Color.Dark" StartIcon="@Icons.Material.Filled.ArrowCircleUp" />
</MudScrollToTop>
<div class="contenu">
<NavBar/>
@Body
<Footer/>
</div>

@ -0,0 +1,3 @@
.contenu {
background-color: var(--couleur-de-fond);
}

@ -0,0 +1,40 @@
@namespace portfolio_siwa.Composants.Projet
@using portfolio_siwa.Composants.Global.BlocsContenu.BlocTexteMedia
@using portfolio_siwa.Composants.Global.Images.ImageDimensionee
@using portfolio_siwa.Composants.Global.Boutons.BoutonDiscord
@using portfolio_siwa.Composants.Global.Boutons.BoutonFondTransparent
<div class="contenu">
<BlocTexteMedia TexteTitre="@Titre" CheminIconeTitre="@CheminIcone"
PositionMedia="@PositionMedia" Texte="@Texte">
<Media>
<ImageDimensionee
CheminImage="@ImagePrincipale"
TexteAlternatif="Image du portfolio"
BordureArrondie="true"/>
</Media>
<SousTexte>
<div class="sous-texte">
@if (this.LienGithub != null)
{
<BoutonGithub Lien="@LienGithub" Texte="Code du projet"/>
}
@if (this.Details != null)
{
<BoutonFondTransparent Texte="Détails sur le projet"
Icone="/Images/logos/logoLampe.png" FondBlanc="true"
DetailsProjet="@Details"/>
}
</div>
</SousTexte>
<BasPage>
@if (this.BasPage != null)
{
@BasPage
}
</BasPage>
</BlocTexteMedia>
</div>

@ -0,0 +1,36 @@
using Microsoft.AspNetCore.Components;
using portfolio_siwa.Composants.Global.BlocsContenu.BlocTexteMedia;
using portfolio_siwa.Modeles;
namespace portfolio_siwa.Composants.Projet
{
public partial class Projet
{
[Parameter]
public string? Titre { get; set; }
[Parameter]
public MarkupString? Description { get; set; }
[Parameter]
public string? ImagePrincipale { get; set; }
[Parameter]
public string? CheminIcone { get; set; }
[Parameter]
public MarkupString? Texte { get; set; }
[Parameter]
public string? LienGithub { get; set; }
[Parameter]
public RenderFragment? BasPage { get; set; }
[Parameter]
public PositionMedia PositionMedia { get; set; }
[Parameter]
public DetailsProjet? Details { get; set; }
}
}

@ -0,0 +1,13 @@
.sous-texte {
display: flex;
flex-direction: row;
justify-content: center;
gap: 1rem;
align-items: center;
}
@media (max-width: 1024px) {
.sous-texte {
flex-direction: column;
}
}

@ -0,0 +1,6 @@
<Router AppAssembly="typeof(Program).Assembly">
<Found Context="routeData">
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
<FocusOnNavigate RouteData="routeData" Selector="h1" />
</Found>
</Router>

@ -0,0 +1,12 @@
@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 portfolio_siwa
@using portfolio_siwa.Composants
@using MudBlazor

@ -0,0 +1,11 @@
using Microsoft.AspNetCore.Components;
namespace portfolio_siwa.Modeles
{
public class DetailsProjet
{
public string? Titre { get; set; }
public MarkupString? Texte { get; set; }
public string? CheminImage { get; set; }
}
}

@ -0,0 +1,36 @@
@page "/Error"
@using System.Diagnostics
<PageTitle>Error</PageTitle>
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
@if (ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@RequestId</code>
</p>
}
<h3>Development Mode</h3>
<p>
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
</p>
<p>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app.
</p>
@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;
}

@ -0,0 +1,155 @@
@using portfolio_siwa.Composants.Global.FondMedia
@using portfolio_siwa.Composants.Global.Boutons.BoutonFondTransparent
@using portfolio_siwa.Composants.Global.BlocsContenu.BlocContenuGaucheDroite
@using portfolio_siwa.Composants.Global.BlocsContenu.BlocTexteMedia
@using portfolio_siwa.Composants.Global.Images.ImageDimensionee
@using portfolio_siwa.Composants.Global.AnchorNavigation
@using portfolio_siwa.Composants.Global.Boutons.BoutonGradientViolet
@using portfolio_siwa.Composants.Global.BlocsContenu.Bloc3Contenu
@using portfolio_siwa.Composants.Projet
@using portfolio_siwa.Composants.Global.Techno
@using portfolio_siwa.Composants.Global.HautPage
@using portfolio_siwa.Composants.Global.BasPage
@page "/"
<AnchorNavigation />
<div class="contenu">
<section id="partie1" class="haut-page separation-contenu marge-pages">
<HautPage />
</section>
<section id="partie2" class="separation-contenu-legere marge-pages">
<Projet
Titre="@this.TitreProjetGenseSense"
ImagePrincipale="/Images/projets/genesense.png"
Texte="@this.DescriptionProjetGenseSense"
PositionMedia="PositionMedia.Gauche"
LienGithub="@this.GenseSenseGithub">
<BasPage>
<div class="technos">
<Techno Nom="Python 3" Description="@this.GeneSenseDescriptionPython" Logo="Images/logos/logoPython.svg" />
<Techno Nom="Flask" Description="@this.GeneSenseDescriptionFlask" Logo="Images/logos/flask.svg" />
<Techno Nom="Blazor Server" Description="@this.GeneSenseDescriptionBlazor" Logo="Images/logos/logoBlazor.png" />
<Techno Nom="YOLO" Description="@this.GeneSenseDescriptionYolo" Logo="Images/logos/logoYolo.png" />
<Techno Nom="Docker" Description="@this.GeneSenseDescriptionDocker" Logo="Images/logos/logoDocker.png" />
</div>
</BasPage>
</Projet>
</section>
<section id="test" class="separation-contenu marge-pages">
<Projet
Titre="@this.TitreProjetDrosolab"
ImagePrincipale="/Images/projets/drosolab.png"
Texte="@this.DescriptionProjetDrosolab"
PositionMedia="PositionMedia.Droite">
<BasPage>
<div class="technos">
<Techno Nom="Vue.js" Description="@this.DrosolabDescriptionVue" Logo="Images/logos/logoVue.png" />
<Techno Nom="Symfony" Description="@this.DrosolabDescriptionSymfony" Logo="Images/logos/logoSymfony.png" />
<Techno Nom="Doctrine & Mysql" Description="@this.DrosolabDescriptionMysql" Logo="Images/logos/logoDoctrine.png" />
<Techno Nom="Docker" Description="@this.DrosolabDescriptionDocker" Logo="Images/logos/logoDocker.png" />
</div>
</BasPage>
</Projet>
</section>
<section class="separation-contenu marge-pages">
<Projet
Titre="@this.TitreProjetSiteElendil"
ImagePrincipale="/Images/projets/elendil.png"
Texte="@this.DescriptionProjetSiteElendil"
PositionMedia="PositionMedia.Gauche">
<BasPage>
<div class="technos">
<Techno Nom="Blazor Server" Description="@this.ElendilDescriptionBlazor" Logo="Images/logos/logoBlazor.png" />
<Techno Nom="Nginx" Description="@this.ElendilDescriptionNginx" Logo="Images/logos/logoNginx.png" />
<Techno Nom="Let's Encrypt" Description="@this.ElendilDescriptionLetsencrypt" Logo="Images/logos/logoEncrypt.png" />
<Techno Nom="Docker" Description="@this.ElendilDescriptionDocker" Logo="Images/logos/logoDocker.png" />
</div>
</BasPage>
</Projet>
</section>
<section class="separation-contenu-legere marge-pages">
<Projet
Titre="@this.TitreProjetLibPais"
ImagePrincipale="/Images/projets/paistv.png"
Texte="@this.DescriptionProjetLibPais"
PositionMedia="PositionMedia.Gauche">
<BasPage>
<div class="technos">
<Techno Nom="Blazor Server" Description="@this.LibPaisDescriptionBlazor" Logo="Images/logos/logoBlazor.png" />
<Techno Nom="Java" Description="@this.LibPaisDescriptionJava" Logo="Images/logos/logoJava.png" />
<Techno Nom="Spring Boot" Description="@this.LibPaisDescriptionSpringBoot" Logo="Images/logos/logoSpring.svg" />
<Techno Nom="MongoDB" Description="@this.LibPaisDescriptionMongoDB" Logo="Images/logos/logoMongo.png" />
<Techno Nom="Google Drive API" Description="@this.LibPaisDescriptionGoogleDriveApi" Logo="Images/logos/logoGoogledrive.png" />
</div>
</BasPage>
</Projet>
</section>
<section class="separation-contenu-legere marge-pages">
<Projet
Titre="@this.TitreProjetMyriade"
ImagePrincipale="/Images/projets/logoMyriade.png"
Texte="@this.DescriptionProjetMyriade"
PositionMedia="PositionMedia.Droite">
<BasPage>
<div class="technos">
<Techno Nom="Java" Description="@this.MyriadeDescriptionJava" Logo="Images/logos/logoJava.png" />
<Techno Nom="Paper Mc" Description="@this.MyriadeDescriptionPaper" Logo="Images/logos/logoPaper.webp" />
<Techno Nom="Spring Boot" Description="@this.MyriadeDescriptionSpringBoot" Logo="Images/logos/logoSpring.svg" />
<Techno Nom="Kotlin" Description="@this.MyriadeDescriptionKotlin" Logo="Images/logos/logoKotlin.png" />
<Techno Nom="MongoDB" Description="@this.MyriadeDescriptionMongoDB" Logo="Images/logos/logoMongo.png" />
<Techno Nom="Redis" Description="@this.MyriadeDescriptionRedis" Logo="Images/logos/logoRedis.svg" />
<Techno Nom="Blazor Server" Description="@this.MyriadeDescriptionBlazor" Logo="Images/logos/logoBlazor.png" />
</div>
</BasPage>
</Projet>
</section>
<section class="separation-contenu marge-pages">
<Projet
Titre="@this.TitreProjetIoaCi"
ImagePrincipale="/Images/projets/ciioa.png"
Texte="@this.DescriptionProjetIoaCi"
PositionMedia="PositionMedia.Gauche">
<BasPage>
<div class="technos">
<Techno Nom="Drone CI" Description="@this.IoaCiDescriptionDroneCI" Logo="Images/logos/logoDrone.svg" />
<Techno Nom="SonarQube" Description="@this.IoaCiDescriptionSonar" Logo="Images/logos/logoSonar.png" />
<Techno Nom="Docker Compose" Description="@this.IoaCiDescriptionDockerCompose" Logo="Images/logos/logoCompose.png" />
<Techno Nom="Docker Registry" Description="@this.IoaCiDescriptionRegistry" Logo="Images/logos/logoRegistry.png" />
<Techno Nom="Debian" Description="@this.IoaCiDescriptionDebian" Logo="Images/logos/logoDebian.png" />
</div>
</BasPage>
</Projet>
</section>
<section class="separation-contenu-legere marge-pages">
<Projet
Titre="@this.TitreProjetJogaires"
ImagePrincipale="/Images/projets/jogaires.png"
Texte="@this.DescriptionProjetJogaires"
PositionMedia="PositionMedia.Droite">
<BasPage>
<div class="technos">
<Techno Nom="Blazor Server" Description="@this.JogairesDescriptionBlazor" Logo="Images/logos/logoBlazor.png" />
<Techno Nom="Kotlin" Description="@this.JogairesDescriptionKotlin" Logo="Images/logos/logoKotlin.png" />
<Techno Nom="Spring Boot" Description="@this.JogairesDescriptionSpringBoot" Logo="Images/logos/logoSpring.svg" />
<Techno Nom="MongoDB" Description="@this.JogairesDescriptionMongoDB" Logo="Images/logos/logoMongo.png" />
<Techno Nom="Docker" Description="@this.JogairesDescriptionDocker" Logo="Images/logos/logoDocker.png" />
</div>
</BasPage>
</Projet>
</section>
<section id="partie3" class="bas-page separation-contenu marge-pages">
<BasPage />
</section>
</div>

@ -0,0 +1,145 @@
using Microsoft.AspNetCore.Components;
using portfolio_siwa.Modeles;
namespace portfolio_siwa.Pages.Index
{
public partial class Index
{
protected string? TitreProjetGenseSense;
protected MarkupString? DescriptionProjetGenseSense;
protected string? GeneSenseDescriptionPython;
protected string? GeneSenseDescriptionFlask;
protected string? GeneSenseDescriptionBlazor;
protected string? GeneSenseDescriptionDocker;
protected string? GeneSenseDescriptionYolo;
protected string? GenseSenseGithub = "https://github.com/Siwa12100/projet-ia";
protected MarkupString? GenseSenseTextePresentation;
protected DetailsProjet? DetailsProjetGenseSense;
protected string? TitreProjetDrosolab;
protected MarkupString? DescriptionProjetDrosolab;
protected string? DrosolabDescriptionSymfony;
protected string? DrosolabDescriptionVue;
protected string? DrosolabDescriptionDocker;
protected string? DrosolabDescriptionMysql;
protected string? TitreProjetSiteElendil;
protected MarkupString? DescriptionProjetSiteElendil;
protected string? ElendilDescriptionBlazor;
protected string? ElendilDescriptionNginx;
protected string? ElendilDescriptionDocker;
protected string? ElendilDescriptionLetsencrypt;
protected string? TitreProjetLibPais;
protected MarkupString? DescriptionProjetLibPais;
protected string? LibPaisDescriptionBlazor;
protected string? LibPaisDescriptionSpringBoot;
protected string? LibPaisDescriptionJava;
protected string? LibPaisDescriptionMongoDB;
protected string? LibPaisDescriptionDocker;
protected string? LibPaisDescriptionGoogleDriveApi;
protected string? TitreProjetIoaCi;
protected MarkupString? DescriptionProjetIoaCi;
protected string? IoaCiDescriptionDroneCI;
protected string? IoaCiDescriptionDockerCompose;
protected string? IoaCiDescriptionSonar;
protected string? IoaCiDescriptionDebian;
protected string? IoaCiDescriptionRegistry;
protected string? TitreProjetJogaires;
protected MarkupString? DescriptionProjetJogaires;
protected string? JogairesDescriptionBlazor;
protected string? JogairesDescriptionKotlin;
protected string? JogairesDescriptionSpringBoot;
protected string? JogairesDescriptionMongoDB;
protected string? JogairesDescriptionDocker;
protected string? TitreProjetMyriade;
protected MarkupString? DescriptionProjetMyriade;
protected string? MyriadeDescriptionBlazor;
protected string? MyriadeDescriptionSpringBoot;
protected string? MyriadeDescriptionMongoDB;
protected string? MyriadeDescriptionJava;
protected string? MyriadeDescriptionKotlin;
protected string? MyriadeDescriptionRedis;
protected string? MyriadeDescriptionPaper;
protected override void OnInitialized()
{
TitreProjetGenseSense = "GenseSense IA";
DescriptionProjetGenseSense = new MarkupString("GenseSense est un projet ayant consisté à développer deux intelligences artificielles. La première détecte le genre d'une personne à partir d'une image, tandis que la seconde reconnaît certaines personnes de ma promotion de BUT. <br> En complément, une IA dédiée à la segmentation des visages facilite la classification par genre et par personne. Enfin, un panel web a été développé pour permettre une interaction simple et directe avec le système.");
this.GeneSenseDescriptionPython = "Langage utilisé pour développer les modèles d'IA et traiter les images.";
this.GeneSenseDescriptionFlask = "Framework employé pour créer l'API Web qui communique entre les IA et le panel web.";
this.GeneSenseDescriptionBlazor = "Plateforme utilisée pour concevoir le panel web interactif. Elle offre une communication en temps réel entre le client et le serveur.";
this.GeneSenseDescriptionDocker = "Utilisation pour le déploiement de l'ensemble du projet dans un environnement homogène.";
this.GeneSenseDescriptionYolo = "Bibliothèque utilisée pour détecter et segmenter les visages dans les images. Elle fournit des performances rapides et précises pour l'extraction des zones d'intérêt.";
this.GenseSenseTextePresentation = new MarkupString("GenseSense repose sur trois modules : segmentation, classification par genre et reconnaissance de personnes. " +
"La segmentation utilise Ultralytics/YOLO pour détecter et isoler automatiquement les visages dans les images. " +
"La classification par genre sappuie sur un dataset équilibré (99% pour les hommes, 95% pour les femmes). " +
"La reconnaissance de personnes identifie des individus spécifiques de ma promotion avec une précision denviron 85%. " +
"Les modèles sont développés en Python 3 et intégrés dans une API Flask assurant la communication avec linterface web. " +
"Le panel interactif, conçu en Blazor Server, permet aux utilisateurs de charger des images et dobtenir des résultats en temps réel. " +
"Lensemble est conteneurisé avec Docker pour garantir un déploiement stable et reproductible sur un serveur.");
this.DetailsProjetGenseSense = new DetailsProjet
{
Titre = "GenseSense IA - Détails",
Texte = this.GenseSenseTextePresentation,
CheminImage = "images/projets/schemaGenseSense.png"
};
this.TitreProjetDrosolab = "Virtogen";
this.DescriptionProjetDrosolab = new MarkupString("Projet réalisé en partenariat avec des professeurs de l'UFR de biologie de Clermont-Ferrand. Il permet de simuler lévolution des caractéristiques génétiques dans des populations de drosophiles. Les étudiants manipulent une paillasse expérimentale où ils ajoutent des flacons contenant des populations d'insectes aux phénotypes spécifiques. Ils peuvent observer lévolution des générations et tester différentes interactions génétiques. ");
this.DrosolabDescriptionVue = "Framework utilisé pour développer linterface interactive, permettant aux étudiants de manipuler facilement les flacons et dobserver lévolution des populations en temps réel.";
this.DrosolabDescriptionSymfony = "Back-end assurant la gestion des données et des règles métier liées aux populations et aux simulations. Il expose une API REST utilisée par le front.";
this.DrosolabDescriptionDocker = "Utilisé pour conteneuriser lensemble du projet et simplifier son déploiement, garantissant un environnement stable et reproductible.";
this.DrosolabDescriptionMysql = "Base de données relationnelle stockant les phénotypes, les configurations de simulations et lévolution des générations, avec Doctrine comme ORM pour gérer les entités.";
this.TitreProjetSiteElendil = "Site de l'Alliance d'Elendil";
this.DescriptionProjetSiteElendil = new MarkupString("Réalisation du site vitrine du projet dans le domaine du jeu vidéo : <a style='color: #2E9CCA;' class='projet' href='https://elendil-mc.fr'>Alliance d'Elendil</a>. Présentation des différents serveurs de jeu, des différents concepts de l'Alliance et récupération depuis l'api du jeu d'informations en direct sur les serveurs et les joueurs.");
this.ElendilDescriptionBlazor = "Framework utilisé pour développer le site web. Utilisation de la bibliothèque de composants MudBlazor. ";
this.ElendilDescriptionNginx = "Utilisé en mode reverse proxy pour rediriger les requêtes vers le bon conteneur Docker.";
this.ElendilDescriptionDocker = "Utilisé pour conteneuriser lensemble du projet et simplifier son déploiement.";
this.ElendilDescriptionLetsencrypt = "Utilisé pour générer des certificats SSL et sécuriser les communications en https.";
this.TitreProjetLibPais = "PAÍS TV & Librariá";
this.DescriptionProjetLibPais = new MarkupString("<a class='projet' style='color: #2E9CCA;' href='https://ioa-pais.fr'>PAÍS TV</a> & <a style='color: #2E9CCA;' class='projet' href='https://libraria.ioa-pais.fr'>Librariá</a> sont deux plateformes créées pour valoriser le patrimoine occitan. Librariá répertorie et met en avant plus de 2500 ouvrages disponibles à l'Institut occitan de l'Aveyron, offrant une exploration détaillée grâce à un moteur de recherche et une interface intuitive. PAÍS TV regroupe et classe les vidéos produites par lIOA sur YouTube, facilitant laccès aux séries pédagogiques et documentaires sur la langue et la culture occitanes.");
this.LibPaisDescriptionBlazor = "Framework utilisé pour le développement du front-end, permettant de créer une interface dynamique et réactive en C#. ";
this.LibPaisDescriptionSpringBoot = "Back-end du projet, servant dAPI pour gérer les opérations CRUD sur les données stockées dans MongoDB. Il gère également linteraction avec Google Drive pour le stockage et la récupération des images.";
this.LibPaisDescriptionJava = "Langage utilisé pour développer lAPI avec Spring Boot.";
this.LibPaisDescriptionMongoDB = "Base de données NoSQL utilisée pour stocker les données du projet sous forme de documents JSON.";
this.LibPaisDescriptionGoogleDriveApi = "Service utilisé pour stocker et gérer les images du projet. LAPI permet à Spring Boot daccéder aux fichiers de manière sécurisée et organisée.";
this.LibPaisDescriptionDocker = "Utilisé pour conteneuriser lensemble du projet et simplifier son déploiement.";
this.TitreProjetIoaCi = "Environnement de développement sécurisé pour l'IOA";
this.DescriptionProjetIoaCi = new MarkupString("Ce projet a consisté en la mise en place d'un environnement de développement sécurisé pour l'Institut occitan de l'Aveyron. Il a s'agit d'administrer un VPS sous Debian, d'assurer sa sécurisation (pare-feu, SSH, etc.) ainsi que le déploiement dun processus de CI/CD complet. Un serveur Drone CI orchestre l'intégration continue, tandis que SonarQube assure l'analyse de la qualité du code. Une registry privée stocke les images Docker, et lensemble est géré via Docker Compose pour assurer la communication et la gestion des services.");
this.IoaCiDescriptionDroneCI = "Outil utilisé pour l'intégration continue, permettant l'exécution automatisée des tests et des déploiements des projets hébergés sur le serveur. Il assure une gestion efficace des workflows CI/CD.";
this.IoaCiDescriptionDockerCompose = "Utilisé pour orchestrer le déploiement et la configuration des services (Drone CI, SonarQube, registry). Il facilite la mise en place des réseaux Docker et la communication entre les différents conteneurs.";
this.IoaCiDescriptionSonar = "Analyse statique du code permettant d'évaluer la qualité, la sécurité et la maintenabilité des projets. Il est intégré au pipeline CI/CD pour fournir des rapports détaillés sur les vulnérabilités et les bonnes pratiques.";
this.IoaCiDescriptionDebian = "Système dexploitation du VPS sur lequel lenvironnement de développement est déployé. La sécurisation inclut la configuration dun pare-feu, le durcissement de SSH et la gestion des accès.";
this.IoaCiDescriptionRegistry = "Stocke en privé sur le VPS les images Docker des projets pour un déploiement contrôlé et sécurisé..";
this.TitreProjetJogaires = "Jogaires Inspector";
this.DescriptionProjetJogaires = new MarkupString("Ce projet permet aux administrateurs de serveurs Minecraft de répertorier les comportements des joueurs. Ils peuvent signaler un bannissement, un avertissement, une information générale ou une recommandation. Chaque administrateur peut consulter les notes laissées par dautres, facilitant la gestion des joueurs entre serveurs. Sécurisé et fiable, le système est déjà utilisé par une dizaine dadministrateurs et recense plusieurs milliers de joueurs.");
this.JogairesDescriptionBlazor = "Utilisé pour le développement du front-end, offrant une interface fluide et interactive permettant aux administrateurs de consulter et ajouter des informations sur les joueurs en temps réel.";
this.JogairesDescriptionSpringBoot = "API principale assurant la gestion des données et la logique métier. Il permet dexposer des endpoints sécurisés pour la création, la modification et la consultation des signalements des joueurs.";
this.JogairesDescriptionMongoDB = "Base de données NoSQL stockant les signalements des joueurs sous forme de documents JSON.";
this.JogairesDescriptionDocker = "Assure la conteneurisation du projet, facilitant le déploiement et la gestion des services sur le serveur, garantissant un environnement stable et sécurisé.";
this.JogairesDescriptionKotlin = "Langage utilisé pour le back-end. Son intégration avec Spring Boot facilite lécriture dun code concis et performant.";
this.TitreProjetMyriade = "Myriade";
this.DescriptionProjetMyriade = new MarkupString("Myriade est un projet encore en bêta, permettant une communication instantanée bidirectionnelle entre serveurs Minecraft sans dépendre dun proxy comme Velocity ou BungeeCord. Il permet de créer des réseaux de serveurs massifs, bien plus étendus que ceux limités par les proxys traditionnels. Un système de portails assure le transfert des joueurs entre serveurs, exploitant cette infrastructure de communication en temps réel. Ce projet s'inscrit dans une dynamique dunivers virtuel interconnecté, posant les prémices dun métavers dans Minecraft et ouvrant la voie à un écosystème de plugins inter-serveurs.");
this.MyriadeDescriptionBlazor = "Framework utilisé pour le front-end du panel web, permettant d'administrer les réseaux de serveurs et les utilisateurs de Myriade.";
this.MyriadeDescriptionPaper = "API Minecraft utilisée pour développer les plugins serveur, permettant lintégration fluide du système de communication et de transfert des joueurs.";
this.MyriadeDescriptionRedis = "Technologie clé du projet, utilisée pour la communication instantanée entre serveurs via Redis Pub/Sub, ainsi que pour stocker en mémoire vive des données temporaires liées aux transferts de joueurs.";
this.MyriadeDescriptionSpringBoot = "Framework utilisé pour créer les APIs REST, assurant la gestion et la sécurisation des échanges de données entre les serveurs et la base de données.";
this.MyriadeDescriptionKotlin = "Utilisé pour le développement des services back-end, apportant une syntaxe moderne et concise, tout en garantissant performance et maintenabilité.";
this.MyriadeDescriptionMongoDB = "Base de données NoSQL stockant les informations des serveurs, les coordonnées des portails, ainsi que les identifiants des utilisateurs pour les connexions.";
this.MyriadeDescriptionJava = "Langage principal des plugins Minecraft, permettant l'intégration du système de communication et des portails directement dans le jeu.";
}
}
}

@ -0,0 +1,40 @@
.contenu {
display: flex;
flex-direction: column;
justify-content: start;
align-items: center;
overflow: hidden;
}
.haut-page {
margin-top: 17vh;
}
.bas-page {
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.technos {
display: flex;
flex-direction: row;
gap: 2rem;
flex-wrap: wrap;
justify-content: center;
align-items: start;
margin-top: 2rem;
margin-bottom: 2rem;
}
@media (max-width: 1024px) {
}

@ -0,0 +1,39 @@
using Microsoft.AspNetCore.StaticFiles;
using MudBlazor.Services;
using portfolio_siwa;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
builder.Services.AddMudServices();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseAntiforgery();
app.MapStaticAssets();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
// Temporaire, pour corriger le bug liés aux vidéos de Blazor .NET 9
app.MapGet("/banniereElendil", () =>
{
var path = Path.Combine(app.Environment.ContentRootPath, "wwwroot", "Videos", "cinematique1.mp4");
return Results.File(path, "video/mp4");
})
.AllowAnonymous();
app.Run();

@ -0,0 +1,23 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:5087",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:7259;http://localhost:5087",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<EnableCompressionInSingleFile>false</EnableCompressionInSingleFile>
<EnableDefaultStaticWebAssetsCompression>false</EnableDefaultStaticWebAssetsCompression>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MudBlazor" Version="8.2.0" />
</ItemGroup>
</Project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

@ -0,0 +1,7 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="#ffffff">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
<g id="SVGRepo_iconCarrier"> <path d="M18.8943 4.34399C17.5183 3.71467 16.057 3.256 14.5317 3C14.3396 3.33067 14.1263 3.77866 13.977 4.13067C12.3546 3.89599 10.7439 3.89599 9.14391 4.13067C8.99457 3.77866 8.77056 3.33067 8.58922 3C7.05325 3.256 5.59191 3.71467 4.22552 4.34399C1.46286 8.41865 0.716188 12.3973 1.08952 16.3226C2.92418 17.6559 4.69486 18.4666 6.4346 19C6.86126 18.424 7.24527 17.8053 7.57594 17.1546C6.9466 16.92 6.34927 16.632 5.77327 16.2906C5.9226 16.184 6.07194 16.0667 6.21061 15.9493C9.68793 17.5387 13.4543 17.5387 16.889 15.9493C17.0383 16.0667 17.177 16.184 17.3263 16.2906C16.7503 16.632 16.153 16.92 15.5236 17.1546C15.8543 17.8053 16.2383 18.424 16.665 19C18.4036 18.4666 20.185 17.6559 22.01 16.3226C22.4687 11.7787 21.2836 7.83202 18.8943 4.34399ZM8.05593 13.9013C7.01058 13.9013 6.15725 12.952 6.15725 11.7893C6.15725 10.6267 6.98925 9.67731 8.05593 9.67731C9.11191 9.67731 9.97588 10.6267 9.95454 11.7893C9.95454 12.952 9.11191 13.9013 8.05593 13.9013ZM15.065 13.9013C14.0196 13.9013 13.1652 12.952 13.1652 11.7893C13.1652 10.6267 13.9983 9.67731 15.065 9.67731C16.121 9.67731 16.985 10.6267 16.9636 11.7893C16.9636 12.952 16.1317 13.9013 15.065 13.9013Z" stroke="#ffffff" stroke-linejoin="round"/> </g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save