commit 25edd4fac730248a589cf93d33967c7ee8a6f7ce Author: Helri Date: Sat May 16 21:29:22 2026 +0200 first push diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..fe1152b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,30 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md +!**/.gitignore +!.git/HEAD +!.git/config +!.git/packed-refs +!.git/refs/heads/** \ No newline at end of file diff --git a/.env b/.env new file mode 100644 index 0000000..54cb40c --- /dev/null +++ b/.env @@ -0,0 +1 @@ +SA_PASSWORD=PWD_SQL \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2f1cfc8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,374 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.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 + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# 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 +# Note: 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 +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable 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 +*.appx +*.appxbundle +*.appxupload + +# 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 +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# 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 +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# 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/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd +/PhotoShareDb/Migrations/20260211083337_InitialCreate.cs +/PhotoShareDb/Migrations/20260211083337_InitialCreate.Designer.cs +/PhotoShareDb/Migrations/20260211123353_UpdateConfigDB.cs +/PhotoShareDb/Migrations/20260211123353_UpdateConfigDB.Designer.cs +/PhotoShareDb/Migrations/20260219190425_InitPostgres.cs +/PhotoShareDb/Migrations/20260219190425_InitPostgres.Designer.cs +/PhotoShareDb/Migrations/20260223141441_AddShootingTable.cs +/PhotoShareDb/Migrations/20260223141441_AddShootingTable.Designer.cs +/PhotoShareDb/Migrations/PhotoShareDbContextModelSnapshot.cs +/photoshare.tar +/testphotoshare.tar diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c657172 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +# Build +FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build +WORKDIR /src +COPY . . +RUN dotnet publish -c Release -o /app/publish + +# Runtime +FROM mcr.microsoft.com/dotnet/aspnet:10.0 +WORKDIR /app +COPY --from=build /app/publish . +ENV ASPNETCORE_URLS=http://+:8080 +EXPOSE 8080 +ENTRYPOINT ["dotnet", "PhotoShareHelri.dll"] \ No newline at end of file diff --git a/ModelsLib/BDD_Models/DesireData.cs b/ModelsLib/BDD_Models/DesireData.cs new file mode 100644 index 0000000..e172041 --- /dev/null +++ b/ModelsLib/BDD_Models/DesireData.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ModelsLib.BDD_Models +{ + [Table("T_DesireData")] + public class DesireData + { + public DesireData(int key, string desire) + { + Key = key; + Desire = desire; + } + + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Key { get; set; } + + public string Desire { get; set; } + } +} diff --git a/ModelsLib/BDD_Models/ParametersDb.cs b/ModelsLib/BDD_Models/ParametersDb.cs new file mode 100644 index 0000000..c4a7a4d --- /dev/null +++ b/ModelsLib/BDD_Models/ParametersDb.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace ModelsLib +{ + /// + /// classe modèle de la table de paramètrage + /// + [Table("T_Params")] + public class ParametersDb + { + public ParametersDb(string key, string value) + { + Key = key; + Value = value; + } + + [Key] + public string Key { get; set; } + + public string Value { get; set; } + + + } +} diff --git a/ModelsLib/BDD_Models/ShootingData.cs b/ModelsLib/BDD_Models/ShootingData.cs new file mode 100644 index 0000000..fdfce8b --- /dev/null +++ b/ModelsLib/BDD_Models/ShootingData.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ModelsLib +{ + [Table("T_ShootingData")] + public class ShootingData + { + public ShootingData(string key, bool isPhotosSelected, int maxChoice, DateTime shootingDate, string instagramName) + { + Key = key; + IsPhotosSelected = isPhotosSelected; + MaxChoice = maxChoice; + ShootingDate = shootingDate; + InstagramName = instagramName; + } + + [Key] + public string Key { get; set; } + + public bool IsPhotosSelected { get; set; } + + public int MaxChoice { get; set; } + + public DateTime ShootingDate { get; set; } + + public string InstagramName { get; set; } + + + } +} diff --git a/ModelsLib/ContactModel.cs b/ModelsLib/ContactModel.cs new file mode 100644 index 0000000..9ac8f15 --- /dev/null +++ b/ModelsLib/ContactModel.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ModelsLib +{ + public class ContactModel + { + [Required(ErrorMessage = "Le nom est obligatoire")] + public string Name { get; set; } = string.Empty; + + public string Title { get; set; } = string.Empty; + + [Required(ErrorMessage = "L'email est obligatoire")] + [EmailAddress(ErrorMessage = "Email invalide")] + public string Email { get; set; } = string.Empty; + + [Required(ErrorMessage = "Le message est obligatoire")] + [MinLength(10, ErrorMessage = "Le message doit contenir au moins 10 caractères")] + public string Message { get; set; } = string.Empty; + } +} diff --git a/ModelsLib/EntityHome.cs b/ModelsLib/EntityHome.cs new file mode 100644 index 0000000..1e84c94 --- /dev/null +++ b/ModelsLib/EntityHome.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ModelsLib +{ + public class EntityHome + { + /// + /// Nom sur haut de page + /// + public string TitlePseudo { get; set; } + + public string SubTitle1 { get; set; } + public string SubTitle2 { get; set; } + + public string InstaLink { get; set; } + + public string InstaQrCode { get; set; } + + public string Credentials { get; set; } + } +} diff --git a/ModelsLib/ModelsLib.csproj b/ModelsLib/ModelsLib.csproj new file mode 100644 index 0000000..3ab765c --- /dev/null +++ b/ModelsLib/ModelsLib.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + enable + enable + + + + + + + diff --git a/ModelsLib/PhotoUnit.cs b/ModelsLib/PhotoUnit.cs new file mode 100644 index 0000000..bd80606 --- /dev/null +++ b/ModelsLib/PhotoUnit.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Mvc; + +namespace ModelsLib +{ + public class PhotoUnit + { + public PhotoUnit() + { + + } + public PhotoUnit(string photoName, string photoPath,bool isSelected) + { + PhotoName = photoName; + PhotoPath = photoPath; + IsSelected = isSelected; + } + + public string PhotoName { get; set; } + + public bool IsSelected { get; set; } + + public string PhotoPath { get; set; } + + + + } +} diff --git a/PhotoShareDb/Migrations/20260315131731_AddDesireTable.Designer.cs b/PhotoShareDb/Migrations/20260315131731_AddDesireTable.Designer.cs new file mode 100644 index 0000000..3e03835 --- /dev/null +++ b/PhotoShareDb/Migrations/20260315131731_AddDesireTable.Designer.cs @@ -0,0 +1,83 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace PhotoShareDb.Migrations +{ + [DbContext(typeof(PhotoShareDbContext))] + [Migration("20260315131731_AddDesireTable")] + partial class AddDesireTable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("ModelsLib.BDD_Models.DesireData", b => + { + b.Property("Key") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Key")); + + b.Property("Desire") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Key"); + + b.ToTable("T_DesireData", (string)null); + }); + + modelBuilder.Entity("ModelsLib.ParametersDb", b => + { + b.Property("Key") + .HasColumnType("text"); + + b.Property("Value") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Key"); + + b.ToTable("T_Params", (string)null); + }); + + modelBuilder.Entity("ModelsLib.ShootingData", b => + { + b.Property("Key") + .HasColumnType("text"); + + b.Property("InstagramName") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsPhotosSelected") + .HasColumnType("boolean"); + + b.Property("MaxChoice") + .HasColumnType("integer"); + + b.Property("ShootingDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Key"); + + b.ToTable("T_ShootingData", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/PhotoShareDb/Migrations/20260315131731_AddDesireTable.cs b/PhotoShareDb/Migrations/20260315131731_AddDesireTable.cs new file mode 100644 index 0000000..6f1ef62 --- /dev/null +++ b/PhotoShareDb/Migrations/20260315131731_AddDesireTable.cs @@ -0,0 +1,35 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace PhotoShareDb.Migrations +{ + /// + public partial class AddDesireTable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "T_DesireData", + columns: table => new + { + Key = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Desire = table.Column(type: "text", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_T_DesireData", x => x.Key); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "T_DesireData"); + } + } +} diff --git a/PhotoShareDb/Migrations/20260315140529_AddDesireTable2.Designer.cs b/PhotoShareDb/Migrations/20260315140529_AddDesireTable2.Designer.cs new file mode 100644 index 0000000..b4e6331 --- /dev/null +++ b/PhotoShareDb/Migrations/20260315140529_AddDesireTable2.Designer.cs @@ -0,0 +1,83 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace PhotoShareDb.Migrations +{ + [DbContext(typeof(PhotoShareDbContext))] + [Migration("20260315140529_AddDesireTable2")] + partial class AddDesireTable2 + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("ModelsLib.BDD_Models.DesireData", b => + { + b.Property("Key") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Key")); + + b.Property("Desire") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Key"); + + b.ToTable("T_DesireData", (string)null); + }); + + modelBuilder.Entity("ModelsLib.ParametersDb", b => + { + b.Property("Key") + .HasColumnType("text"); + + b.Property("Value") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Key"); + + b.ToTable("T_Params", (string)null); + }); + + modelBuilder.Entity("ModelsLib.ShootingData", b => + { + b.Property("Key") + .HasColumnType("text"); + + b.Property("InstagramName") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsPhotosSelected") + .HasColumnType("boolean"); + + b.Property("MaxChoice") + .HasColumnType("integer"); + + b.Property("ShootingDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Key"); + + b.ToTable("T_ShootingData", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/PhotoShareDb/Migrations/20260315140529_AddDesireTable2.cs b/PhotoShareDb/Migrations/20260315140529_AddDesireTable2.cs new file mode 100644 index 0000000..36d3b15 --- /dev/null +++ b/PhotoShareDb/Migrations/20260315140529_AddDesireTable2.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace PhotoShareDb.Migrations +{ + /// + public partial class AddDesireTable2 : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/PhotoShareDb/PhotoShareDb.csproj b/PhotoShareDb/PhotoShareDb.csproj new file mode 100644 index 0000000..c9ccbec --- /dev/null +++ b/PhotoShareDb/PhotoShareDb.csproj @@ -0,0 +1,21 @@ + + + + net10.0 + enable + enable + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/PhotoShareDb/PhotoShareDbContext.cs b/PhotoShareDb/PhotoShareDbContext.cs new file mode 100644 index 0000000..07efe2c --- /dev/null +++ b/PhotoShareDb/PhotoShareDbContext.cs @@ -0,0 +1,21 @@ +using Microsoft.EntityFrameworkCore; +using ModelsLib; +using ModelsLib.BDD_Models; +using System.Collections.Generic; + +public class PhotoShareDbContext : DbContext +{ + public PhotoShareDbContext(DbContextOptions options) + : base(options) + { + } + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity().ToTable("T_Params"); + modelBuilder.Entity().ToTable("T_ShootingData"); + modelBuilder.Entity().ToTable("T_DesireData"); + } + public DbSet ConfigDb { get; set; } + public DbSet ShootingDb { get; set; } + public DbSet DesireDb { get; set; } +} \ No newline at end of file diff --git a/PhotoShareDb/PhotoShareDbContextFactory.cs b/PhotoShareDb/PhotoShareDbContextFactory.cs new file mode 100644 index 0000000..0c15581 --- /dev/null +++ b/PhotoShareDb/PhotoShareDbContextFactory.cs @@ -0,0 +1,23 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.Extensions.Configuration; +using System.IO; + +public class PhotoShareDbContextFactory : IDesignTimeDbContextFactory +{ + public PhotoShareDbContext CreateDbContext(string[] args) + { + // Récupère la configuration depuis appsettings.json + var configuration = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json") + .Build(); + + var optionsBuilder = new DbContextOptionsBuilder(); + var connectionString = configuration.GetConnectionString("DefaultConnection"); + + optionsBuilder.UseNpgsql(connectionString); + + return new PhotoShareDbContext(optionsBuilder.Options); + } +} diff --git a/PhotoShareDll/Files/FileTools.cs b/PhotoShareDll/Files/FileTools.cs new file mode 100644 index 0000000..8be13bd --- /dev/null +++ b/PhotoShareDll/Files/FileTools.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ToolsPhotoShare +{ + public class FileTools + { + public void TestExistingFile(string path) + { + if (!System.IO.File.Exists(Path.Combine(path, "select.csv"))) + { + System.IO.File.Create(Path.Combine(path, "select.csv")); + } + } + + public List GetFilesSelected(string path) + { + TestExistingFile(path); + + return new List(); + } + + public static bool TestExistingShooting(string path) + { + return System.IO.Directory.Exists(path); + } + + public void AddSelectedToSaveCsv(List files) + { + + } + + } +} diff --git a/PhotoShareDll/Properties/launchSettings.json b/PhotoShareDll/Properties/launchSettings.json new file mode 100644 index 0000000..afaeddf --- /dev/null +++ b/PhotoShareDll/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "ToolsPhotoShare": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:60332;http://localhost:60333" + } + } +} \ No newline at end of file diff --git a/PhotoShareDll/Services/ConfigAppServices.cs b/PhotoShareDll/Services/ConfigAppServices.cs new file mode 100644 index 0000000..a26a6ec --- /dev/null +++ b/PhotoShareDll/Services/ConfigAppServices.cs @@ -0,0 +1,53 @@ +using Microsoft.EntityFrameworkCore; +using ModelsLib; + +public class ConfigAppServices +{ + private readonly IDbContextFactory _factory; + + public ConfigAppServices(IDbContextFactory factory) + { + _factory = factory; + } + + public async Task FillConfigTableWithDataPerDefault() + { + using var db = _factory.CreateDbContext(); + + db.ConfigDb.AddRange( + new ParametersDb("InstaQrCode", "/Images/qr.png"), + new ParametersDb("InstaLink", "https://www.instagram.com/helrira/"), + new ParametersDb("TitlePseudo", "Kévin"), + new ParametersDb("SubTitle1", "Passionné par la photo depuis 2025, je me spécialise dans le portrait avec modèles pour capturer l'essence et l'émotion de chaque rencontre."), + new ParametersDb("SubTitle2", "J'aime que chaque séance soit cadrée tout en gardant une part de surprise, de non prévu et surtout un bon moment pour chacun."), + new ParametersDb("Credentials", "Kévin Harlay(Helrira)") + ); + + await db.SaveChangesAsync(); + } + public async Task HasAnyParametersAsync() + { + using var db = _factory.CreateDbContext(); + return await db.ConfigDb.AnyAsync(); + } + + + public async Task readParametersApplication() + { + using var db = _factory.CreateDbContext(); + + var confDb = await db.ConfigDb + .AsNoTracking() + .ToDictionaryAsync(p => p.Key, p => p.Value); + + return new EntityHome + { + InstaQrCode = confDb["InstaQrCode"], + InstaLink = confDb["InstaLink"], + TitlePseudo = confDb["TitlePseudo"], + SubTitle1 = confDb["SubTitle1"], + SubTitle2 = confDb["SubTitle2"], + Credentials = confDb["Credentials"] + }; + } +} diff --git a/PhotoShareDll/Services/DesireService.cs b/PhotoShareDll/Services/DesireService.cs new file mode 100644 index 0000000..859c95b --- /dev/null +++ b/PhotoShareDll/Services/DesireService.cs @@ -0,0 +1,25 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.EntityFrameworkCore; +using ModelsLib; +using System; +using System.Collections.Generic; +using System.Text; + +public class DesireService +{ + private readonly IDbContextFactory _factory; + private readonly IWebHostEnvironment _env; + + public DesireService(IDbContextFactory factory, IWebHostEnvironment env) + { + _factory = factory; + _env = env; + } + + public List GetDEsireShooting() + { + using var db = _factory.CreateDbContext(); + + return db.DesireDb.Select(ds => ds.Desire).ToList(); + } +} diff --git a/PhotoShareDll/Services/MailTools.cs b/PhotoShareDll/Services/MailTools.cs new file mode 100644 index 0000000..63c9ef0 --- /dev/null +++ b/PhotoShareDll/Services/MailTools.cs @@ -0,0 +1,70 @@ +using System.Net; +using System.Net.Mail; +using System.Text; +using Microsoft.Extensions.Configuration; + +public class EmailService +{ + private readonly IConfiguration _configuration; + + public EmailService(IConfiguration configuration) + { + _configuration = configuration; + } + + public async Task EnvoyerMailAsync(string sujet, StringBuilder contenuHtml) + { + var smtpConfig = _configuration.GetSection("Smtp"); + + var client = new SmtpClient + { + Host = smtpConfig["Host"], + Port = int.Parse(smtpConfig["Port"]), + EnableSsl = true, + Credentials = new NetworkCredential( + smtpConfig["User"], + smtpConfig["Password"] + ) + }; + + var mail = new MailMessage + { + From = new MailAddress(smtpConfig["From"]), + Subject = sujet, + Body = contenuHtml.ToString(), + IsBodyHtml = true + }; + + mail.To.Add(smtpConfig["From"]); + + await client.SendMailAsync(mail); + } + + public async Task EnvoyerMailContactAsync(string Contact, string sujet, StringBuilder contenuHtml) + { + var smtpConfig = _configuration.GetSection("Smtp"); + + var client = new SmtpClient + { + Host = smtpConfig["Host"], + Port = int.Parse(smtpConfig["Port"]), + EnableSsl = true, + Credentials = new NetworkCredential( + smtpConfig["User"], + smtpConfig["Password"] + ) + }; + + var mail = new MailMessage + { + From = new MailAddress(smtpConfig["From"]), + Subject = sujet, + Body = contenuHtml.ToString(), + IsBodyHtml = true + }; + + mail.To.Add(Contact); + + await client.SendMailAsync(mail); + } +} diff --git a/PhotoShareDll/Services/PhotoServices.cs b/PhotoShareDll/Services/PhotoServices.cs new file mode 100644 index 0000000..dd64af9 --- /dev/null +++ b/PhotoShareDll/Services/PhotoServices.cs @@ -0,0 +1,83 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.EntityFrameworkCore; +using ModelsLib; +using System.IO; + + +public class PhotoService +{ + private readonly IDbContextFactory _factory; + private readonly IWebHostEnvironment _env; + + public PhotoService(IDbContextFactory factory, IWebHostEnvironment env) + { + _factory = factory; + _env = env; + } + + /// + /// + /// + /// + /// Si true alors post traitement donc chargement du dossier export du dossier du shooting + /// + public List LireContenu(string IdShooting, bool isSelected = false) + { + string path = "Photos/" + IdShooting; + if (isSelected) + { + path = path + "/export"; + } + var photosPath = Path.Combine(_env.WebRootPath, path); + List resultPathsAllPhotos = new List(); + + + if (Directory.Exists(photosPath)) + { + var files = Directory.GetFiles(photosPath, "*.jpg"); + + foreach (var file in files) + { + var fullPath = file; // ton chemin complet + + var relativePath = Path.GetRelativePath(_env.WebRootPath, fullPath); + resultPathsAllPhotos.Add(relativePath); + } + } + + return resultPathsAllPhotos; + } + + public ShootingData GetInfosShooting(string idShooting) + { + using var db = _factory.CreateDbContext(); + + return db.ShootingDb.Where(s => s.Key == idShooting).FirstOrDefault(); + } + + public async Task AddShootingInfoPerDefault(string idShooting) + { + await using var db = _factory.CreateDbContext(); + + db.ShootingDb.Add( + new ShootingData(idShooting, false, 20, DateTime.UtcNow, "") + ); + + await db.SaveChangesAsync(); + } + + public async Task UpdateShootingInfo(ShootingData shootingData) + { + using var db = _factory.CreateDbContext(); + + var entity = await db.ShootingDb.FirstOrDefaultAsync(x => x.Key == shootingData.Key); + + if (entity != null) + { + entity.IsPhotosSelected = true; + + await db.SaveChangesAsync(); + } + } + +} diff --git a/PhotoShareDll/ToolsPhotoShare.csproj b/PhotoShareDll/ToolsPhotoShare.csproj new file mode 100644 index 0000000..5fda34f --- /dev/null +++ b/PhotoShareDll/ToolsPhotoShare.csproj @@ -0,0 +1,24 @@ + + + + net10.0 + enable + enable + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + diff --git a/PhotoShareHelri.sln b/PhotoShareHelri.sln new file mode 100644 index 0000000..7a5fac9 --- /dev/null +++ b/PhotoShareHelri.sln @@ -0,0 +1,43 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.2.11408.102 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ToolsPhotoShare", "PhotoShareDll\ToolsPhotoShare.csproj", "{1E146612-8436-47F6-9D54-7488DD3822FB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModelsLib", "ModelsLib\ModelsLib.csproj", "{4EC10D38-2A5D-4B3E-935E-43C552C5A16E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PhotoShareHelri", "PhotoShareHelri\PhotoShareHelri.csproj", "{0592C280-4C4A-455A-8C45-7DC86CF26AF0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PhotoShareDb", "PhotoShareDb\PhotoShareDb.csproj", "{2D1B67E3-5CB7-4A0E-B0E7-A2E31D23E003}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1E146612-8436-47F6-9D54-7488DD3822FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1E146612-8436-47F6-9D54-7488DD3822FB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1E146612-8436-47F6-9D54-7488DD3822FB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1E146612-8436-47F6-9D54-7488DD3822FB}.Release|Any CPU.Build.0 = Release|Any CPU + {4EC10D38-2A5D-4B3E-935E-43C552C5A16E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4EC10D38-2A5D-4B3E-935E-43C552C5A16E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4EC10D38-2A5D-4B3E-935E-43C552C5A16E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4EC10D38-2A5D-4B3E-935E-43C552C5A16E}.Release|Any CPU.Build.0 = Release|Any CPU + {0592C280-4C4A-455A-8C45-7DC86CF26AF0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0592C280-4C4A-455A-8C45-7DC86CF26AF0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0592C280-4C4A-455A-8C45-7DC86CF26AF0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0592C280-4C4A-455A-8C45-7DC86CF26AF0}.Release|Any CPU.Build.0 = Release|Any CPU + {2D1B67E3-5CB7-4A0E-B0E7-A2E31D23E003}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2D1B67E3-5CB7-4A0E-B0E7-A2E31D23E003}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2D1B67E3-5CB7-4A0E-B0E7-A2E31D23E003}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2D1B67E3-5CB7-4A0E-B0E7-A2E31D23E003}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {EABE6739-9C04-4B68-9164-AF0564AC4D0A} + EndGlobalSection +EndGlobal diff --git a/PhotoShareHelri/Components/App.razor b/PhotoShareHelri/Components/App.razor new file mode 100644 index 0000000..b584014 --- /dev/null +++ b/PhotoShareHelri/Components/App.razor @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PhotoShareHelri/Components/Blocs/ModalDiapo.razor b/PhotoShareHelri/Components/Blocs/ModalDiapo.razor new file mode 100644 index 0000000..6890291 --- /dev/null +++ b/PhotoShareHelri/Components/Blocs/ModalDiapo.razor @@ -0,0 +1,88 @@ +@using MudBlazor + +@if (IsOpen && !string.IsNullOrWhiteSpace(CurrentImage)) +{ + + + +
+ + + + + + + ⬅ Précédent + + + + @(IsSelector ? (IsSelected ? "Retirer" : "Choisir") : "Télécharger") + + + + Suivant ➡ + + + + + @(CurrentIndex + 1) / @Total + +
+ + +
+
+} + +@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 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(); + } +} + diff --git a/PhotoShareHelri/Components/Blocs/ModalDiapo.razor.css b/PhotoShareHelri/Components/Blocs/ModalDiapo.razor.css new file mode 100644 index 0000000..d864ede --- /dev/null +++ b/PhotoShareHelri/Components/Blocs/ModalDiapo.razor.css @@ -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); + } diff --git a/PhotoShareHelri/Components/Layout/MainLayout.razor b/PhotoShareHelri/Components/Layout/MainLayout.razor new file mode 100644 index 0000000..d5c850f --- /dev/null +++ b/PhotoShareHelri/Components/Layout/MainLayout.razor @@ -0,0 +1,29 @@ +@using MudBlazor +@inherits LayoutComponentBase +@* Required *@ + + + +@* Needed for dialogs *@ + + +@* Needed for snackbars *@ + + + + + + + + + + @Body + + + + + +
+ Une erreur dans le traitement est survenue. + Recharger +
diff --git a/PhotoShareHelri/Components/Layout/MainLayout.razor.css b/PhotoShareHelri/Components/Layout/MainLayout.razor.css new file mode 100644 index 0000000..3b75473 --- /dev/null +++ b/PhotoShareHelri/Components/Layout/MainLayout.razor.css @@ -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; + } diff --git a/PhotoShareHelri/Components/Layout/NavMenu.razor b/PhotoShareHelri/Components/Layout/NavMenu.razor new file mode 100644 index 0000000..c7d6033 --- /dev/null +++ b/PhotoShareHelri/Components/Layout/NavMenu.razor @@ -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 + + + + + + + + Photo Sharing Helrira + + + + + + + + + Projets + + + Contact + + + + + + + + + Rechercher + + + + + + + + + + + + + + + + Photo Sharing Helrira + + + + + + + + + + + + + + + Projets + Contact + + + + + +@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(this, SendSelectionMobil)); + var options = new DialogOptions + { + MaxWidth = MaxWidth.Small, + FullWidth = true + }; + var dialogRef = await DialogService.ShowAsync("", 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; + /// + /// a la validation de la saisie + /// + private async void SendSelectionMobil(string value) + { + Id = value; + //si une saisie est faite dans le champs + SendSelection(); + } + + + /// + /// a la validation de la saisie + /// + 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 + } + + + } + } +} diff --git a/PhotoShareHelri/Components/Layout/NavMenu.razor.css b/PhotoShareHelri/Components/Layout/NavMenu.razor.css new file mode 100644 index 0000000..a24ae3d --- /dev/null +++ b/PhotoShareHelri/Components/Layout/NavMenu.razor.css @@ -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; + } +} diff --git a/PhotoShareHelri/Components/Layout/SearchDialog.razor b/PhotoShareHelri/Components/Layout/SearchDialog.razor new file mode 100644 index 0000000..269da80 --- /dev/null +++ b/PhotoShareHelri/Components/Layout/SearchDialog.razor @@ -0,0 +1,33 @@ +@using MudBlazor + + + + + + + Rechercher + + + + +@code { + private string SearchTerm { get; set; } = ""; + + [Parameter] public EventCallback 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 + } +} \ No newline at end of file diff --git a/PhotoShareHelri/Components/Layout/SearchDialog.razor.css b/PhotoShareHelri/Components/Layout/SearchDialog.razor.css new file mode 100644 index 0000000..6a95d3e --- /dev/null +++ b/PhotoShareHelri/Components/Layout/SearchDialog.razor.css @@ -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; +} diff --git a/PhotoShareHelri/Components/Pages/Component.razor b/PhotoShareHelri/Components/Pages/Component.razor new file mode 100644 index 0000000..e69de29 diff --git a/PhotoShareHelri/Components/Pages/Contact.razor b/PhotoShareHelri/Components/Pages/Contact.razor new file mode 100644 index 0000000..a26156f --- /dev/null +++ b/PhotoShareHelri/Components/Pages/Contact.razor @@ -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 + + + + + Une question ? Un projet ? Envie d'une collaboration ? Écrivez-moi directement. + + + + + + + + + + + + + + + + + + + Laisser un message + + + + + + + @if (messageEnvoye) + { + + ✅ Message envoyé, merci ! + + } + + + +
+ +@code { + + #region properties + private ContactModel contact = new(); + private EditContext? editContext; + private bool messageEnvoye = false; + #endregion + + /// + /// Chargement de la page + /// + protected override async Task OnInitializedAsync() + { + editContext = new EditContext(contact); + } + + /// + /// Méthode d'envoi de mail + /// + private void EnvoyerMessage() + { + var body = new StringBuilder(); + body.Append("

").Append(contact.Message).Append("

"); + mailService.EnvoyerMailContactAsync(contact.Email,contact.Name+" : " +contact.Title, body).GetAwaiter(); + + messageEnvoye = true; + contact = new ContactModel(); + editContext = new EditContext(contact); + } +} diff --git a/PhotoShareHelri/Components/Pages/Contact.razor.css b/PhotoShareHelri/Components/Pages/Contact.razor.css new file mode 100644 index 0000000..ce68ad8 --- /dev/null +++ b/PhotoShareHelri/Components/Pages/Contact.razor.css @@ -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; +} \ No newline at end of file diff --git a/PhotoShareHelri/Components/Pages/Error.razor b/PhotoShareHelri/Components/Pages/Error.razor new file mode 100644 index 0000000..576cc2d --- /dev/null +++ b/PhotoShareHelri/Components/Pages/Error.razor @@ -0,0 +1,36 @@ +@page "/Error" +@using System.Diagnostics + +Error + +

Error.

+

An error occurred while processing your request.

+ +@if (ShowRequestId) +{ +

+ Request ID: @RequestId +

+} + +

Development Mode

+

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

+

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

+ +@code{ + [CascadingParameter] + private HttpContext? HttpContext { get; set; } + + private string? RequestId { get; set; } + private bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + + protected override void OnInitialized() => + RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; +} diff --git a/PhotoShareHelri/Components/Pages/Home.razor b/PhotoShareHelri/Components/Pages/Home.razor new file mode 100644 index 0000000..3666154 --- /dev/null +++ b/PhotoShareHelri/Components/Pages/Home.razor @@ -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 +Accueil + +
+ + +
+
Photographe Amateur
+

Hello,
Moi c'est @DataPage.TitlePseudo

+

+ @DataPage.SubTitle1 +

+

+ @DataPage.SubTitle2 +

+
+ + + + + + +
+ +
+ +

© 2026 @DataPage.Credentials — Droits réservés

+
+ + +@code { + #region properties + private EntityHome DataPage = new(); + #endregion + + /// + /// Chargement de la page + /// + protected override async Task OnInitializedAsync() + { + + if (!await _configDb.HasAnyParametersAsync()) + { + await _configDb.FillConfigTableWithDataPerDefault(); + } + + DataPage = await _configDb.readParametersApplication(); + + } + + +} diff --git a/PhotoShareHelri/Components/Pages/Home.razor.css b/PhotoShareHelri/Components/Pages/Home.razor.css new file mode 100644 index 0000000..099fd84 --- /dev/null +++ b/PhotoShareHelri/Components/Pages/Home.razor.css @@ -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); +} diff --git a/PhotoShareHelri/Components/Pages/PhotoSelectError.razor b/PhotoShareHelri/Components/Pages/PhotoSelectError.razor new file mode 100644 index 0000000..7d1119e --- /dev/null +++ b/PhotoShareHelri/Components/Pages/PhotoSelectError.razor @@ -0,0 +1,9 @@ +@page "/PhotoSelectError" + +

Oups !!!

+ +Il semblerait que ce shooting soit inexistant ou bien que cet recherche est infructueuse. + +@code { + +} diff --git a/PhotoShareHelri/Components/Pages/PhotoSelected.razor b/PhotoShareHelri/Components/Pages/PhotoSelected.razor new file mode 100644 index 0000000..42b8b86 --- /dev/null +++ b/PhotoShareHelri/Components/Pages/PhotoSelected.razor @@ -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 + + + + + + + Résultat du shooting + @Title + + + + + + @for (int index = 0; index < Images.Count; index++) + { + var img = Images[index]; + + + + + + + + + } + + + + + + +@* Modal de zoom sur une image *@ + + + +@code { + #region Variables + HashSet SelectedImages = new(); + private int MaxSelect = 1; + + // Liste des images disponibles + private List 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 + /// + /// Ouverture de la modal à partir du clic sur image voulu + /// + void OpenModalZoom(string img) + { + CurrentImage = img; + IsModalZoomOpen = true; + } + + /// + /// fermeture de la modal + /// + void CloseModalZoom() + { + IsModalZoomOpen = false; + } + + /// + /// photo suivante dans la modal + /// dans la liste des photos affichés + /// + void NextModalZoom() + { + if (CanNext) + CurrentImage = Images[CurrentIndex + 1]; + } + + /// + /// photo précédente + /// + void PreviousModalZoom() + { + if (CanPrevious) + CurrentImage = Images[CurrentIndex - 1]; + } + #endregion + + /// + /// arrivé sur la page + /// + 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 + } + + /// + /// Déclenche le téléchargement de l'image originale sur la modal de visualisation + /// + /// image sélectionné par son hash + private async Task ToggleImage(string img) + { + var filePath = $"{img}"; // chemin dans wwwroot + await JS.InvokeVoidAsync("downloadFile", filePath, $"{img}"); + } +} \ No newline at end of file diff --git a/PhotoShareHelri/Components/Pages/PhotoSelected.razor.css b/PhotoShareHelri/Components/Pages/PhotoSelected.razor.css new file mode 100644 index 0000000..9f49992 --- /dev/null +++ b/PhotoShareHelri/Components/Pages/PhotoSelected.razor.css @@ -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; + } diff --git a/PhotoShareHelri/Components/Pages/PhotoSelector.razor b/PhotoShareHelri/Components/Pages/PhotoSelector.razor new file mode 100644 index 0000000..c720bf5 --- /dev/null +++ b/PhotoShareHelri/Components/Pages/PhotoSelector.razor @@ -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 + + + + + + + Sélection des photos + @Title + + + + + + Valider la sélection + + + + + + @SelectedImages.Count / @MaxSelect photo(s) sélectionnée(s) + + + + + @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")}"; + + + + + + +
+ +
+ +
+
+ } +
+ + + + + Valider la sélection + + + +
+
+ + + + + +@code { + #region Variables + HashSet SelectedImages = new(); + private int MaxSelect = 1; + + // Liste des images disponibles + private List 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 + /// + /// Ouverture de la modal à partir du clic sur image voulu + /// + void OpenModalZoom(string img) + { + CurrentImage = img; + IsModalZoomOpen = true; + } + + + /// + /// fermeture de la modal + /// + void CloseModalZoom() + { + IsModalZoomOpen = false; + } + + /// + /// photo suivante dans la modal + /// dans la liste des photos affichés + /// + void NextModalZoom() + { + if (CanNext) + CurrentImage = Images[CurrentIndex + 1]; + } + + /// + /// photo précédente + /// + void PreviousModalZoom() + { + if (CanPrevious) + CurrentImage = Images[CurrentIndex - 1]; + } + #endregion + + /// + /// arrivé sur la page + /// + 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; + } + } + + /// + /// Séléction de l'image dans la liste des photos choisies + /// + /// image sélectionné par son hash + private void ToggleImage(string img) + { + if (SelectedImages.Contains(img)) + { + SelectedImages.Remove(img); + } + else + { + SelectedImages.Add(img); + } + } + + /// + /// envoi par mail de la sélection des photos + /// + 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("

Photos sélectionnées 📸

"); + body.Append("
    "); + + foreach (var fichier in fichiers) + { + body.Append($"
  • {fichier}
  • "); + } + + body.Append("
"); + mailService.EnvoyerMailAsync("Confirmation sélection du shooting" + ShootingId, body).GetAwaiter(); + + Datas.IsPhotosSelected = true; + await ShootingDb.UpdateShootingInfo(Datas); + } +} \ No newline at end of file diff --git a/PhotoShareHelri/Components/Pages/PhotoSelector.razor.css b/PhotoShareHelri/Components/Pages/PhotoSelector.razor.css new file mode 100644 index 0000000..9f49992 --- /dev/null +++ b/PhotoShareHelri/Components/Pages/PhotoSelector.razor.css @@ -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; + } diff --git a/PhotoShareHelri/Components/Pages/ShootingDesire.razor b/PhotoShareHelri/Components/Pages/ShootingDesire.razor new file mode 100644 index 0000000..d409331 --- /dev/null +++ b/PhotoShareHelri/Components/Pages/ShootingDesire.razor @@ -0,0 +1,58 @@ +@page "/ShootingDesire" +@rendermode InteractiveServer +@inject IWebHostEnvironment Env +@inject DesireService DesireDb +Mes Envies de shooting futur + +
+
+ + + + + + Mes envies de shootings + + + + + 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 ! + + + + + + + + @foreach (var envie in EnviesShooting) + { + + 📷 @envie + + } + + + Et bien d'autres encore !!! + + + + + + + + + +
+
+@code { + private List EnviesShooting; + + + protected override void OnInitialized() + { + EnviesShooting = DesireDb.GetDEsireShooting(); + } +} diff --git a/PhotoShareHelri/Components/Pages/ShootingDesire.razor.css b/PhotoShareHelri/Components/Pages/ShootingDesire.razor.css new file mode 100644 index 0000000..336b5e8 --- /dev/null +++ b/PhotoShareHelri/Components/Pages/ShootingDesire.razor.css @@ -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); +} \ No newline at end of file diff --git a/PhotoShareHelri/Components/Routes.razor b/PhotoShareHelri/Components/Routes.razor new file mode 100644 index 0000000..f756e19 --- /dev/null +++ b/PhotoShareHelri/Components/Routes.razor @@ -0,0 +1,6 @@ + + + + + + diff --git a/PhotoShareHelri/Components/_Imports.razor b/PhotoShareHelri/Components/_Imports.razor new file mode 100644 index 0000000..5a4b18a --- /dev/null +++ b/PhotoShareHelri/Components/_Imports.razor @@ -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 diff --git a/PhotoShareHelri/Dockerfile b/PhotoShareHelri/Dockerfile new file mode 100644 index 0000000..f55ca8a --- /dev/null +++ b/PhotoShareHelri/Dockerfile @@ -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"] \ No newline at end of file diff --git a/PhotoShareHelri/PhotoShareHelri.csproj b/PhotoShareHelri/PhotoShareHelri.csproj new file mode 100644 index 0000000..9b8582b --- /dev/null +++ b/PhotoShareHelri/PhotoShareHelri.csproj @@ -0,0 +1,36 @@ + + + + net10.0 + enable + enable + a841ad02-d180-4ec3-90a2-908747712f4c + Linux + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + diff --git a/PhotoShareHelri/Program.cs b/PhotoShareHelri/Program.cs new file mode 100644 index 0000000..3ffc626 --- /dev/null +++ b/PhotoShareHelri/Program.cs @@ -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(); +builder.Services.AddDbContextFactory(options => + options.UseNpgsql( + builder.Configuration.GetConnectionString("DefaultConnection") + )); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +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() + .AddInteractiveServerRenderMode(); + +app.Run(); diff --git a/PhotoShareHelri/Properties/launchSettings.json b/PhotoShareHelri/Properties/launchSettings.json new file mode 100644 index 0000000..c46b1cc --- /dev/null +++ b/PhotoShareHelri/Properties/launchSettings.json @@ -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 + } + } +} \ No newline at end of file diff --git a/PhotoShareHelri/appsettings.Development.json b/PhotoShareHelri/appsettings.Development.json new file mode 100644 index 0000000..399d4de --- /dev/null +++ b/PhotoShareHelri/appsettings.Development.json @@ -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" + } +} diff --git a/PhotoShareHelri/appsettings.json b/PhotoShareHelri/appsettings.json new file mode 100644 index 0000000..eb6df61 --- /dev/null +++ b/PhotoShareHelri/appsettings.json @@ -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." + } +} diff --git a/PhotoShareHelri/wwwroot/Idees/ShootingDesire.txt b/PhotoShareHelri/wwwroot/Idees/ShootingDesire.txt new file mode 100644 index 0000000..69716ed --- /dev/null +++ b/PhotoShareHelri/wwwroot/Idees/ShootingDesire.txt @@ -0,0 +1,21 @@ +Dans les dunes +Cosplay +Foret tenue transparente fée nature +Tenue guerrier cote de maille +Sous l’eau +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 diff --git a/PhotoShareHelri/wwwroot/Images/qr.png b/PhotoShareHelri/wwwroot/Images/qr.png new file mode 100644 index 0000000..8d93aff Binary files /dev/null and b/PhotoShareHelri/wwwroot/Images/qr.png differ diff --git a/PhotoShareHelri/wwwroot/Photos/testphoto/1.jpg b/PhotoShareHelri/wwwroot/Photos/testphoto/1.jpg new file mode 100644 index 0000000..bcd100e Binary files /dev/null and b/PhotoShareHelri/wwwroot/Photos/testphoto/1.jpg differ diff --git a/PhotoShareHelri/wwwroot/Photos/testphoto/2.jpg b/PhotoShareHelri/wwwroot/Photos/testphoto/2.jpg new file mode 100644 index 0000000..bcd100e Binary files /dev/null and b/PhotoShareHelri/wwwroot/Photos/testphoto/2.jpg differ diff --git a/PhotoShareHelri/wwwroot/Photos/testphoto/3.jpg b/PhotoShareHelri/wwwroot/Photos/testphoto/3.jpg new file mode 100644 index 0000000..bcd100e Binary files /dev/null and b/PhotoShareHelri/wwwroot/Photos/testphoto/3.jpg differ diff --git a/PhotoShareHelri/wwwroot/Photos/testphoto/export/1.jpg b/PhotoShareHelri/wwwroot/Photos/testphoto/export/1.jpg new file mode 100644 index 0000000..bcd100e Binary files /dev/null and b/PhotoShareHelri/wwwroot/Photos/testphoto/export/1.jpg differ diff --git a/PhotoShareHelri/wwwroot/Photos/testphoto/select.csv b/PhotoShareHelri/wwwroot/Photos/testphoto/select.csv new file mode 100644 index 0000000..e69de29 diff --git a/PhotoShareHelri/wwwroot/app.css b/PhotoShareHelri/wwwroot/app.css new file mode 100644 index 0000000..2bd9b78 --- /dev/null +++ b/PhotoShareHelri/wwwroot/app.css @@ -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; +} diff --git a/PhotoShareHelri/wwwroot/favicon.png b/PhotoShareHelri/wwwroot/favicon.png new file mode 100644 index 0000000..8422b59 Binary files /dev/null and b/PhotoShareHelri/wwwroot/favicon.png differ diff --git a/PhotoShareHelri/wwwroot/js/download.js b/PhotoShareHelri/wwwroot/js/download.js new file mode 100644 index 0000000..70f189f --- /dev/null +++ b/PhotoShareHelri/wwwroot/js/download.js @@ -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); +}; \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..2e6a8d5 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# PhotoShareHelri \ No newline at end of file