A modern, flexible prediction platform for sports competitions built with .NET 10, Blazor, .NET MAUI, and Azure Table Storage.
-
Podium.Shared - Shared Razor components, models, and services
- Used by both Web and MAUI apps
- Pages (Razor components for UI)
- Models (Entity models)
- Services (Business logic, API client, state management, configuration)
- Styles (CSS in wwwroot)
-
Podium.Web - Blazor WebAssembly app for web browsers
- Runs entirely in the browser
- Consumes the API
- Shares all UI with MAUI via Podium.Shared
-
Podium.Native - .NET MAUI app for iOS, Android, Windows, macOS
- Cross-platform mobile and desktop
- Shares UI with Podium.Web via Podium.Shared
- Uses BlazorWebView for hybrid apps
-
Podium.Api - ASP.NET Core Minimal API
- Secure backend for data access
- Protects Azure Storage credentials
- RESTful endpoints
-
Tools/Podium.DbInit - Database initialization tool
- Creates Azure Table Storage schema
- Generates sample data for testing
Client apps NEVER directly access Azure Storage. All data flows through the secure API:
[Web/MAUI Apps] → HTTPS → [Podium.Api] → [Azure Table Storage]
(Secure) (Hidden credentials)
Both Web and MAUI apps use the shared IAppConfiguration service for environment-aware settings.
- Development: Reads from
wwwroot/appsettings.json - Production: Reads from
wwwroot/appsettings.Production.json - Auto-detects environment via
IWebAssemblyHostEnvironment.IsDevelopment()
- Development:
#if DEBUG- hardcoded inMauiProgram.cs - Production:
#else- hardcoded inMauiProgram.cs - Android emulator: Use
http://10.0.2.2:50242(not localhost) - iOS simulator: Use
https://localhost:50242
The app now includes automatic session persistence:
- Sign In Once: Your session is saved to browser localStorage (Web) or device storage (MAUI)
- Stay Signed In: Sessions persist for 14 days automatically
- Survives Refreshes: Page refreshes don't sign you out
- Cross-Tab Support: Sign in once, stay signed in across all tabs
- Secure: Session data stored locally, never sent to other sites
- User ID
- Username
- Session ID
- Expiry date (14 days)
- Use the "Sign Out" button to clear your session
- Session is removed from storage immediately
- All tabs/windows will update
- Data stored locally in your browser/device only
- Not accessible by other websites
- Automatically expires after 14 days
- No sensitive data (passwords) stored
cd Tools/Podium.DbInit
# Configure Azure Storage (choose one):
# Option A: User Secrets (recommended for dev)
dotnet user-secrets set "AzureStorage:StorageUri" "https://youraccount.table.core.windows.net/"
dotting user-secrets set "AzureStorage:AccountName" "youraccount"
dotnet user-secrets set "AzureStorage:AccountKey" "your-account-key"
# Option B: appsettings.json
# Edit appsettings.json with your credentials
# Run initialization
dotnet runCreates all tables and adds sample data (F1 2025 season with drivers, races, test users).
cd ../../Podium/Podium.Api
# Set Azure Storage credentials
dotnet user-secrets set "AzureStorage:StorageUri" "https://youraccount.table.core.windows.net/"
dotnet user-secrets set "AzureStorage:AccountName" "youraccount"
dotnet user-secrets set "AzureStorage:AccountKey" "your-account-key"
# Run the API
dotnet runAPI starts at https://localhost:50242 (check console output for actual port).
cd ../Podium.Web
# Edit wwwroot/appsettings.json if API runs on different port
# {
# "ApiBaseUrl": "https://localhost:50242"
# }
dotnet runOr run from Visual Studio - set Podium.Web as startup project.
From Visual Studio:
- Set Podium.Native as startup project
- Select target platform (Windows, Android, iOS)
- Update
MauiProgram.csif needed:- Android emulator:
http://10.0.2.2:50242 - iOS simulator:
https://localhost:50242 - Physical device: Your computer's IP address
- Android emulator:
- Run (F5)
After running DbInit:
- Email:
john@example.com| Password:password123 - Email:
jane@example.com| Password:password123 - Email:
alex@example.com| Password:password123
- ✅ Database schema (13 Azure Tables)
- ✅ Database initialization tool with sample data
- ✅ Complete entity models
- ✅ Repository pattern data access layer
- ✅ RESTful API with authentication
- ✅ Environment-aware configuration (Dev/Prod)
- ✅ Shared service architecture (Web + MAUI)
- ✅ User registration
- ✅ Sign in with password
- ✅ Sign in with email OTP (code logs to console for now)
- ✅ Session management
- ✅ Auth state service
- ✅ Session persistence (survives page refreshes and app restarts)
- ✅ Home/Landing page
- ✅ Sign In page (dual auth)
- ✅ Sign Up page
- ✅ Sport selection
- ✅ Tier/Competition selection
- ✅ Event selection with dates
- ✅ Prediction submission (select 3 competitors)
- ✅ My Predictions view
- ✅ Leaderboard
- ✅ Responsive design (mobile & desktop)
- ✅ Modern, clean CSS styling
- ✅ Navigation header with auth state
- ✅ Footer
- ✅ Loading states
- ✅ Error handling
- ✅ Success messages
- 📧 Email service integration (real OTP sending)
- 💾 Session persistence (browser/device storage)
- ⚙️ Points calculation engine
- 🛠️ Admin panel (manage sports, events, results)
- 📊 Results display after events
- 👤 User profile page
- 📅 Multiple seasons support
- 🔔 Push notifications for events
- 💬 Social features (comments, sharing)
- 📥 Export predictions
- 📚 Historical data/archives
- 🏅 More sports and competitions
- .NET 10 - Latest framework
- Blazor WebAssembly - Web UI (runs in browser)
- .NET MAUI - Cross-platform mobile/desktop
- Azure Table Storage - NoSQL database
- ASP.NET Core Minimal API - Secure backend
- Shared Razor Components - Write once, run everywhere
POST /api/auth/register- Create accountPOST /api/auth/signin- Sign in with passwordPOST /api/auth/send-otp- Send email verification codePOST /api/auth/verify-otp- Verify OTP and sign inPOST /api/auth/validate-session- Validate sessionPOST /api/auth/signout- Sign out
GET /api/disciplines- List all active disciplinesGET /api/disciplines/{disciplineId}/series- Get series for a disciplineGET /api/series/{seriesId}/seasons- Get seasonsGET /api/series/{seriesId}/seasons/active- Get active seasonGET /api/seasons/{seasonId}/events- Get all eventsGET /api/seasons/{seasonId}/events/upcoming- Get upcoming eventsGET /api/seasons/{seasonId}/competitors- Get competitorsGET /api/events/{eventId}- Get event detailsGET /api/events/{eventId}/result- Get event result
POST /api/predictions- Submit predictionGET /api/predictions/{eventId}/user/{userId}- Get user's predictionGET /api/predictions/{eventId}- Get all predictions for eventGET /api/predictions/user/{userId}/season/{seasonId}- Get user's season predictions
GET /api/leaderboard/season/{seasonId}- Get season leaderboardGET /api/leaderboard/season/{seasonId}/user/{userId}- Get user stats
GET /api/health- Health check
Terminal 1 (API):
cd Podium/Podium.Api
dotnet watch runTerminal 2 (Web):
cd Podium/Podium.Web
dotnet watch runAPI allows requests from:
https://localhost:7001https://localhost:5001http://localhost:5000https://localhost:7002http://localhost:5002
Update Podium.Api/Program.cs if your app runs on different ports.
The Android emulator can't access localhost from the host machine. Use:
http://10.0.2.2:50242- Maps to host's localhost
Update in Podium.Native/MauiProgram.cs:
ApiBaseUrl = "http://10.0.2.2:50242"iOS simulator can access localhost directly:
ApiBaseUrl = "https://localhost:50242"Build all projects:
dotnet build Podium.slnPublish Web app:
cd Podium/Podium.Web
dotnet publish -c ReleasePublish MAUI app:
cd Podium/Podium.Native
# For Android
dotnet publish -f net10.0-android -c Release
# For iOS
dotnet publish -f net10.0-ios -c Release
# For Windows
dotnet publish -f net10.0-windows10.0.19041.0 -c ReleaseMyDreamPodium/
├── Docs/
│ └── DatabaseStructure.md # Complete DB schema
├── Tools/
│ └── Podium.DbInit/ # Database initialization
├── Podium/
│ ├── Podium.Shared/ # Shared UI & logic ⭐
│ │ ├── Models/ # Entity classes
│ │ ├── Services/
│ │ │ ├── Configuration/ # Environment config
│ │ │ ├── Data/ # Repositories
│ │ │ ├── Auth/ # Authentication
│ │ │ ├── Api/ # API client
│ │ │ └── State/ # State management
│ │ ├── Pages/ # Razor pages (shared!)
│ │ ├── Layout/ # App layout
│ │ └── wwwroot/ # CSS & static files
│ ├── Podium.Web/ # Blazor WebAssembly
│ │ ├── wwwroot/
│ │ │ └── appsettings.json # API URL config
│ │ └── Program.cs # App setup
│ ├── Podium.Native/ # .NET MAUI
│ │ └── MauiProgram.cs # App setup + config
│ └── Podium.Api/ # Secure backend
│ ├── Endpoints/ # API routes
│ └── Program.cs # API setup
├── MyPodiumLegacy/ # Old app (reference)
└── README.md
- Write once, run everywhere - Same UI for Web and Mobile
- Consistent UX - Users get identical experience
- Less maintenance - Fix bugs once, deploy everywhere
- Faster development - No need to sync features
- Lightweight - No unnecessary overhead
- Modern - Uses latest .NET features
- Fast - Optimized for performance
- Simple - Easy to understand and extend
- Cost-effective - Pay for what you use
- Scalable - Handles millions of records
- No SQL required - NoSQL flexibility
- Fast - Low latency queries
- Serverless - No server management
- Security - Different credentials for dev/prod
- Flexibility - Easy to test locally
- Best practice - Industry standard approach
- Safe deployment - Production credentials never in code
- Check Azure Storage credentials are set
- Verify port 50242 is available
- Check console for specific error
- Ensure
dotnet runis from Podium.Api folder
- Ensure API is running first
- Check
wwwroot/appsettings.jsonhas correct API URL - Verify CORS settings in API
Program.cs - Check browser console for errors
- Android emulator: Use
http://10.0.2.2:50242 - iOS simulator: Use
https://localhost:50242 - Physical device: Use your computer's IP address
- Ensure API allows CORS from all origins (dev only)
- Run
Tools/Podium.DbInitfirst - Check sample data was created
- Verify Azure Storage credentials
- CSS is in
Podium.Shared/wwwroot/app.css - Blazor serves it as
_content/Podium.Shared/app.css - Check browser dev tools Network tab
- Clear browser cache
- OTP currently logs to API console (development mode)
- Check API terminal output for the code
- Real email service not yet implemented
[Your license here]
This is a learning/portfolio project. Contributions welcome!
Built with ❤️ using .NET 10, Blazor, and MAUI
Status: ✅ MVP Complete - Ready for testing!