first push

This commit is contained in:
2026-05-16 21:29:22 +02:00
commit 25edd4fac7
68 changed files with 3475 additions and 0 deletions
+36
View File
@@ -0,0 +1,36 @@
<!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="app.css" />
<link rel="stylesheet" href="PhotoShareHelri.styles.css" />
<link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet" />
<link href="@Assets["_content/MudBlazor/MudBlazor.min.css"]" rel="stylesheet" />
<link rel="icon" type="image/png" href="favicon.png" />
<HeadOutlet />
</head>
<body>
<Routes @rendermode="InteractiveServer" />
<!-- Add chart.js reference if chart components are used in your application. -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.0.1/chart.umd.js" integrity="sha512-gQhCDsnnnUfaRzD8k1L5llCCV6O9HN09zClIzzeJ8OJ9MpGmIlCxm+pdCkqTwqJ4JcjbojFr79rl2F1mzcoLMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<!-- Add chartjs-plugin-datalabels.min.js reference if chart components with data label feature is used in your application. -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-datalabels/2.2.0/chartjs-plugin-datalabels.min.js" integrity="sha512-JPcRR8yFa8mmCsfrw4TNte1ZvF1e3+1SdGMslZvmrzDYxS69J7J49vkFL8u6u8PlPJK+H3voElBtUCzaXj+6ig==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<!-- Add sortable.js reference if SortableList component is used in your application. -->
<script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script>
<script src="@Assets["_content/MudBlazor/MudBlazor.min.js"]"></script>
<script src="_framework/blazor.web.js"></script>
<script src="js/download.js"></script>
</body>
</html>
@@ -0,0 +1,88 @@
@using MudBlazor
@if (IsOpen && !string.IsNullOrWhiteSpace(CurrentImage))
{
<MudOverlay Visible="true" DarkBackground="true" ZIndex="1300" @onclick="CloseFromOverlay">
<MudPaper Class="pa-4 mx-auto"
@onclick:stopPropagation="true"
Style="
width:95%;
max-width:1200px;
margin-top:5vh;
background-color:#1e1e1e;
color:white;
position:relative;
border-radius:16px;">
<div class="text-center">
<MudImage Src="@CurrentImage"
Fluid="true"
Class="mb-3"
Style="max-height:75vh; object-fit:contain;" />
<MudStack Row="true"
Justify="Justify.SpaceBetween"
AlignItems="AlignItems.Center"
Class="mt-2">
<MudButton Variant="Variant.Outlined"
Color="Color.Inherit"
OnClick="OnPrevious"
Disabled="@(!CanPrevious)">
⬅ Précédent
</MudButton>
<MudButton Variant="Variant.Outlined"
Color="Color.Inherit"
Disabled="@(SelectedImages.Count() >= MaxSelect && !SelectedImages.Contains(CurrentImage))"
OnClick="Toggle">
@(IsSelector ? (IsSelected ? "Retirer" : "Choisir") : "Télécharger")
</MudButton>
<MudButton Variant="Variant.Outlined"
Color="Color.Inherit"
OnClick="OnNext"
Disabled="@(!CanNext)">
Suivant ➡
</MudButton>
</MudStack>
<MudText Class="mt-3" Align="Align.Center" Typo="Typo.body1">
@(CurrentIndex + 1) / @Total
</MudText>
</div>
<MudIconButton Icon="@Icons.Material.Filled.Close"
Color="Color.Inherit"
OnClick="OnClose"
Style="position:absolute; top:10px; right:10px;" />
</MudPaper>
</MudOverlay>
}
@code {
[Parameter] public bool IsOpen { get; set; }
[Parameter] public string? CurrentImage { get; set; }
[Parameter] public bool IsSelector { get; set; }
[Parameter] public int CurrentIndex { get; set; }
[Parameter] public int Total { get; set; }
[Parameter] public HashSet<string> SelectedImages { get; set; }
[Parameter] public int MaxSelect { get; set; }
[Parameter] public bool CanNext { get; set; }
[Parameter] public bool CanPrevious { get; set; }
[Parameter] public bool IsSelected { get; set; }
[Parameter] public EventCallback OnClose { get; set; }
[Parameter] public EventCallback OnNext { get; set; }
[Parameter] public EventCallback OnPrevious { get; set; }
[Parameter] public EventCallback Toggle { get; set; }
private async Task CloseFromOverlay()
{
await OnClose.InvokeAsync();
}
}
@@ -0,0 +1,79 @@
.gallery {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.gallery-thumb {
cursor: pointer;
transition: transform 0.2s;
}
.gallery-thumb:hover {
transform: scale(1.05);
}
.header-selection {
display: flex;
align-items: center;
width: 60%;
}
.gallery-wrapper {
display: flex;
flex-direction: column;
}
.gallery-actions {
display: flex;
justify-content: flex-end;
margin: 1rem 0;
}
.titles {
flex: 1;
text-align: center;
}
.image-container {
position: relative;
display: inline-block;
width: 300px;
height: 300px;
overflow: hidden; /* cache le dépassement */
border-radius: 8px;
}
.image-container img {
width: 100%;
height: 100%;
object-fit: contain;
background-color: #000;
transition: opacity 0.2s, border 0.2s, transform 0.2s;
}
/* Image sélectionnée */
.image-container.selected img {
opacity: 0.6;
border: 3px solid limegreen;
}
/* Bouton overlay */
.select-btn {
position: absolute;
bottom: 8px;
right: 8px;
background-color: rgba(0, 0, 0, 0.6);
color: white;
border: none;
border-radius: 50%;
width: 30px;
height: 30px;
font-size: 18px;
cursor: pointer;
transition: background-color 0.2s;
}
.select-btn:hover {
background-color: rgba(0, 128, 0, 0.8);
}
@@ -0,0 +1,29 @@
@using MudBlazor
@inherits LayoutComponentBase
@* Required *@
<MudThemeProvider />
<MudPopoverProvider />
@* Needed for dialogs *@
<MudDialogProvider />
@* Needed for snackbars *@
<MudSnackbarProvider />
<MudLayout>
<!-- Ton menu -->
<NavMenu />
<MudMainContent>
<MudBreakpointProvider>
@Body
</MudBreakpointProvider>
</MudMainContent>
</MudLayout>
<div id="blazor-error-ui">
Une erreur dans le traitement est survenue.
<a href="" class="reload">Recharger</a>
</div>
@@ -0,0 +1,112 @@
.page {
position: relative;
display: flex;
flex-direction: column;
}
main {
flex: 1;
}
.main-wrapper {
flex: 1;
display: flex;
justify-content: center;
}
.content {
width: 100%;
max-width: 1200px;
}
.navbar {
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
}
.top-row {
background-color: #f7f7f7;
border-bottom: 1px solid #d6d5d5;
justify-content: flex-end;
height: 3.5rem;
display: flex;
align-items: center;
}
.top-row ::deep a, .top-row ::deep .btn-link {
white-space: nowrap;
margin-left: 1.5rem;
text-decoration: none;
}
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
text-decoration: underline;
}
.top-row ::deep a:first-child {
overflow: hidden;
text-overflow: ellipsis;
}
@media (max-width: 640.98px) {
.top-row {
justify-content: space-between;
}
.top-row ::deep a, .top-row ::deep .btn-link {
margin-left: 0;
}
}
@media (min-width: 1px) {
.page {
flex-direction: column;
}
.navbar {
position: sticky;
top: 0;
}
.top-row {
position: sticky;
top: 0;
z-index: 1;
}
.top-row.auth ::deep a:first-child {
flex: 1;
text-align: right;
width: 0;
}
.top-row, article {
padding-left: 2rem !important;
padding-right: 1.5rem !important;
}
}
.content-inner {
display: flex;
flex-direction: column;
align-items: center;
}
#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;
}
@@ -0,0 +1,175 @@
@using Microsoft.AspNetCore.Components.Routing
@using ModelsLib
@using MudBlazor
@inject NavigationManager Navigation
@rendermode InteractiveServer
@inject IWebHostEnvironment Env
@inject PhotoService ShootingDb
@inject IDialogService DialogService
<MudHidden Breakpoint="Breakpoint.MdAndDown">
<MudAppBar Color="Color.Dark" Elevation="4">
<!-- Logo -->
<MudText Typo="Typo.h6" Class="ml-2">
<MudNavLink Href="/" Class="nav-link">
Photo Sharing Helrira
</MudNavLink>
</MudText>
<MudSpacer />
<!-- Menu desktop -->
<MudNavMenu Class="d-flex">
<MudNavLink Href="/shootingdesire" Class="nav-link">
Projets
</MudNavLink>
<MudNavLink Href="/Contact" Class="nav-link">
Contact
</MudNavLink>
</MudNavMenu>
<!-- Recherche desktop -->
<MudStack Row="true" Spacing="2" Class="ml-4">
<MudTextField @bind-Value="Id"
Label="Recherche"
Variant="Variant.Outlined"
Style="color: white;"
InputStyle="color: white;" />
<MudButton OnClick="SendSelection"
Variant="Variant.Outlined"
Color="Color.Inherit">
Rechercher
</MudButton>
</MudStack>
</MudAppBar>
</MudHidden>
<MudHidden Breakpoint="Breakpoint.LgAndUp">
<!-- Drawer mobile -->
<!-- AppBar mobile -->
<MudAppBar Color="Color.Dark" Elevation="4">
<!-- Bouton burger -->
<MudIconButton Icon="@Icons.Material.Filled.Menu"
Color="Color.Default"
OnClick="@(() => _drawerOpen = true)" />
<MudText Typo="Typo.h6" Class="ml-2">
<MudNavLink Href="/" Class="nav-link">
Photo Sharing Helrira
</MudNavLink>
</MudText>
<MudSpacer />
<!-- Bouton recherche -->
<MudIconButton Icon="@Icons.Material.Filled.Search"
Color="Color.Default"
OnClick="OpenSearchDialogAsync" />
</MudAppBar>
<!-- Drawer mobile -->
<MudDrawer @bind-Open="_drawerOpen"
Anchor="Anchor.Left"
Variant="DrawerVariant.Temporary"
Color="Color.Dark"
Class="custom-drawer drawer-fixed-width">
<MudStack Spacing="2" Class="pa-3" Style="display:flex; flex-direction:column; height:100%;">
<MudNavMenu Class="flex-grow-1">
<MudNavLink Href="/shootingdesire">Projets</MudNavLink>
<MudNavLink Href="/Contact">Contact</MudNavLink>
</MudNavMenu>
</MudStack>
</MudDrawer>
</MudHidden>
@code {
private string Id { get; set; } = "";
private bool _drawerOpen = false;
private async Task OpenSearchDialogAsync()
{
try
{
var parameters = new DialogParameters();
parameters.Add("OnSearch", EventCallback.Factory.Create<string>(this, SendSelectionMobil));
var options = new DialogOptions
{
MaxWidth = MaxWidth.Small,
FullWidth = true
};
var dialogRef = await DialogService.ShowAsync<SearchDialog>("", parameters, options);
var result = await dialogRef.Result;
if (!result.Canceled)
{
Console.WriteLine("Résultat : " + result.Data);
}
}
catch (Exception ex)
{
Console.WriteLine("ERREUR DIALOG : " + ex.Message);
Console.WriteLine(ex.StackTrace);
}
}
void ToggleDrawer() => _drawerOpen = !_drawerOpen;
/// <summary>
/// a la validation de la saisie
/// </summary>
private async void SendSelectionMobil(string value)
{
Id = value;
//si une saisie est faite dans le champs
SendSelection();
}
/// <summary>
/// a la validation de la saisie
/// </summary>
private async void SendSelection()
{
//si une saisie est faite dans le champs
if (!string.IsNullOrWhiteSpace(Id))
{
string idSaisie = Id.ToLower().Trim();//passage en full minuscule + suppression espace
var path = Path.Combine(Env.WebRootPath, "Photos", idSaisie);
bool dossierExiste = Directory.Exists(path);//verification de présence du dossier
if (dossierExiste)
{
ShootingData datas = ShootingDb.GetInfosShooting(idSaisie);
if (datas == null)
{
await ShootingDb.AddShootingInfoPerDefault(idSaisie);
}
else
{
if (datas.IsPhotosSelected)
{
Navigation.NavigateTo($"/Share?Id={idSaisie}");//redirection vers le shooting post retouche
}
else
{
Navigation.NavigateTo($"/PhotoSelect?Id={idSaisie}");//redirection vers le shooting à selectionné
}
}
}
else
{
Navigation.NavigateTo($"/PhotoSelectError");//redirection vers page d'erreur
}
}
}
}
@@ -0,0 +1,105 @@
.navbar-toggler {
appearance: none;
cursor: pointer;
width: 3.5rem;
height: 2.5rem;
color: white;
position: absolute;
top: 0.5rem;
right: 1rem;
border: 1px solid rgba(255, 255, 255, 0.1);
background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1);
}
.navbar-toggler:checked {
background-color: rgba(255, 255, 255, 0.5);
}
.top-row {
height: 3.5rem;
background-color: rgba(0,0,0,0.4);
}
.navbar-brand {
font-size: 1.1rem;
}
.drawer-fixed-width {
width: 250px; /* largeur fixe */
min-width: 250px; /* ne peut pas rétrécir */
max-width: 250px; /* ne peut pas grandir */
}
.drawer-fixed-width .mud-drawer-content {
overflow-x: hidden; /* empêche le scroll horizontal */
}
.custom-drawer.mud-shrink {
height: 100vh !important; /* prend toute la hauteur de l’écran */
min-height: 100vh !important;
max-height: 100vh !important;
overflow-y: auto; /* scroll uniquement si contenu > hauteur */
}
.bi {
display: inline-block;
position: relative;
width: 1.25rem;
height: 1.25rem;
margin-right: 0.75rem;
top: -1px;
background-size: cover;
}
.nav-item {
font-size: 1.1rem;
padding-bottom: 0.5rem;
font-weight: bold;
}
.nav-item:first-of-type {
padding-top: 1rem;
}
.nav-item:last-of-type {
padding-top: 1rem;
}
.nav-item ::deep .nav-link {
color: #d7d7d7;
background: auto;
background-color: rgba(255,255,255,0.1);
border: none;
border-radius: 4px;
height: 3rem;
display: flex;
align-items: center;
line-height: 3rem;
width: 100%;
}
.nav-item ::deep .nav-link:hover {
background-color: rgba(255,255,255,0.5);
color: white;
}
.nav-scrollable {
display: none;
}
.navbar-toggler:checked ~ .nav-scrollable {
display: block;
}
@media (min-width: 641px) {
.navbar-toggler {
display: none;
}
.nav-scrollable {
/* Never collapse the sidebar for wide screens */
display: block;
/* Allow sidebar to scroll for tall menus */
height: calc(100vh - 3.5rem);
overflow-y: auto;
}
}
@@ -0,0 +1,33 @@
@using MudBlazor
<MudDialog>
<DialogContent>
<MudTextField @bind-Value="SearchTerm"
Label="Recherche"
Variant="Variant.Outlined"
FullWidth="true" />
<MudButton Variant="Variant.Filled"
Color="Color.Primary"
Class="mt-2"
OnClick="Submit">
Rechercher
</MudButton>
</DialogContent>
</MudDialog>
@code {
private string SearchTerm { get; set; } = "";
[Parameter] public EventCallback<string> OnSearch { get; set; }
[CascadingParameter] IMudDialogInstance Dialog { get; set; }
private async Task Submit()
{
if (OnSearch.HasDelegate)
await OnSearch.InvokeAsync(SearchTerm);
Dialog?.Close(DialogResult.Ok(SearchTerm)); // IMPORTANT
}
}
@@ -0,0 +1,12 @@
.drawer-fixed-width {
width: 250px;
min-width: 250px;
max-width: 250px;
height: 100vh;
display: flex;
flex-direction: column;
}
.custom-drawer .mud-drawer-content {
overflow-y: auto;
}
@@ -0,0 +1,103 @@
@page "/Contact"
@using System.ComponentModel.DataAnnotations
@using System.Text
@using ModelsLib
@inject EmailService mailService
@inject IJSRuntime JS
@rendermode InteractiveServer
@inject NavigationManager Navigation
<!-- Contact -->
<section class="contact-section">
<MudContainer MaxWidth="MaxWidth.Small" Class="text-center">
<!-- Titre -->
<MudText Typo="Typo.h5" Class="mb-2">
Contact
</MudText>
<!-- Sous-titre -->
<MudText Typo="Typo.body1" Class="mb-6">
Une question ? Un projet ? Envie d'une collaboration ? Écrivez-moi directement.
</MudText>
<!-- Formulaire -->
<EditForm EditContext="@editContext" OnValidSubmit="EnvoyerMessage">
<DataAnnotationsValidator />
<ValidationSummary />
<MudStack Spacing="2">
<MudTextField @bind-Value="contact.Name"
Label="Votre nom"
Variant="Variant.Outlined"
Required="true" />
<MudTextField @bind-Value="contact.Email"
Label="Votre adresse mail"
Variant="Variant.Outlined"
Required="true" />
<MudTextField @bind-Value="contact.Title"
Label="Sujet du message"
Variant="Variant.Outlined" />
<MudTextField @bind-Value="contact.Message"
Label="Message"
Variant="Variant.Outlined"
Lines="6"
TextArea="true" />
<MudButton ButtonType="ButtonType.Submit"
Variant="Variant.Filled"
Color="Color.Primary"
Class="mt-4">
Laisser un message
</MudButton>
</MudStack>
</EditForm>
<!-- Message succès -->
@if (messageEnvoye)
{
<MudAlert Severity="Severity.Success" Class="mt-6">
✅ Message envoyé, merci !
</MudAlert>
}
</MudContainer>
</section>
@code {
#region properties
private ContactModel contact = new();
private EditContext? editContext;
private bool messageEnvoye = false;
#endregion
/// <summary>
/// Chargement de la page
/// </summary>
protected override async Task OnInitializedAsync()
{
editContext = new EditContext(contact);
}
/// <summary>
/// Méthode d'envoi de mail
/// </summary>
private void EnvoyerMessage()
{
var body = new StringBuilder();
body.Append("<p>").Append(contact.Message).Append("</p>");
mailService.EnvoyerMailContactAsync(contact.Email,contact.Name+" : " +contact.Title, body).GetAwaiter();
messageEnvoye = true;
contact = new ContactModel();
editContext = new EditContext(contact);
}
}
@@ -0,0 +1,61 @@
body {
}
.contact-section {
padding: 80px 20px;
background: #f4f4f4;
}
.contact-title {
font-size: 40px;
font-weight: 700;
margin-bottom: 10px;
}
.contact-subtitle {
color: #777;
margin-bottom: 50px;
}
.contact-form {
max-width: 800px;
margin: auto;
display: flex;
flex-direction: column;
gap: 20px;
}
.contact-input {
padding: 15px;
border: 1px solid #ddd;
font-size: 16px;
width: 100%;
}
.contact-textarea {
padding: 15px;
border: 1px solid #ddd;
font-size: 16px;
width: 100%;
min-height: 180px;
}
.contact-button {
width: 250px;
padding: 15px;
border: 2px solid #222;
background: white;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
}
.contact-button:hover {
background: #222;
color: white;
}
.success-message {
margin-top: 20px;
color: green;
font-weight: 500;
}
@@ -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,83 @@
@page "/"
@using System.ComponentModel.DataAnnotations
@using System.Text
@using ModelsLib
@using ToolsPhotoShare
@inject EmailService mailService
@inject IJSRuntime JS
@inject ConfigAppServices _configDb
@rendermode InteractiveServer
@using Microsoft.EntityFrameworkCore;
@inject NavigationManager Navigation
<PageTitle>Accueil</PageTitle>
<main class="home-container">
<!-- Hero -->
<section class="hero-section text-center">
<div class="badge">Photographe Amateur</div>
<h1 class="hero-title">Hello,<br /><span class="italic">Moi c'est @DataPage.TitlePseudo</span></h1>
<p class="hero-subtitle">
@DataPage.SubTitle1
</p>
<p class="hero-subtitle">
@DataPage.SubTitle2
</p>
</section>
<section class="social-section text-center">
<div class="social-buttons">
<NavLink href="/ShootingDesire">
<h2 class="social-title">Mes envies de shooting</h2>
</NavLink>
</div>
</section>
<!-- Social -->
<section class="social-section text-center">
<h2 class="social-title">Où me retrouver ?</h2>
<p class="social-subtitle">Suivez mon travail et mes derniers shootings</p>
<div class="social-buttons">
<a href="@DataPage.InstaLink">
<div class="qr-wrapper">
<img src="@DataPage.InstaQrCode" alt="QR Code Instagram" class="qr-image" />
</div>
</a>
</div>
</section>
</main>
<footer class="footer">
<div class="footer-line"></div>
<p>© 2026 @DataPage.Credentials — Droits réservés</p>
</footer>
@code {
#region properties
private EntityHome DataPage = new();
#endregion
/// <summary>
/// Chargement de la page
/// </summary>
protected override async Task OnInitializedAsync()
{
if (!await _configDb.HasAnyParametersAsync())
{
await _configDb.FillConfigTableWithDataPerDefault();
}
DataPage = await _configDb.readParametersApplication();
}
}
@@ -0,0 +1,209 @@
/* --- Containers --- */
.home-container {
width: 90%; /* occupe 90% de l’écran sur petits écrans */
max-width: 800px; /* limite la largeur sur grands écrans */
margin: 0 auto;
padding: 3rem 1.5rem;
display: flex;
flex-direction: column;
gap: 3rem;
overflow-y: scroll; /* Permet le scroll vertical */
scrollbar-width: none; /* Firefox */
}
.home-container::-webkit-scrollbar {
display: none; /* Chrome, Safari, Edge */
}
/* --- Hero --- */
.badge {
display: inline-block;
padding: 0.25rem 0.75rem;
font-size: 0.75rem;
font-weight: 500;
text-transform: uppercase;
border-radius: 9999px;
border: 1px solid #d1d5db;
color: #71717a;
}
.hero-title {
font-family: 'Playfair Display', serif;
font-size: 2.5rem;
font-weight: 500;
}
.hero-subtitle {
color: #71717a;
font-weight: 300;
line-height: 1.6;
}
.hero-image-wrapper {
position: relative;
margin-top: 3rem;
border-radius: 1rem;
overflow: hidden;
}
.hero-image {
width: 100%;
height: auto;
display: block;
}
.hero-overlay {
position: absolute;
inset: 0;
background: rgba(255,255,255,0.4);
pointer-events: none;
border-radius: 1rem;
}
.dark .hero-overlay {
background: rgba(18,18,18,0.7);
}
/* --- Social --- */
.social-title {
font-family: 'Playfair Display', serif;
font-size: 1.75rem;
font-style: italic;
}
.social-subtitle {
color: #71717a;
font-weight: 300;
}
.social-buttons {
display: flex;
flex-direction: column;
align-items: center;
gap: 1.5rem;
}
.instagram-button {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.75rem 2rem;
border: 1px solid #d1d5db;
border-radius: 9999px;
background: white;
font-weight: 500;
text-decoration: none;
transition: all 0.2s;
}
.instagram-button:hover {
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.qr-wrapper {
padding: 1.5rem;
background: white;
border: 1px solid #d1d5db;
border-radius: 1rem;
display: flex;
flex-direction: column;
align-items: center;
}
.qr-image {
width: 128px;
height: 128px;
}
.qr-text {
margin-top: 0.5rem;
font-size: 0.625rem;
color: #71717a;
letter-spacing: 0.05em;
text-transform: uppercase;
font-weight: 600;
}
/* --- Contact --- */
.contact-title {
font-family: 'Playfair Display', serif;
font-size: 2rem;
}
.contact-subtitle {
color: #71717a;
font-weight: 300;
margin-bottom: 1.5rem;
}
.contact-form {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.contact-input, .contact-textarea {
border: none;
border-bottom: 1px solid #d1d5db;
padding: 0.75rem 0;
font-size: 1rem;
outline: none;
background: transparent;
color: #1a1a1a;
}
.contact-input::placeholder, .contact-textarea::placeholder {
color: #d1d5db;
}
.contact-button {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
padding: 0.5rem 1.5rem;
border-radius: 9999px;
background: #1a1a1a;
color: white;
font-weight: 500;
border: none;
cursor: pointer;
margin: 0 auto;
transition: all 0.2s;
}
.contact-button:hover {
opacity: 0.9;
}
.success-message {
color: green;
font-weight: 500;
}
/* --- Footer --- */
.footer {
text-align: center;
color: #71717a;
font-size: 0.625rem;
text-transform: uppercase;
letter-spacing: 0.1em;
padding-bottom: 3rem;
}
.footer-line {
height: 1px;
width: 3rem;
background: #d1d5db;
margin: 0 auto 1rem auto;
}
/* --- Dark mode toggle --- */
.dark-toggle {
position: fixed;
bottom: 1.5rem;
right: 1.5rem;
padding: 0.75rem;
border-radius: 9999px;
background: white;
border: 1px solid #d1d5db;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
@@ -0,0 +1,9 @@
@page "/PhotoSelectError"
<h3>Oups !!!</h3>
Il semblerait que ce shooting soit inexistant ou bien que cet recherche est infructueuse.
@code {
}
@@ -0,0 +1,154 @@
@page "/Share"
@rendermode InteractiveServer
@using BlazorBootstrap;
@using Microsoft.AspNetCore.WebUtilities
@using ModelsLib
@using PhotoShareHelri.Components.Blocs
@using System.Text
@inject NavigationManager NavManager
@inject EmailService mailService
@inject PhotoService ShootingDb
@inject IJSRuntime JS
@using MudBlazor
<MudContainer MaxWidth="MaxWidth.Large" Class="d-flex justify-center mt-6 mb-6">
<MudPaper Elevation="3" Class="pa-6 selection-wrapper">
<!-- Header -->
<MudStack Spacing="1" AlignItems="AlignItems.Center" Class="mb-4 text-center">
<MudText Typo="Typo.h4">Résultat du shooting</MudText>
<MudText Typo="Typo.h6" Class="title-shoot">@Title</MudText>
</MudStack>
<!-- Galerie -->
<MudGrid Justify="Justify.Center" Spacing="3">
@for (int index = 0; index < Images.Count; index++)
{
var img = Images[index];
<MudItem xs="12" sm="6" md="4" lg="3" xl="2">
<MudPaper Class="image-card pa-2 position-relative" Elevation="2">
<MudImage Src="@img"
Alt="Photo sélection"
Class="gallery-thumb"
Fluid="true"
@onclick="() => OpenModalZoom(img)" />
</MudPaper>
</MudItem>
}
</MudGrid>
</MudPaper>
</MudContainer>
@* Modal de zoom sur une image *@
<ModalDiapo IsOpen="@IsModalZoomOpen"
CurrentImage="@CurrentImage"
CurrentIndex="@CurrentIndex"
IsSelector=false
Total="@Images.Count"
CanNext="@CanNext"
CanPrevious="@CanPrevious"
MaxSelect="@MaxSelect"
SelectedImages="@SelectedImages"
IsSelected="@(CurrentImage != null && @SelectedImages.Contains(@CurrentImage))"
OnClose="CloseModalZoom"
OnNext="NextModalZoom"
OnPrevious="PreviousModalZoom"
Toggle="() => ToggleImage(CurrentImage!)" />
@code {
#region Variables
HashSet<string> SelectedImages = new();
private int MaxSelect = 1;
// Liste des images disponibles
private List<string> Images = new();
public string Title { get; set; }
bool IsModalZoomOpen = false;
string? CurrentImage;
int CurrentIndex =>
CurrentImage == null ? -1 : Images.IndexOf(CurrentImage);
bool CanNext => CurrentIndex >= 0 && CurrentIndex < Images.Count - 1;
bool CanPrevious => CurrentIndex > 0;
private string ShootingId;
#endregion
#region modal zoom
/// <summary>
/// Ouverture de la modal à partir du clic sur image voulu
/// </summary>
void OpenModalZoom(string img)
{
CurrentImage = img;
IsModalZoomOpen = true;
}
///<summary>
/// fermeture de la modal
/// </summary>
void CloseModalZoom()
{
IsModalZoomOpen = false;
}
/// <summary>
/// photo suivante dans la modal
/// dans la liste des photos affichés
/// </summary>
void NextModalZoom()
{
if (CanNext)
CurrentImage = Images[CurrentIndex + 1];
}
/// <summary>
/// photo précédente
/// </summary>
void PreviousModalZoom()
{
if (CanPrevious)
CurrentImage = Images[CurrentIndex - 1];
}
#endregion
/// <summary>
/// arrivé sur la page
/// </summary>
protected override async Task OnInitializedAsync()
{
//récupération de l'identifiant de shooting, si identifiant null ou si recherche inexistante redirection vers la page de recherche de shooting
var uri = NavManager.ToAbsoluteUri(NavManager.Uri);
if (QueryHelpers.ParseQuery(uri.Query).TryGetValue("Id", out var id))
{
if (!string.IsNullOrEmpty(id))
{
ShootingId = id;
Images = ShootingDb.LireContenu(ShootingId, true);
}
}
// Code exécuté au chargement du composant
}
/// <summary>
/// Déclenche le téléchargement de l'image originale sur la modal de visualisation
/// </summary>
/// <param name="img">image sélectionné par son hash</param>
private async Task ToggleImage(string img)
{
var filePath = $"{img}"; // chemin dans wwwroot
await JS.InvokeVoidAsync("downloadFile", filePath, $"{img}");
}
}
@@ -0,0 +1,80 @@
.selection-wrapper {
width: 100%;
max-width: 1400px;
border-radius: 20px;
}
.title-shoot {
opacity: 0.85;
}
.image-card {
border-radius: 16px;
overflow: hidden;
transition: transform 0.2s ease, box-shadow 0.2s ease;
background-color: white;
}
.image-card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0,0,0,0.12);
}
.gallery-thumb {
width: 100%;
height: 250px;
object-fit: cover;
border-radius: 12px;
cursor: pointer;
display: block;
}
.select-fab {
position: absolute !important;
top: 14px;
right: 14px;
z-index: 10;
}
/* Mobile */
@media (max-width: 768px) {
.selection-wrapper {
padding: 20px 12px !important;
border-radius: 14px;
}
.gallery-thumb {
height: 220px;
}
}
.select-icon-btn {
position: absolute !important;
top: 12px;
right: 12px;
z-index: 10;
background-color: rgba(255, 255, 255, 0.92) !important;
backdrop-filter: blur(4px);
border-radius: 50%;
box-shadow: 0 4px 12px rgba(0,0,0,0.18);
transition: all 0.2s ease;
}
.select-icon-btn:hover {
transform: scale(1.08);
}
.select-icon-btn.selected {
border: 2px solid #2e7d32;
color: #2e7d32 !important;
}
.select-icon-btn.not-selected {
border: 2px solid #c62828;
color: #c62828 !important;
}
.select-icon-btn:disabled {
opacity: 0.45;
cursor: not-allowed;
}
@@ -0,0 +1,224 @@
@page "/PhotoSelect"
@rendermode InteractiveServer
@using BlazorBootstrap;
@using Microsoft.AspNetCore.WebUtilities
@using ModelsLib
@using PhotoShareHelri.Components.Blocs
@using System.Text
@inject NavigationManager NavManager
@inject EmailService mailService
@inject PhotoService ShootingDb
@inject IDialogService DialogService
@using MudBlazor
<MudContainer MaxWidth="MaxWidth.Large" Class="d-flex justify-center mt-6 mb-6">
<MudPaper Elevation="3" Class="pa-6 selection-wrapper">
<!-- Header -->
<MudStack Spacing="1" AlignItems="AlignItems.Center" Class="mb-4 text-center">
<MudText Typo="Typo.h4">Sélection des photos</MudText>
<MudText Typo="Typo.h6" Class="title-shoot">@Title</MudText>
</MudStack>
<!-- Action top -->
<MudStack AlignItems="AlignItems.Center" Class="mb-3">
<MudButton Variant="Variant.Outlined"
Color="Color.Dark"
Disabled="@(SelectedImages.Count == 0)"
OnClick="SendSelection">
Valider la sélection
</MudButton>
</MudStack>
<!-- Compteur -->
<MudText Typo="Typo.body1" Align="Align.Center" Class="mb-4">
@SelectedImages.Count / @MaxSelect photo(s) sélectionnée(s)
</MudText>
<!-- Galerie -->
<MudGrid Justify="Justify.Center" Spacing="3">
@for (int index = 0; index < Images.Count; index++)
{
var img = Images[index];
var isSelected = SelectedImages.Contains(img);
var buttonClass = $"select-icon-btn {(isSelected ? "selected" : "not-selected")}";
<MudItem xs="12" sm="6" md="4" lg="3" xl="2">
<MudPaper Class="image-card pa-2 position-relative" Elevation="2">
<MudImage Src="@img"
Alt="Photo sélection"
Class="gallery-thumb"
Fluid="true"
@onclick="() => OpenModalZoom(img)" />
<div @onclick:stopPropagation="true">
<MudIconButton Icon="@(isSelected? Icons.Material.Filled.Check : Icons.Material.Outlined.RadioButtonUnchecked)"
Class="@buttonClass"
Disabled="@(SelectedImages.Count >= MaxSelect && !isSelected)"
OnClick="() => ToggleImage(img)" />
</div>
</MudPaper>
</MudItem>
}
</MudGrid>
<!-- Action bottom -->
<MudStack AlignItems="AlignItems.Center" Class="mt-5">
<MudButton Variant="Variant.Outlined"
Disabled="@(SelectedImages.Count == 0)"
Color="Color.Dark"
OnClick="SendSelection">
Valider la sélection
</MudButton>
</MudStack>
</MudPaper>
</MudContainer>
<ModalDiapo IsOpen="@IsModalZoomOpen"
CurrentImage="@CurrentImage"
CurrentIndex="@CurrentIndex"
IsSelector="true"
Total="@Images.Count"
CanNext="@CanNext"
CanPrevious="@CanPrevious"
MaxSelect="@MaxSelect"
SelectedImages="@SelectedImages"
IsSelected="@(CurrentImage != null && SelectedImages.Contains(CurrentImage))"
OnClose="CloseModalZoom"
OnNext="NextModalZoom"
OnPrevious="PreviousModalZoom"
Toggle="() => ToggleImage(CurrentImage!)" />
@code {
#region Variables
HashSet<string> SelectedImages = new();
private int MaxSelect = 1;
// Liste des images disponibles
private List<string> Images = new();
public string Title { get; set; }
bool IsModalZoomOpen = false;
string? CurrentImage;
int CurrentIndex =>
CurrentImage == null ? -1 : Images.IndexOf(CurrentImage);
bool CanNext => CurrentIndex >= 0 && CurrentIndex < Images.Count - 1;
bool CanPrevious => CurrentIndex > 0;
private string ShootingId;
private ShootingData Datas { get; set; }
#endregion
#region modal zoom
/// <summary>
/// Ouverture de la modal à partir du clic sur image voulu
/// </summary>
void OpenModalZoom(string img)
{
CurrentImage = img;
IsModalZoomOpen = true;
}
///<summary>
/// fermeture de la modal
/// </summary>
void CloseModalZoom()
{
IsModalZoomOpen = false;
}
/// <summary>
/// photo suivante dans la modal
/// dans la liste des photos affichés
/// </summary>
void NextModalZoom()
{
if (CanNext)
CurrentImage = Images[CurrentIndex + 1];
}
/// <summary>
/// photo précédente
/// </summary>
void PreviousModalZoom()
{
if (CanPrevious)
CurrentImage = Images[CurrentIndex - 1];
}
#endregion
/// <summary>
/// arrivé sur la page
/// </summary>
protected override async Task OnInitializedAsync()
{
//récupération de l'identifiant de shooting, si identifiant null ou si recherche inexistante redirection vers la page de recherche de shooting
var uri = NavManager.ToAbsoluteUri(NavManager.Uri);
if (QueryHelpers.ParseQuery(uri.Query).TryGetValue("Id", out var id))
{
Datas = ShootingDb.GetInfosShooting(id);
if (!string.IsNullOrEmpty(id))
{
ShootingId = id;
Images = ShootingDb.LireContenu(ShootingId);
}
Title = "Résultat du shooting " + Datas.Key;
}
}
/// <summary>
/// Séléction de l'image dans la liste des photos choisies
/// </summary>
/// <param name="img">image sélectionné par son hash</param>
private void ToggleImage(string img)
{
if (SelectedImages.Contains(img))
{
SelectedImages.Remove(img);
}
else
{
SelectedImages.Add(img);
}
}
/// <summary>
/// envoi par mail de la sélection des photos
/// </summary>
private async void SendSelection()
{
if (SelectedImages == null || SelectedImages.Count == 0)
return;
var fichiers = string.Join(";", SelectedImages)
.Split(';', StringSplitOptions.RemoveEmptyEntries)
.Select(Path.GetFileName)
.ToList();
var body = new StringBuilder();
body.Append("<h3>Photos sélectionnées 📸</h3>");
body.Append("<ul>");
foreach (var fichier in fichiers)
{
body.Append($"<li>{fichier}</li>");
}
body.Append("</ul>");
mailService.EnvoyerMailAsync("Confirmation sélection du shooting" + ShootingId, body).GetAwaiter();
Datas.IsPhotosSelected = true;
await ShootingDb.UpdateShootingInfo(Datas);
}
}
@@ -0,0 +1,80 @@
.selection-wrapper {
width: 100%;
max-width: 1400px;
border-radius: 20px;
}
.title-shoot {
opacity: 0.85;
}
.image-card {
border-radius: 16px;
overflow: hidden;
transition: transform 0.2s ease, box-shadow 0.2s ease;
background-color: white;
}
.image-card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0,0,0,0.12);
}
.gallery-thumb {
width: 100%;
height: 250px;
object-fit: cover;
border-radius: 12px;
cursor: pointer;
display: block;
}
.select-fab {
position: absolute !important;
top: 14px;
right: 14px;
z-index: 10;
}
/* Mobile */
@media (max-width: 768px) {
.selection-wrapper {
padding: 20px 12px !important;
border-radius: 14px;
}
.gallery-thumb {
height: 220px;
}
}
.select-icon-btn {
position: absolute !important;
top: 12px;
right: 12px;
z-index: 10;
background-color: rgba(255, 255, 255, 0.92) !important;
backdrop-filter: blur(4px);
border-radius: 50%;
box-shadow: 0 4px 12px rgba(0,0,0,0.18);
transition: all 0.2s ease;
}
.select-icon-btn:hover {
transform: scale(1.08);
}
.select-icon-btn.selected {
border: 2px solid #2e7d32;
color: #2e7d32 !important;
}
.select-icon-btn.not-selected {
border: 2px solid #c62828;
color: #c62828 !important;
}
.select-icon-btn:disabled {
opacity: 0.45;
cursor: not-allowed;
}
@@ -0,0 +1,58 @@
@page "/ShootingDesire"
@rendermode InteractiveServer
@inject IWebHostEnvironment Env
@inject DesireService DesireDb
<PageTitle>Mes Envies de shooting futur</PageTitle>
<main class="home-container">
<section class="shooting-section">
<MudContainer MaxWidth="MaxWidth.Medium" Class="text-center">
<!-- Titre -->
<MudText Typo="Typo.h4" Class="mb-4">
Mes envies de shootings
</MudText>
<!-- Intro -->
<MudText Typo="Typo.body1" Class="mb-6">
Envie de partager un moment ensemble ?
Un de ces projets vous parle et vous souhaiteriez qu'on le réalise ensemble ?
Contactez-moi afin que l'on puisse en discuter et mettre
tout en œuvre pour la réalisation !
</MudText>
<!-- Cartes -->
<MudGrid Justify="Justify.Center" Spacing="3">
<MudItem xs="12" sm="8" md="6">
<MudPaper Class="pa-4 text-center" Elevation="2">
@foreach (var envie in EnviesShooting)
{
<MudText Typo="Typo.body1">
📷 @envie
</MudText>
}
<!-- Texte bas -->
<MudText Typo="Typo.body2" Class="mt-6">
Et bien d'autres encore !!!
</MudText>
</MudPaper>
</MudItem>
</MudGrid>
</MudContainer>
</section>
</main>
@code {
private List<string> EnviesShooting;
protected override void OnInitialized()
{
EnviesShooting = DesireDb.GetDEsireShooting();
}
}
@@ -0,0 +1,133 @@
.social-title {
font-family: 'Playfair Display', serif;
font-size: 1.75rem;
font-style: italic;
}
/* --- Containers --- */
.home-container {
width: 90%; /* occupe 90% de l’écran sur petits écrans */
max-width: 800px; /* limite la largeur sur grands écrans */
margin: 0 auto;
padding: 3rem 1.5rem;
display: flex;
flex-direction: column;
gap: 3rem;
overflow-y: scroll; /* Permet le scroll vertical */
scrollbar-width: none; /* Firefox */
}
.home-container::-webkit-scrollbar {
display: none; /* Chrome, Safari, Edge */
}
.social-subtitle {
color: #71717a;
font-weight: 300;
}
.social-buttons {
display: flex;
flex-direction: column;
align-items: center;
gap: 1.5rem;
}
.instagram-button {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.75rem 2rem;
border: 1px solid #d1d5db;
.title {
margin-top: 40px;
margin-bottom: 20px;
}
.intro {
max-width: 800px;
margin: auto;
margin-bottom: 40px;
}
.shooting-container {
display: grid;
grid-template-columns: repeat(auto-fit,minmax(220px,1fr));
gap: 20px;
max-width: 900px;
margin: auto;
}
.shooting-card {
background: white;
border-radius: 12px;
padding: 15px;
box-shadow: 0 6px 15px rgba(0,0,0,0.15);
transition: transform 0.2s, box-shadow 0.2s;
font-weight: 500;
}
.shooting-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 25px rgba(0,0,0,0.25);
}
border-radius: 9999px;
background: white;
font-weight: 500;
text-decoration: none;
transition: all 0.2s;
}
.instagram-button:hover {
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.qr-wrapper {
padding: 1.5rem;
background: white;
border: 1px solid #d1d5db;
border-radius: 1rem;
display: flex;
flex-direction: column;
align-items: center;
}
.qr-image {
width: 128px;
height: 128px;
}
.qr-text {
margin-top: 0.5rem;
font-size: 0.625rem;
color: #71717a;
letter-spacing: 0.05em;
text-transform: uppercase;
font-weight: 600;
}
.gallery-wrapper {
display: flex;
flex-direction: column;
}
.header-selection {
display: flex;
align-items: center;
width: 60%;
}
.titles {
flex: 1;
text-align: center;
}
.shooting-box {
border: 2px solid #444;
border-radius: 12px;
padding: 20px;
max-width: 500px;
margin: auto;
background-color: #f8f8f8;
box-shadow: 0 4px 10px rgba(0,0,0,0.15);
}
+6
View File
@@ -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>
+11
View File
@@ -0,0 +1,11 @@
@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 PhotoShareHelri
@using PhotoShareHelri.Components
@using MudBlazor
+13
View File
@@ -0,0 +1,13 @@
# Build
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY . .
RUN dotnet publish -c Release -o /app/publish
# Runtime
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY --from=build /app/publish .
ENV ASPNETCORE_URLS=http://+:8080
EXPOSE 8080
ENTRYPOINT ["dotnet", "PhotoShareHelri.dll"]
+36
View File
@@ -0,0 +1,36 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>a841ad02-d180-4ec3-90a2-908747712f4c</UserSecretsId>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<ItemGroup>
<Compile Remove="wwwroot\bootstrap\**" />
<Content Remove="wwwroot\bootstrap\**" />
<EmbeddedResource Remove="wwwroot\bootstrap\**" />
<None Remove="wwwroot\bootstrap\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Blazor.Bootstrap" Version="3.5.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.23.0" />
<PackageReference Include="MudBlazor" Version="9.2.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ModelsLib\ModelsLib.csproj" />
<ProjectReference Include="..\PhotoShareDb\PhotoShareDb.csproj" />
<ProjectReference Include="..\PhotoShareDll\ToolsPhotoShare.csproj" />
</ItemGroup>
</Project>
+40
View File
@@ -0,0 +1,40 @@
using Microsoft.EntityFrameworkCore;
using MudBlazor.Services;
using Npgsql;
using PhotoShareHelri.Components;
using System;
using ToolsPhotoShare;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
builder.Services.AddMudServices();
builder.Services.AddScoped<EmailService>();
builder.Services.AddDbContextFactory<PhotoShareDbContext>(options =>
options.UseNpgsql(
builder.Configuration.GetConnectionString("DefaultConnection")
));
builder.Services.AddScoped<ConfigAppServices>();
builder.Services.AddScoped<PhotoService>();
builder.Services.AddScoped<DesireService>();
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.UseStaticFiles();
app.UseAntiforgery();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
app.Run();
@@ -0,0 +1,49 @@
{
"profiles": {
"http": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"dotnetRunMessages": true,
"applicationUrl": "http://localhost:5262"
},
"https": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"dotnetRunMessages": true,
"applicationUrl": "https://localhost:7042;http://localhost:5262"
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Container (Dockerfile)": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
"environmentVariables": {
"ASPNETCORE_HTTPS_PORTS": "8081",
"ASPNETCORE_HTTP_PORTS": "8080"
},
"publishAllPorts": true,
"useSSL": true
}
},
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:17310",
"sslPort": 44351
}
}
}
@@ -0,0 +1,22 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Smtp": {
"Host": "smtp.gmail.com",
"Port": 587,
"User": "harlay.k1@gmail.com",
"Password": "zned sjls vypl bqgo",
"From": "harlay.k1@gmail.com"
},
"ConnectionStrings": {
"DefaultConnection": "Host=192.168.1.50;Port=5433;Database=PhotoShareDB;Username=Helri;Password=HarKev1191."
//"DefaultConnection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=PhotoShareDB;Integrated Security=True;Connect Timeout=30;Encrypt=True;Trust Server Certificate=False;Application Intent=ReadWrite;Multi Subnet Failover=False;Command Timeout=30"
}
}
+19
View File
@@ -0,0 +1,19 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Smtp": {
"Host": "smtp.gmail.com",
"Port": 587,
"User": "harlay.k1@gmail.com",
"Password": "zned sjls vypl bqgo",
"From": "harlay.k1@gmail.com"
},
"ConnectionStrings": {
"DefaultConnection": "Host=192.168.1.50;Port=5433;Database=PhotoShareDB;Username=Helri;Password=HarKev1191."
}
}
@@ -0,0 +1,21 @@
Dans les dunes
Cosplay
Foret tenue transparente fée nature
Tenue guerrier cote de maille
Sous leau
Piscine / sauna
Geisha
Pin-up
Fermier/ière
Femme enceinte
Tenue de sport annee 80
Halloween sorcière
Cabane en forêt
Avec drap soie transparence
Ballerine (dans un context anormal)
Tenue de soirée haut de gamme soirée mondaine
Style Grèce antique/ déesse
Toit immeuble domination monde
Cafe en terasse
Horreur
Bain de lait
Binary file not shown.

After

Width:  |  Height:  |  Size: 713 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

+51
View File
@@ -0,0 +1,51 @@
html, body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
a, .btn-link {
color: #006bb7;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}
.content {
padding-top: 1.1rem;
}
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;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

+8
View File
@@ -0,0 +1,8 @@
window.downloadFile = (url, fileName) => {
const link = document.createElement('a');
link.href = url;
link.download = fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};