Psyter Web Portal - Detailed Structure (Part 1 of 2)

Project: Web Portal (.NET Framework MVC)
Technology: ASP.NET MVC 5, .NET Framework 4.7.2, jQuery, Bootstrap
Purpose: Web-based portal for patients, providers, and administrators
Last Updated: November 5, 2025


📁 Root Structure

Web/
├── Psyter/                        # Main web application
│   ├── Controllers/               # MVC controllers
│   │   ├── Admin/                 # Admin controllers
│   │   ├── Accountant/            # Accountant controllers
│   │   ├── Patient/               # Patient controllers
│   │   ├── Physician/             # Provider controllers
│   │   ├── WebController.cs       # Public website
│   │   ├── CommonController.cs    # Shared functionality
│   │   ├── UserManagerController.cs
│   │   ├── OrganizationController.cs
│   │   ├── EventController.cs
│   │   └── ...
│   ├── Views/                     # Razor views
│   │   ├── Web/                   # Public pages
│   │   ├── Patient/               # Patient views
│   │   ├── Physician/             # Provider views
│   │   ├── Admin/                 # Admin views
│   │   ├── Shared/                # Shared layouts
│   │   └── ...
│   ├── Models/                    # View models
│   ├── DataAccess/                # Data layer
│   ├── Common/                    # Utilities
│   ├── Filters/                   # Action filters
│   ├── Content/                   # CSS, images
│   ├── Scripts/                   # JavaScript
│   ├── App_Start/                 # Config files
│   ├── Global.asax.cs             # Application entry
│   └── Web.config                 # Configuration
├── Psyter.sln                     # Solution file
├── azure-pipelines.yml            # CI/CD pipeline
├── packages/                      # NuGet packages
└── .git/                          # Git repository

🎯 Project Overview

Purpose

Multi-tenant web portal providing:
- Public Website: Marketing pages, therapist listing, psychological screening
- Patient Portal: Book appointments, manage profile, attend sessions
- Provider Portal: Manage schedule, view appointments, access patient records
- Admin Portal: Manage users, content, analytics, payments
- Content Management: SEO, blogs, events, educational content
- Organization Management: Corporate accounts, charity bookings

User Roles

Role Access Level Key Features
Guest Public Home, therapist search, screening test
Patient Authenticated Book sessions, manage appointments, profile
Provider Authenticated Schedule, appointments, patient history, earnings
Admin Super User Full system access, user management, analytics
Accountant Finance Payment reports, refunds, financial analytics
Content Manager Content Blog posts, SEO, events, educational content
SEO Manager Marketing Page optimization, keywords, meta tags
Marketing Manager Marketing Campaigns, analytics, promotions
Organization Corporate Bulk bookings, employee access, reporting

Architecture

ASP.NET MVC 5 Pattern:
- Model: Data models and view models
- View: Razor templates (Arabic + English)
- Controller: Business logic and API orchestration
- Filter: Authentication and authorization

Backend API Integration:
- Web portal is a frontend only (no direct database access)
- All data operations via REST API calls to PsyterAPI
- API base URL: https://dvx.innotech-sa.com/Psyter/Master/APIs/
- Token-based authentication (OAuth 2.0)
- Scheduling API integration for availability


📦 Dependencies & Packages

Core Framework

ASP.NET MVC 5:
- .NET Framework 4.7.2
- Razor view engine
- Bundling and minification
- JSON.NET for serialization

Key NuGet Packages (Psyter.csproj)

<!-- Google APIs -->
<PackageReference Include="Google.Analytics.Data.V1Beta" Version="2.0.0-beta01" />
<PackageReference Include="Google.Api.Gax" Version="4.0.0" />
<PackageReference Include="Google.Apis.Auth" Version="1.56.0" />

<!-- gRPC (for Google services) -->
<PackageReference Include="Grpc.Core" Version="2.46.3" />
<PackageReference Include="Grpc.Net.Client" Version="2.46.0" />

<!-- Application Insights -->
<PackageReference Include="Microsoft.ApplicationInsights" Version="2.20.0" />
<PackageReference Include="Microsoft.ApplicationInsights.Web" Version="2.9.0" />

<!-- JWT Tokens -->
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.25.1" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.25.1" />

<!-- JSON -->
<PackageReference Include="Newtonsoft.Json" Version="13.0.0" />

<!-- Office Documents -->
<PackageReference Include="DocumentFormat.OpenXml" Version="2.11.3" />

<!-- Security -->
<PackageReference Include="BouncyCastle" Version="1.8.9" />

<!-- Frontend -->
<PackageReference Include="jQuery" Version="3.6.0" />
<PackageReference Include="Bootstrap" Version="4.6.0" />

Purpose of Key Packages

Google Analytics Data:
- Track user behavior
- Generate analytics reports
- Monitor page performance

gRPC:
- Google service communication
- Protobuf serialization

Application Insights:
- Performance monitoring
- Error tracking
- Usage analytics

JWT Tokens:
- Parse API authentication tokens
- Validate user sessions

OpenXML:
- Generate Excel reports
- Export appointment data
- Financial reports


⚙️ Configuration

Web.config

Key Settings:

<appSettings>
    <!-- API Endpoints -->
    <add key="PsyterApiBasePath" value="https://dvx.innotech-sa.com/Psyter/Master/APIs/" />

    <!-- Application Token (for scheduling API) -->
    <add key="PsyterApplicationToken" value="f97f3496-a2c8-4c20-84ef-b5a8e6388038" />

    <!-- Session Timeout -->
    <add key="SessionTimeoutCountDown" value="180" />

    <!-- JSON Max Size -->
    <add key="aspnet:MaxJsonDeserializerMembers" value="10000000"/>

    <!-- Web Pages -->
    <add key="webpages:Version" value="3.0.0.0" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
</appSettings>

<system.web>
    <compilation debug="true" targetFramework="4.7.2" />
    <httpRuntime targetFramework="4.7.2" enableVersionHeader="false" />

    <!-- Security -->
    <httpCookies httpOnlyCookies="true" requireSSL="true" />

    <!-- Error Handling -->
    <customErrors mode="Off">
        <error statusCode="404" redirect="~/Error/NotFound" />
    </customErrors>
</system.web>

Application Settings

PsyterApiBasePath:
- Base URL for all API calls
- Points to PsyterAPI backend
- All CRUD operations proxied through API

SessionTimeoutCountDown:
- Session timeout in seconds (180 = 3 minutes)
- User warned before auto-logout
- JavaScript countdown timer

PsyterApplicationToken:
- Application-level authentication
- Used for scheduling API
- Required for availability queries


🏗️ Application Architecture

Data Flow

User Browser
    ↓
Web Portal (MVC)
    ↓
Controllers
    ↓
DALManager / ApiDataAccess
    ↓
HTTP Client → PsyterAPI (REST)
    ↓
Database (SQL Server)

Key Differences from Direct DB Access

Traditional MVC:

Controller → Entity Framework → Database

Psyter Web Portal:

Controller → HttpClient → PsyterAPI → Database

Benefits:
- Shared business logic across platforms (Web, Android, iOS)
- Centralized authentication
- API versioning support
- Easier to scale and maintain
- Consistent validation rules


📂 Data Access Layer

DALManager.cs

Purpose: Centralized API communication manager

Key Methods:

public class DALManager
{
    private HttpClient mObjHttpClient;

    // Constructor - Initialize HTTP client
    public DALManager(ApiType apiType)
    {
        string apiBaseUri = GetApiBaseUri(apiType);
        mObjHttpClient = new HttpClient();
        mObjHttpClient.BaseAddress = new Uri(apiBaseUri);
        mObjHttpClient.DefaultRequestHeaders.Accept.Add(
            new MediaTypeWithQualityHeaderValue("application/json")
        );
    }

    // GET request with authentication
    public async Task<JObject> GetData(
        string requestObject, 
        string methodName, 
        string token
    )
    {
        mObjHttpClient.DefaultRequestHeaders.Authorization = 
            new AuthenticationHeaderValue("Bearer", token);

        HttpResponseMessage response = 
            await mObjHttpClient.GetAsync(methodName);

        if (response.IsSuccessStatusCode)
        {
            var responseJson = await response.Content.ReadAsStringAsync();
            return JObject.Parse(responseJson);
        }

        return HandleError(response);
    }

    // POST request with authentication
    public async Task<JObject> PostData(
        string requestObject, 
        string methodName, 
        string token
    )
    {
        var content = new StringContent(
            requestObject, 
            Encoding.UTF8, 
            "application/json"
        );

        mObjHttpClient.DefaultRequestHeaders.Authorization = 
            new AuthenticationHeaderValue("Bearer", token);

        HttpResponseMessage response = 
            await mObjHttpClient.PostAsync(methodName, content);

        var responseJson = await response.Content.ReadAsStringAsync();
        return JObject.Parse(responseJson);
    }
}

ApiDataAccess.cs

Purpose: Specific API method implementations

Total Methods: 40+ API integration methods

Categories:
- Authentication & Registration
- User Profile Management
- Appointment Booking
- Payment Processing
- Screening & Assessment
- Provider Management
- Admin Operations

Sample Methods:

GetUserDetails()

public async Task<JObject> GetUserDetails(string userName, string password)
{
    // OAuth-style login
    var formContent = new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string, string>("grant_type", "password"),
        new KeyValuePair<string, string>("username", userName),
        new KeyValuePair<string, string>("password", password),
    });

    HttpResponseMessage response = 
        await mObjHttpClient.PostAsync("authenticate", formContent);

    if (response.IsSuccessStatusCode)
    {
        var responseJson = await response.Content.ReadAsStringAsync();
        var jObject = JObject.Parse(responseJson);
        return jObject;
    }

    return CreateErrorResponse("Error while loading user details");
}

RegisterUser()

public async Task<UserRegistrationResponse> RegisterUser(
    UserRegistrationRequest userRegistration
)
{
    var json = JsonConvert.SerializeObject(userRegistration);
    var content = new StringContent(json, Encoding.UTF8, "application/json");

    HttpResponseMessage response = 
        await mObjHttpClient.PostAsync("user/RegUser", content);

    var responseJson = await response.Content.ReadAsStringAsync();
    var jObject = JObject.Parse(responseJson);
    return jObject.ToObject<UserRegistrationResponse>();
}

BookAppointment()

public async Task<JObject> BookAppointment(
    string requestObject, 
    string token
)
{
    var content = new StringContent(
        requestObject, 
        Encoding.UTF8, 
        "application/json"
    );

    mObjHttpClient.DefaultRequestHeaders.Authorization = 
        new AuthenticationHeaderValue("Bearer", token);

    HttpResponseMessage response = 
        await mObjHttpClient.PostAsync("booking/bookslot", content);

    var responseJson = await response.Content.ReadAsStringAsync();
    return JObject.Parse(responseJson);
}

API Method Categories

Authentication (5 methods):
- GetUserDetails() - Login
- RegisterUser() - Registration
- VerifyUser() - Email verification
- ForgotPassword() - Password reset
- ChangePassword() - Update password

Profile Management (8 methods):
- GetUserProfile() - Load profile
- UpdatePersonalInfo() - Update personal data
- UpdateContactInfo() - Update contact
- UploadProfileImage() - Upload photo
- GetEducationHistory() - Provider education
- SaveEducationHistory() - Save education
- GetWorkExperience() - Provider experience
- SaveWorkExperience() - Save experience

Booking (10 methods):
- GetAvailableProviders() - Search therapists
- GetProviderSchedule() - Check availability
- BookAppointment() - Create booking
- GetMyBookings() - List appointments
- CancelBooking() - Cancel appointment
- RescheduleBooking() - Change appointment
- GetBookingDetails() - Appointment info
- JoinSession() - Get video link
- RateProvider() - Submit rating
- GetBookingHistory() - Past appointments

Payment (7 methods):
- InitiatePayment() - Start payment
- VerifyPayment() - Check status
- GetPaymentHistory() - Transaction list
- RequestRefund() - Refund request
- GetWalletBalance() - Check balance
- AddToWallet() - Top up wallet
- GetTransactionDetails() - Payment info

Screening (4 methods):
- GetScreeningQuestions() - Assessment questions
- SubmitScreeningAnswers() - Save answers
- GetScreeningResult() - Get recommendation
- GetScreeningHistory() - Past assessments

Admin (6+ methods):
- GetAllUsers() - User list
- GetUserDetails() - User details
- ActivateUser() - Enable account
- DeactivateUser() - Disable account
- GetPlatformStatistics() - Analytics
- GetFinancialReports() - Revenue data


🔐 Authentication & Session

Session Variables (SessionVariables.cs)

User Session:

public static class SessionVariables
{
    // User Information
    public const string UserInfo = "UserInfo";
    public const string UserType = "UserType";
    public const string UserProfileStatus = "UserProfileStatus";

    // Authentication
    public const string APIAuthTokenList = "APIAuthTokenList";
    public const string ScheduleAPIAccessToken = "ScheduleAPIAccessToken";

    // Application State
    public const string CurrentLanguage = "CurrentLanguage";
    public const string CurrentPage = "CurrentPage";

    // Configuration
    public const string PsyterApplicationToken = "PsyterApplicationToken";
}

Authentication Flow

1. Initial Session (Global.asax.cs):

protected void Session_Start(Object sender, EventArgs e)
{
    // Initialize common data
    Common.Common.initCommonData();

    // Get API authentication token
    Common.APIAuthenticationToken.GetAPIAuthenticationTokens();
}

2. Get API Token (APIAuthenticationToken.cs):

public static async void GetAPIAuthenticationTokens()
{
    // Request app-level token from API
    var formContent = new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string, string>("grant_type", "password"),
        new KeyValuePair<string, string>("applicationtoken", appToken)
    });

    HttpResponseMessage response = 
        await httpClient.PostAsync("authenticate", formContent);

    if (response.IsSuccessStatusCode)
    {
        var responseJson = await response.Content.ReadAsStringAsync();
        var authResponse = JsonConvert.DeserializeObject<APIAuthTokensResponse>(responseJson);

        // Store in session
        HttpContext.Current.Session[SessionVariables.APIAuthTokenList] = authResponse;
    }
}

3. User Login (WebController.cs):

[HttpPost]
public async Task<ActionResult> Login(string username, string password)
{
    ApiDataAccess api = new ApiDataAccess();
    JObject userDetails = await api.GetUserDetails(username, password);

    if (userDetails["access_token"] != null)
    {
        // Store user info in session
        Session[SessionVariables.UserInfo] = userDetails;

        // Redirect based on user type
        int userType = (int)userDetails["UserTypeId"];
        if (userType == 1) // Patient
            return RedirectToAction("Index", "Client");
        else if (userType == 2) // Provider
            return RedirectToAction("Index", "ServiceProvider");
        else if (userType == 3) // Admin
            return RedirectToAction("Index", "Admin");
    }

    return View("Login");
}


🛡️ Authorization Filters

Filter Hierarchy

Total Filters: 10 authorization filters

  1. AuthenticateUser - Base authentication
  2. IsClient - Patient access only
  3. IsServiceProvider - Provider access only
  4. IsAdmin - Admin access only
  5. IsAccountant - Accountant access only
  6. IsContentManager - Content manager access
  7. IsSeoManager - SEO manager access
  8. IsMarkeetingManager - Marketing manager access
  9. IsAuthorized - Generic authorization
  10. exceptionFilter - Global error handler

AuthenticateUser.cs (Base Filter)

Purpose: Verify user is logged in

public class AuthenticateUser : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        // Check if user session exists
        if (HttpContext.Current.Session[SessionVariables.UserInfo] == null)
        {
            // Not logged in - redirect to login
            filterContext.Result = new RedirectToRouteResult(
                new RouteValueDictionary(new
                {
                    controller = "Web",
                    action = "Login"
                })
            );
        }

        base.OnActionExecuting(filterContext);
    }
}

IsClient.cs (Patient Filter)

Purpose: Ensure user is a patient

public class IsClient : AuthenticateUser
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        // First check authentication
        base.OnActionExecuting(filterContext);

        // Check user type
        if (HttpContext.Current.Session[SessionVariables.UserInfo] != null)
        {
            var userInfo = HttpContext.Current.Session[SessionVariables.UserInfo];
            int userType = GetUserType(userInfo);

            if (userType != 1) // Not a patient
            {
                filterContext.Result = new RedirectToRouteResult(
                    new RouteValueDictionary(new
                    {
                        controller = "Error",
                        action = "Unauthorized"
                    })
                );
            }
        }
    }
}

IsServiceProvider.cs (Provider Filter)

Purpose: Ensure user is a care provider

[IsServiceProvider]
public class ServiceProviderController : Controller
{
    // Only care providers can access these actions
}

IsAdmin.cs (Admin Filter)

Purpose: Ensure user is administrator

[IsAdmin]
public class AdminController : Controller
{
    // Only admins can access these actions
}

exceptionFilter.cs (Global Error Handler)

Purpose: Catch and log all exceptions

public class exceptionFilter : HandleErrorAttribute
{
    public override void OnException(ExceptionContext filterContext)
    {
        // Log error
        LogError(filterContext.Exception);

        // Check if AJAX request
        if (filterContext.HttpContext.Request.IsAjaxRequest())
        {
            // Return JSON error
            filterContext.Result = new JsonResult
            {
                Data = new
                {
                    success = false,
                    error = filterContext.Exception.Message
                },
                JsonRequestBehavior = JsonRequestBehavior.AllowGet
            };

            filterContext.ExceptionHandled = true;
        }
        else
        {
            // Redirect to error page
            filterContext.Result = new RedirectToRouteResult(
                new RouteValueDictionary(new
                {
                    controller = "Error",
                    action = "Index"
                })
            );
        }

        base.OnException(filterContext);
    }
}

🧩 Common Utilities

Common.cs

Purpose: Shared utility functions

Total Methods: 30+ utility methods

Key Categories:

1. Initialization

public static void initCommonData()
{
    // Initialize language (default Arabic)
    if (HttpContext.Current.Session[SessionVariables.CurrentLanguage] == null)
    {
        HttpContext.Current.Session[SessionVariables.CurrentLanguage] = 
            AppConstants.Languages.Arabic;
    }

    // Initialize HTTP clients for APIs
    if (mObjHttpClient == null)
    {
        string apiBaseUri = ConfigurationManager.AppSettings["PsyterApiBasePath"];
        mObjHttpClient = new HttpClient();
        mObjHttpClient.BaseAddress = new Uri(apiBaseUri);
        mObjHttpClient.DefaultRequestHeaders.Accept.Add(
            new MediaTypeWithQualityHeaderValue("application/json")
        );
    }

    // Initialize scheduling API client
    if (mObjAvailabilityHttpClient == null)
    {
        var authTokens = (APIAuthTokensResponse)
            HttpContext.Current.Session[SessionVariables.APIAuthTokenList];

        string schedulingApiBaseUri = authTokens.APIAuthToken.SchedulingAPIBaseURL;
        mObjAvailabilityHttpClient = new HttpClient();
        mObjAvailabilityHttpClient.BaseAddress = new Uri(schedulingApiBaseUri);
    }
}

2. Encryption/Decryption

// Encrypt query string parameters
public static string EncryptQS(string plainText, bool toLower = false)
{
    string encrypted = Encrypt(plainText, "&%#@?,:*");

    if (toLower)
        encrypted = encrypted.ToLower();

    return encrypted;
}

// Decrypt query string parameters
public static string DecryptQS(string encryptedText)
{
    return Decrypt(encryptedText, "&%#@?,:*");
}

// Base encryption (Rijndael)
private static string Encrypt(string plainText, string passPhrase)
{
    byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);

    using (RijndaelManaged rijndael = new RijndaelManaged())
    {
        rijndael.Key = DeriveKeyFromPassword(passPhrase);
        rijndael.IV = new byte[16]; // Initialization vector

        ICryptoTransform encryptor = rijndael.CreateEncryptor();
        byte[] encryptedBytes = encryptor.TransformFinalBlock(
            plainTextBytes, 0, plainTextBytes.Length
        );

        return Convert.ToBase64String(encryptedBytes);
    }
}

3. SEO Page Management

public static async Task<ApplicationPageDetail> GetPageDetailForSEOFromDB(
    string pageName
)
{
    DALManager dalManager = new DALManager(ApiType.psyter);
    APIAuthTokensResponse authTokens = 
        (APIAuthTokensResponse)HttpContext.Current.Session[SessionVariables.APIAuthTokenList];

    JObject response = await dalManager.GetData(
        "", 
        $"Common/GetPageDetail/{pageName}", 
        authTokens.APIAuthToken.access_token
    );

    ApplicationPageDetailResponse pageDetailResponse = 
        response.ToObject<ApplicationPageDetailResponse>();

    if (pageDetailResponse.Status == 1)
    {
        return pageDetailResponse.Data;
    }

    return new ApplicationPageDetail();
}

4. Image Upload

public static async Task<string> UploadImage(
    HttpPostedFileBase file, 
    string userId, 
    int imageType
)
{
    if (file != null && file.ContentLength > 0)
    {
        // Convert to base64
        byte[] fileBytes = new byte[file.ContentLength];
        file.InputStream.Read(fileBytes, 0, file.ContentLength);
        string base64Image = Convert.ToBase64String(fileBytes);

        // Upload to media API
        var uploadRequest = new
        {
            UserId = userId,
            ImageType = imageType,
            ImageData = base64Image,
            FileName = file.FileName
        };

        string json = JsonConvert.SerializeObject(uploadRequest);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        HttpResponseMessage response = 
            await mObjHttpClient.PostAsync("media/upload", content);

        if (response.IsSuccessStatusCode)
        {
            var responseJson = await response.Content.ReadAsStringAsync();
            var jObject = JObject.Parse(responseJson);
            return jObject["ImageUrl"].ToString();
        }
    }

    return null;
}

5. Date/Time Utilities

// Convert to user timezone
public static DateTime ConvertToUserTime(DateTime utcTime, int gmtOffset)
{
    return utcTime.AddHours(gmtOffset);
}

// Format date for display
public static string FormatDate(DateTime date, string language)
{
    if (language == AppConstants.Languages.Arabic)
    {
        return date.ToString("dd/MM/yyyy", new CultureInfo("ar-SA"));
    }
    else
    {
        return date.ToString("dd/MM/yyyy", new CultureInfo("en-US"));
    }
}

// Calculate age
public static int CalculateAge(DateTime birthDate)
{
    DateTime today = DateTime.Today;
    int age = today.Year - birthDate.Year;

    if (birthDate.Date > today.AddYears(-age))
        age--;

    return age;
}

📋 Models

Total Model Files: 40+ classes

Base Models

UserLoginInfo.cs

public class UserLoginInfo
{
    public long UserLoginInfoId { get; set; }
    public string UserName { get; set; }
    public string Email { get; set; }
    public int UserTypeId { get; set; }
    public string UserType { get; set; }
    public bool IsActive { get; set; }
    public bool IsVerified { get; set; }
    public DateTime CreatedDate { get; set; }
    public string AccessToken { get; set; }
    public string RefreshToken { get; set; }
    public int TokenExpiresIn { get; set; }
}

PersonalInformation.cs

public class PersonalInformation
{
    public long UserLoginInfoId { get; set; }
    public string FirstNamePLang { get; set; }
    public string FirstNameSLang { get; set; }
    public string LastNamePLang { get; set; }
    public string LastNameSLang { get; set; }
    public DateTime? DateOfBirth { get; set; }
    public int? Age { get; set; }
    public int? GenderType { get; set; }
    public int? CatSalutationId { get; set; }
    public string ProfileImagePath { get; set; }
    public string IdentificationNumber { get; set; }
    public string AboutMePLang { get; set; }
    public string AboutMeSLang { get; set; }
}

ContactInformation.cs

public class ContactInformation
{
    public long UserLoginInfoId { get; set; }
    public string MobileNumber { get; set; }
    public string PhoneNumber { get; set; }
    public string EmergencyContactName { get; set; }
    public string EmergencyContactNumber { get; set; }
    public string AddressLine1 { get; set; }
    public string AddressLine2 { get; set; }
    public long? CityId { get; set; }
    public string CityName { get; set; }
    public long? CountryId { get; set; }
    public string CountryName { get; set; }
    public string ZipCode { get; set; }
}

Provider-Specific Models

EducationHistory.cs

public class EducationHistory
{
    public long EducationHistoryId { get; set; }
    public long UserLoginInfoId { get; set; }
    public string DegreePLang { get; set; }
    public string DegreeSLang { get; set; }
    public string UniversityPLang { get; set; }
    public string UniversitySLang { get; set; }
    public int? CatEducationTypeId { get; set; }
    public string EducationType { get; set; }
    public int? GraduationYear { get; set; }
    public bool IsDeleted { get; set; }
}

WorkExperience.cs

public class WorkExperience
{
    public long WorkExperienceId { get; set; }
    public long UserLoginInfoId { get; set; }
    public string OrganizationNamePLang { get; set; }
    public string OrganizationNameSLang { get; set; }
    public string PositionPLang { get; set; }
    public string PositionSLang { get; set; }
    public DateTime? StartDate { get; set; }
    public DateTime? EndDate { get; set; }
    public bool IsCurrentlyWorking { get; set; }
    public string DescriptionPLang { get; set; }
    public string DescriptionSLang { get; set; }
    public bool IsDeleted { get; set; }
}

CvDetails.cs

public class CvDetails
{
    public long UserLoginInfoId { get; set; }
    public string CVFilePath { get; set; }
    public DateTime? CVUploadDate { get; set; }
    public string LicenseCertificateFilePath { get; set; }
    public DateTime? LicenseUploadDate { get; set; }
    public string CertificateFilePath { get; set; }
    public DateTime? CertificateUploadDate { get; set; }
}

Booking Models

Booking.cs

public class Booking
{
    public long SlotBookingId { get; set; }
    public long ServiceProviderId { get; set; }
    public string ServiceProviderName { get; set; }
    public long ClientId { get; set; }
    public string ClientName { get; set; }
    public DateTime BookingDate { get; set; }
    public string SlotDate { get; set; }
    public string SlotStartTime { get; set; }
    public string SlotEndTime { get; set; }
    public int CatBookingStatusId { get; set; }
    public string BookingStatus { get; set; }
    public int CatCommunicationTypeId { get; set; }
    public string CommunicationType { get; set; }
    public decimal ConsultationFee { get; set; }
    public string VideoSDKMeetingId { get; set; }
    public bool IsRated { get; set; }
    public decimal? Rating { get; set; }
    public string RatingComments { get; set; }
}

BookingOrder.cs

public class BookingOrder
{
    public long SlotBookingPayForId { get; set; }
    public long SlotBookingId { get; set; }
    public decimal TotalAmount { get; set; }
    public decimal PlatformFee { get; set; }
    public decimal VATAmount { get; set; }
    public decimal NetAmount { get; set; }
    public int CatPaymentStatusId { get; set; }
    public string PaymentStatus { get; set; }
    public string TransactionId { get; set; }
    public DateTime? PaymentDate { get; set; }
    public int CatPaymentMethodId { get; set; }
    public string PaymentMethod { get; set; }
}

Screening Models

CatScreeningQuestion.cs

public class CatScreeningQuestion
{
    public int ScreeningQuestionId { get; set; }
    public string QuestionPLang { get; set; }
    public string QuestionSLang { get; set; }
    public int QuestionType { get; set; } // 1=Single Choice, 2=Multiple Choice, 3=Text
    public int DisplayOrder { get; set; }
    public bool IsActive { get; set; }
    public List<CatScreeningQuestinOption> Options { get; set; }
}

CatScreeningQuestinOption.cs

public class CatScreeningQuestinOption
{
    public int CatScreeningQuetionOptionId { get; set; }
    public int ScreeningQuestionId { get; set; }
    public string OptionTextPLang { get; set; }
    public string OptionTextSLang { get; set; }
    public int OptionValue { get; set; }
    public int DisplayOrder { get; set; }
}

Catalogue Models

CatSpeciality.cs

public class CatSpeciality
{
    public long CatSpecialityId { get; set; }
    public string SpecialityNamePLang { get; set; }
    public string SpecialityNameSLang { get; set; }
    public string SpecialityDescriptionPLang { get; set; }
    public string SpecialityDescriptionSLang { get; set; }
    public string IconPath { get; set; }
    public bool IsActive { get; set; }
    public int DisplayOrder { get; set; }
}

Language.cs

public class Language
{
    public long LanguageId { get; set; }
    public string LanguageNamePLang { get; set; }
    public string LanguageNameSLang { get; set; }
    public string LanguageCode { get; set; } // ar, en, fr, etc.
    public bool IsActive { get; set; }
}

Country.cs

public class Country
{
    public long CountryId { get; set; }
    public string CountryNamePLang { get; set; }
    public string CountryNameSLang { get; set; }
    public string CountryCode { get; set; } // SA, AE, EG, etc.
    public string PhoneCode { get; set; } // +966, +971, +20, etc.
    public bool IsActive { get; set; }
}

City.cs

public class City
{
    public long CityId { get; set; }
    public long CountryId { get; set; }
    public string CityNamePLang { get; set; }
    public string CityNameSLang { get; set; }
    public bool IsActive { get; set; }
}

📊 Enumerations

Enumeration.cs

Purpose: Define all system enumerations

public class Enumeration
{
    // User Types
    public enum UserTypes
    {
        Patient = 1,
        CareProvider = 2,
        Admin = 3,
        Accountant = 4,
        ContentManager = 5,
        SEOManager = 6,
        MarketingManager = 7,
        Organization = 8
    }

    // Gender Types
    public enum GenderTypes
    {
        Male = 1,
        Female = 2,
        Other = 3
    }

    // Booking Status
    public enum BookingStatus
    {
        Pending = 1,
        Confirmed = 2,
        Cancelled = 3,
        Completed = 4,
        NoShow = 5,
        Rescheduled = 6
    }

    // Payment Status
    public enum PaymentStatus
    {
        Pending = 1,
        Completed = 2,
        Failed = 3,
        Refunded = 4,
        PartiallyRefunded = 5
    }

    // Communication Types
    public enum CommunicationTypes
    {
        InPerson = 1,
        VideoCall = 2,
        AudioCall = 3,
        Chat = 4
    }

    // Profile Components
    public enum ProfileComponents
    {
        PersonalInformation = 1,
        ContactInformation = 2,
        Education = 3,
        Experience = 4,
        Documents = 5,
        Specialties = 6,
        Languages = 7,
        Availability = 8
    }

    // Document Types
    public enum DocumentTypes
    {
        ProfileImage = 1,
        CV = 2,
        License = 3,
        Certificate = 4,
        IdentityDocument = 5
    }

    // Notification Types
    public enum NotificationTypes
    {
        BookingConfirmed = 1,
        BookingCancelled = 2,
        BookingReminder = 3,
        PaymentReceived = 4,
        MessageReceived = 5,
        ProfileUpdated = 6
    }
}

🌐 Request/Response Models

Request Models (Models/Request/)

UserRegistrationRequest.cs

public class UserRegistrationRequest
{
    public string Email { get; set; }
    public string Password { get; set; }
    public string ConfirmPassword { get; set; }
    public int UserTypeId { get; set; }
    public string FirstNamePLang { get; set; }
    public string LastNamePLang { get; set; }
    public string MobileNumber { get; set; }
    public int? GenderType { get; set; }
    public DateTime? DateOfBirth { get; set; }
    public long? CountryId { get; set; }
    public long? CityId { get; set; }
    public List<int> SpecialityIds { get; set; } // For providers
    public List<int> LanguageIds { get; set; } // For providers
}

BookingRequest.cs

public class BookingRequest
{
    public long ServiceProviderId { get; set; }
    public string SlotDate { get; set; }
    public string SlotStartTime { get; set; }
    public string SlotEndTime { get; set; }
    public long SlotAvailabilityId { get; set; }
    public int CatCommunicationTypeId { get; set; }
    public string BookingNotes { get; set; }
    public int PaymentMethodId { get; set; }
    public string PromoCode { get; set; }
}

Response Models (Models/Response/)

BaseResponse.cs

public class BaseResponse
{
    public int Status { get; set; } // 1=Success, 0=Error
    public string Reason { get; set; }
    public string Message { get; set; }
    public object Data { get; set; }
}

UserRegistrationResponse.cs

public class UserRegistrationResponse : BaseResponse
{
    public new UserRegistrationData Data { get; set; }
}

public class UserRegistrationData
{
    public long UserLoginInfoId { get; set; }
    public string Email { get; set; }
    public string VerificationCode { get; set; }
    public bool RequiresVerification { get; set; }
}

ScreenigQuestionOptionResponse.cs

public class ScreenigQuestionOptionResponse : BaseResponse
{
    public new List<CatScreeningQuestion> ScreeningQuestions { get; set; }
}

🔧 Constants

AppConstants.cs

public class AppConstants
{
    // Languages
    public class Languages
    {
        public const string Arabic = "ar";
        public const string English = "en";
    }

    // Date Formats
    public class DateFormats
    {
        public const string DisplayDate = "dd/MM/yyyy";
        public const string DisplayDateTime = "dd/MM/yyyy hh:mm tt";
        public const string ApiDate = "yyyy-MM-dd";
        public const string ApiDateTime = "yyyy-MM-dd HH:mm:ss";
    }

    // Session Timeout
    public const int SessionTimeoutMinutes = 30;
    public const int SessionWarningSeconds = 180;

    // Image Settings
    public class ImageSettings
    {
        public const int MaxFileSizeKB = 5120; // 5MB
        public const string AllowedExtensions = ".jpg,.jpeg,.png,.gif";
        public const int ProfileImageWidth = 300;
        public const int ProfileImageHeight = 300;
    }

    // Pagination
    public class Pagination
    {
        public const int DefaultPageSize = 20;
        public const int MaxPageSize = 100;
    }

    // Payment
    public class PaymentSettings
    {
        public const decimal PlatformFeePercentage = 15; // 15%
        public const decimal VATPercentage = 15; // 15%
        public const decimal MinBookingAmount = 50;
        public const decimal MaxBookingAmount = 5000;
    }
}

Pages Enumeration

public enum Pages
{
    Home = 1,
    Login = 2,
    Register = 3,
    ScreeningTest = 4,
    ClientIndex = 5,
    ServiceProviderIndex = 6,
    AdminIndex = 7,
    TherapistList = 8,
    BookingConfirmation = 9,
    MyAppointments = 10
}

🚀 Application Lifecycle

Global.asax.cs

Application Events:

public class MvcApplication : System.Web.HttpApplication
{
    // Application startup
    protected void Application_Start()
    {
        // Register areas
        AreaRegistration.RegisterAllAreas();

        // Register global filters
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);

        // Register routes
        RouteConfig.RegisterRoutes(RouteTable.Routes);

        // Register bundles (CSS/JS)
        BundleConfig.RegisterBundles(BundleTable.Bundles);
    }

    // New session started
    protected void Session_Start(Object sender, EventArgs e)
    {
        // Initialize common data
        Common.Common.initCommonData();

        // Get API authentication tokens
        Common.APIAuthenticationToken.GetAPIAuthenticationTokens();
    }

    // Request started
    protected void Application_BeginRequest()
    {
        // Disable caching
        Response.Cache.SetCacheability(HttpCacheability.NoCache);
        Response.Cache.SetExpires(DateTime.UtcNow.AddHours(-1));
        Response.Cache.SetNoStore();

        // Fix double slash issue
        string requestUrl = Request.ServerVariables["REQUEST_URI"];
        string rewriteUrl = Request.ServerVariables["UNENCODED_URL"];

        if (rewriteUrl.Contains("//") && !requestUrl.Contains("//"))
        {
            Response.RedirectPermanent(requestUrl);
        }
    }

    // Before response sent
    protected void Application_PreSendRequestHeaders(Object sender, EventArgs e)
    {
        // Ensure no caching
        Response.Cache.SetCacheability(HttpCacheability.NoCache);
    }
}

📦 Bundling & Minification

BundleConfig.cs

CSS Bundles:

public class BundleConfig
{
    public static void RegisterBundles(BundleCollection bundles)
    {
        // jQuery
        bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
            "~/Scripts/jquery-{version}.js"
        ));

        // Bootstrap
        bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include(
            "~/Scripts/bootstrap.js"
        ));

        bundles.Add(new StyleBundle("~/Content/bootstrap").Include(
            "~/Content/bootstrap.css"
        ));

        // Custom styles
        bundles.Add(new StyleBundle("~/Content/css").Include(
            "~/Content/site.css",
            "~/Content/custom.css"
        ));

        // Custom scripts
        bundles.Add(new ScriptBundle("~/bundles/custom").Include(
            "~/Scripts/common.js",
            "~/Scripts/validation.js",
            "~/Scripts/api-client.js"
        ));

        // Enable optimization in production
        #if !DEBUG
            BundleTable.EnableOptimizations = true;
        #endif
    }
}


🛣️ Routing

RouteConfig.cs

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        // Default route
        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new
            {
                controller = "Web",
                action = "Home",
                id = UrlParameter.Optional
            }
        );

        // SEO-friendly routes
        routes.MapRoute(
            name: "TherapistProfile",
            url: "therapist/{id}/{name}",
            defaults: new
            {
                controller = "Web",
                action = "TherapistProfile"
            }
        );

        routes.MapRoute(
            name: "BlogPost",
            url: "blog/{id}/{slug}",
            defaults: new
            {
                controller = "Web",
                action = "BlogPost"
            }
        );
    }
}

END OF PART 1


What’s in Part 1:
✅ Project overview & architecture
✅ Configuration & settings
✅ Data access layer (API integration)
✅ Models (user, booking, screening, catalogues)
✅ Common utilities & helpers
✅ Authentication & session management
✅ Authorization filters
✅ Request/response models
✅ Constants & enumerations
✅ Application lifecycle
✅ Bundling & routing

Coming in Part 2:
- WebController (public website)
- Patient/ClientController (patient portal)
- Physician/ServiceProviderController (provider portal)
- Admin controllers (admin, accountant, content, SEO, marketing)
- Views & Razor templates
- JavaScript & frontend
- Deployment pipeline