diff --git a/.gitignore b/.gitignore index ed36831..1b5cedb 100644 --- a/.gitignore +++ b/.gitignore @@ -501,6 +501,7 @@ typings/ #bower and libman wwwroot/lib **/wwwroot/lib **/logs/internal-nlog.txt +Events-WebApi/Events.FilesAPI/GeneratedFiles/ # some needed exceptions /DataGenerator/.dotnet/.dotnet diff --git a/Events-WebApi/Events.ClientApp/package-lock.json b/Events-WebApi/Events.ClientApp/package-lock.json index 6b5ff74..6cb3ef9 100644 --- a/Events-WebApi/Events.ClientApp/package-lock.json +++ b/Events-WebApi/Events.ClientApp/package-lock.json @@ -1488,9 +1488,9 @@ } }, "node_modules/postcss": { - "version": "8.5.8", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", - "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", "funding": [ { "type": "opencollective", @@ -1630,9 +1630,9 @@ "license": "MIT" }, "node_modules/vite": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", - "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz", + "integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/Events-WebApi/Events.ClientApp/src/components/EventsPanel.vue b/Events-WebApi/Events.ClientApp/src/components/EventsPanel.vue index 7fd33f2..d0ab2da 100644 --- a/Events-WebApi/Events.ClientApp/src/components/EventsPanel.vue +++ b/Events-WebApi/Events.ClientApp/src/components/EventsPanel.vue @@ -254,9 +254,13 @@ watch(eventsCatalogVersion, () => { filterDisplay="menu" lazy paginator + paginatorTemplate="CurrentPageReport FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink RowsPerPageDropdown" + showCurrentPageReport :rows="pageSize" + :rowsPerPageOptions="[10, 25, 50, 100]" :first="(page - 1) * pageSize" :total-records="totalRecords" + currentPageReportTemplate="{first} to {last} of {totalRecords}" :sort-field="sort" :sort-order="sortOrder" @filter="onFilter" diff --git a/Events-WebApi/Events.ClientApp/src/components/PeoplePanel.vue b/Events-WebApi/Events.ClientApp/src/components/PeoplePanel.vue index d78dad3..de215e0 100644 --- a/Events-WebApi/Events.ClientApp/src/components/PeoplePanel.vue +++ b/Events-WebApi/Events.ClientApp/src/components/PeoplePanel.vue @@ -327,9 +327,13 @@ onMounted(async () => { filterDisplay="menu" lazy paginator + paginatorTemplate="CurrentPageReport FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink RowsPerPageDropdown" + showCurrentPageReport :rows="pageSize" + :rowsPerPageOptions="[10, 25, 50, 100]" :first="(page - 1) * pageSize" :total-records="totalRecords" + currentPageReportTemplate="{first} to {last} of {totalRecords}" :sort-field="sort" :sort-order="sortOrder" @filter="onFilter" diff --git a/Events-WebApi/Events.ClientApp/src/components/RegistrationsPanel.vue b/Events-WebApi/Events.ClientApp/src/components/RegistrationsPanel.vue index 7cd8822..7aea934 100644 --- a/Events-WebApi/Events.ClientApp/src/components/RegistrationsPanel.vue +++ b/Events-WebApi/Events.ClientApp/src/components/RegistrationsPanel.vue @@ -405,9 +405,13 @@ onMounted(async () => { filterDisplay="menu" lazy paginator + paginatorTemplate="CurrentPageReport FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink RowsPerPageDropdown" + showCurrentPageReport :rows="pageSize" + :rowsPerPageOptions="[10, 25, 50, 100]" :first="(page - 1) * pageSize" :total-records="totalRecords" + currentPageReportTemplate="{first} to {last} of {totalRecords}" :sort-field="sort" :sort-order="sortOrder" @filter="onFilter" @@ -500,8 +504,8 @@ onMounted(async () => { optionLabel="name" :minLength="1" completeOnFocus - dropdown - dropdownMode="current" + fluid + showClear force-selection @complete="completePeopleLookup" > diff --git a/Events-WebApi/Events.ClientApp/src/components/SportsPanel.vue b/Events-WebApi/Events.ClientApp/src/components/SportsPanel.vue index 98ab477..1d0c450 100644 --- a/Events-WebApi/Events.ClientApp/src/components/SportsPanel.vue +++ b/Events-WebApi/Events.ClientApp/src/components/SportsPanel.vue @@ -188,9 +188,13 @@ onMounted(() => { filterDisplay="menu" lazy paginator + paginatorTemplate="CurrentPageReport FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink RowsPerPageDropdown" + showCurrentPageReport :rows="pageSize" + :rowsPerPageOptions="[10, 25, 50, 100]" :first="(page - 1) * pageSize" :total-records="totalRecords" + currentPageReportTemplate="{first} to {last} of {totalRecords}" :sort-field="sort" :sort-order="sortOrder" @filter="onFilter" diff --git a/Events-WebApi/Events.ClientApp/src/style.css b/Events-WebApi/Events.ClientApp/src/style.css index b91d0ab..8195676 100644 --- a/Events-WebApi/Events.ClientApp/src/style.css +++ b/Events-WebApi/Events.ClientApp/src/style.css @@ -42,6 +42,28 @@ body { box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9), 0 8px 24px rgba(15, 35, 56, 0.06); } +.panel-card .p-paginator { + position: relative; + justify-content: center; +} + +.panel-card .p-paginator-current { + position: absolute; + left: 0; +} + +@media (max-width: 768px) { + .panel-card .p-paginator { + justify-content: flex-start; + row-gap: 0.5rem; + padding-top: 2rem; + } + + .panel-card .p-paginator-current { + top: 0.25rem; + } +} + .tabs-header-bar { display: grid; grid-template-columns: 1fr auto 1fr; diff --git a/Events-WebApi/Events.FilesAPI/Program.cs b/Events-WebApi/Events.FilesAPI/Program.cs index 79a22c3..478ab89 100644 --- a/Events-WebApi/Events.FilesAPI/Program.cs +++ b/Events-WebApi/Events.FilesAPI/Program.cs @@ -1,4 +1,8 @@ using Events.Auth; +using Events.FilesAPI.Features.Certificates.Download; +using Events.FilesAPI.Features.Certificates.Synchronize; +using Events.FilesAPI.Features.RegistrationsExcel.Download; +using Events.FilesAPI.Features.RegistrationsExcel.Synchronize; using Events.FilesAPI.Infrastructure.Messaging; using Events.FilesAPI.Infrastructure.Options; using Events.WebAPI.Handlers.EF.Data.Postgres; @@ -19,6 +23,11 @@ builder.Services.AddOptions() "GeneratedFilesOptions:OutputPath must be configured.") .ValidateOnStart(); +builder.Services.AddTransient(); +builder.Services.AddTransient(); +builder.Services.AddTransient(); +builder.Services.AddTransient(); + builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(Program).Assembly)); builder.Services.SetupMassTransit(builder.Configuration); diff --git a/Events-WebApi/Events.FilesAPI/appsettings.json b/Events-WebApi/Events.FilesAPI/appsettings.json index e667cfe..ba8451e 100644 --- a/Events-WebApi/Events.FilesAPI/appsettings.json +++ b/Events-WebApi/Events.FilesAPI/appsettings.json @@ -12,7 +12,7 @@ "Password": "guest" }, "Paths": { - "OutputPath": "./Certificates" + "OutputPath": "./GeneratedFiles" }, "ConnectionStrings": { "EventsPostgres": "Host=localhost;Port=5432;Database=events;Username=sport;Password=go and look in the secrets file;Persist Security Info=True;" diff --git a/Events-WebApi/README.md b/Events-WebApi/README.md index 28ebadb..f0e0fbd 100644 --- a/Events-WebApi/README.md +++ b/Events-WebApi/README.md @@ -159,6 +159,35 @@ Once the APIs are running: - most `WebAPI` endpoints require a bearer token - `FilesAPI` download endpoints are also protected and require the `events:read` scope +## Running Everything Without VS Code + +From the `Events-WebApi` directory, you can start all three processes together with: + +```bash +./start-all.sh +``` + +This starts: + +- `Events.WebAPI` on `https://localhost:7295` +- `Events.FilesAPI` on `https://localhost:7296` +- `Events.ClientApp` on `http://localhost:5173` + +The script keeps all processes attached to the terminal and stops them together when you press `Ctrl+C`. + +If you want to stop the stack from another terminal, use: + +```bash +./stop-all.sh +``` + +Before the first run, make sure the client dependencies are installed: + +```bash +cd Events.ClientApp +npm install +``` + ## Running The Client App See [Events.ClientApp/README.md](/C:/GitRepos/FPMOZ-PI/predavanja/Events-WebApi/Events.ClientApp/README.md:1) for more details. @@ -180,7 +209,7 @@ npm run dev According to the current `appsettings.json`, the default is: ```text -./Certificates +./GeneratedFiles ``` ## Troubleshooting diff --git a/Events-WebApi/start-all.sh b/Events-WebApi/start-all.sh new file mode 100755 index 0000000..f762d8d --- /dev/null +++ b/Events-WebApi/start-all.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash + +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +WEBAPI_PROJECT="$ROOT_DIR/Events.WebAPI/Events.WebAPI.csproj" +FILESAPI_PROJECT="$ROOT_DIR/Events.FilesAPI/Events.FilesAPI.csproj" +CLIENTAPP_DIR="$ROOT_DIR/Events.ClientApp" + +pids=() + +start_process() { + if command -v setsid >/dev/null 2>&1; then + setsid "$@" & + else + "$@" & + fi + + pids+=($!) +} + +stop_process() { + local signal="$1" + local pid="$2" + + kill "-$signal" -- "-$pid" 2>/dev/null || kill "-$signal" "$pid" 2>/dev/null || true +} + +cleanup() { + local exit_code=$? + trap - INT TERM EXIT + + if ((${#pids[@]} > 0)); then + echo + echo "Stopping Events stack..." + + for pid in "${pids[@]}"; do + stop_process INT "$pid" + done + + sleep 1 + + for pid in "${pids[@]}"; do + stop_process TERM "$pid" + done + + sleep 1 + + for pid in "${pids[@]}"; do + stop_process KILL "$pid" + done + + wait "${pids[@]}" 2>/dev/null || true + fi + + exit "$exit_code" +} + +trap cleanup INT TERM EXIT + +if [[ ! -d "$CLIENTAPP_DIR/node_modules" ]]; then + echo "Missing node_modules in Events.ClientApp." + echo "Run: cd \"$CLIENTAPP_DIR\" && npm install" + exit 1 +fi + +"$ROOT_DIR/stop-all.sh" >/dev/null 2>&1 || true + +echo "Starting Events.WebAPI on https://localhost:7295" +start_process dotnet run --launch-profile https --project "$WEBAPI_PROJECT" + +echo "Starting Events.FilesAPI on https://localhost:7296" +start_process dotnet run --launch-profile https --project "$FILESAPI_PROJECT" + +echo "Starting Events.ClientApp on http://localhost:5173" +start_process env CLIENTAPP_DIR="$CLIENTAPP_DIR" bash -c 'cd "$CLIENTAPP_DIR" && npm run dev' + +echo +echo "Events stack is starting." +echo "WebAPI: https://localhost:7295" +echo "FilesAPI: https://localhost:7296" +echo "ClientApp: http://localhost:5173" +echo +echo "Press Ctrl+C to stop all processes." + +wait diff --git a/Events-WebApi/stop-all.sh b/Events-WebApi/stop-all.sh new file mode 100755 index 0000000..cff1e5d --- /dev/null +++ b/Events-WebApi/stop-all.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +set -euo pipefail + +kill_port() { + local port="$1" + local pids + + pids="$(lsof -tiTCP:"$port" -sTCP:LISTEN 2>/dev/null || true)" + if [[ -z "$pids" ]]; then + return 0 + fi + + kill $pids 2>/dev/null || true + sleep 1 + + pids="$(lsof -tiTCP:"$port" -sTCP:LISTEN 2>/dev/null || true)" + if [[ -n "$pids" ]]; then + kill -9 $pids 2>/dev/null || true + fi +} + +echo "Stopping Events.WebAPI..." +kill_port 7295 +pkill -f "/Events.WebAPI/bin/.*/Events.WebAPI" || true + +echo "Stopping Events.FilesAPI..." +kill_port 7296 +pkill -f "/Events.FilesAPI/bin/.*/Events.FilesAPI" || true + +echo "Stopping Events.ClientApp..." +kill_port 5173 +pkill -f "Events.ClientApp.*node_modules/.bin/vite" || true +pkill -f "vite" || true + +echo "Events stack stopped."