DataLunge

// Latest working register
[HttpPost]
[DisableRequestSizeLimit]
[Route("Register")]
public async Task<IActionResult> Register([FromForm] UserViewModel uvm)
{
    try
    {
        var formCollection = await Request.ReadFormAsync();
        var photo = formCollection.Files.FirstOrDefault();
 
        var user = await _userManager.FindByEmailAsync(uvm.Email);
        int lastUserID = _appDbContext.Users
                         .OrderByDescending(u => u.User_ID)
                         .Select(u => u.User_ID)
                         .FirstOrDefault();
 
        // Validate Phone Number Pattern
        var phoneNumberPattern = @"^\d{10}$";
 
        bool isValidPhoneNumber = Regex.IsMatch(uvm.PhoneNumber, phoneNumberPattern);
 
        if (!isValidPhoneNumber) return BadRequest("Enter valid 10-digit phone number.");
 
        // Validate South African ID number
        if (!IsValidSouthAfricanIDNumber(uvm.Id_Number, uvm.Date_of_Birth))
        {
            return BadRequest("Enter a valid South African ID number.");
        }
 
        if (user == null)
        {
            if (photo != null && photo.Length > 0)
            {
                using (var memoryStream = new MemoryStream())
                {
                    await photo.CopyToAsync(memoryStream);
                    var fileBytes = memoryStream.ToArray();
                    string base64Image = Convert.ToBase64String(fileBytes);
 
                    user = new User
                    {
                        User_ID = lastUserID + 1,
                        Name = uvm.Name,
                        Surname = uvm.Surname,
                        UserName = uvm.Email,
                        Email = uvm.Email,
                        PasswordHash = _userManager.PasswordHasher.HashPassword(null, uvm.Password),
                        User_Status_ID = uvm.User_Status_ID,
                        User_Type_ID = uvm.User_Type_ID,
                        PhoneNumber = uvm.PhoneNumber,
                        Date_of_Birth = uvm.Date_of_Birth,
                        ID_Number = uvm.Id_Number,
                        Physical_Address = uvm.Physical_Address,
                        Photo = base64Image // Store the base64 string of the photo
                    };
 
                    IdentityResult result = await _userManager.CreateAsync(user);
 
                    if (result.Succeeded)
                    {
                        // Assign role based on User_Type_ID
                        string roleName = GetRoleNameByUserType(uvm.User_Type_ID);
                        if (!string.IsNullOrEmpty(roleName))
                        {
                            var roleResult = await _userManager.AddToRoleAsync(user, roleName);
                            if (!roleResult.Succeeded)
                            {
                                return BadRequest(new { Status = "Error", Errors = roleResult.Errors });
                            }
                        }
 
 
                        return Ok(new { Status = "Success", Message = "User created successfully!" });
                    }
                    else
                    {
                        return StatusCode(StatusCodes.Status500InternalServerError, result.Errors.FirstOrDefault()?.Description);
                    }
                }
            }
            else
            {
                return BadRequest("Photo is required.");
            }
        }
        else
        {
            return Forbid("Account already exists.");
        }
    }
    catch (DbUpdateException dbEx)
    {
        return StatusCode(StatusCodes.Status500InternalServerError, dbEx.InnerException?.Message ?? "An error occurred while processing your request.");
    }
    catch (Exception ex)
    {
        return StatusCode(StatusCodes.Status500InternalServerError, "An error occurred while processing your request.");
    }
}
 
// Method to validate South African ID number
private bool IsValidSouthAfricanIDNumber(string idNumber, DateTime dateOfBirth)
{
    // Check if the ID number is exactly 13 digits long
    if (idNumber.Length != 13 || !long.TryParse(idNumber, out _))
    {
        return false;
    }
 
    // Validate date of birth (first six digits)
    string dateOfBirthPart = idNumber.Substring(0, 6);
    if (!DateTime.TryParseExact(dateOfBirthPart, "yyMMdd", null, System.Globalization.DateTimeStyles.None, out DateTime parsedDate))
    {
        return false;
    }
 
    // Check if the last two digits of the ID number match the last two digits of the year of birth
    if (parsedDate.Year % 100 != dateOfBirth.Year % 100)
    {
        return false;
    }
 
    // If it passes the length, date of birth, and year checks, it is considered valid
    return true;
}
 
 
private string GetRoleNameByUserType(int userTypeId)
{
    return userTypeId switch
    {
        1 => "Administrator",
        2 => "Employee",
        3 => "Member",
        _ => string.Empty,
    };
}

  
//untested login
[HttpPost]
[Route("Login")]
public async Task<ActionResult> Login(LoginViewModel lv)
{
    var user = await _userManager.FindByNameAsync(lv.Email);
 
    if (user != null && await _userManager.CheckPasswordAsync(user, lv.Password))
    {
        try
        {
            var principal = await _claimsPrincipalFactory.CreateAsync(user);
            return await GenerateJWTToken(user);
        }
        catch (Exception)
        {
 
            return StatusCode(StatusCodes.Status500InternalServerError, "Internal Server Error. Please contact support.");
        }
    }
    else
    {
        return NotFound("Incorrect email or password, Please Try Again");
    }
}
 
[HttpGet]
private async Task<ActionResult> GenerateJWTToken(User user)
{
    var role = await _userManager.GetRolesAsync(user);
    IdentityOptions _identityOptions = new IdentityOptions();
    // Create JWT Token
    var claims = new List<Claim>
    {
        new Claim(JwtRegisteredClaimNames.Sub, user.Email),
        new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
        new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName),
        // Add user ID claim
        new Claim("userId", user.Id.ToString()),
 
        new Claim("User_Type_ID", user.User_Type_ID.ToString()),
 
    };
 
    if (role.Count() > 0)
    {
        claims.Add(new Claim(_identityOptions.ClaimsIdentity.RoleClaimType, role.FirstOrDefault()));
    }
 
    var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Tokens:Key"]));
    var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
 
    var token = new JwtSecurityToken(
       issuer:  _configuration["Tokens:Issuer"],
       audience: _configuration["Tokens:Audience"],
       claims: claims,
       signingCredentials: credentials,
       expires: DateTime.UtcNow.AddHours(3)
    );
 
    return Created("", new
    {
        token = new JwtSecurityTokenHandler().WriteToken(token),
        user = user.UserName,
 
        userTypeId = user.User_Type_ID,
        // Include user ID in the response
        userId = user.Id
    });
}

//latest working editUser before base64
[HttpPut]
[Route("editUser/{id}")]
public async Task<IActionResult> EditUser(int id, UpdateUserViewModel uv)
{
    try
    {

        var user = await _userManager.FindByIdAsync(id.ToString());

        if (user == null)
        {
            return NotFound("User not found.");
        }

        user.Name = uv.Name;
        user.Surname = uv.Surname;
        user.Email = uv.Email;
        user.Physical_Address = uv.Physical_Address;
        user.PhoneNumber = uv.PhoneNumber;
        user.Photo = uv.Photo;

        // Update the user
        var result = await _userManager.UpdateAsync(user);

        if (result.Succeeded)
        {
            return Ok("User updated successfully.");
        }
        else
        {
            return BadRequest(result.Errors);
        }
    }
    catch (Exception)
    {

        return BadRequest("An Error Occured, Please Try Again");
    }
}

//latest working getUserById before base64
[HttpGet]
[Route("getUserById/{id}")]
public async Task<IActionResult> GetUserById(int id)
{

    try
    {
        var user = await _userManager.FindByIdAsync(id.ToString());

        if (user == null)
        {
            return NotFound("User not found.");
        }

        // Map User entity to UserViewModel or return the User entity directly
        var userViewModel = new UserViewModel
        {

            Name = user.Name,
            Surname = user.Surname,
            Email = user.Email,
            Physical_Address = user.Physical_Address,
            PhoneNumber = user.PhoneNumber,
            //Photo = user.Photo,
            Date_of_Birth = user.Date_of_Birth,
            User_Status_ID = user.User_Status_ID,
            User_Type_ID = user.User_Type_ID,
            Id_Number = user.ID_Number
        };

        return Ok(userViewModel);
    }
    catch (Exception)
    {

        return BadRequest("Try again");
    }
}
import { CommonModule } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';
import { FlexLayoutModule } from '@angular/flex-layout';
import {jwtDecode} from 'jwt-decode';
 
@Component({
  selector: 'app-login',
  standalone: true,
  imports: [
    MaterialModule,
    ReactiveFormsModule,
    RouterLink,
    CommonModule,
    HttpClientModule,
    FlexLayoutModule   
  ],
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})
export class LoginComponent {
 
  loginFormGroup: FormGroup;
  isLoading: boolean = false;
 
  constructor(
    private router: Router,
    private userService: UserService,
    private fb: FormBuilder,
    private snackBar: MatSnackBar
  ) { 
    this.loginFormGroup = this.fb.group({
      email: ['', [Validators.required, Validators.email]],
      password: ['', Validators.required],
    });
  }
 
  ngOnInit() {}
 
  async LoginUser() {
    if (this.loginFormGroup.valid) {
      this.isLoading = true;
  
      this.userService.LoginUser(this.loginFormGroup.value).subscribe({
        next: (result: any) => {
          if (result && result.token) {
            localStorage.setItem('User', JSON.stringify(result));
  
            // Decode the JWT token to extract user information
            const decodedToken: any = jwtDecode(result.token);
            console.log('Decoded Token:', decodedToken);
 
            const userId = decodedToken.userId;
            const userTypeID = decodedToken.User_Type_ID;
            console.log('User Type ID:', userTypeID);
            console.log('User Type ID:', userTypeID);
            
            
            this.loginFormGroup.reset();
            
            // Navigate based on user type
            if (userTypeID === "1") {  // Ensure userTypeID is compared as string
              this.router.navigateByUrl(`/OwnerHome/${userId}`);
            } else if (userTypeID === "2") {
              this.router.navigateByUrl(`/EmployeeHome/${userId}`);
            } else if (userTypeID === "3") {
              this.router.navigateByUrl(`/Home/${userId}`);
            } else {
              this.snackBar.open('Error: Invalid user type: Please register as an authorized user', 'Close', { duration: 5000 });
            }
          } else {
            this.snackBar.open('Login failed', 'Close', { duration: 5000 });
          }
        },
        error: () => {
          this.isLoading = false;
          this.snackBar.open('Login failed', 'Close', { duration: 5000 });
        },
        complete: () => {
          this.isLoading = false;
        }
      });
    }
  }
}
[HttpGet]
[Route("getUserById/{id}")]
public async Task<IActionResult> GetUserById(int id)
{
    try
    {
        var user = await _userManager.FindByIdAsync(id.ToString());

        if (user == null)
        {
            return NotFound("User not found.");
        }

        var userViewModel = new UserViewModel
        {
            Name = user.Name,
            Surname = user.Surname,
            Email = user.Email,
            Physical_Address = user.Physical_Address,
            PhoneNumber = user.PhoneNumber,
            Date_of_Birth = user.Date_of_Birth,
            User_Status_ID = user.User_Status_ID,
            User_Type_ID = user.User_Type_ID,
            Id_Number = user.ID_Number
        };

        // Fetch the base64 string photo from user.Photo
        string base64Photo = user.Photo;

        // Convert base64 string to byte array
        byte[] photoBytes = Convert.FromBase64String(base64Photo);

        // Convert byte array to IFormFile-like object
        var memoryStream = new MemoryStream(photoBytes);

        userViewModel.Photo = new FormFile(memoryStream, 0, photoBytes.Length, "Photo", "photo.jpg")
        {
            Headers = new HeaderDictionary(),
            ContentType = "image/jpeg" // Set the content type as appropriate
        };

        return Ok(userViewModel);
    }
    catch (Exception ex)
    {
        // Log the exception for debugging
        Console.WriteLine(ex.Message);
        return BadRequest("An error occurred while fetching user details.");
    }
}
[HttpGet]
[Route("getUserById/{id}")]
public async Task<IActionResult> GetUserById(int id)
{
    try
    {
        IQueryable<User> query =  _appDbContext.Users.Include(u => u.User_Status).Include(u => u.User_Type);

        var results = await query.ToArrayAsync();

        dynamic users = results.Select(u => new
        {
            u.Id,
            u.Name,
            u.Surname,
            u.Email,
            u.Physical_Address,
            u.PhoneNumber,
            u.Date_of_Birth,
            UserStatus = u.User_Status.User_Status_Description,
            UserType = u.User_Type.User_Type_Name,
            u.Photo,
            u.ID_Number
        });

        return Ok(users);
    }
    catch (Exception ex)
    {
        // Log the exception for debugging
        Console.WriteLine(ex.Message);
        return BadRequest("An error occurred while fetching user details.");
    }
}
//HTML
<div class="text-center mb-3">
          <div class="profile-photo-wrapper">
            <img src="https://cdn.create.vista.com/api/media/small/248105354/stock-vector-user-vector-icon-grey-background" alt src="assets/images/user.jpg" class="img-fluid rounded-circle profile-photo">
            <div class="edit-photo-wrapper">
              <a href="#" (click)="enableEditMode($event)" class="edit-link">Edit</a>
              <label [class.disabled-icon]="!isEditMode" for="photoUpload" class="photo-upload-icon">
                <i class="bi bi-camera"></i>
              </label>
            </div>
            <input type="file" id="photoUpload" formControlName="photo" class="d-none" (change)="onPhotoChange($event)" [readonly]="!isEditMode" >
          </div>
        </div>

//TS
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl, Validators, FormBuilder } from '@angular/forms';
import { MatSnackBar } from '@angular/material/snack-bar';
import { UserProfile } from '../Models/UserProfile';
import { UserService } from '../Services/userprofile.service';
import { Router, ActivatedRoute } from '@angular/router';
import { Location } from '@angular/common';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule } from '@angular/forms';
import { updateUser } from '../shared/update-user';

declare var $: any; // Import jQuery

@Component({
  selector: 'app-profile-page',
  standalone: true,
  imports: [CommonModule, ReactiveFormsModule],
  templateUrl: './profile-page.component.html',
  styleUrls: ['./profile-page.component.css']
})
export class ProfilePageComponent implements OnInit {
  profileForm: FormGroup;
  user!: updateUser;
  isEditMode = false;
  errorMessage = '';

  constructor(
    private userService: UserService,
    private router: Router,
    private fb: FormBuilder,
    private location: Location) 
  {
    this.profileForm = this.fb.group({
      email: ['', [Validators.required, Validators.email]],
      name: ['', [Validators.required, Validators.minLength(3), Validators.maxLength(20)]],
      surname: ['', [Validators.required, Validators.minLength(3), Validators.maxLength(20)]],
      phoneNumber: ['', [Validators.required, Validators.maxLength(10)]],
      physical_Address: ['', [Validators.required, Validators.maxLength(255)]],
      photo: ['']
    });
  }

  ngOnInit(): void {
    const userId = JSON.parse(localStorage.getItem('User')!).userId;
    console.log('User ID from local storage:', userId);
    this.userService.getUserById(userId).subscribe({
      next: (result) => {
        console.log('User data received:', result);
        this.user = result;
        this.profileForm.patchValue(this.user);
      },
      error: () => {
        console.error('Error fetching user profile:'); // Add error console log
        alert('Error fetching user profile');
      }
    });
    this.isEditMode = false;
  }

  clearForm() {
    this.profileForm.reset();
  }

  enableEditMode(event: Event) {
    event.preventDefault();
    this.isEditMode = true;
    this.profileForm.enable();
  }

  openSaveModal() {
    if (this.profileForm.invalid) {
      this.showValidationErrors();
      $('#errorModal').modal('show');
      return;
    }
    $('#saveConfirmationModal').modal('show');
  }

  showValidationErrors() {
    const invalidFields: string[] = [];
    Object.keys(this.profileForm.controls).forEach(key => {
      const controlErrors = this.profileForm.get(key)?.errors;
      if (controlErrors) {
        Object.keys(controlErrors).forEach(errorKey => {
          invalidFields.push(`${key}: ${errorKey}`);
        });
      }
    });
    this.errorMessage = `Please enter a valid input: ${invalidFields.join(', ')}`;
  }

  dismissModal() {
    $('#saveConfirmationModal').modal('hide');
  }

  dismissErrorModal() {
    $('#errorModal').modal('hide');
  }

  confirmSave() {
    this.dismissModal();
    this.onSubmit();
    this.isEditMode = false; // Disable edit mode after confirmation
  }

  onSubmit() {
    if (this.profileForm.valid) {
      const userId = JSON.parse(localStorage.getItem('User')!).userId;
      this.userService.updateUser(userId,this.profileForm.value).subscribe({
        next: (result) => {
          console.log('User to be updated:', result);
          this.router.navigateByUrl(`/ProfilePage/${userId}`);
          alert('Successfully updated profile');
        },
        error: () => {
          console.error('Error updating user profile:');
          alert('Error updating profile');
        }
      });
    }
  }

  onPhotoChange(event: Event): void {
    if (!this.isEditMode) return;

    const input = event.target as HTMLInputElement;
    if (input.files && input.files[0]) {
      const reader = new FileReader();
      reader.onload = (e: any) => {
        const img = document.querySelector('.profile-photo') as HTMLImageElement;
        img.src = e.target.result;
      };
      reader.readAsDataURL(input.files[0]);
    }
  }

  goBack() {
    const userTypeId = JSON.parse(localStorage.getItem('User')!).userTypeId;
    const userId = JSON.parse(localStorage.getItem('User')!).userId;
    if (userTypeId === 1) {  // Ensure userTypeID is compared as string
      this.router.navigateByUrl(`/OwnerHome/${userId}`);
    } else if (userTypeId === 2) {
      this.router.navigateByUrl(`/EmployeeHome/${userId}`);
    } else if (userTypeId === 3) {
      this.router.navigateByUrl(`/Home/${userId}`);
    }
  }

  changePassword() {
    this.router.navigateByUrl('/ChangePasswordPage');
  }
}
//Before Domain
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

//After domain
SG.a1b2C3d4E5F6g7H8i9J0kL1m2N3O4P5Q6R7s8T9u0V1W2X3Y4Z5a6B7c8D9E0fG1H2i3J4K5L6

//latest
test api key 2.0
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
//// Forgot password
//[HttpPost("ForgotPassword")]
//public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model)
//{
//    try
//    {
//        if (string.IsNullOrEmpty(model.Email))
//        {
//            return BadRequest("Email is required.");
//        }

//        var user = await _userManager.FindByEmailAsync(model.Email);
//        if (user == null)
//        {
//            return Ok("Do not exist.");
//        }

//        var token = await _userManager.GeneratePasswordResetTokenAsync(user);
//        var resetLink = Url.Action("ResetPassword", "User",
//                                   new { token, email = user.Email },
//                                   protocol: HttpContext.Request.Scheme);

//        var mailtoLink = $"mailto:{model.Email}?subject=Password%20Reset&body=Reset%20your%20password%20by%20clicking%20the%20following%20link:%20{WebUtility.UrlEncode(resetLink)}";

//        await SendResetPasswordEmail(model.Email, resetLink); // Send reset password email

//        return Ok("Please check your email for password reset instructions.");
//    }
//    catch (Exception ex)
//    {
//        _logger.LogError(ex, "Error sending reset password email.");
//        return StatusCode(StatusCodes.Status500InternalServerError, "Error sending reset password email.");
//    }
//}

//[HttpPost]
//[Route("ForgotPassword")]
//public IActionResult ForgotPassword(ForgotPasswordViewModel model)
//{
//    try
//    {

//        if (string.IsNullOrEmpty(model.Email))
//        {
//            return BadRequest("Email is required.");
//        }

//        var resetLink = $"mailto:{WebUtility.UrlEncode(model.Email)}?subject=Reset%20Password&body=Click%20the%20following%20link%20to%20reset%20your%20password:%20[RESET_LINK_HERE]";

//        // Redirect the user to the mailto link
//        return Redirect(resetLink);
//    }
//    catch (Exception)
//    {
//        return BadRequest("Failed");
//    }
//}


// Reset password
//[HttpPost("ResetPassword")]
//public async Task<IActionResult> ResetPassword(ResetPasswordViewModel model)
//{
//    if (!ModelState.IsValid)
//    {
//        return BadRequest("Invalid data.");
//    }

//    var user = await _userManager.FindByEmailAsync(model.Email);
//    if (user == null)
//    {
//        return NotFound("User not found.");
//    }

//    var result = await _userManager.ResetPasswordAsync(user, model.Token, model.Password);
//    if (result.Succeeded)
//    {
//        return Ok("Password has been reset successfully.");
//    }

//    return BadRequest("Error while resetting the password.");
//}

//private async Task SendResetPasswordEmail(string email, string resetLink)
//{
//    try
//    {
//        var smtpClient = new SmtpClient(_configuration["Smtp:Host"])
//        {
//            Port = int.Parse(_configuration["Smtp:Port"]),
//            Credentials = new NetworkCredential(_configuration["Smtp:Username"], _configuration["Smtp:Password"]),
//            EnableSsl = bool.Parse(_configuration["Smtp:EnableSsl"]),
//        };

//        var mailMessage = new MailMessage
//        {
//            From = new MailAddress(_configuration["Smtp:From"]),
//            Subject = "Reset Password",
//            Body = $"<h4>Reset your password by <a href='{resetLink}'>clicking here</a></h4>",
//            IsBodyHtml = true,
//        };
//        mailMessage.To.Add(email);

//        await smtpClient.SendMailAsync(mailMessage);
//    }
//    catch (SmtpException ex)
//    {
//        _logger.LogError(ex, "Error sending reset password email.");
//        throw;
//    }
//}
//PaymentsClassDemo L12

//Cancel Component
//HTML
<h1>Cancelled</h1>
<p>Not sure you want to buy?;<br /> we'll preserve your cart until you're ready!</p>
//TS
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-cancel',
  templateUrl: './cancel.component.html',
  styleUrls: ['./cancel.component.css']
})
export class CancelComponent implements OnInit {

  constructor() { }

  ngOnInit(): void {
  }
}

//Cart Component
//HTML
<h2 class="my-5">Items in cart</h2>

<div class="container products-container">
    <table class="table">
        <thead class="table-dark">
            <tr>
                <th scope="col">Subscription</th>
                <th scope="col">Price</th>
                <th scope="col">Quantity</th>
                <th scope="col">Total Cost</th>
            </tr>
        </thead>


        <tbody class="table-body">
            <tr *ngFor="let cartSub of subscriptionsInCart">
                <td>{{cartSub.subscription.name}}</td>
                <td>{{cartSub.subscription.price}}</td>
                <td>
                    <span class="increase" style="color:#89cff0">
                        <i class="fa-solid fa-circle-left fa-lg" (click)="reduceProdCount(cartSub.subscription)"></i>
                    </span>
                    {{cartSub.quantity}}
                    <span class="decrease" style="color:#89cff0">
                        <i class="fa-solid fa-circle-right fa-lg" (click)="increaseProdCount(cartSub.subscription)"></i>
                    </span></td>
                <td>{{cartSub.totalCost}}</td>
            </tr>
        </tbody>

        <tfoot class="table-footer">
            <tr>
                <td></td>
                <td></td>
                <td><b>Total:</b></td>
                <td>{{totalCostOfSubcriptionsInCart}}</td>
            </tr>
        </tfoot>

    </table>

    <br>

    <app-payfastcheckout></app-payfastcheckout>
</div>
//TS
import { Component, OnInit } from '@angular/core';
import { SubscriptionCartOrganiserService } from '../services/SubscriptionCartOrganiser.service';
import { CartSubScription } from '../models/CartSubscriptionVM.model';
import { Subscription } from '../models/Subscription.model';

@Component({
  selector: 'app-cart',
  templateUrl: './cart.component.html',
  styleUrls: ['./cart.component.css']
})
export class CartComponent implements OnInit {
  subscriptionsInCart : CartSubScription [] = [];
  totalCostOfSubcriptionsInCart :number = 0;

  constructor(private cartManager : SubscriptionCartOrganiserService) {
    this.loadSubscriptions();
    cartManager.cartProductsNumberDS.subscribe(num => {
        this.loadSubscriptions();
    });
  }

  ngOnInit(): void {
  }

  loadSubscriptions() {
    this.subscriptionsInCart = this.cartManager.getSubscriptionsInCart();
    this.totalCostOfSubcriptionsInCart = this.cartManager.getTotalCostOfSubcriptionsInCart();
  }

  increaseProdCount (sub : Subscription) {
    for (var idx = 0; idx < this.subscriptionsInCart.length; idx++) {
      if (this.subscriptionsInCart[idx].subscription.id == sub.id) {
        this.cartManager.addProdFromCart(this.subscriptionsInCart[idx].subscription);
      }
    }
  }

  reduceProdCount (sub : Subscription) {
    for (var idx = 0; idx < this.subscriptionsInCart.length; idx++) {
      if (this.subscriptionsInCart[idx].subscription.id == sub.id) {
         this.cartManager.removeProdFromCart(this.subscriptionsInCart[idx].subscription);
      }
    }
  }
}

//Models
//CartSubscriptionVM.model.ts
import { Subscription } from "./Subscription.model";

export class CartSubScription {
    subscription : Subscription;
    quantity : number = 1;
    totalCost : number = 0;

    constructor(subscr : Subscription, quant: number) {
        this.subscription = subscr;
        this.quantity = quant;
        this.totalCost = quant * subscr.price;
    }

    increment() {
        this.quantity +=1
    }
}

//Subscription.model.ts
export class Subscription {
    id : number = 0;
    name : string = "";
    description : string = "";
    price : number = 0;
}

//navigation-bar component
//HTML
<nav class="navbar navbar-expand-lg navbar-light bg-light">
  <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
    <span class="navbar-toggler-icon"></span>
  </button>

  <div class="collapse navbar-collapse" id="navbarSupportedContent">
    <ul class="navbar-nav mr-auto">
      <li class="nav-item active">
        <a class="nav-link" routerLink="">Home <span class="sr-only">(current)</span></a>
      </li>
    </ul>
    <a class="navbar-brand" routerLink="cart">
        <span class="itemCount">{{numCartItems}}</span>
        <i class="fa-solid fa-cart-shopping"></i>
      </a>
  </div>
</nav>
//TS
import { Component, OnInit } from '@angular/core';
import { SubscriptionCartOrganiserService } from '../services/SubscriptionCartOrganiser.service';

@Component({
  selector: 'app-navigation-bar',
  templateUrl: './navigation-bar.component.html',
  styleUrls: ['./navigation-bar.component.css']
})
export class NavigationBarComponent implements OnInit {

  numCartItems : number = 0;
  constructor(private cartManager : SubscriptionCartOrganiserService) {
    this.numCartItems = cartManager.getNumberOfItemsInCart();
    
    cartManager.cartProductsNumberDS.subscribe(num => {
      this.numCartItems = num;
    });
   }

  ngOnInit(): void {
  }
}

//payfastcheckout component
//HTML
<button type="button" class="btn btn-primary m-3" (click)="doOnSitePayment()">Checkout</button>
//TS
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Router } from '@angular/router';
import { SubscriptionCartOrganiserService } from '../services/SubscriptionCartOrganiser.service';
import { Md5 } from 'ts-md5';
import { FormBuilder } from '@angular/forms'
import { environment } from 'src/environments/environment';
declare function payfast_do_onsite_payment(param1 : any, callback: any): any;

@Component({
  selector: 'app-payfastcheckout',
  templateUrl: './payfastcheckout.component.html',
  styleUrls: ['./payfastcheckout.component.css']
})
export class PayfastcheckoutComponent implements OnInit {

  constructor(private httpComms : HttpClient, private pageRouter : Router, private cartManager : SubscriptionCartOrganiserService, private formBuilder: FormBuilder) {
    
  }

  ngOnInit(): void {

  }

  getSignature(data : Map<string, string>) : string {
    let tmp = new URLSearchParams();
    data.forEach((v, k)=> {
      tmp.append(k, v)
    });
    let queryString = tmp.toString();
    let sig = Md5.hashStr(queryString);
    return sig;
  }

  async doOnSitePayment() {
    let onSiteUserData = new Map<string, string>();
    onSiteUserData.set("merchant_id", "10033427")
    onSiteUserData.set("merchant_key", "mu83ipbgas9p7")

    onSiteUserData.set('return_url', window.location.origin + '/success')
    onSiteUserData.set('cancel_url', window.location.origin + '/cancel')

    onSiteUserData.set("email_address", 'test@user.com');
    
    onSiteUserData.set("amount", this.cartManager.getTotalCostOfSubcriptionsInCart().toString());
    onSiteUserData.set("item_name", this.cartManager.getCartOrderName());

    onSiteUserData.set('passphrase', 'HelloWorldHello');

    let signature = this.getSignature(onSiteUserData);
    onSiteUserData.set('signature', signature);


    let formData = new FormData();
    onSiteUserData.forEach((val, key) => {
      formData.append(key, val);
    }); 
    
    let response = await fetch(environment.payfastOnsiteEndpoint, {
      method: 'POST',
      body: formData,
      redirect: 'follow'
    });
    
    let respJson = await response.json();
    let uuid = respJson['uuid'];
    payfast_do_onsite_payment({'uuid': uuid},  (res: any) => {
      if (res == true) {
        this.pageRouter.navigate(['/success'])
      }
      else {
        this.pageRouter.navigate(['/cancel'])
      }
    });
  }

  doFormPayment() {
    let onSiteUserData = new Map<string, string>();
    onSiteUserData.set("merchant_id", "10033427")
    onSiteUserData.set("merchant_key", "mu83ipbgas9p7")

    onSiteUserData.set('return_url', window.location.origin + '/success')
    onSiteUserData.set('cancel_url', window.location.origin + '/cancel')

    onSiteUserData.set("email_address", 'test@user.com');
    
    onSiteUserData.set("amount", this.cartManager.getTotalCostOfSubcriptionsInCart().toString());
    onSiteUserData.set("item_name", this.cartManager.getCartOrderName());

    onSiteUserData.set('passphrase', 'HelloWorldHello');

    let signature = this.getSignature(onSiteUserData);
    onSiteUserData.set('signature', signature);

    let autoPaymentForm = this.formBuilder.group(onSiteUserData);
    
    this.httpComms.post('https://sandbox.payfast.co.za/eng/process', onSiteUserData).subscribe(resp => {
      console.log(resp);
    });
  }

}

//productcatalog component
//HTML
<div class="row mt-5">
        <div class="card m-3" style="width: 20rem;" *ngFor="let prod of products">
            <div class="card-body">
                <h5 class="card-title">{{prod.name}}</h5>
                <p class="card-text">
                    {{prod.description}}
                </p>
                <button type="button" class="btn btn-primary" (click)="addSubscriptionToCart(prod)">
                    Add to cart
                </button>
            </div>
        </div>
</div>

<div aria-live="polite" aria-atomic="true" style="position: relative; min-height: 200px;">
    <div class="toast" style="position: absolute; top: 0; right: 0;">
      <div class="toast-header">
        <img src="..." class="rounded mr-2" alt="...">
        <strong class="mr-auto">Cart items</strong>
        <small>Now</small>
        <button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="toast-body">
        Added item to cart.
      </div>
    </div>
  </div>
//TS
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
import { Subscription } from '../models/Subscription.model';
import { FakeSubscriptionDataService } from '../services/FakeSubscriptionData.service';
import { SubscriptionCartOrganiserService } from '../services/SubscriptionCartOrganiser.service';

@Component({
  selector: 'app-productcatalog',
  templateUrl: './productcatalog.component.html',
  styleUrls: ['./productcatalog.component.css']
})
export class ProductcatalogComponent implements OnInit {

  products : Subscription [] = [];

  constructor(private fakeDataProvider : FakeSubscriptionDataService, private cartSubscriptionService : SubscriptionCartOrganiserService) {
    this.products = fakeDataProvider.getOfferedSubscriptions();
  }

  ngOnInit(): void {
  }

  addSubscriptionToCart(product : Subscription) {
    this.cartSubscriptionService.addProdFromCart(product);
  }

}

//Services
//FakeSubscriptionData.service.ts
import { Injectable } from "@angular/core";
import { Subscription } from "../models/Subscription.model";

@Injectable({
    providedIn: 'root'
})
export class FakeSubscriptionDataService {
    subscriptions : Subscription[];

    constructor () {
        this.subscriptions = [
            {
              id: 1,
              name: "Netflix",
              description:  "At Netflix, we want to entertain the world. Whatever your taste, and no matter where you live, we give you access to best-in-class TV series, documentaries, feature films and mobile games.",
              price : 100
            },
            {
              id: 2,
              name: "Showmax",
              description: "Showmax is an internet TV service. What sets Showmax apart is a unique combination of hit African content, first and exclusive international series, movies, the best kids’ shows, and live sport.",
              price : 500
            },
            {
              id: 3,
              name: "Tencent Video",
              description: "Tencent Video is China's second-largest video-streaming platform. It includes a variety of categories of online videos. The most popular categories on the platform include Chinese TV shows  and China-made animation shows.",
              price : 800
            },
            {
              id: 4,
              name: "BBC iPlayer",
              description: "BBC iPlayer is a video on demand service from the BBC. The service is available on a wide range of devices, including mobile phones and tablets, personal computers and smart televisions.",
              price : 900
            },
          ];
    }

    getOfferedSubscriptions () {
        return this.subscriptions;
    }
}

//SubscriptionCartOrganiser.service.ts
import { Injectable } from "@angular/core";
import { Subject } from "rxjs";
import { CartSubScription } from "../models/CartSubscriptionVM.model";
import { Subscription } from "../models/Subscription.model";

@Injectable({
    providedIn: 'root'
})
export class SubscriptionCartOrganiserService {
    static tmpSubscriptionsCartName : string = "ls-cart-subscriptions";
    cartProductsNumberDS = new Subject<number>();
    cartItemsOrderName : string = "Subs Order @ ";

    notifyOnNewItemInCart() {
        this.cartProductsNumberDS.next(this.getNumberOfItemsInCart());
    }

    getLocalStorageSubscriptions(): Subscription[] {
        let storedSubString = localStorage.getItem(SubscriptionCartOrganiserService.tmpSubscriptionsCartName)
        let cartSubscriptions = [];
        if (storedSubString) {
          cartSubscriptions = JSON.parse(storedSubString)
        }
        return cartSubscriptions;
    }
    getNumberOfItemsInCart() : number {
        return this.getLocalStorageSubscriptions().length
    }

    getSubscriptionsInCart() : CartSubScription[] {
        let localStorageSubs = this.getLocalStorageSubscriptions();
        let cartSubscriptions : CartSubScription[] = [];

        let subCounts = new Map<Number, Number>(); //temporary storage
        localStorageSubs.forEach(sub => {
            if (!subCounts.has(sub.id)) {
                let count = localStorageSubs.filter(currSub => currSub.id == sub.id).length;
                subCounts.set(sub.id, count)
                let cartSub = new CartSubScription(sub, count);
                cartSubscriptions.push(cartSub);
            }
        });
        return cartSubscriptions;
    }

    getTotalCostOfSubcriptionsInCart() : number {
        let totalCost = 0;
        
        let cartSubs = this.getSubscriptionsInCart();
        cartSubs.forEach(cartSub => {
            totalCost += (cartSub.subscription.price * cartSub.quantity);
        });

        return totalCost;
    }

    getCartOrderName() {
        return this.cartItemsOrderName + Date.now();
    }

    addSubscriptionToCart(product : Subscription) {
        let storedSubString = localStorage.getItem(SubscriptionCartOrganiserService.tmpSubscriptionsCartName)
    
        let cartSubscriptions = [];
        if (storedSubString) {
          cartSubscriptions = JSON.parse(storedSubString)
        }
        cartSubscriptions.push(product);
        localStorage.setItem(SubscriptionCartOrganiserService.tmpSubscriptionsCartName, JSON.stringify(cartSubscriptions))
    
        this.notifyOnNewItemInCart();
      }
    

    removeProdFromCart(subscr : Subscription) {
        let storedSubString = localStorage.getItem(SubscriptionCartOrganiserService.tmpSubscriptionsCartName)
    
        let cartSubscriptions = [];
        if (storedSubString) {
          cartSubscriptions = JSON.parse(storedSubString)
        }
        for (var idx = 0; idx < cartSubscriptions.length; idx++) {
            if (cartSubscriptions[idx].id == subscr.id) {
                cartSubscriptions.splice(idx, 1);
                break;
            }
        }

        localStorage.setItem(SubscriptionCartOrganiserService.tmpSubscriptionsCartName, JSON.stringify(cartSubscriptions))

        this.notifyOnNewItemInCart();
    }

    addProdFromCart(subscr : Subscription) {
        this.addSubscriptionToCart(subscr);
        this.notifyOnNewItemInCart();   
    }

    clearCart () {
        localStorage.removeItem(SubscriptionCartOrganiserService.tmpSubscriptionsCartName);
        this.notifyOnNewItemInCart();
    }
}

//Success component
//HTML
<h1>Success</h1>
<p>We received your purchase;<br /> Your items will be sent shortly!</p>
//TS
import { Component, OnInit } from '@angular/core';
import { SubscriptionCartOrganiserService } from '../services/SubscriptionCartOrganiser.service';

@Component({
  selector: 'app-success',
  templateUrl: './success.component.html',
  styleUrls: ['./success.component.css']
})
export class SuccessComponent implements OnInit {

  constructor(private cartManager : SubscriptionCartOrganiserService) {

  }

  ngOnInit(): void {
    this.cartManager.clearCart();
  }

}

//app component
//app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { CancelComponent } from './cancel/cancel.component';
import { CartComponent } from './cart/cart.component';
import { ProductcatalogComponent } from './productcatalog/productcatalog.component';
import { SuccessComponent } from './success/success.component';

const routes: Routes = [
  {path : '', component: ProductcatalogComponent},
  {path : 'cart', component: CartComponent},
  {path : 'success', component: SuccessComponent},
  {path: 'cancel', component: CancelComponent}
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

//app.component.html
<app-navigation-bar></app-navigation-bar>
<div class="container mt-1">
<router-outlet></router-outlet>
</div>

//app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'PaymentsClassDemo';
}

//app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { SquarecheckoutComponent } from './squarecheckout/squarecheckout.component';
import { PayfastcheckoutComponent } from './payfastcheckout/payfastcheckout.component';
import { ProductcatalogComponent } from './productcatalog/productcatalog.component';
import { CartComponent } from './cart/cart.component';
import { NavigationBarComponent } from './navigation-bar/navigation-bar.component';
import { HttpClientModule } from '@angular/common/http';
import { SuccessComponent } from './success/success.component';
import { CancelComponent } from './cancel/cancel.component'
import { FormsModule, ReactiveFormsModule } from '@angular/forms';

@NgModule({
  declarations: [
    AppComponent,
    SquarecheckoutComponent,
    PayfastcheckoutComponent,
    ProductcatalogComponent,
    CartComponent,
    NavigationBarComponent,
    SuccessComponent,
    CancelComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    FormsModule, ReactiveFormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

//environment
//environment.prod.ts
export const environment = {
  production: true,
  payfastEndpoint: 'https://www.payfast.co.za/en/process',
  payfastOnsiteEndpoint: 'https://www.payfast.co.za/onsite/​process',
};

//environment.ts
export const environment = {
  production: false,
  payfastEndpoint: 'https://sandbox.payfast.co.za/eng/process',
  payfastOnsiteEndpoint: 'https://sandbox.payfast.co.za/onsite/process'
};
[Route("api/[controller]")]
[ApiController]
public class PaymentController : ControllerBase
{
    private readonly AppDbContext _context;

    public PaymentController(AppDbContext context)
    {
        _context = context;
    }

    // 8.1 Make Payment
    [HttpPost("make-payment")]
    public async Task<IActionResult> MakePayment([FromBody] Payment payment)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        _context.Payments.Add(payment);
        await _context.SaveChangesAsync();

        return CreatedAtAction(nameof(ViewPaymentStatus), new { paymentId = payment.Payment_ID }, payment);
    }

    // 8.2 Upload Proof Of Payment
    [HttpPost("upload-proof-of-payment/{paymentId}")]
    public async Task<IActionResult> UploadProofOfPayment(int paymentId, IFormFile file)
    {
        var payment = await _context.Payments.FindAsync(paymentId);
        if (payment == null)
        {
            return NotFound("Payment not found.");
        }

        if (file == null || file.Length == 0)
        {
            return BadRequest("Invalid file.");
        }

        var filePath = Path.Combine("ProofOfPayments", $"{paymentId}_{file.FileName}");

        using (var stream = new FileStream(filePath, FileMode.Create))
        {
            await file.CopyToAsync(stream);
        }

        // Assuming you have a field to store the proof of payment path
        // payment.ProofOfPaymentPath = filePath; // Uncomment and add this field to the Payment model
        _context.Entry(payment).State = EntityState.Modified;
        await _context.SaveChangesAsync();

        return Ok(new { FilePath = filePath });
    }

    // 8.3 Search Payment
    [HttpGet("search-payment")]
    public async Task<IActionResult> SearchPayment([FromQuery] string searchTerm)
    {
        var payments = await _context.Payments
            .Include(p => p.Contract)
            .Include(p => p.Payment_Type)
            .Where(p => p.Contract.Description.Contains(searchTerm) ||
                        p.Payment_Type.Payment_Type_Name.Contains(searchTerm))
            .ToListAsync();

        if (payments == null || !payments.Any())
        {
            return NotFound("No payments found matching the search term.");
        }

        return Ok(payments);
    }

    // 8.4 View Payment Status
    [HttpGet("view-payment-status/{paymentId}")]
    public async Task<IActionResult> ViewPaymentStatus(int paymentId)
    {
        var payment = await _context.Payments
            .Include(p => p.Contract)
            .Include(p => p.Payment_Type)
            .FirstOrDefaultAsync(p => p.Payment_ID == paymentId);

        if (payment == null)
        {
            return NotFound("Payment not found.");
        }

        return Ok(new
        {
            payment.Payment_ID,
            payment.Amount,
            payment.Payment_Date,
            ContractDescription = payment.Contract.Description,
            PaymentTypeName = payment.Payment_Type.Payment_Type_Name,
            // payment.ProofOfPaymentPath // Uncomment if this field is added to the Payment model
        });
    }
}
public class Payment
{
    [Key]
    public int Payment_ID { get; set; }

    [Required]
    public decimal Amount { get; set; }

    [Required]
    public DateTime Payment_Date { get; set; }

    public int Contract_ID { get; set; }

    [ForeignKey(nameof(Contract_ID))]

    public Contract Contract { get; set; }



    public int Payment_Type_ID { get; set; }



    [ForeignKey(nameof(Payment_Type_ID))]

    public Payment_Type Payment_Type { get; set; }
}

//appDbContext
var Payments = new Payment[]
{
  new Payment { Payment_ID = 1, Amount = 50.00m, Payment_Date = new DateTime(2024, 4, 12), Contract_ID = 1, Payment_Type_ID = 1 }
};
builder.Entity<Payment>().HasData(Payments);
//Models
//Reward
public class Reward
{
    [Key]

    public int Reward_ID { get; set; }

    [Required]
    public string Prize { get; set; }

    [Required]
    public DateTime Reward_Issue_Date { get; set; }

    public int Member_ID { get; set; }

    [ForeignKey(nameof(Member_ID))]
    public Member Member { get; set; }

    
    public int Reward_Type_ID { get; set; }


    [ForeignKey(nameof(Reward_Type_ID))]
    public Reward_Type Reward_Type { get; set; }


    public bool IsRedeemed { get; set; } // New property to track redemption status
}

//RewardType
public class Reward_Type
{
    [Key]
    public int Reward_Type_ID { get; set; }

    [Required]
    public string Reward_Type_Name { get; set; }

    [Required]
    public string Reward_Criteria { get; set; }
}

//Reward_Member
public class Reward_Member
{
    [Key]
    public int Reward_Member_ID { get; set; }

    public int Number_Of_Qualified_Members { get; set; }

    public int Member_ID { get; set; }

    [ForeignKey(nameof(Member_ID))]

    public Member Member { get; set; }


    public int Reward_ID { get; set; }

    [ForeignKey(nameof(Reward_ID))]

    public Reward Reward { get; set; }
}

//ViewModels
//RewardViewModel
public class RewardViewModel
{
    public int MemberId { get; set; }
    public int RewardId { get; set; }
}

//AppDbContext
//Reward Seed Data
var Rewards = new Reward[]
{
    new Reward { Reward_ID = 1, Prize = "Free Gym Membership", Reward_Issue_Date = new DateTime(2024, 4, 10), Member_ID = 1, Reward_Type_ID = 1 }

};
builder.Entity<Reward>().HasData(Rewards);


//Reward_Member Seed Data
var Reward_Members = new Reward_Member[]
{
    new Reward_Member{ Reward_Member_ID = 1,Number_Of_Qualified_Members = 3,Member_ID = 1, Reward_ID = 1}
};
builder.Entity<Reward_Member>().HasData(Reward_Members);

//Controller
[Route("api/[controller]")]
[ApiController]
public class RewardController : ControllerBase
{
    private readonly AppDbContext _appDbContext;

    public RewardController(AppDbContext appDbContext)
    {
        _appDbContext = appDbContext;
    }

    [HttpPost]
    [Route("createRewardType")]
    public async Task<IActionResult> CreateRewardType(RewardTypeViewModel rt)
    {
        try
        {
            var rewardType = new Reward_Type
            {
                Reward_Type_Name = rt.Reward_Type_Name,
                Reward_Criteria = rt.Reward_Criteria
                
            };
            _appDbContext.Reward_Types.Add(rewardType);
            await _appDbContext.SaveChangesAsync();

            return CreatedAtAction(nameof(GetRewardTypeById), new { id = rewardType.Reward_Type_ID }, rewardType);
        }
        catch (Exception)
        {
            return BadRequest();
        }
    }

    [HttpGet]
    [Route("getAllRewardTypes")]
    public async Task<IActionResult> GetAllRewardTypes()
    {
        try
        {
            var rewardTypes = await _appDbContext.Reward_Types.ToListAsync();
            return Ok(rewardTypes);
        }
        catch (Exception)
        {
            return BadRequest();
        }
    }

    [HttpGet]
    [Route("getRewardTypeById/{id}")]
    public async Task<IActionResult> GetRewardTypeById(int id)
    {
        try
        {
            var rewardType = await _appDbContext.Reward_Types.FindAsync(id);

            if (rewardType == null)
            {
                return NotFound();
            }

            return Ok(rewardType);
        }
        catch (Exception)
        {
            return BadRequest();
        }
    }

    [HttpPut]
    [Route("updateRewardType/{id}")]
    public async Task<IActionResult> UpdateRewardType(int id, [FromBody] RewardTypeViewModel rt)
    {
        try
        {
            var rewardType = await _appDbContext.Reward_Types.FindAsync(id);

            if(rewardType == null)
            {
                return NotFound();
            }

            //rewardType = new Reward_Type
            //{
            //    Reward_Type_Name = rt.Reward_Type_Name,
            //    Reward_Criteria = rt.Reward_Criteria
            //};

            rewardType.Reward_Type_Name = rt.Reward_Type_Name;
            rewardType.Reward_Criteria = rt.Reward_Criteria;

            _appDbContext.Entry(rewardType).State = EntityState.Modified;
            await _appDbContext.SaveChangesAsync();

            return NoContent();
        }
        catch (Exception)
        {
            return BadRequest();
        }
    }

    [HttpDelete]
    [Route("deleteRewardType/{id}")]
    public async Task<IActionResult> DeleteRewardType(int id)
    {
        try
        {
            var rewardType = await _appDbContext.Reward_Types.FindAsync(id);

            if(rewardType == null)
            {
                return NotFound();
            }

            _appDbContext.Reward_Types.Remove(rewardType);
            await _appDbContext.SaveChangesAsync();

            return NoContent();
        }
        catch (Exception)
        {
            return BadRequest();
        }
    }



    [HttpPost]
    [Route("redeemReward")]
    public async Task<IActionResult> RedeemReward([FromBody] RewardViewModel request)
    {
        try
        {
            // Fetch the reward by ID
            var reward = await _appDbContext.Rewards
                                            .FirstOrDefaultAsync(r => r.Reward_ID == request.RewardId && r.Member_ID == request.MemberId);

            if (reward == null)
            {
                return NotFound("Reward not found or not eligible for the member.");
            }

            // Check if the reward is already redeemed
            if (reward.IsRedeemed)
            {
                return BadRequest("Reward is already redeemed.");
            }

            // Mark the reward as redeemed
            reward.IsRedeemed = true;
            _appDbContext.Entry(reward).State = EntityState.Modified;
            await _appDbContext.SaveChangesAsync();

            return Ok(new { Status = "Success", Message = "Reward redeemed successfully!" });
        }
        catch (Exception)
        {
            return BadRequest("An error occurred while redeeming the reward.");
        }
    }

    [HttpPost]
    [Route("setReward")]
    public async Task<IActionResult> SetReward(Reward r)
    {
        try
        {
            var reward = new Reward
            {
                Prize = r.Prize,
                Reward_Issue_Date = r.Reward_Issue_Date,
                Member_ID = r.Member_ID,
                Reward_Type_ID = r.Reward_Type_ID
            };
            _appDbContext.Rewards.Add(reward);
            await _appDbContext.SaveChangesAsync();

            return CreatedAtAction(nameof(GetRewardById), new { id = reward.Reward_ID }, reward);
        }
        catch (Exception)
        {
            return BadRequest();
        }
    }

    [HttpGet]
    [Route("getRewardById/{id}")]
    public async Task<IActionResult> GetRewardById(int id)
    {
        try
        {
            var reward = await _appDbContext.Reward_Types.FindAsync(id);

            if (reward == null)
            {
                return NotFound();
            }

            return Ok(reward);
        }
        catch (Exception)
        {
            return BadRequest();
        }
    }

    [HttpGet]
    [Route("getAllRewards")]
    public async Task<IActionResult> GetAllRewards()
    {
        try
        {
            var rewards = await _appDbContext.Rewards.ToListAsync();
            return Ok(rewards);
        }
        catch (Exception)
        {
            return BadRequest();
        }
    }
}
//Models
public class Reward
{
    [Key]
    public int Reward_ID { get; set; }

    [Required]
    public DateTime Reward_Issue_Date { get; set; }

    public bool IsPosted { get; set; } // New property to track posted status

    public int Reward_Type_ID { get; set; }

    [ForeignKey(nameof(Reward_Type_ID))]
    public Reward_Type Reward_Type { get; set; }

}

public class Reward_Member
{
    [Key]
    public int Reward_Member_ID { get; set; }

    public bool IsRedeemed { get; set; } // New property to track redemption status

    public int Member_ID { get; set; }

    [ForeignKey(nameof(Member_ID))]

    public Member Member { get; set; }


    public int Reward_ID { get; set; }

    [ForeignKey(nameof(Reward_ID))]

    public Reward Reward { get; set; }
}

//ViewModels
public class RewardRedeemViewModel
{

    public int MemberId { get; set; }

    public int RewardId { get; set; }
}

public class RewardSetViewModel
{
    public DateTime Reward_Issue_Date { get; set; }

    public int Reward_Type_ID { get; set; }

    public bool IsPosted { get; set; } = false;
}

public class RewardPostViewModel
{
    public int RewardId { get; set; }
}

//Controller

[Route("api/[controller]")]
[ApiController]
public class RewardController : ControllerBase
{
  private readonly AppDbContext _appDbContext;

  public RewardController(AppDbContext appDbContext)
  {
    _appDbContext = appDbContext;
  }
  
      [HttpGet]
    [Route("getRewardById/{id}")]
    public async Task<IActionResult> GetRewardById(int id)
    {
        try
        {
            var reward = await _appDbContext.Rewards.FindAsync(id);

            if (reward == null)
            {
                return NotFound();
            }

            return Ok(reward);
        }
        catch (Exception)
        {
            return BadRequest();
        }
    }

    [HttpGet]
    [Route("getAllRewards")]
    public async Task<IActionResult> GetAllRewards()
    {
        try
        {
            var rewards = await _appDbContext.Rewards.ToListAsync();
            return Ok(rewards);
        }
        catch (Exception)
        {
            return BadRequest();
        }
    }

    [HttpPost]
    [Route("redeemReward")]
    public async Task<IActionResult> RedeemReward([FromBody] RewardRedeemViewModel request)
    {
        try
        {
            // Fetch the reward by ID
            var reward = await _appDbContext.Reward_Members
                                            .FirstOrDefaultAsync(r => r.Reward_ID == request.RewardId && r.Member_ID == request.MemberId);

            if (reward == null)
            {
                return NotFound("Reward not found or not eligible for the member.");
            }

            // Check if the reward is already redeemed
            if (reward.IsRedeemed)
            {
                return BadRequest("Reward is already redeemed.");
            }

            // Mark the reward as redeemed
            reward.IsRedeemed = true;
            _appDbContext.Entry(reward).State = EntityState.Modified;
            await _appDbContext.SaveChangesAsync();

            return Ok(new { Status = "Success", Message = "Reward redeemed successfully!" });
        }
        catch (Exception)
        {
            return BadRequest("An error occurred while redeeming the reward.");
        }
    }

    [HttpPost]
    [Route("setReward")]
    public async Task<IActionResult> SetReward(RewardSetViewModel r)
    {
        try
        {
            var reward = new Reward
            {
                Reward_Issue_Date = r.Reward_Issue_Date,
                Reward_Type_ID = r.Reward_Type_ID,
                IsPosted = r.IsPosted
            };
            _appDbContext.Rewards.Add(reward);
            await _appDbContext.SaveChangesAsync();

            return CreatedAtAction(nameof(GetRewardById), new { id = reward.Reward_ID }, reward);
        }
        catch (Exception)
        {
            return BadRequest();
        }
    }

    [HttpPost]
    [Route("postReward")]
    public async Task<IActionResult> PostReward([FromBody] RewardPostViewModel request)
    {
        try
        {
            // Fetch the reward by ID
            var reward = await _appDbContext.Rewards
                                            .FirstOrDefaultAsync(r => r.Reward_ID == request.RewardId);

            if (reward == null)
            {
                return NotFound("Reward not found.");
            }

            // Check if the reward is already posted
            if (reward.IsPosted)
            {
                return BadRequest("Reward is already posted.");
            }

            // Mark the reward as posted
            reward.IsPosted = true;
            _appDbContext.Entry(reward).State = EntityState.Modified;
            await _appDbContext.SaveChangesAsync();

            return Ok(new { Status = "Success", Message = "Reward posted successfully!" });
        }
        catch (Exception)
        {
            return BadRequest("An error occurred while posting the reward.");
        }
    }
}

//AppDbContext
var Rewards = new Reward[]
{
    new Reward { Reward_ID = 1, IsPosted = false, Reward_Issue_Date = new DateTime(2024, 4, 10), Reward_Type_ID = 1 }

};
builder.Entity<Reward>().HasData(Rewards);

var Reward_Members = new Reward_Member[]
{
    new Reward_Member{ Reward_Member_ID = 1, IsRedeemed = false, Member_ID = 1, Reward_ID = 1}
};
builder.Entity<Reward_Member>().HasData(Reward_Members);
//Service
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { RewardTypeViewModel } from '../shared/reward-type';
import { Observable } from 'rxjs';
import { RewardPostViewModel, RewardRedeemViewModel, RewardSetViewModel, RewardViewModel } from '../shared/reward';

@Injectable({
  providedIn: 'root'
})
export class RewardService {

  httpOptions = {
    headers: new HttpHeaders({
      'Content-Type': 'application/json'
    })
  };
  
  constructor(private http: HttpClient) {}
  apiUrl: string = "https://localhost:7185/api/Reward";

  //RewardType endpoints
  createRewardType(rewardType: RewardTypeViewModel): Observable<RewardTypeViewModel> {
    return this.http.post<RewardTypeViewModel>(`${this.apiUrl}/createRewardType`, rewardType, this.httpOptions);
  }

  getAllRewardTypes(): Observable<RewardTypeViewModel[]> {
    return this.http.get<RewardTypeViewModel[]>(`${this.apiUrl}/getAllRewardTypes`, this.httpOptions);
  }

  getRewardTypeById(id: number): Observable<RewardTypeViewModel> {
    return this.http.get<RewardTypeViewModel>(`${this.apiUrl}/getRewardTypeById/${id}`, this.httpOptions);
  }

  updateRewardType(rewardTypeId: number, rewardTypeName: string, rewardCriteria: string): Observable<void> {
    const body = { reward_Type_Name: rewardTypeName, reward_Criteria: rewardCriteria }; // Correctly format the body as JSON
    return this.http.put<void>(`${this.apiUrl}/updateRewardType/${rewardTypeId}`, body, this.httpOptions);
  }

  deleteRewardType(id: number): Observable<void> {
    return this.http.delete<void>(`${this.apiUrl}/deleteRewardType/${id}`, this.httpOptions);
  }

  //Reward endpoints
  getRewardById(id: number): Observable<RewardViewModel> {
    return this.http.get<RewardViewModel>(`${this.apiUrl}/getRewardById/${id}`, this.httpOptions);
  }  

  getAllRewards(): Observable<RewardViewModel[]> {
    return this.http.get<RewardViewModel[]>(`${this.apiUrl}/getAllRewards`, this.httpOptions);
  }

  setReward(reward: RewardSetViewModel): Observable<RewardViewModel> {
    return this.http.post<RewardViewModel>(`${this.apiUrl}/setReward`, reward, this.httpOptions);
  }

  redeemReward(request: RewardRedeemViewModel): Observable<any> {
    return this.http.post<any>(`${this.apiUrl}/redeemReward`, request, this.httpOptions);
  }

  postReward(request: RewardPostViewModel): Observable<any> {
    return this.http.post<any>(`${this.apiUrl}/postReward`, request, this.httpOptions);
  }

}

//Models
export class RewardViewModel{
    reward_ID!: number;
    reward_Issue_Date!: Date;
    reward_Type_ID!: number;
    isPosted!: boolean;
}

export class RewardRedeemViewModel {
    MemberId!: number;
    RewardId!: number;
}

export class RewardSetViewModel {
    reward_Issue_Date!: Date;
    reward_Type_ID!: number;
    isPosted: boolean = false;
}

export class RewardPostViewModel {
    RewardId!: number;
}

//Component
//TS
import { Component } from '@angular/core';
import { RewardViewModel } from '../shared/reward';
import { RewardService } from '../Services/reward.service';
import { CommonModule, Location } from '@angular/common';

@Component({
  selector: 'app-reward',
  standalone: true,
  imports: [CommonModule],
  templateUrl: './reward.component.html',
  styleUrl: './reward.component.css'
})
export class RewardComponent {
  unlistedRewards: RewardViewModel[] = [];
  listedRewards: RewardViewModel[] = [];

  constructor(private rewardService: RewardService, private location:Location) {}

  ngOnInit(): void {
    this.loadUnlistedRewards();

    this.loadListedRewards();
  }

  loadUnlistedRewards(): void {
    this.rewardService.getAllRewards().subscribe(rewards => {
      this.unlistedRewards = rewards.filter(reward => !reward.isPosted);
      console.log('Unlisted Rewards:', this.unlistedRewards);
    });
  }

  postReward(rewardId: number): void {
    const request = { RewardId: rewardId };
    this.rewardService.postReward(request).subscribe(() => {
      this.loadUnlistedRewards(); // Reload the unlisted rewards
    });
  }

  openSetRewardModal(): void {
    // Logic to open a modal for setting a new reward
  }

  loadListedRewards(): void {
    this.rewardService.getAllRewards().subscribe(rewards => {
      this.listedRewards = rewards.filter(reward => reward.isPosted);
      console.log('Listed Rewards:', this.listedRewards);
    });
  }

  goBack(): void {
  this.location.back();
  }
} 

//HTML
<div class="reward-container">
    <div class="back-button">
        <button class="btn btn-link" (click)="goBack()">
            <i class="bi bi-arrow-left-circle"></i>Back
        </button>
    </div>

    <div class="unlisted-reward-container">
        <div class="content">
            <div class="header">
                <h1>Unlisted Rewards</h1>
            </div>

            <button class="btn btn-primary mb-3" (click)="openSetRewardModal()">Set New Reward</button>

            <table class="table table-hover table-centered">
                <thead>
                  <tr>
                    <th>Reward ID</th>
                    <th>Issue Date</th>
                    <th>Reward Name</th>
                    <th>Posted</th>
                    <th>Actions</th>
                  </tr>
                </thead>
                <tbody>
                  <tr *ngFor="let reward of unlistedRewards">
                    <td>{{ reward.reward_ID }}</td>
                    <td>{{ reward.reward_Issue_Date | date }}</td>
                    <td>{{ reward.reward_Type_ID }}</td>
                    <td>{{ reward.isPosted }}</td>
                    <td>
                      <button (click)="postReward(reward.reward_ID)">Post Reward</button>
                    </td>
                  </tr>
                </tbody>
            </table>
        </div>
    </div>

    <br/>

    <div class="listed-reward-container">
        <div class="content">
            <div class="header">
                <h1>Listed Rewards</h1>
            </div>

            <table class="table table-hover table-centered">
                <thead>
                  <tr>
                    <th>Reward ID</th>
                    <th>Issue Date</th>
                    <th>Reward Name</th>
                    <th>Posted</th>
                  </tr>
                </thead>
                <tbody>
                  <tr *ngFor="let reward of listedRewards">
                    <td>{{ reward.reward_ID }}</td>
                    <td>{{ reward.reward_Issue_Date | date }}</td>
                    <td>{{ reward.reward_Type_ID }}</td>
                    <td>{{ reward.isPosted }}</td>
                  </tr>
                </tbody>
            </table>
        </div>
    </div>
</div>

//CSS
.reward-container {
    padding: 20px;
}

.back-button {
    position: absolute;
    top: 10px;
    left: 10px;
    margin-top: 70px;
}

.content {
    background-color: #f8f9fa;
    padding: 20px;
    border-radius: 5px;
    margin-top: 20px; /* Adjusted to move the content up */
    text-align: center; /* Center the heading */
}

.header {
    display: flex;
    justify-content: center;
    align-items: center;
    margin-bottom: 20px;
}

.table-centered th,
.table-centered td {
  text-align: center; /* Center align table contents */
}

.table-hover tbody tr:hover {
    background-color: #f1f1f1;
}

//Suggested changes
1. Create Separate ViewModels: Define different ViewModels for each user type (Member, Employee, Owner) with the specific fields required for each.

2. Determine User Type First: Create a step in your registration process to first capture the user type.

3. Use Specific ViewModel Based on User Type: Use the captured user type to select and validate the appropriate ViewModel.

//Viewmodels
public class UserViewModel
{
    public string Email { get; set; }
    public string Password { get; set; }
    public int User_Type_ID { get; set; }
    // Common fields...
}

public class MemberViewModel : UserViewModel
{
    public int Contract_ID { get; set; }
    public bool Is_Active { get; set; }
    // Other member-specific fields...
}

public class EmployeeViewModel : UserViewModel
{
    public int Employee_Type_ID { get; set; }
    public int Shift_ID { get; set; }
    public int Hours_Worked { get; set; }
    // Other employee-specific fields...
}

public class OwnerViewModel : UserViewModel
{
    // Owner-specific fields...
}

//UserTypeVM
public class UserTypeVM
{
    public int User_Type_ID { get; set; }
}


//Controller
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Identity;
using System.Text.RegularExpressions;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;

[Route("api/[controller]")]
[ApiController]
public class UserController : ControllerBase
{
    private readonly UserManager<User> _userManager;
    private readonly AppDbContext _appDbContext;
    private readonly ILogger<UserController> _logger;

    public UserController(UserManager<User> userManager, AppDbContext appDbContext, ILogger<UserController> logger)
    {
        _userManager = userManager;
        _appDbContext = appDbContext;
        _logger = logger;
    }

    [HttpPost]
    [Route("DetermineUserType")]
    public async Task<IActionResult> DetermineUserType([FromForm] UserTypeViewModel userTypeViewModel)
    {
        if (userTypeViewModel == null || userTypeViewModel.User_Type_ID <= 0)
        {
            return BadRequest("Invalid user type.");
        }

        // Determine user type based on the provided information
        int userTypeID = userTypeViewModel.User_Type_ID;

        // Redirect to the appropriate registration method
        switch (userTypeID)
        {
            case 1:
                return await RegisterMember(userTypeViewModel);
            case 2:
                return await RegisterEmployee(userTypeViewModel);
            case 3:
                return await RegisterOwner(userTypeViewModel);
            default:
                return BadRequest("Invalid user type.");
        }
    }

    private async Task<IActionResult> RegisterMember(UserTypeViewModel userTypeViewModel)
    {
        // Map UserTypeViewModel to MemberViewModel
        var memberViewModel = new MemberViewModel
        {
            Email = userTypeViewModel.Email,
            Password = userTypeViewModel.Password,
            User_Type_ID = userTypeViewModel.User_Type_ID,
            // Initialize other member-specific fields
        };

        return await Register(memberViewModel);
    }

    private async Task<IActionResult> RegisterEmployee(UserTypeViewModel userTypeViewModel)
    {
        // Map UserTypeViewModel to EmployeeViewModel
        var employeeViewModel = new EmployeeViewModel
        {
            Email = userTypeViewModel.Email,
            Password = userTypeViewModel.Password,
            User_Type_ID = userTypeViewModel.User_Type_ID,
            // Initialize other employee-specific fields
        };

        return await Register(employeeViewModel);
    }

    private async Task<IActionResult> RegisterOwner(UserTypeViewModel userTypeViewModel)
    {
        // Map UserTypeViewModel to OwnerViewModel
        var ownerViewModel = new OwnerViewModel
        {
            Email = userTypeViewModel.Email,
            Password = userTypeViewModel.Password,
            User_Type_ID = userTypeViewModel.User_Type_ID,
            // Initialize other owner-specific fields
        };

        return await Register(ownerViewModel);
    }

    private async Task<IActionResult> Register(UserViewModel uvm)
    {
        try
        {
            var formCollection = await Request.ReadFormAsync();
            var photo = formCollection.Files.FirstOrDefault();

            // Validate Phone Number Pattern
            var phoneNumberPattern = @"^\d{10}$";
            bool isValidPhoneNumber = Regex.IsMatch(uvm.PhoneNumber, phoneNumberPattern);

            if (!isValidPhoneNumber) return BadRequest("Enter valid 10-digit phone number.");

            // Validate South African ID number
            if (!IsValidSouthAfricanIDNumber(uvm.Id_Number, uvm.Date_of_Birth))
            {
                return BadRequest("Enter a valid South African ID number.");
            }

            var user = await _userManager.FindByEmailAsync(uvm.Email);
            int lastUserID = _appDbContext.Users
                                .OrderByDescending(u => u.User_ID)
                                .Select(u => u.User_ID)
                                .FirstOrDefault();

            if (user == null)
            {
                if (photo != null && photo.Length > 0)
                {
                    using (var memoryStream = new MemoryStream())
                    {
                        await photo.CopyToAsync(memoryStream);
                        var fileBytes = memoryStream.ToArray();
                        string base64Image = Convert.ToBase64String(fileBytes);

                        user = new User
                        {
                            User_ID = lastUserID + 1,
                            Name = uvm.Name,
                            Surname = uvm.Surname,
                            UserName = uvm.Email,
                            Email = uvm.Email,
                            PasswordHash = _userManager.PasswordHasher.HashPassword(null, uvm.Password),
                            User_Status_ID = uvm.User_Status_ID,
                            User_Type_ID = uvm.User_Type_ID,
                            PhoneNumber = uvm.PhoneNumber,
                            Date_of_Birth = uvm.Date_of_Birth,
                            ID_Number = uvm.Id_Number,
                            Physical_Address = uvm.Physical_Address,
                            Photo = base64Image // Store the base64 string of the photo
                        };

                        IdentityResult result = await _userManager.CreateAsync(user);

                        if (result.Succeeded)
                        {
                            // Assign role based on User_Type_ID
                            string roleName = GetRoleNameByUserType(uvm.User_Type_ID);
                            if (!string.IsNullOrEmpty(roleName))
                            {
                                var roleResult = await _userManager.AddToRoleAsync(user, roleName);
                                if (!roleResult.Succeeded)
                                {
                                    return BadRequest(new { Status = "Error", Errors = roleResult.Errors });
                                }
                            }

                            if (uvm.User_Type_ID == 1) // Member
                            {
                                var memberViewModel = uvm as MemberViewModel;
                                int lastMemberID = _appDbContext.Members
                                    .OrderByDescending(m => m.Member_ID)
                                    .Select(m => m.Member_ID)
                                    .FirstOrDefault();

                                var newMember = new Member
                                {
                                    Member_ID = lastMemberID + 1,
                                    User_ID = user.User_ID,
                                    Contract_ID = memberViewModel.Contract_ID,
                                    Is_Active = memberViewModel.Is_Active // Use the value from the view model
                                };

                                _appDbContext.Members.Add(newMember);
                                _logger.LogInformation("Adding new member with User_ID: {User_ID}, Member_ID: {Member_ID}", user.User_ID, newMember.Member_ID);
                                await _appDbContext.SaveChangesAsync();
                                _logger.LogInformation("Member added successfully with User_ID: {User_ID}, Member_ID: {Member_ID}", user.User_ID, newMember.Member_ID);
                            }
                            else if (uvm.User_Type_ID == 2) // Employee
                            {
                                var employeeViewModel = uvm as EmployeeViewModel;
                                int lastEmployeeID = _appDbContext.Employees
                                    .OrderByDescending(e => e.Employee_ID)
                                    .Select(e => e.Employee_ID)
                                    .FirstOrDefault();

                                var newEmployee = new Employee
                                {
                                    Employee_ID = lastEmployeeID == 0 ? 1 : lastEmployeeID + 1,
                                    User_ID = user.User_ID,
                                    Employment_Date = DateTime.UtcNow,
                                    Hours_Worked = employeeViewModel.Hours_Worked, // Use the value from the view model (initialized to zero)
                                    Employee_Type_ID = employeeViewModel.Employee_Type_ID,
                                    Shift_ID = employeeViewModel.Shift_ID
                                };

                                _appDbContext.Employees.Add(newEmployee);
                                _logger.LogInformation("Adding new employee with User_ID: {User_ID}, Employee_ID: {Employee_ID}", user.User_ID, newEmployee.Employee_ID);
                                await _appDbContext.SaveChangesAsync();
                                _logger.LogInformation("Employee added successfully with User_ID: {User_ID}, Employee_ID: {Employee_ID}", user.User_ID, newEmployee.Employee_ID);
                            }
                            else if (uvm.User_Type_ID == 3) // Owner
                            {
                                var ownerViewModel = uvm as OwnerViewModel;
                                int lastOwnerID = _appDbContext.Owners
                                    .OrderByDescending(o => o.Owner_ID)
                                    .Select(o => o.Owner_ID)
                                    .FirstOrDefault();

                                var newOwner = new Owner
                                {
                                    Owner_ID = lastOwnerID == 0 ? 1 : lastOwnerID + 1,
                                    User_ID = user.User_ID
                                };

                                _appDbContext.Owners.Add(newOwner);
                                _logger.LogInformation("Adding new owner with User_ID: {User_ID}, Owner_ID: {Owner_ID}", user.User_ID, newOwner.Owner_ID);
                                await _appDbContext.SaveChangesAsync();
                                _logger.LogInformation("Owner added successfully with User_ID: {User_ID}, Owner_ID: {Owner_ID}", user.User_ID, newOwner.Owner_ID);
                            }

                            return Ok(new { Status = "Success", Message = "Your profile has been created successfully!" });
                        }
                        else
                        {
                            return StatusCode(StatusCodes.Status500InternalServerError, result.Errors.FirstOrDefault()?.Description);
                        }
                    }
                }
                else
                {
                    return BadRequest("Photo is required.");
                }
            }
            else
            {
                return Forbid("User already exists.");
            }
        }
        catch (DbUpdateException dbEx)
        {
            return StatusCode(StatusCodes.Status500InternalServerError, dbEx.InnerException?.Message ?? "An error occurred while processing your request.");
        }
        catch (Exception ex)
        {
            return StatusCode(StatusCodes.Status500InternalServerError, "An error occurred while processing your request.");
        }
    }

    private bool IsValidSouthAfricanIDNumber(string idNumber, DateTime dateOfBirth)
    {
        // Implement South African ID number validation logic here
        return true;
    }

    private string GetRoleNameByUserType(int userTypeID)
    {
        // Implement logic to get role name by user type ID
        return "RoleName";
    }
}
//Models
public class Reward_Type
{
    [Key]
    public int Reward_Type_ID { get; set; }

    [Required]
    public string Reward_Type_Name { get; set; }

    [Required]
    public string Reward_Criteria { get; set; }

    public int Inventory_ID { get; set; } // Link to Inventory
    [ForeignKey(nameof(Inventory_ID))]
    public Inventory Inventory { get; set; } // Reference to Inventory item
}

//Viewmodels
public class RewardTypeViewModel
{
    public string Reward_Type_Name { get; set; }
    public string Reward_Criteria { get; set; }
    public int Inventory_ID { get; set; }
}

/controller
[HttpGet]
    [Route("getAllRewardTypes")]
    public async Task<IActionResult> GetAllRewardTypes()
    {
        try
        {
            var rewardTypes = await _appDbContext.Reward_Types
                .Include(rt => rt.Inventory) // Include Inventory details
                .ToListAsync();
            return Ok(rewardTypes);
        }
        catch (Exception)
        {
            return BadRequest();
        }
    }

    [HttpPost]
    [Route("createRewardType")]
    public async Task<IActionResult> CreateRewardType(RewardTypeViewModel rt)
    {
        try
        {
            var rewardType = new Reward_Type
            {
                Reward_Type_Name = rt.Reward_Type_Name,
                Reward_Criteria = rt.Reward_Criteria,
                Inventory_ID = rt.Inventory_ID
            };
            _appDbContext.Reward_Types.Add(rewardType);
            await _appDbContext.SaveChangesAsync();

            return CreatedAtAction(nameof(GetRewardTypeById), new { id = rewardType.Reward_Type_ID }, rewardType);
        }
        catch (Exception)
        {
            return BadRequest();
        }
    }

[HttpPost]
    [Route("setReward")]
    public async Task<IActionResult> SetReward(RewardSetViewModel r)
    {
        try
        {
            var reward = new Reward
            {
                Reward_Issue_Date = r.Reward_Issue_Date,
                Reward_Type_ID = r.Reward_Type_ID,
                IsPosted = r.IsPosted
            };
            _appDbContext.Rewards.Add(reward);
            await _appDbContext.SaveChangesAsync();

            // Automatically populate Reward_Member based on criteria
            var members = await _appDbContext.Members // Assuming members criteria is stored in members
                .Where(m => /* apply criteria based on reward type */ true)
                .ToListAsync();

            foreach (var member in members)
            {
                var rewardMember = new Reward_Member
                {
                    Member_ID = member.Member_ID,
                    Reward_ID = reward.Reward_ID,
                    IsRedeemed = false
                };
                _appDbContext.Reward_Members.Add(rewardMember);
            }
            await _appDbContext.SaveChangesAsync();

            return CreatedAtAction(nameof(GetRewardById), new { id = reward.Reward_ID }, reward);
        }
        catch (Exception)
        {
            return BadRequest();
        }
    }

[HttpPost]
    [Route("redeemReward")]
    public async Task<IActionResult> RedeemReward([FromBody] RewardRedeemViewModel request)
    {
        try
        {
            var rewardMember = await _appDbContext.Reward_Members
                .Include(rm => rm.Reward)
                .ThenInclude(r => r.Reward_Type)
                .FirstOrDefaultAsync(r => r.Reward_ID == request.RewardId && r.Member_ID == request.MemberId);

            if (rewardMember == null)
            {
                return NotFound("Reward not found or not eligible for the member.");
            }

            if (rewardMember.IsRedeemed)
            {
                return BadRequest("Reward is already redeemed.");
            }

            // Reduce inventory quantity
            var inventory = await _appDbContext.Inventories.FindAsync(rewardMember.Reward.Reward_Type.Inventory_ID);
            if (inventory == null || inventory.Inventory_Item_Quantity <= 0)
            {
                return BadRequest("Inventory item is not available.");
            }
            inventory.Inventory_Item_Quantity -= 1;
            _appDbContext.Entry(inventory).State = EntityState.Modified;

            // Mark reward as redeemed
            rewardMember.IsRedeemed = true;
            _appDbContext.Entry(rewardMember).State = EntityState.Modified;

            await _appDbContext.SaveChangesAsync();

            return Ok(new { Status = "Success", Message = "Reward redeemed successfully!" });
        }
        catch (Exception)
        {
            return BadRequest("An error occurred while redeeming the reward.");
        }
    }

public async Task<List<Inventory>> GetAvailableInventoryItems()
{
    return await _appDbContext.Inventories
        .Where(i => i.Inventory_Item_Quantity > 0)
        .ToListAsync();
}

public async Task<bool> ValidateRewardTypeCreation(RewardTypeViewModel rt)
{
    var inventory = await _appDbContext.Inventories.FindAsync(rt.Inventory_ID);
    return inventory != null && inventory.Inventory_Item_Quantity > 0;
}
//Type Host Value TTL
CNAME Record www parkingpage.namecheap.com. 30 min
URL Redirect Record @ http://www.avsfitness.com/ unmasked
    //Predined Reward Types Methods
    private async Task<List<Member>> GetQualifyingMembers(string criteria)
    {
        var qualifyingMembers = new List<Member>();

        if (criteria == "Completed 10 Bookings in a Month")
        {
            qualifyingMembers = await GetMembersWithBookingsInMonth(10);
        }
        else if (criteria == "Member for Over a Year")
        {
            qualifyingMembers = await GetLoyalMembers();
        }
        else if (criteria == "Made 20 Bookings in Last 3 Months")
        {
            qualifyingMembers = await GetFrequentBookers(20, 3);
        }
        //else if (criteria == "Perfect Attendance for a Month")
        //{
        //    qualifyingMembers = await GetPerfectAttendanceMembers();
        //}
        //else if (criteria == "Consistent Attendance for a Quarter")
        //{
        //    qualifyingMembers = await GetConsistentAttendanceMembers();
        //}

        return qualifyingMembers;
    }

    private async Task<List<Member>> GetMembersWithBookingsInMonth(int bookingCount)
    {
        var oneMonthAgo = DateTime.Now.AddMonths(-1);
        var qualifyingMembers = await _appDbContext.Members
            .Where(m => _appDbContext.Bookings
                .Where(b => b.Member_ID == m.Member_ID)
                .Join(_appDbContext.Booking_Time_Slots,
                      b => b.Booking_ID,
                      bts => bts.Booking_ID,
                      (b, bts) => bts.Time_Slot_ID)
                .Join(_appDbContext.Time_Slots,
                      btsId => btsId,
                      ts => ts.Time_Slot_ID,
                      (btsId, ts) => ts)
                .Count(ts => ts.Slot_Date >= oneMonthAgo) >= bookingCount)
            .ToListAsync();

        return qualifyingMembers;
    }

    private async Task<List<Member>> GetLoyalMembers()
    {
        var oneYearAgo = DateTime.Now.AddYears(-1);
        var qualifyingMembers = await _appDbContext.Members
            .Join(_appDbContext.Contracts,
                  m => m.Contract_ID,
                  c => c.Contract_ID,
                  (m, c) => new { Member = m, Contract = c })
            .Where(mc => mc.Contract.Subscription_Date <= oneYearAgo)
            .Select(mc => mc.Member)
            .ToListAsync();

        return qualifyingMembers;
    }


    private async Task<List<Member>> GetFrequentBookers(int bookingCount, int months)
    {
        var dateLimit = DateTime.Now.AddMonths(-months);
        var qualifyingMembers = await _appDbContext.Members
            .Where(m => _appDbContext.Bookings
                .Where(b => b.Member_ID == m.Member_ID)
                .Join(_appDbContext.Booking_Time_Slots,
                      b => b.Booking_ID,
                      bts => bts.Booking_ID,
                      (b, bts) => bts.Time_Slot_ID)
                .Join(_appDbContext.Time_Slots,
                      btsId => btsId,
                      ts => ts.Time_Slot_ID,
                      (btsId, ts) => ts)
                .Count(ts => ts.Slot_Date >= dateLimit) >= bookingCount)
            .ToListAsync();

        return qualifyingMembers;
    }

    //private async Task<List<Member>> GetPerfectAttendanceMembers()
    //{
    //    var startDate = DateTime.Now.AddMonths(-1);
    //    var qualifyingMembers = await _appDbContext.Members
    //        .Where(m => _appDbContext.Attendance_Lists
    //            .Count(a => a.Member_ID == m.Member_ID && a.Slot_Date >= startDate && a.Members_Present > 0) == 30)
    //        .ToListAsync();

    //    return qualifyingMembers;
    //}

    //private async Task<List<Member>> GetConsistentAttendanceMembers()
    //{
    //    var startDate = DateTime.Now.AddMonths(-3);
    //    var qualifyingMembers = await _appDbContext.Members
    //        .Where(m => _appDbContext.Attendance_Lists
    //            .Count(a => a.Member_ID == m.Member_ID && a.Slot_Date >= startDate && a.Members_Present > 0) >= 12)
    //        .ToListAsync();

    //    return qualifyingMembers;
    //}
}
//Controller

using av_motion_api.Data;
using av_motion_api.Models;
using av_motion_api.ViewModels;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace av_motion_api.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class RewardController : ControllerBase
    {
        private readonly AppDbContext _appDbContext;

        public RewardController(AppDbContext appDbContext)
        {
            _appDbContext = appDbContext;
        }

        [HttpGet]
        [Route("getAllRewardTypes")]
        public async Task<IActionResult> GetAllRewardTypes()
        {
            try
            {
                var rewardTypes = await _appDbContext.Reward_Types.ToListAsync();
                return Ok(rewardTypes);
            }
            catch (Exception)
            {
                return BadRequest();
            }
        }

        [HttpGet]
        [Route("getRewardTypeById/{id}")]
        public async Task<IActionResult> GetRewardTypeById(int id)
        {
            try
            {
                var rewardType = await _appDbContext.Reward_Types.FindAsync(id);

                if (rewardType == null)
                {
                    return NotFound();
                }

                return Ok(rewardType);
            }
            catch (Exception)
            {
                return BadRequest();
            }
        }

        [HttpPost]
        [Route("createRewardType")]
        public async Task<IActionResult> CreateRewardType(RewardTypeViewModel rt)
        {
            try
            {
                var existingRewardType = await _appDbContext.Reward_Types
                    .FirstOrDefaultAsync(r => r.Reward_Type_Name == rt.Reward_Type_Name);

                if (existingRewardType != null)
                {
                    return Conflict(new { message = "Reward type already exists." });
                }


                var rewardType = new Reward_Type
                {
                    Reward_Type_Name = rt.Reward_Type_Name,
                    Reward_Criteria = rt.Reward_Criteria
                    
                };
                _appDbContext.Reward_Types.Add(rewardType);
                await _appDbContext.SaveChangesAsync();

                return CreatedAtAction(nameof(GetRewardTypeById), new { id = rewardType.Reward_Type_ID }, rewardType);
            }
            catch (Exception)
            {
                return BadRequest();
            }
        }

        [HttpPut]
        [Route("updateRewardType/{id}")]
        public async Task<IActionResult> UpdateRewardType(int id, [FromBody] RewardTypeViewModel rt)
        {
            try
            {
                var rewardType = await _appDbContext.Reward_Types.FindAsync(id);

                if(rewardType == null)
                {
                    return NotFound();
                }

                var existingRewardType = await _appDbContext.Reward_Types
                    .FirstOrDefaultAsync(r => r.Reward_Type_Name == rt.Reward_Type_Name && r.Reward_Type_ID != id);

                if (existingRewardType != null)
                {
                    return Conflict(new { message = "Reward type already exists." });
                }

                rewardType.Reward_Type_Name = rt.Reward_Type_Name;
                rewardType.Reward_Criteria = rt.Reward_Criteria;

                _appDbContext.Entry(rewardType).State = EntityState.Modified;
                await _appDbContext.SaveChangesAsync();

                return NoContent();
            }
            catch (Exception)
            {
                return BadRequest();
            }
        }

        [HttpDelete]
        [Route("deleteRewardType/{id}")]
        public async Task<IActionResult> DeleteRewardType(int id)
        {
            try
            {
                var rewardType = await _appDbContext.Reward_Types.FindAsync(id);

                if(rewardType == null)
                {
                    return NotFound();
                }

                _appDbContext.Reward_Types.Remove(rewardType);
                await _appDbContext.SaveChangesAsync();

                return NoContent();
            }
            catch (Exception)
            {
                return BadRequest();
            }
        }



        [HttpGet]
        [Route("getRewardById/{id}")]
        public async Task<IActionResult> GetRewardById(int id)
        {
            try
            {
                var reward = await _appDbContext.Rewards.FindAsync(id);

                if (reward == null)
                {
                    return NotFound();
                }

                return Ok(reward);
            }
            catch (Exception)
            {
                return BadRequest();
            }
        }

        [HttpGet]
        [Route("getAllRewards")]
        public async Task<IActionResult> GetAllRewards()
        {
            try
            {
                var rewards = await _appDbContext.Rewards
                                         .Join(_appDbContext.Reward_Types,
                                               reward => reward.Reward_Type_ID,
                                               rewardType => rewardType.Reward_Type_ID,
                                               (reward, rewardType) => new RewardViewModel
                                               {
                                                   Reward_ID = reward.Reward_ID,
                                                   Reward_Issue_Date = reward.Reward_Issue_Date,
                                                   Reward_Type_Name = rewardType.Reward_Type_Name,
                                                   IsPosted = reward.IsPosted
                                               })
                                         .ToListAsync();
                return Ok(rewards);
            }
            catch (Exception)
            {
                return BadRequest();
            }
        }

        [HttpPost]
        [Route("redeemReward")]
        public async Task<IActionResult> RedeemReward([FromBody] RewardRedeemViewModel request)
        {
            try
            {
                // Fetch the reward by ID
                var reward = await _appDbContext.Reward_Members
                                                .FirstOrDefaultAsync(r => r.Reward_ID == request.RewardId && r.Member_ID == request.MemberId);

                if (reward == null)
                {
                    return NotFound("Reward not found or not eligible for the member.");
                }

                // Check if the reward is already redeemed
                if (reward.IsRedeemed)
                {
                    return BadRequest("Reward is already redeemed.");
                }

                // Mark the reward as redeemed
                reward.IsRedeemed = true;
                _appDbContext.Entry(reward).State = EntityState.Modified;
                await _appDbContext.SaveChangesAsync();

                return Ok(new { Status = "Success", Message = "Reward redeemed successfully!" });
            }
            catch (Exception)
            {
                return BadRequest("An error occurred while redeeming the reward.");
            }
        }

        [HttpPost]
        [Route("setReward")]
        public async Task<IActionResult> SetReward(RewardSetViewModel r)
        {
            try
            {
                var reward = new Reward
                {
                    Reward_Issue_Date = r.Reward_Issue_Date,
                    Reward_Type_ID = r.Reward_Type_ID,
                    IsPosted = r.IsPosted
                };
                _appDbContext.Rewards.Add(reward);
                await _appDbContext.SaveChangesAsync();

                return CreatedAtAction(nameof(GetRewardById), new { id = reward.Reward_ID }, reward);
            }
            catch (Exception)
            {
                return BadRequest();
            }
        }


        [HttpPost]
        [Route("postReward")]
        public async Task<IActionResult> PostReward([FromBody] RewardPostViewModel request)
        {
            try
            {
                // Fetch the reward by ID
                var reward = await _appDbContext.Rewards
                                                .FirstOrDefaultAsync(r => r.Reward_ID == request.RewardId);

                if (reward == null)
                {
                    return NotFound("Reward not found.");
                }

                // Check if the reward is already posted
                if (reward.IsPosted)
                {
                    return BadRequest("Reward is already posted.");
                }

                // Mark the reward as posted
                reward.IsPosted = true;
                _appDbContext.Entry(reward).State = EntityState.Modified;
                await _appDbContext.SaveChangesAsync();

                // Fetch members who qualify for this reward based on the criteria
                var rewardType = await _appDbContext.Reward_Types.FindAsync(reward.Reward_Type_ID);
                var qualifyingMembers = await GetQualifyingMembers(rewardType.Reward_Criteria);

                // Add qualifying members to the Reward_Member table
                foreach (var member in qualifyingMembers)
                {
                    var rewardMember = new Reward_Member
                    {
                        Member_ID = member.Member_ID,
                        Reward_ID = reward.Reward_ID,
                        IsRedeemed = false
                    };
                    _appDbContext.Reward_Members.Add(rewardMember);
                }
                await _appDbContext.SaveChangesAsync();

                return Ok(new { Status = "Success", Message = "Reward posted successfully!" });
            }
            catch (Exception)
            {
                return BadRequest("An error occurred while posting the reward.");
            }
        }

        //Predined Reward Types Methods
        private async Task<List<Member>> GetQualifyingMembers(string criteria)
        {
            var qualifyingMembers = new List<Member>();

            if (criteria == "Completed 10 Bookings in a Month")
            {
                qualifyingMembers = await GetMembersWithBookingsInMonth(10);
            }
            else if (criteria == "Member for Over a Year")
            {
                qualifyingMembers = await GetLoyalMembers();
            }
            else if (criteria == "Made 20 Bookings in Last 3 Months")
            {
                qualifyingMembers = await GetFrequentBookers(20, 3);
            }
            //else if (criteria == "Perfect Attendance for a Month")
            //{
            //    qualifyingMembers = await GetPerfectAttendanceMembers();
            //}
            //else if (criteria == "Consistent Attendance for a Quarter")
            //{
            //    qualifyingMembers = await GetConsistentAttendanceMembers();
            //}

            return qualifyingMembers;
        }

        private async Task<List<Member>> GetMembersWithBookingsInMonth(int bookingCount)
        {
            var oneMonthAgo = DateTime.Now.AddMonths(-1);
            var qualifyingMembers = await _appDbContext.Members
                .Where(m => _appDbContext.Bookings
                    .Where(b => b.Member_ID == m.Member_ID)
                    .Join(_appDbContext.Booking_Time_Slots,
                          b => b.Booking_ID,
                          bts => bts.Booking_ID,
                          (b, bts) => bts.Time_Slot_ID)
                    .Join(_appDbContext.Time_Slots,
                          btsId => btsId,
                          ts => ts.Time_Slot_ID,
                          (btsId, ts) => ts)
                    .Count(ts => ts.Slot_Date >= oneMonthAgo) >= bookingCount)
                .ToListAsync();

            return qualifyingMembers;
        }

        private async Task<List<Member>> GetLoyalMembers()
        {
            var oneYearAgo = DateTime.Now.AddYears(-1);
            var qualifyingMembers = await _appDbContext.Members
                .Join(_appDbContext.Contracts,
                      m => m.Contract_ID,
                      c => c.Contract_ID,
                      (m, c) => new { Member = m, Contract = c })
                .Where(mc => mc.Contract.Subscription_Date <= oneYearAgo)
                .Select(mc => mc.Member)
                .ToListAsync();

            return qualifyingMembers;
        }


        private async Task<List<Member>> GetFrequentBookers(int bookingCount, int months)
        {
            var dateLimit = DateTime.Now.AddMonths(-months);
            var qualifyingMembers = await _appDbContext.Members
                .Where(m => _appDbContext.Bookings
                    .Where(b => b.Member_ID == m.Member_ID)
                    .Join(_appDbContext.Booking_Time_Slots,
                          b => b.Booking_ID,
                          bts => bts.Booking_ID,
                          (b, bts) => bts.Time_Slot_ID)
                    .Join(_appDbContext.Time_Slots,
                          btsId => btsId,
                          ts => ts.Time_Slot_ID,
                          (btsId, ts) => ts)
                    .Count(ts => ts.Slot_Date >= dateLimit) >= bookingCount)
                .ToListAsync();

            return qualifyingMembers;
        }

        //private async Task<List<Member>> GetPerfectAttendanceMembers()
        //{
        //    var startDate = DateTime.Now.AddMonths(-1);
        //    var qualifyingMembers = await _appDbContext.Members
        //        .Where(m => _appDbContext.Attendance_Lists
        //            .Count(a => a.Member_ID == m.Member_ID && a.Slot_Date >= startDate && a.Members_Present > 0) == 30)
        //        .ToListAsync();

        //    return qualifyingMembers;
        //}

        //private async Task<List<Member>> GetConsistentAttendanceMembers()
        //{
        //    var startDate = DateTime.Now.AddMonths(-3);
        //    var qualifyingMembers = await _appDbContext.Members
        //        .Where(m => _appDbContext.Attendance_Lists
        //            .Count(a => a.Member_ID == m.Member_ID && a.Slot_Date >= startDate && a.Members_Present > 0) >= 12)
        //        .ToListAsync();

        //    return qualifyingMembers;
        //}
    }
}
import { Component, OnDestroy, OnInit } from '@angular/core';
import { FormGroup, Validators, FormBuilder } from '@angular/forms';
import { UserService } from '../Services/userprofile.service';
import { Router } from '@angular/router';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule } from '@angular/forms';
import { Member, updateUser } from '../shared/update-user';
import { Subscription, catchError } from 'rxjs';
import { RewardRedeemViewModel, RewardViewModel, UnredeemedRewardModel } from '../shared/reward';
import { RewardService } from '../Services/reward.service';
import { RewardTypeViewModel } from '../shared/reward';

declare var $: any; // Import jQuery

@Component({
  selector: 'app-profile-page',
  standalone: true,
  imports: [CommonModule, ReactiveFormsModule],
  templateUrl: './profile-page.component.html',
  styleUrls: ['./profile-page.component.css']
})
export class ProfilePageComponent implements OnInit, OnDestroy {
  profileForm: FormGroup;
  user: updateUser | undefined;
  member!: Member;
  isEditMode = false;
  errorMessage = '';
  userProfileImage: string | ArrayBuffer = ''; // Add this property to store the Base64 image data
  unredeemedRewards: UnredeemedRewardModel[] = [];
  selectedReward: UnredeemedRewardModel | null = null;

  private userSubscription: Subscription | undefined;
  private redeemSubscription: Subscription | undefined;
  

  constructor(
    private userService: UserService,
    private rewardService: RewardService,
    private router: Router,
    private fb: FormBuilder
  ) {
    this.profileForm = this.fb.group({
      email: ['', [Validators.required, Validators.email]],
      name: ['', [Validators.required, Validators.minLength(3), Validators.maxLength(20)]],
      surname: ['', [Validators.required, Validators.minLength(3), Validators.maxLength(20)]],
      phoneNumber: ['', [Validators.required, Validators.maxLength(10)]],
      physical_Address: ['', [Validators.required, Validators.maxLength(255)]],
      photo: ['']
    });
  }

  ngOnInit(): void {
    const userId = JSON.parse(localStorage.getItem('User') || '').userId;
    console.log('User ID from local storage:', userId);
    
    this.userSubscription = this.userService.getUserById(userId).pipe(
      catchError(error => {
        console.error('Error fetching user profile:', error);
        // Handle error
        return []; // Return empty array or default value as per your logic
      })
    ).subscribe({
      next: (result) => {
        console.log('User data received:', result);
        this.user = result;
        this.profileForm.patchValue(this.user);
        
        // Assign userProfileImage
        this.userProfileImage = `data:image/jpeg;base64,${this.user.Photo}`; // Ensure this.user.Photo contains the Base64 data
        
        // Log userProfileImage and user for debugging
        console.log('User Profile Image:', this.userProfileImage);
        console.log('User:', this.user);

        // Check if the user is a member and fetch member details
      if (this.user.User_Type_ID !== 3) {
        this.userService.getMemberByUserId(userId).subscribe(
          memberResult => {
            this.member = memberResult;
            console.log('Member data received:', this.member);
            this.loadUnredeemedRewards(); // Load rewards if the user is a member
          },
          memberError => {
            console.error('Error fetching member data:', memberError);
          }
        );
      }
      },
      complete: () => {
        console.log('getUserById subscription completed');
      }
    });
  
    this.isEditMode = false;
    this.loadUnredeemedRewards();
  }

  ngOnDestroy(): void {
    // Clean up subscriptions
    if (this.userSubscription) {
      this.userSubscription.unsubscribe();
    }
    if (this.redeemSubscription) {
      this.redeemSubscription.unsubscribe();
    }
  }

  clearForm() {
    this.profileForm.reset();
  }

  enableEditMode(event: Event) {
    event.preventDefault();
    this.isEditMode = true;
    this.profileForm.enable();
  }

  openSaveModal() {
    if (this.profileForm.invalid) {
      this.showValidationErrors();
      $('#errorModal').modal('show');
      return;
    }
    $('#saveConfirmationModal').modal('show');
  }

  showValidationErrors() {
    const invalidFields: string[] = [];
    Object.keys(this.profileForm.controls).forEach(key => {
      const controlErrors = this.profileForm.get(key)?.errors;
      if (controlErrors) {
        Object.keys(controlErrors).forEach(errorKey => {
          invalidFields.push(`${key}: ${errorKey}`);
        });
      }
    });
    this.errorMessage = `Please enter a valid input: ${invalidFields.join(', ')}`;
  }

  dismissModal() {
    $('#saveConfirmationModal').modal('hide');
  }

  dismissErrorModal() {
    $('#errorModal').modal('hide');
  }

  confirmSave() {
    this.dismissModal();
    this.onSubmit();
    this.isEditMode = false; // Disable edit mode after confirmation
  }

  onSubmit() {
    if (this.profileForm.valid) {
      const userId = JSON.parse(localStorage.getItem('User')!).userId;
      this.userService.updateUser(userId,this.profileForm.value).subscribe({
        next: (result) => {
          console.log('User to be updated:', result);
          this.router.navigateByUrl(`/ProfilePage/${userId}`);
          alert('Successfully updated profile');
        },
        error: () => {
          console.error('Error updating user profile:');
          alert('Error updating profile');
        }
      });
    }
  }

  onPhotoChange(event: Event): void {
    if (!this.isEditMode) return;

    const input = event.target as HTMLInputElement;
    if (input.files && input.files[0]) {
      const reader = new FileReader();
      reader.onload = (e: any) => {
        this.userProfileImage = e.target.result; // Update the image source
        this.profileForm.patchValue({ photo: e.target.result }); // Update the form control
      };
      reader.readAsDataURL(input.files[0]);
    }
  }

  goBack() {
    const userTypeId = JSON.parse(localStorage.getItem('User')!).userTypeId;
    const userId = JSON.parse(localStorage.getItem('User')!).userId;
    if (userTypeId === 1) {  // Ensure userTypeID is compared as string
      this.router.navigateByUrl(`/OwnerHome/${userId}`);
    } else if (userTypeId === 2) {
      this.router.navigateByUrl(`/EmployeeHome/${userId}`);
    } else if (userTypeId === 3) {
      this.router.navigateByUrl(`/Home/${userId}`);
    }
  }

  changePassword() {
    this.router.navigateByUrl('/ChangePasswordPage');
  }


  // Method to load rewards for the current user
  loadUnredeemedRewards(): void {
    const memberId = this.member.Member_ID;
    this.rewardService.getUnredeemedRewardsForMember(memberId).subscribe(
      rewards => {
        this.unredeemedRewards = rewards;
        console.log('Unredeemed Rewards:', this.unredeemedRewards);
      },
      error => {
        console.error('Error fetching unredeemed rewards:', error);
      }
    );
  }

  // Method to open redeem modal for a reward
  openRedeemModal(reward: UnredeemedRewardModel): void {
    this.selectedReward = reward;
    $('#redeemRewardModal').modal('show');
  }

  // Method to dismiss redeem modal
  dismissRedeemModal(): void {
    $('#redeemRewardModal').modal('hide');
  }

  // Method to confirm redeeming a reward
  confirmRedeem(): void {
    if (!this.selectedReward) {
      return;
    }
    const redeemRequest = new RewardRedeemViewModel();
    redeemRequest.MemberId = this.member?.Member_ID ?? 0;
    redeemRequest.RewardId = this.selectedReward.reward_ID;

    // Call backend service to redeem the reward
    this.redeemSubscription = this.rewardService.redeemReward(redeemRequest).subscribe({
      next: () => {
        // Show success modal on successful redemption
        $('#successModal').modal('show');
        // Remove redeemed reward from the list
        this.unredeemedRewards = this.unredeemedRewards.filter(r => r.reward_ID !== this.selectedReward?.reward_ID);
      },
      error: (error) => {
        console.error('Error redeeming reward:', error);
        // Handle error
      }
    });
    this.dismissRedeemModal();
  }

  // Method to dismiss success modal
  dismissSuccessModal(): void {
    $('#successModal').modal('hide');
  }
}
//profilepage component
//html
<div class="container-fluid" style="margin-top: 60px;">
  <div class="row justify-content-start">
    <!-- Back Button -->
    <div class="col-6">
      <button class="btn btn-link" (click)="goBack()">
        <i class="bi bi-arrow-left-circle"></i> Back
      </button>
    </div>

    <!-- My Profile Heading -->
    <div class="col-6">
      <h2 class="text-left">My Profile</h2>
    </div>

    <!-- Left Side Menu -->
    <div class="col-md-3">
      <h5 class="small-heading">
        <i class="bi bi-bell-fill"></i>
        Notifications
      </h5>
      <!-- Notification placeholder -->
      <div class="card mt-3">
        <div class="card-body">
          <div *ngIf="unredeemedRewards.length === 0" class="notification-item">
            <span>No Notifications</span>
          </div>
          <div *ngFor="let reward of unredeemedRewards" class="notification-item">
            <span>{{ reward.reward_Type_Name }}</span>
            <button class="btn btn-sm btn-primary" (click)="openRedeemModal(reward)">Redeem</button>
          </div>
        </div>
      </div>
    </div>

    

    <!-- Vertical Line Separator -->
    <div class="col-md-1">
      <div class="vertical-line"></div>
    </div>

    <!-- Right Side Form -->
    <div class="col-md-6">
      <form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
        <h5 class="small-heading">
          <i class="bi bi-house-gear-fill"></i>
          Personal Details
        </h5>
        <div class="text-center mb-3">
          <div class="profile-photo-wrapper">
            <img [src]="userProfileImage" alt="Profile Photo" class="img-fluid rounded-circle profile-photo" *ngIf="userProfileImage">
            <div class="edit-photo-wrapper">
              <a href="#" (click)="enableEditMode($event)" class="edit-link">Edit</a>
              <label [class.disabled-icon]="!isEditMode" for="photoUpload" class="photo-upload-icon">
                <i class="bi bi-camera"></i>
              </label>
            </div>
            <input type="file" id="photoUpload" formControlName="photo" class="d-none" (change)="onPhotoChange($event)" [readonly]="!isEditMode" >
          </div>
        </div>
        

        <br>

      
        <div class="row">
          <div class="col-md-6 mb-3">
            <div class="form-group">
              <label for="name" class="form-label">Name</label>
              <input type="text" class="form-control" id="name" formControlName="name" [readonly]="!isEditMode" [ngClass]="{'disabled-input': !isEditMode}">
              <div *ngIf="profileForm.controls['name'].invalid && (profileForm.controls['name'].dirty || profileForm.controls['name'].touched)" class="alert">
                <div *ngIf="profileForm.controls['name'].errors?.['required']">Name is required.</div>
                <div *ngIf="profileForm.controls['name'].errors?.['minlength']">Name must be at least 3 characters long.</div>
                <div *ngIf="profileForm.controls['name'].errors?.['maxlength']">Name must be at most 50 characters long.</div>
              </div>
            </div>
          </div>

          <div class="col-md-6 mb-3">
            <div class="form-group">            
              <label for="surname" class="form-label">Surname</label>
              <input type="text" class="form-control" id="surname" formControlName="surname" [readonly]="!isEditMode" [ngClass]="{'disabled-input': !isEditMode}">
              <div *ngIf="profileForm.controls['surname'].invalid && (profileForm.controls['surname'].dirty || profileForm.controls['surname'].touched)" class="alert">
                <div *ngIf="profileForm.controls['surname'].errors?.['required']">Surname is required.</div>
                <div *ngIf="profileForm.controls['surname'].errors?.['minlength']">Surname must be at least 3 characters long.</div>
                <div *ngIf="profileForm.controls['surname'].errors?.['maxlength']">Surname must be at most 50 characters long.</div>
              </div>
            </div>
          </div>

          <div class="col-md-6 mb-3">
            <div class="form-group">
              <label for="email" class="form-label">Email Address</label>
              <input type="email" class="form-control" id="email" formControlName="email" [readonly]="!isEditMode" [ngClass]="{'disabled-input': !isEditMode}">
              <div *ngIf="profileForm.controls['email'].invalid && (profileForm.controls['email'].dirty || profileForm.controls['email'].touched)" class="alert">
                <div *ngIf="profileForm.controls['email'].errors?.['required']">Email is required.</div>
              </div>
            </div>
          </div>

          <div class="col-md-6 mb-3">
            <div class="form-group">
              <label for="phoneNumber" class="form-label">Contact Number</label>
              <input type="text" class="form-control" id="phoneNumber" formControlName="phoneNumber" [readonly]="!isEditMode" [ngClass]="{'disabled-input': !isEditMode}">
              <div *ngIf="profileForm.controls['phoneNumber'].invalid && (profileForm.controls['phoneNumber'].dirty || profileForm.controls['phoneNumber'].touched)" class="alert">
                <div *ngIf="profileForm.controls['phoneNumber'].errors?.['required']">Contact number is required.</div>
                <div *ngIf="profileForm.controls['phoneNumber'].errors?.['maxlength']">Contact number must be a valid 10-digit number.</div>
              </div>
            </div>
          </div>

          <div class="col-md-6 mb-3">
            <div class="form-group">
              <label for="physical_Address" class="form-label">Physical Address</label>
              <input type="text" class="form-control" id="physical_Address" formControlName="physical_Address" [readonly]="!isEditMode" [ngClass]="{'disabled-input': !isEditMode}">
              <div *ngIf="profileForm.controls['physical_Address'].invalid && (profileForm.controls['physical_Address'].dirty || profileForm.controls['physical_Address'].touched)" class="alert">
                <div *ngIf="profileForm.controls['physical_Address'].errors?.['required']">Physical address is required.</div>
                <div *ngIf="profileForm.controls['physical_Address'].errors?.['maxlength']">Physical address must be at most 255 characters long.</div>
              </div>
            </div>
          </div>
        </div>

        <div class="d-flex justify-content-end">
          <button type="button" class="btn btn-primary me-2" (click)="openSaveModal()" [disabled]="!isEditMode">Save</button>
          <button type="button" class="btn btn-info" (click)="changePassword()" [disabled]="!isEditMode">Change Password</button>
        </div>
      </form>
    </div>
  </div>
</div>

<!-- Save Confirmation Modal -->
<div class="modal fade" id="saveConfirmationModal" tabindex="-1" role="dialog" aria-labelledby="saveConfirmationModalTitle" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="saveConfirmationModalTitle">Save Changes</h5>
      </div>
      <div class="modal-body">
        Are you sure you want to update your profile details?
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" (click)="dismissModal()">Cancel</button>
        <button type="button" class="btn btn-primary" (click)="confirmSave()">Confirm</button>
      </div>
    </div>
  </div>
</div>

<!-- Error Modal -->
<div class="modal fade" id="errorModal" tabindex="-1" role="dialog" aria-labelledby="errorModalTitle" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="errorModalTitle">Error</h5>
      </div>
      <div class="modal-body">
        Please enter a valid input for the following fields:
        <ul id="errorList"></ul>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-primary" (click)="dismissErrorModal()">OK</button>
      </div>
    </div>
  </div>
</div>

<!-- Redeem Reward Modal -->
<div class="modal fade" id="redeemRewardModal" tabindex="-1" role="dialog" aria-labelledby="redeemRewardModalTitle" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="redeemRewardModalTitle">Redeem Reward</h5>
      </div>
      <div class="modal-body">
        Are you sure you want to redeem the reward {{ selectedReward?.reward_Type_Name }}?
        <p>{{ selectedReward?.reward_Criteria }}</p>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" (click)="dismissRedeemModal()">No</button>
        <button type="button" class="btn btn-primary" (click)="confirmRedeem()">Yes</button>
      </div>
    </div>
  </div>
</div>

<!-- Success Modal -->
<div class="modal fade" id="successModal" tabindex="-1" role="dialog" aria-labelledby="successModalTitle" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="successModalTitle">Success</h5>
        <button type="button" class="close" (click)="dismissSuccessModal()">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        Congratulations! You've successfully redeemed this reward {{ selectedReward?.reward_Type_Name }}. Please contact the gym for further instructions.
      </div>
    </div>
  </div>
</div>

//ts
import { Component, OnDestroy, OnInit } from '@angular/core';
import { FormGroup, Validators, FormBuilder } from '@angular/forms';
import { UserService } from '../Services/userprofile.service';
import { Router } from '@angular/router';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule } from '@angular/forms';
import { Member, updateUser } from '../shared/update-user';
import { Subscription, catchError } from 'rxjs';
import { RewardRedeemViewModel, UnredeemedRewardModel } from '../shared/reward';
import { RewardService } from '../Services/reward.service';
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';

declare var $: any; // Import jQuery

@Component({
  selector: 'app-profile-page',
  standalone: true,
  imports: [CommonModule, ReactiveFormsModule],
  templateUrl: './profile-page.component.html',
  styleUrls: ['./profile-page.component.css']
})
export class ProfilePageComponent implements OnInit, OnDestroy {
  profileForm: FormGroup;
  user: updateUser | undefined;
  member: Member | undefined;
  isEditMode = false;
  errorMessage = '';  
  userProfileImage: string | null = null;
  unredeemedRewards: UnredeemedRewardModel[] = [];
  selectedReward: UnredeemedRewardModel | null = null;

  private userSubscription: Subscription | undefined;
  private memberSubscription: Subscription | undefined;
  private redeemSubscription: Subscription | undefined;
  
  constructor(
    private userService: UserService,
    private rewardService: RewardService,
    private router: Router,
    private fb: FormBuilder
  ) {
    this.profileForm = this.fb.group({
      email: ['', [Validators.required, Validators.email]],
      name: ['', [Validators.required, Validators.minLength(3), Validators.maxLength(20)]],
      surname: ['', [Validators.required, Validators.minLength(3), Validators.maxLength(20)]],
      phoneNumber: ['', [Validators.required, Validators.maxLength(10)]],
      physical_Address: ['', [Validators.required, Validators.maxLength(255)]],
      photo: ['']
    });
  }

  ngOnInit(): void {
    const userId = JSON.parse(localStorage.getItem('User') || '{}').userId;
    console.log('User ID from local storage:', userId);
    this.loadUserProfile(userId);
    this.isEditMode = false;
  }

  ngOnDestroy(): void {
    // Clean up subscriptions
    if (this.userSubscription) {
      this.userSubscription.unsubscribe();
    }
    if (this.memberSubscription) {
      this.memberSubscription.unsubscribe();
    }
    if (this.redeemSubscription) {
      this.redeemSubscription.unsubscribe();
    }
  }

  loadUserProfile(userId: number): void {
    this.userSubscription = this.userService.getUserById(userId).pipe(
      catchError(error => {
        console.error('Error fetching user profile:', error);
        return [];
      })
    ).subscribe({
      next: (result) => {
        console.log('User data received:', result);
        this.user = result; 
        // Log photo to debug
        console.log('Photo:', this.user.photo);        
        // Set user profile image
        this.userProfileImage = `data:image/jpeg;base64,${this.user.photo}`;
        this.profileForm.patchValue(this.user);
        console.log('User:', this.user);

        if (this.user.user_Type_ID === 3) {
          this.loadMemberProfile(userId);
        }
      },
      complete: () => {
        console.log('getUserById subscription completed');
      }
    });
  }

  loadMemberProfile(userId: number): void {
    this.memberSubscription = this.userService.getMemberByUserId(userId).pipe(
      catchError(error => {
        console.error('Error fetching member profile:', error);
        return [];
      })
    ).subscribe({
      next: (result) => {
        this.member = result;
        if (this.member) {
          this.loadUnredeemedRewards(this.member.Member_ID);
        }
      },
      complete: () => {
        console.log('getMemberByUserId subscription completed');
      }
    });
  }

  clearForm() {
    this.profileForm.reset();
  }

  enableEditMode(event: Event) {
    event.preventDefault();
    this.isEditMode = true;
    this.profileForm.enable();
  }

  openSaveModal() {
    if (this.profileForm.invalid) {
      this.showValidationErrors();
      $('#errorModal').modal('show');
      return;
    }
    $('#saveConfirmationModal').modal('show');
  }

  showValidationErrors() {
    const invalidFields: string[] = [];
    Object.keys(this.profileForm.controls).forEach(key => {
      const controlErrors = this.profileForm.get(key)?.errors;
      if (controlErrors) {
        Object.keys(controlErrors).forEach(errorKey => {
          invalidFields.push(`${key}: ${errorKey}`);
        });
      }
    });
    this.errorMessage = `Please enter a valid input: ${invalidFields.join(', ')}`;
  }

  dismissModal() {
    $('#saveConfirmationModal').modal('hide');
  }

  dismissErrorModal() {
    $('#errorModal').modal('hide');
  }

  confirmSave() {
    this.dismissModal();
    this.onSubmit();
    this.isEditMode = false; // Disable edit mode after confirmation
  }

  onSubmit() {
    if (this.profileForm.valid) {
      const userId = JSON.parse(localStorage.getItem('User')!).userId;
      this.userService.updateUser(userId, this.profileForm.value).subscribe({
        next: (result) => {
          console.log('User to be updated:', result);
          this.router.navigateByUrl(`/ProfilePage/${userId}`);
          alert('Successfully updated profile');
        },
        error: () => {
          console.error('Error updating user profile:');
          alert('Error updating profile');
        }
      });
    }
  }

  onPhotoChange(event: Event): void {
    if (!this.isEditMode) return;

    const input = event.target as HTMLInputElement;
    if (input.files && input.files[0]) {
      const reader = new FileReader();
      reader.onload = (e: any) => {
        this.userProfileImage = e.target.result; // Update the image source
        this.profileForm.patchValue({ photo: e.target.result }); // Update the form control
      };
      reader.readAsDataURL(input.files[0]);
    }
  }

  goBack() {
    const userTypeId = JSON.parse(localStorage.getItem('User')!).userTypeId;
    const userId = JSON.parse(localStorage.getItem('User')!).userId;
    if (userTypeId === 1) {  // Ensure userTypeID is compared as string
      this.router.navigateByUrl(`/OwnerHome/${userId}`);
    } else if (userTypeId === 2) {
      this.router.navigateByUrl(`/EmployeeHome/${userId}`);
    } else if (userTypeId === 3) {
      this.router.navigateByUrl(`/Home/${userId}`);
    }
  }

  changePassword() {
    this.router.navigateByUrl('/ChangePasswordPage');
  }

  // Method to load rewards for the current user
  loadUnredeemedRewards(memberId: number): void {
    this.rewardService.getUnredeemedRewardsForMember(memberId).subscribe(
      rewards => {
        this.unredeemedRewards = rewards;
      },
      error => {
        console.error('Error fetching unredeemed rewards:', error);
      }
    );
  }

  // Method to open redeem modal for a reward
  openRedeemModal(reward: UnredeemedRewardModel): void {
    this.selectedReward = reward;
    $('#redeemRewardModal').modal('show');
  }

  // Method to dismiss redeem modal
  dismissRedeemModal(): void {
    $('#redeemRewardModal').modal('hide');
  }

  // Method to confirm redeeming a reward
  confirmRedeem(): void {
    if (!this.selectedReward) {
      return;
    }
    const redeemRequest = new RewardRedeemViewModel();
    redeemRequest.MemberId = this.member?.Member_ID ?? 0;
    redeemRequest.RewardId = this.selectedReward.reward_ID;

    // Call backend service to redeem the reward
    this.redeemSubscription = this.rewardService.redeemReward(redeemRequest).subscribe({
      next: () => {
        // Show success modal on successful redemption
        $('#successModal').modal('show');
        // Remove redeemed reward from the list
        this.unredeemedRewards = this.unredeemedRewards.filter(r => r.reward_ID !== this.selectedReward?.reward_ID);
      },
      error: (error) => {
        console.error('Error redeeming reward:', error);
        // Handle error
      }
    });
    this.dismissRedeemModal();
  }

  // Method to dismiss success modal
  dismissSuccessModal(): void {
    $('#successModal').modal('hide');
  }
}


//models
export class updateUser
{
   name!: string;
   surname!: string;
   email!: string;
   physical_Address!: string;
   phoneNumber!: string;
   photo!: string;
   user_Type_ID!: number;
}
export class UserProfile
{   
   User_ID !: number
   Id!: number
   Name!: string
   Surname!: string
   ID_Number!: string
   Email!: string
   Physical_Address!: string
   PhoneNumber!: string
   Photo!: string
   UserName!: string
   PasswordHash!: string
   Date_of_Birth!: Date
   User_Status_ID !: number
   User_Type_ID!: number
}

//service
import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, catchError, map, throwError } from 'rxjs';
import { UserProfile } from '../Models/UserProfile';
import { LoginUser } from '../shared/login-user';
import { User } from '../shared/user';
import { updateUser } from '../shared/update-user';
import { UserTypeViewModel } from '../shared/user-type-vm';
import { UserViewModel } from '../shared/search-user';

@Injectable({
  providedIn: 'root'
})
export class UserService {

  httpOptions = {
    headers: new HttpHeaders({
      'Content-Type': 'application/json'
    })
  };

  constructor(private http: HttpClient) {}
  endPoint: string = "https://localhost:7185/api/";

  //User EndPoints
  // RegisterUser(registerUser: RegisterUser) {
  //   return this.http.post(`${this.endPoint}User/Register`, registerUser, this.httpOptions);
  // }

  RegisterUser(formData: FormData): Observable<any> {
    return this.http.post(`${this.endPoint}User/Register`, formData);
  }

  
  LoginUser(loginUser: LoginUser): Observable<User> {
    return this.http.post<User>(`${this.endPoint}User/Login`, loginUser, this.httpOptions);
  }

  getAllUsers(): Observable<UserProfile[]> {
    return this.http.get<UserProfile[]>(this.endPoint + "User/getAllUsers");
  }

  getUserById(userId: number): Observable<updateUser> {
    return this.http.get<updateUser>(`${this.endPoint}User/getUserById/${userId}`)
    .pipe(map(result => result))
  }

  getMemberByUserId(userId: number): Observable<any> {
    return this.http.get<any>(`${this.endPoint}User/GetMemberByUserId/${userId}`, this.httpOptions);
  }

  updateUser(userId: string, user: updateUser): Observable<any> {
    return this.http.put(`${this.endPoint}User/editUser/${userId}`, user, { responseType: 'text' })
    .pipe(
      map((response: string) => {
        try {
          // Attempt to parse as JSON if the response is in JSON format
          return JSON.parse(response);
        } catch {
          // Return as plain text if not JSON
          return response;
        }
      }),
      catchError(this.handleError)
    );
  }

  private handleError(error: HttpErrorResponse) {
    if (error.error instanceof ErrorEvent) {
      console.error('An error occurred:', error.error.message);
    } else {
      console.error(
        `Backend returned code ${error.status}, ` +
        `body was: ${error.error}`);
    }
    return throwError(() => new Error('Something bad happened; please try again later.'))    
  }
  
  searchUsers(criteria: string): Observable<UserViewModel[]> {
    return this.http.get<UserViewModel[]>(`${this.endPoint}User/SearchUsers?criteria=${criteria}`);
  }

  deleteUser(userId: number): Observable<string> {
    return this.http.delete<string>(`${this.endPoint}User/deleteUser/${userId}`);
  }

  //UserType EndPoints
  addUserType(userType: UserTypeViewModel): Observable<UserTypeViewModel> {
    return this.http.post<UserTypeViewModel>(`${this.endPoint}UserType/addUserType`, userType, this.httpOptions);
  }

  getAllUserTypes(): Observable<UserTypeViewModel[]> {
    return this.http.get<UserTypeViewModel[]>(`${this.endPoint}UserType/getAllUserTypes`, this.httpOptions);
  }

  getUserTypeById(id: number): Observable<UserTypeViewModel> {
    return this.http.get<UserTypeViewModel>(`${this.endPoint}UserType/getUserTypeById/${id}`, this.httpOptions);
  }

  updateUserType(userTypeId: number, userTypeName: string): Observable<void> {
    const body = { user_Type_Name: userTypeName }; // Correctly format the body as JSON
    return this.http.put<void>(`${this.endPoint}UserType/updateUserType/${userTypeId}`, body, this.httpOptions);
  }

  deleteUserType(id: number): Observable<void> {
    return this.http.delete<void>(`${this.endPoint}UserType/deleteUserType/${id}`, this.httpOptions);
  }
}
//html
<div class="gym-manager-container">
    <div class="back-button">
      <button class="btn btn-link" (click)="goBack()">
        <i class="bi bi-arrow-left-circle"></i>Back
      </button>
    </div>
    <div class="content">
      <div class="header">
        <h1>Search Users</h1>
      </div>
  
      <input type="text" class="form-control mb-3" placeholder="Search User Types" [(ngModel)]="searchCriteria" (ngModelChange)="filterUsers()">
  
      <div class="table-responsive">
        <table class="table table-hover table-centered">
          <thead class="thead-light">
            <tr>
              <th>User ID</th>
              <th>Name</th>
              <th>Surname</th>
              <th>Actions</th>
            </tr>
          </thead>
          <tbody>
            <tr *ngFor="let user of filteredUsers">
              <td>{{ user.user_ID }}</td>
              <td>{{ user.name }}</td>
              <td>{{ user.surname }}</td>
              <td>
                <button class="btn btn-link" (click)="openModal(user)">
                  <i class="fa fa-eye"></i> View
                </button>
              </td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
  
    <div class="modal fade" id="userModal" tabindex="-1" aria-labelledby="userModalLabel" aria-hidden="true">
      <div class="modal-dialog modal-lg">
        <div class="modal-content" style="text-align: center;">
          <div class="modal-header">
            <h5 class="modal-title" id="userModalLabel"><strong>User Details</strong></h5>
          </div>
          <div class="modal-body">
            <p><strong>User ID:</strong> {{ selectedUser?.user_ID }}</p>
            <p><strong>Name:</strong> {{ selectedUser?.name }}</p>
            <p><strong>Surname:</strong> {{ selectedUser?.surname }}</p>
          </div>
          <div class="modal-footer">
            <button type="button" class="btn btn-secondary" (click)="closeModal()">Close</button>
          </div>
        </div>
      </div>
    </div>
  </div>
  
//TS
import { CommonModule } from '@angular/common';
import { Observable } from 'rxjs';
import { Component, OnInit } from '@angular/core';
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
import { Router, RouterLink } from '@angular/router';
import { UserService } from '../Services/userprofile.service';
import { UserViewModel } from '../shared/search-user';
import { Location } from '@angular/common';
declare var $: any;

@Component({
  selector: 'app-member-manager',
  standalone: true,
  imports: [CommonModule, ReactiveFormsModule, FormsModule, RouterLink],
  templateUrl: './member-manager.component.html',
  styleUrl: './member-manager.component.css'
})
export class MemberManagerComponent implements OnInit {
  searchCriteria: string = '';
  users: UserViewModel[] = [];
  filteredUsers: UserViewModel[] = [];
  selectedUser: UserViewModel | null = null;

  constructor(private router: Router, private userService: UserService, private location: Location) { }

  ngOnInit(): void {
    this.loadUsers();
  }

  loadUsers(): void {
    this.userService.searchUsers('').subscribe(users => {
      this.users = users;
      this.filteredUsers = users;
    });
  }

  filterUsers(): void {
    const term = this.searchCriteria.toLowerCase();
    if (!term) {
      this.filteredUsers = this.users;
    } else {
      this.filteredUsers = this.users.filter(user =>
        user.user_ID.toString().includes(term) ||
        user.name.toLowerCase().includes(term) ||
        user.surname.toLowerCase().includes(term)
      );
    }
  }

  openModal(user: UserViewModel): void {
    this.selectedUser = user;
    $('#userModal').modal('show');
  }

  closeModal(): void {
    $('#userModal').modal('hide');
  }

  goBack(): void {
    this.location.back();
  }
}

//css
.gym-manager-container {
  padding: 20px;
}

.back-button {
  margin-bottom: 20px;
}

.content {
  background-color: #f8f9fa;
  padding: 20px;
  border-radius: 5px;
}

.header {
  display: flex;
  justify-content: center;
  align-items: center;
  margin-bottom: 20px;
}

.table-centered thead th, 
.table-centered tbody td {
  text-align: center;
}

.table-hover tbody tr:hover {
  background-color: #f1f1f1;
}

.modal-dialog {
  max-width: 400px;
  margin: 1.75rem auto;
}

.modal-content {
  text-align: center;
  padding: 20px;
}

.modal-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.modal-body p {
  margin-bottom: 10px;
}

[Authorize(Policy = "Owner")]
[HttpGet]
[Route("SearchUsers")]
public async Task<IActionResult> SearchUsers([FromQuery] string? criteria) 
{
    try
    {
        if (string.IsNullOrEmpty(criteria)) // Check if criteria is null or empty
        {
            // If criteria is not provided, return all users
            var users = await _userManager.Users
                                         .Select(u => new SearchUserViewmodel
                                         {
                                             User_ID = u.User_ID,
                                             Name = u.Name,
                                             Surname = u.Surname
                                         })
                                         .ToListAsync();

            return Ok(users);
        }

        // If criteria is provided, filter users based on the criteria
        var filteredUsers = await _userManager.Users
                                              .Where(u => u.Name.Contains(criteria) || u.Surname.Contains(criteria))
                                              .Select(u => new SearchUserViewmodel
                                              {
                                                  User_ID = u.User_ID,
                                                  Name = u.Name,
                                                  Surname = u.Surname
                                              })
                                              .ToListAsync();

        if (filteredUsers == null || filteredUsers.Count == 0)
        {
            return NotFound("No users found.");
        }

        return Ok(filteredUsers);
    }
    catch (Exception)
    {
        return StatusCode(StatusCodes.Status500InternalServerError, "Internal Server Error. Please contact support.");
    }
}
using av_motion_api.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using System.Net.NetworkInformation;
using System.Reflection.Emit;
using System.Security.Claims;
using System.Xml.Linq;

namespace av_motion_api.Data
{
    public class AppDbContext : IdentityDbContext<User, Role, int>

    {
        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

        //set tables


        public DbSet<Attendance_List> Attendance_Lists { get; set; }
        public DbSet<Audit_Trail> Audit_Trails { get; set; }
        public DbSet<Booking> Bookings { get; set; }
        public DbSet<Booking_Time_Slot> Booking_Time_Slots { get; set; }
        public DbSet<Contract> Contracts { get; set; }
        public DbSet<Contract_History> Contract_History { get; set; }
        public DbSet<Contract_Type> Contract_Types { get; set; }
        public DbSet<Discount> Discounts { get; set; }
        public DbSet<Employee> Employees { get; set; }
        public DbSet<Employee_Type> Employee_Types { get; set; }
        public DbSet<Equipment> Equipment { get; set; }
        public DbSet<Inspection> Inspection { get; set; }
        public DbSet<Inspection_Status> Inspection_Status { get; set; }
        public DbSet<Inspection_Type> Inspection_Type { get; set; }
        public DbSet<Inventory> Inventory { get; set; }
        public DbSet<Lesson_Plan> Lesson_Plans { get; set; }

        public DbSet<Lesson_Plan_Workout> lesson_Plan_Workout { get; set; }

        public DbSet<Member> Members { get; set; }
        public DbSet<Order> Orders { get; set; }
        public DbSet<Order_Line> Order_Lines { get; set; }
        public DbSet<Order_Status> Order_Status { get; set; }
        public DbSet<Outstanding_Payment> Outstanding_Payments { get; set; }
        public DbSet<Owner> Owners { get; set; }
        public DbSet<Payment> Payments { get; set; }
        public DbSet<Payment_Method> Payment_Methods { get; set; }
        public DbSet<Payment_Type> Payment_Types { get; set; }
        public DbSet<Price> Prices { get; set; }
        public DbSet<Product> Products { get; set; }
        public DbSet<Product_Category> Product_Categories { get; set; }
        public DbSet<Received_Supplier_Order> Received_Supplier_Orders { get; set; }
        public DbSet<Received_Supplier_Order_Line> Received_Supplier_Order_Lines { get; set; }
        public DbSet<Report> Reports { get; set; }
        public DbSet<Reward> Rewards { get; set; }

        public DbSet<Role> Roles { get; set; }
        public DbSet<Reward_Member> Reward_Members { get; set; }
        public DbSet<Reward_Type> Reward_Types { get; set; }
        public DbSet<Shift> Shifts { get; set; }
        public DbSet<Supplier> Suppliers { get; set; }
        public DbSet<Supplier_Order> Supplier_Orders { get; set; }
        public DbSet<Supplier_Order_Line> Supplier_Order_Lines { get; set; }
        public DbSet<Time_Slot> Time_Slots { get; set; }

        public DbSet<User> Users { get; set; }
        public DbSet<User_Status> Users_Status{ get; set; }

        public DbSet<User_Type> User_Types { get; set; }
        public DbSet<VAT> VAT { get; set; }

        public DbSet<Workout_Category> Workout_Category { get; set; }
        public DbSet<Workout> Workout { get; set; }
  
        public DbSet<Write_Off> Write_Offs { get; set; }



        protected override void OnModelCreating(ModelBuilder builder)
        {
            //Renaming of Default asp Tables
            builder.Entity<User>().ToTable("Users");
            builder.Entity<IdentityUserRole<int>>().ToTable("User_Roles");
            builder.Entity<IdentityUserLogin<int>>().ToTable("User_Logins");
            builder.Entity<Role>().ToTable("Roles");
            builder.Entity<IdentityRoleClaim<int>>().ToTable("Role_Claims");
            builder.Entity<IdentityUserClaim<int>>().ToTable("User_Claims");
            builder.Entity<IdentityUserToken<int>>().ToTable("Tokens");

            //Validation fix for database, specifying coulm types to be type decimal
            builder.Entity<Contract>()
           .Property(c => c.Initial_Fee)
           .HasColumnType("decimal(18, 2)");

            builder.Entity<VAT>()
            .Property(v => v.VAT_Percentage)
            .HasColumnType("decimal(18, 2)");

            builder.Entity<Order>()
           .Property(o => o.Total_Price)
           .HasColumnType("decimal(18, 2)");

            builder.Entity<Outstanding_Payment>()
           .Property(op => op.Amount_Due)
           .HasColumnType("decimal(18, 2)");

            builder.Entity<Outstanding_Payment>()
           .Property(op => op.Late_Fee)
           .HasColumnType("decimal(18, 2)");

            builder.Entity<Payment>()
           .Property(pay => pay.Amount)
           .HasColumnType("decimal(18, 2)");

            builder.Entity<Price>()
           .Property(pr => pr.Unit_Price)
           .HasColumnType("decimal(18, 2)");

            builder.Entity<Supplier_Order>()
           .Property(so => so.Total_Price)
           .HasColumnType("decimal(18, 2)");

            builder.Entity<Discount>()
           .Property(d => d.Discount_Percentage)
           .HasColumnType("decimal(18, 2)");

            //Delete cascade error fix
            builder.Entity<Payment>()
            .HasOne(p => p.Payment_Type)
            .WithMany()
            .HasForeignKey(p => p.Payment_Type_ID)
            .OnDelete(DeleteBehavior.NoAction);

            builder.Entity<Supplier_Order_Line>()
             .HasOne(s => s.Product)
             .WithMany()
             .HasForeignKey(s => s.Product_ID)
             .OnDelete(DeleteBehavior.NoAction); 

            builder.Entity<Supplier_Order_Line>()
            .HasOne(s => s.Supplier)
            .WithMany()
            .HasForeignKey(s => s.Supplier_ID)
            .OnDelete(DeleteBehavior.NoAction);

            builder.Entity<Received_Supplier_Order_Line>()
            .HasOne(r => r.Received_Supplier_Order)
            .WithMany()
            .HasForeignKey(r => r.Received_Supplier_Order_ID)
            .OnDelete(DeleteBehavior.NoAction); 

            builder.Entity<Received_Supplier_Order_Line>()
            .HasOne(r => r.Supplier_Order)
            .WithMany()
            .HasForeignKey(r => r.Supplier_Order_ID)
            .OnDelete(DeleteBehavior.NoAction); 

            builder.Entity<Received_Supplier_Order_Line>()
            .HasOne(r => r.Product)
            .WithMany()
            .HasForeignKey(r => r.Product_ID)
            .OnDelete(DeleteBehavior.NoAction);

            builder.Entity<Outstanding_Payment>()
            .HasOne(op => op.Member)
            .WithMany()
            .HasForeignKey(op => op.Member_ID)
            .OnDelete(DeleteBehavior.NoAction); 

            builder.Entity<Outstanding_Payment>()
            .HasOne(op => op.Payment)
            .WithMany()
            .HasForeignKey(op => op.Payment_ID)
            .OnDelete(DeleteBehavior.NoAction);


            builder.Entity<Booking>()
            .HasOne(b => b.Member)
            .WithMany()
            .HasForeignKey(b => b.Member_ID)
            .OnDelete(DeleteBehavior.NoAction);

            builder.Entity<Reward_Member>()
            .HasOne(rm => rm.Member)
            .WithMany()
            .HasForeignKey(rm => rm.Member_ID)
            .OnDelete(DeleteBehavior.NoAction);

            builder.Entity<Reward_Member>()
            .HasOne(rm => rm.Reward)
            .WithMany()
            .HasForeignKey(rm => rm.Reward_ID)
            .OnDelete(DeleteBehavior.NoAction);

           builder.Entity<Booking_Time_Slot>()
            .HasOne(bts => bts.Booking)
            .WithMany()
            .HasForeignKey(bts => bts.Booking_ID)
            .OnDelete(DeleteBehavior.NoAction);

           builder.Entity<Booking_Time_Slot>()
            .HasOne(bts => bts.Time_Slot)
            .WithMany()
            .HasForeignKey(bts => bts.Time_Slot_ID)
            .OnDelete(DeleteBehavior.NoAction);

            builder.Entity<Attendance_List>()
            .HasOne(b => b.Time_Slot)
            .WithMany()
            .HasForeignKey(b => b.Time_Slot_ID)
            .OnDelete(DeleteBehavior.NoAction);

            builder.Entity<Lesson_Plan_Workout>()
            .HasOne(lpw => lpw.Workout)
            .WithMany()
            .HasForeignKey(lpw => lpw.Workout_ID)
            .OnDelete(DeleteBehavior.NoAction);

            builder.Entity<Lesson_Plan_Workout>()
            .HasOne(lpw => lpw.Lesson_Plan)
            .WithMany()
            .HasForeignKey(lpw => lpw.Lesson_Plan_ID)
            .OnDelete(DeleteBehavior.NoAction);

            builder.Entity<Workout>()
           .HasOne(w => w.Workout_Category)
           .WithMany()
           .HasForeignKey(w => w.Workout_Category_ID)
           .IsRequired()
           .OnDelete(DeleteBehavior.Restrict);

            base.OnModelCreating(builder);



            var Contract_Types = new Contract_Type[]
            {
                new Contract_Type { Contract_Type_ID = 1, Contract_Type_Name = "3-Month Membership", Contract_Description = "Three-month gym membership contract" },              
            };
            builder.Entity<Contract_Type>().HasData(Contract_Types);



            var Discounts = new Discount[]
            {
                new Discount { Discount_ID = 1, Discount_Percentage = 10.00m, Discount_Date = new DateTime(2024, 4, 10) }

            };
            builder.Entity<Discount>().HasData(Discounts);


            var Employee_Types = new Employee_Type[]
            {
                new Employee_Type { Employee_Type_ID = 1, Job_Title = "Administrator", Job_Description = "Responsible for managing administrative tasks and operations" }
            };
            builder.Entity<Employee_Type>().HasData(Employee_Types);

            var Inspection_Statuses = new Inspection_Status[]
            {
                new Inspection_Status { Inspection_Status_ID = 1, Inspection_Status_Description= "Pending" }


            };
            builder.Entity<Inspection_Status>().HasData(Inspection_Statuses);

            var Inspection_Types = new Inspection_Type[]
            {
                new Inspection_Type { Inspection_Type_ID = 1, Inspection_Type_Name = "Safety Inspection", Inspection_Type_Criteria = "Ensure compliance with safety standards" }
    
            };
            builder.Entity<Inspection_Type>().HasData(Inspection_Types);

            var Membership_Statuses = new Membership_Status[]
            {
                new Membership_Status { Membership_Status_ID = 1, Membership_Status_Description = "Active" }

            };
            builder.Entity<Membership_Status>().HasData(Membership_Statuses);

            var Newsletters = new Newsletter[]
            {
                new Newsletter { Newsletter_ID = 1, Newsletter_Title = "Fitness Tips", Newsletter_Photo = "fitness_tips.jpg", Newsletter_Description = "Stay updated with our latest fitness tips!" }

            };
            builder.Entity<Newsletter>().HasData(Newsletters);

            var Payment_Methods = new Payment_Method[]
            {
                new Payment_Method { Payment_Method_ID = 1, Payment_Method_Name = "Card" },
                new Payment_Method { Payment_Method_ID = 2, Payment_Method_Name = "EFT" },

  
            };
            builder.Entity<Payment_Method>().HasData(Payment_Methods);

            var Payment_Types = new Payment_Type[]
            {
                new Payment_Type { Payment_Type_ID = 1, Payment_Type_Name = "Online Payment" },
                new Payment_Type { Payment_Type_ID = 2, Payment_Type_Name = "Cash Payment" },
             
            };
            builder.Entity<Payment_Type>().HasData(Payment_Types);

            var Product_Categories = new Product_Category[]
            {
                new Product_Category { Product_Category_ID = 1, Category_Name = "Clothing", Category_Description = "Fitness clothing for various activities" }
            };
            builder.Entity<Product_Category>().HasData(Product_Categories);

            var Reports = new Report[]
            {
                new Report { Report_ID = 1, Report_Name = "Monthly Sales Report", Report_Description = "Report summarizing monthly sales data", Generated_Date = new DateTime(2024, 4, 10) }
            };
            builder.Entity<Report>().HasData(Reports);

            var Reward_Types = new Reward_Type[]
            {
                new Reward_Type { Reward_Type_ID = 1, Reward_Type_Name = "Membership Renewal Discount", Reward_Criteria = "Receive a discount on membership renewal after completing a certain number of workouts" }

            };
            builder.Entity<Reward_Type>().HasData(Reward_Types);

            var Suppliers = new Supplier[]
            {
                new Supplier { Supplier_ID = 1, Name = "FitnessGear", Contact_Number = "1234567890", Email_Address = "info@fitnessgear.com", Physical_Address = "123 Fitness Street, Cityville, South Africa" }
            };
            builder.Entity<Supplier>().HasData(Suppliers);


            var userStatus = new User_Status[]
            {
                new User_Status { User_Status_ID = 1, User_Status_Description = "Actived" },
                new User_Status { User_Status_ID = 2, User_Status_Description = "Deactivated" },
                new User_Status { User_Status_ID = 3, User_Status_Description = "Idle" }
            };
            builder.Entity<User_Status>().HasData(userStatus);

            var userTypes = new User_Type[]
    {
                new User_Type { User_Type_ID = 1, User_Type_Name = "Owner" },
                new User_Type { User_Type_ID = 2, User_Type_Name = "Employee" },
                new User_Type { User_Type_ID = 3, User_Type_Name = "Member" }
    };
            builder.Entity<User_Type>().HasData(userTypes);

            var Users = new User[]                
            {
                new User 
                {
                    User_ID = 1,
                    Id = 1,
                    Name = "Don",
                    Surname = "Percival",
                    ID_Number = "0203057644931",
                    Email = "DonPercival@gmail.com",
                    Physical_Address = "456 Oak Avenue",
                    PhoneNumber = "0734457681",
                    Photo = "DonProfilePic.jpg",
                    PasswordHash = "AEWR54Q35H5T4HRGRGQ",
                    Date_of_Birth = new DateTime(1994,10,11),
                    User_Type_ID =1,
                    User_Status_ID =1
                },
                new User
                {
                    User_ID = 2,
                    Id = 2,
                    Name = "Barbra",
                    Surname = "Gordon",
                    ID_Number = "1220231231312",
                    Email = "barbragordon@gmail.com",
                    Physical_Address = "456 Oak Avenue",
                    PhoneNumber = "9876543210",
                    Photo = "barbra_photo.jpg",
                    PasswordHash = "HJDKL3948SJDF3JSHFD",
                    Date_of_Birth = new DateTime(1985, 5, 15),
                    User_Type_ID =2,
                    User_Status_ID =1
                },

                new User

                {
                User_ID = 3,
                Id = 3,
                Name = "Jane",
                Surname = "Smith",
                ID_Number = "1220231231312",
                Email = "JaneSmith@gmail.com",
                Physical_Address = "456 Oak Avenue",
                PhoneNumber = "9876543210",
                Photo = "jane_smith_photo.jpg",    
                PasswordHash = "JKLFSF34JKLRE983JFSD",
                Date_of_Birth = new DateTime(1985, 5, 15),
                User_Type_ID =3,
                User_Status_ID =1
                }


            };
        builder.Entity<User>().HasData(Users);








            var Contracts = new Contract[]
            {
               new Contract { Contract_ID = 1, Subscription_Date = new DateTime(2023, 1, 1), Expiry_Date = new DateTime(2023, 4, 1), Terms_Of_Agreement = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", Approval_Status = true, Approval_Date = new DateTime(2023, 1, 1), Initial_Fee = 100.00m, Contract_Type_ID = 1, Payment_Type_ID = 1 }
            };
            builder.Entity<Contract>().HasData(Contracts);


            var ContractHistories = new Contract_History[]
            {
                new Contract_History { Contract_History_ID = 1, Modification_Date = new DateTime(2023, 5, 15), Previous_Terms = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", Updated_Terms = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut enim ad minim veniam.", Reasons_For_Changes = "To include additional benefits for members.", Contract_ID = 1 },

            };
            builder.Entity<Contract_History>().HasData(ContractHistories);


            var VAT = new VAT[]
            {
               new VAT { VAT_ID = 1, VAT_Percentage = 15.00m, VAT_Date = new DateTime(2024, 1, 1) }
            };
            builder.Entity<VAT>().HasData(VAT);


            var Audit_Trails = new Audit_Trail[]
            {
                new Audit_Trail { Audit_Trail_ID = 1, Action = "Action description", Timestamp = DateTime.Now }

            };
            builder.Entity<Audit_Trail>().HasData(Audit_Trails);

            var Shifts = new Shift[]
            {
              new Shift { Shift_ID = 1, Start_Time = new TimeSpan(8, 0, 0), End_Time = new TimeSpan(12, 0, 0) }

            };
            builder.Entity<Shift>().HasData(Shifts);


            var Employees = new Employee[]
            {
               new Employee { Employee_ID = 1, Employment_Date = new DateTime(2024, 4, 12), Employee_Type_ID = 1 , Shift_ID =1, User_ID = 2 }
            };
            builder.Entity<Employee>().HasData(Employees);


            var Members = new Member[]
            {
               new Member { Member_ID = 1, Contract_ID = 1,User_ID = 3 }
               
            };
            builder.Entity<Member>().HasData(Members);


            var AttendanceLists = new Attendance_List[]
            {

                new Attendance_List { Attendance_ID = 1, Number_Of_Bookings = 1, Members_Present = 10, Members_Absent = 5, Time_Slot_ID = 1}
            };
            builder.Entity<Attendance_List>().HasData(AttendanceLists);


            var Bookings = new Booking[]
            {
                new Booking { Booking_ID = 1,  Member_ID = 1}
            };
            builder.Entity<Booking>().HasData(Bookings);

            var workoutcategories = new Workout_Category[]
            {
                new Workout_Category { Workout_Category_ID = 1, Workout_Category_Name = "Cardio", Workout_Category_Description = "Cardio workouts to improve endurance and burn calories." },
                new Workout_Category { Workout_Category_ID = 2, Workout_Category_Name = "Strength", Workout_Category_Description = "Strength training workouts to build muscle and increase strength." },
                new Workout_Category { Workout_Category_ID = 3, Workout_Category_Name = "Flexibility", Workout_Category_Description = "Flexibility workouts to improve range of motion and reduce injury risk." }
            };
            builder.Entity<Workout_Category>().HasData(workoutcategories);


            var workouts = new Workout[]
            {
                    new Workout
                    {
                        Workout_ID = 1,
                        Workout_Name = "Cardio Blast",
                        Workout_Description = "High-intensity cardio workout to burn calories and improve endurance.",
                        Sets = 4,
                        Reps = 10,
                        Workout_Category_ID = 1
                    },
                    new Workout
                    {
                        Workout_ID = 2,
                        Workout_Name = "Strength Training",
                        Workout_Description = "Build muscle strength and endurance.",
                        Sets = 3,
                        Reps = 12,
                        Workout_Category_ID = 2
                    },
                    new Workout
                    {
                        Workout_ID = 3,
                        Workout_Name = "Flexibility Routine",
                        Workout_Description = "Improve your flexibility with this stretching routine.",
                        Sets = 2,
                        Reps = 15,
                        Workout_Category_ID = 3
                    }
            };
            builder.Entity<Workout>().HasData(workouts);


            var Lesson_Plans = new Lesson_Plan[]
            {
                new Lesson_Plan { Lesson_Plan_ID = 1, Program_Name = "Base", Program_Description = "Base program description", }

            };
            builder.Entity<Lesson_Plan>().HasData(Lesson_Plans);


            var orderStatuses = new Order_Status[]
            {
                new Order_Status { Order_Status_ID = 1, Order_Status_Description = "Pending" },
            };
            builder.Entity<Order_Status>().HasData(orderStatuses);


            var Orders = new Order[]
            {
               new Order { Order_ID = 1, Order_Date = new DateTime(2024, 4, 12), Order_Details = "Example order details", Total_Price = 100.00m, Member_ID = 1, Order_Status_ID = 1 }
            };
            builder.Entity<Order>().HasData(Orders);


            var Outstanding_Payments = new Outstanding_Payment[]
            {
               new Outstanding_Payment { Outstanding_Payment_ID = 1, Due_Date = new DateTime(2024, 4, 12), Amount_Due = 50.00m, Late_Fee = 0.00m, Member_ID = 1, Payment_ID = 1 }
            };
            builder.Entity<Outstanding_Payment>().HasData(Outstanding_Payments);


            var Owners = new Owner[]
            {
               new Owner { Owner_ID = 1, User_ID = 1 }
            };
            builder.Entity<Owner>().HasData(Owners);


            var Payments = new Payment[]
            {
              new Payment { Payment_ID = 1, Amount = 50.00m, Payment_Date = new DateTime(2024, 4, 12), Order_ID = 1, Payment_Type_ID = 1, Payment_Method_ID = 1 }
            };
            builder.Entity<Payment>().HasData(Payments);


            var Products = new Product[]
            {
               new Product { Product_ID = 1, Product_Name = "T-Shirt", Product_Description = "Cotton Shirt sleevless", Create_Date = new DateTime(2024, 4, 12), Last_Update_Date = new DateTime(2024, 4, 12), IsActive = true,Size = "XS" ,Product_Category_ID = 1, Supplier_ID = 1 }
            };
            builder.Entity<Product>().HasData(Products);


            var prices = new Price[]
            {
                new Price { Price_ID = 1, Unit_Price = 50.00m, Product_ID = 1 }
            };
            builder.Entity<Price>().HasData(prices);


            var OrderLines = new Order_Line[]
            {
                new Order_Line { Order_Line_ID = 1, Order_ID = 1, Product_ID = 1 }

            };
            builder.Entity<Order_Line>().HasData(OrderLines);


            var Received_Supplier_Orders = new Received_Supplier_Order[]
            {
                new Received_Supplier_Order { Received_Supplier_Order_ID = 1, Supplies_Received_Date = new DateTime(20, 04, 10) }
            };

            builder.Entity<Received_Supplier_Order>().HasData(Received_Supplier_Orders);



            var Received_Supplier_Order_Lines = new Received_Supplier_Order_Line[]
            {
                new Received_Supplier_Order_Line { Received_Supplier_Order_Line_ID = 1,Received_Supplier_Order_ID = 1,Supplier_Order_ID = 1,Product_ID = 1,Received_Supplies_Quantity = 10 }
            };

            builder.Entity<Received_Supplier_Order_Line>().HasData(Received_Supplier_Order_Lines);

            var Rewards = new Reward[]
            {
                new Reward { Reward_ID = 1, IsPosted = false, Reward_Issue_Date = new DateTime(2024, 4, 10), Reward_Type_ID = 1 }

            };
            builder.Entity<Reward>().HasData(Rewards);

            var Reward_Members = new Reward_Member[]
            {
                new Reward_Member{ Reward_Member_ID = 1, IsRedeemed = false, Member_ID = 1, Reward_ID = 1}
            };
            builder.Entity<Reward_Member>().HasData(Reward_Members);

            var Roles = new Role[]
            {
                new Role{ Id = 1, Name = "Owner", NormalizedName= "OWNER", isEditable = false},
                new Role{ Id = 2, Name = "Employee", NormalizedName= "EMPLOYEE", isEditable =true},
                new Role{ Id = 3, Name = "Member", NormalizedName= "MEMBER", isEditable =true}
            };
            builder.Entity<Role>().HasData(Roles);


            int claimId = 1;
            //Owner Claims
            //for each admin claim
            var ownerClaims = new Claim[]

            {
                new Claim("Booking Manager", "Create"),
                new Claim("Booking Manager", "Read"),
                new Claim("Booking Manager", "Update"),
                new Claim("Booking Manager", "Delete"),

                new Claim("Equipment Manager", "Create"),
                new Claim("Equipment Manager", "Read"),
                new Claim("Equipment Manager", "Update"),
                new Claim("Equipment Manager", "Delete"),

                new Claim("Employee Manager", "Create"),
                new Claim("Employee Manager", "Read"),
                new Claim("Employee Manager", "Update"),
                new Claim("Employee Manager", "Delete"),

                new Claim("Inventory Manager", "Create"),
                new Claim("Inventory Manager", "Read"),
                new Claim("Inventory  Manager", "Update"),
                new Claim("Inventory Manager", "Delete"),

                new Claim("Gym Manager", "Create"),
                new Claim("Gym Manager", "Read"),
                new Claim("Gym  Manager", "Update"),
                new Claim("Gym Manager", "Delete"),
            };
            //create a refrence of it in the Role Claims table
            foreach (var claim in ownerClaims) 
            {
                builder.Entity<IdentityRoleClaim<int>>().HasData(new IdentityRoleClaim<int>
                { 
                   Id = claimId++,
                   RoleId = Roles[0].Id,
                   ClaimType = claim.Type,
                   ClaimValue = claim.Value
                });
            }

            //Employee Claims , they are admin too but just for separation        
            //for each employee claim
            var employeeClaims = new Claim[]
            {
                new Claim("Booking Manager", "Create"),
                new Claim("Booking Manager", "Read"),
                new Claim("Booking Manager", "Update"),
                new Claim("Booking Manager", "Delete"),

                new Claim("Equipment Manager", "Create"),
                new Claim("Equipment Manager", "Read"),
                new Claim("Equipment Manager", "Update"),
                new Claim("Equipment Manager", "Delete"),

                new Claim("Employee Manager", "Read"),
                new Claim("Employee Manager", "Update"),

                new Claim("Inventory Manager", "Create"),
                new Claim("Inventory Manager", "Read"),
                new Claim("Inventory Manager", "Update"),
                new Claim("Inventory Manager", "Delete"),
            };
            //create a refrence of it in the Role Claims table
            foreach (var claim in employeeClaims)
            {
                builder.Entity<IdentityRoleClaim<int>>().HasData(new IdentityRoleClaim<int>
                {
                    Id = claimId++,
                    RoleId = Roles[1].Id,
                    ClaimType = claim.Type,
                    ClaimValue = claim.Value
                });
            }

            var memberClaims = new Claim[]
            {
                new Claim("Booking Interface", "Create"),
                new Claim("Booking Interface", "Read"),
                new Claim("Booking Interface", "Update"),
                new Claim("Booking Interface", "Delete"),

                new Claim("Profile", "Create"),
                new Claim("Profile", "Read"),
                new Claim("Profile", "Update"),

            };
            //create a refrence of it in the Role Claims table
            foreach (var claim in memberClaims)
            {
                builder.Entity<IdentityRoleClaim<int>>().HasData(new IdentityRoleClaim<int>
                {
                    Id = claimId++,
                    RoleId = Roles[2].Id,
                    ClaimType = claim.Type,
                    ClaimValue = claim.Value
                });
            }

            var Supplier_Orders = new Supplier_Order[]
            {
                new Supplier_Order { Supplier_Order_ID = 1, Date = new DateTime(2024, 4, 10), Supplier_Order_Details = "Ordered 50 units of dumbbells and 20 yoga mats", Total_Price = 1500.00m, Supplier_ID = 1, Owner_ID = 1 }

            };
            builder.Entity<Supplier_Order>().HasData(Supplier_Orders);

            var Supplier_Order_Lines = new Supplier_Order_Line[]
            {
                new Supplier_Order_Line { Supplier_Order_Line_ID = 1, Supplier_Qauntity = 12, Supplier_ID = 1, Product_ID = 1 }

            };
            builder.Entity<Supplier_Order_Line>().HasData(Supplier_Order_Lines);

            var Inventory = new Inventory[]
            {
                new Inventory { Inventory_ID = 1, Inventory_Item_Category = "Clothes", Inventory_Item_Name = "Men's Dry-fit Tops", Inventory_Item_Quantity = 20, Inventory_Item_Photo = "dry_fit_tops.jpg", Received_Supplier_Order_ID = 1, Supplier_ID = 1 }

            };
            builder.Entity<Inventory>().HasData(Inventory);

            var Equipments = new Equipment[]
            {
                
                 new Equipment{ Equipment_ID = 1, Equipment_Name = "Treadmill", Equipment_Description = "A motorized device used for running or walking while staying in one place." }
                
            };
            builder.Entity<Equipment>().HasData(Equipments);

            var Inspections = new Inspection[]
            {
                new Inspection { Inspection_ID = 1, Inspection_Date = new DateTime(2024, 4, 12),Inspection_Notes = "Holes in AVS pants" , Equipment_ID = 1,  Inspection_Type_ID = 1, Inspection_Status_ID = 1 }

            };
            builder.Entity<Inspection>().HasData(Inspections);

            var Booking_Time_Slots = new Booking_Time_Slot[]
            {
                new Booking_Time_Slot{Booking_Time_Slot_ID = 1, Booking_ID = 1,Time_Slot_ID = 1}
            };
            builder.Entity<Booking_Time_Slot>();


            var Time_Slots = new Time_Slot[]
            {

                new Time_Slot{Time_Slot_ID = 1,Slot_Date =  new DateTime(2024, 4, 12)  , Slot_Time = DateTime.Parse("11:00:00"), Availability = true, Lesson_Plan_ID= 1, Employee_ID=1}

            };
            builder.Entity<Time_Slot>().HasData(Time_Slots);


            var Write_Offs = new Write_Off[]
            {
                new Write_Off { Write_Off_ID = 1, Date = new DateTime(2024, 4, 12), Write_Off_Reason = "Expired items", Inventory_ID = 1 }

            };
            builder.Entity<Write_Off>().HasData(Write_Offs);


            var lessonPlanWorkOuts = new Lesson_Plan_Workout[]
            {
                new Lesson_Plan_Workout {Lesson_Plan_Workout_ID =1, Lesson_Plan_ID = 1, Workout_ID = 1}
            };
            builder.Entity<Lesson_Plan_Workout>().HasData(lessonPlanWorkOuts);

        }

    }
}

using av_motion_api.Data;
using av_motion_api.Models;
using av_motion_api.ViewModels;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using System.Data;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using System.Net;
using System.Net.Mail;
using Microsoft.AspNetCore.Cors;
using System.Text.RegularExpressions;
using SendGrid.Helpers.Mail;
using SendGrid;

namespace av_motion_api.Controllers
{
    [Route("api/[controller]")]
    [EnableCors("AllowAll")]
    [ApiController]
    public class UserController : ControllerBase
    {
        private readonly UserManager<User> _userManager;
        private readonly IUserClaimsPrincipalFactory<User> _claimsPrincipalFactory;
        private readonly IConfiguration _configuration;
        private readonly AppDbContext _appDbContext;
        private readonly RoleManager<Role> _roleManager;
        private readonly ILogger<UserController> _logger;

        public UserController(AppDbContext context, UserManager<User> userManager, IUserClaimsPrincipalFactory<User> claimsPrincipalFactory, IConfiguration configuration, RoleManager<Role> roleManager, ILogger<UserController> logger)
        {
            _appDbContext = context;
            _userManager = userManager;
            _claimsPrincipalFactory = claimsPrincipalFactory;
            _configuration = configuration;
            _roleManager = roleManager;
            _logger = logger;
        }

        ////addUser
        //[HttpPost]
        //[Route("Register")]
        //public async Task<IActionResult> Register(UserViewModel uvm)
        //{
        //    var user = await _userManager.FindByEmailAsync(uvm.Email);
        //    int lastUserID = _appDbContext.Users
        //                     .OrderByDescending(u => u.User_ID)
        //                     .Select(u => u.User_ID)
        //                     .FirstOrDefault();

        //    if (user == null)
        //    {
        //        user = new User
        //        {
        //            User_ID = lastUserID + 1,
        //            Name = uvm.Name,
        //            Surname = uvm.Surname,
        //            UserName = uvm.Email,
        //            Email = uvm.Email,
        //            PasswordHash = uvm.Password,
        //            User_Status_ID = uvm.User_Status_ID,
        //            User_Type_ID = uvm.User_Type_ID,
        //            PhoneNumber = uvm.PhoneNumber,
        //            Date_of_Birth = uvm.Date_of_Birth,
        //            ID_Number = uvm.Id_Number,
        //            Physical_Address = uvm.Physical_Address,
        //            Photo = uvm.Photo
        //        };

        //        IdentityResult result = await _userManager.CreateAsync(user, uvm.Password);

        //        if (result.Succeeded)
        //        {
        //            // Assign role based on User_Type_ID
        //            string roleName = GetRoleNameByUserType(uvm.User_Type_ID);
        //            if (!string.IsNullOrEmpty(roleName))
        //            {
        //                var roleResult = await _userManager.AddToRoleAsync(user, roleName);
        //                if (!roleResult.Succeeded)
        //                {
        //                    return BadRequest(roleResult.Errors);
        //                }
        //            }
        //        }
        //        else
        //        {
        //            return StatusCode(StatusCodes.Status500InternalServerError, "Internal Server Error. Please contact support.");
        //        }
        //    }
        //    else
        //    {
        //        return Forbid("Account already exists.");
        //    }

        //    return Ok();
        //}

        //private string GetRoleNameByUserType(int userTypeId)
        //{
        //    return userTypeId switch
        //    {
        //        1 => "Administrator",
        //        2 => "Employee",
        //        3 => "Member",
        //        _ => string.Empty,
        //    };
        //}

        //addUser
        [HttpPost]
        [DisableRequestSizeLimit]
        [Route("Register")]
        public async Task<IActionResult> Register([FromForm] UserViewModel uvm)
        {
            try
            {
                var formCollection = await Request.ReadFormAsync();
                var photo = formCollection.Files.FirstOrDefault();

                var user = await _userManager.FindByEmailAsync(uvm.Email);
                int lastUserID = _appDbContext.Users
                                 .OrderByDescending(u => u.User_ID)
                                 .Select(u => u.User_ID)
                                 .FirstOrDefault();

                // Validate Phone Number Pattern
                var phoneNumberPattern = @"^\d{10}$";

                bool isValidPhoneNumber = Regex.IsMatch(uvm.PhoneNumber, phoneNumberPattern);

                if (!isValidPhoneNumber) return BadRequest("Enter valid 10-digit phone number.");

                // Validate South African ID number
                if (!IsValidSouthAfricanIDNumber(uvm.Id_Number, uvm.Date_of_Birth))
                {
                    return BadRequest("Enter a valid South African ID number.");
                }

                if (user == null)
                {
                    if (photo != null && photo.Length > 0)
                    {
                        using (var memoryStream = new MemoryStream())
                        {
                            await photo.CopyToAsync(memoryStream);
                            var fileBytes = memoryStream.ToArray();
                            string base64Image = Convert.ToBase64String(fileBytes);

                            user = new User
                            {
                                User_ID = lastUserID + 1,
                                Name = uvm.Name,
                                Surname = uvm.Surname,
                                UserName = uvm.Email,
                                Email = uvm.Email,
                                PasswordHash = _userManager.PasswordHasher.HashPassword(null, uvm.Password),
                                User_Status_ID = uvm.User_Status_ID,
                                User_Type_ID = uvm.User_Type_ID,
                                PhoneNumber = uvm.PhoneNumber,
                                Date_of_Birth = uvm.Date_of_Birth,
                                ID_Number = uvm.Id_Number,
                                Physical_Address = uvm.Physical_Address,
                                Photo = base64Image // Store the base64 string of the photo
                            };

                            IdentityResult result = await _userManager.CreateAsync(user);

                            if (result.Succeeded)
                            {
                                // Assign role based on User_Type_ID
                                string roleName = GetRoleNameByUserType(uvm.User_Type_ID);
                                if (!string.IsNullOrEmpty(roleName))
                                {
                                    var roleResult = await _userManager.AddToRoleAsync(user, roleName);
                                    if (!roleResult.Succeeded)
                                    {
                                        return BadRequest(new { Status = "Error", Errors = roleResult.Errors });
                                    }
                                }


                                return Ok(new { Status = "Success", Message = "Your profile has been created successfully!" });
                            }
                            else
                            {
                                return StatusCode(StatusCodes.Status500InternalServerError, result.Errors.FirstOrDefault()?.Description);
                            }
                        }
                    }
                    else
                    {
                        return BadRequest("Photo is required.");
                    }
                }
                else
                {
                    return Forbid("User already exists.");
                }
            }
            catch (DbUpdateException dbEx)
            {
                return StatusCode(StatusCodes.Status500InternalServerError, dbEx.InnerException?.Message ?? "An error occurred while processing your request.");
            }
            catch (Exception ex)
            {
                return StatusCode(StatusCodes.Status500InternalServerError, "An error occurred while processing your request.");
            }
        }

        // Method to validate South African ID number
        private bool IsValidSouthAfricanIDNumber(string idNumber, DateTime dateOfBirth)
        {
            // Check if the ID number is exactly 13 digits long
            if (idNumber.Length != 13 || !long.TryParse(idNumber, out _))
            {
                return false;
            }

            // Validate date of birth (first six digits)
            string dateOfBirthPart = idNumber.Substring(0, 6);
            if (!DateTime.TryParseExact(dateOfBirthPart, "yyMMdd", null, System.Globalization.DateTimeStyles.None, out DateTime parsedDate))
            {
                return false;
            }

            // Check if the last two digits of the ID number match the last two digits of the year of birth
            if (parsedDate.Year % 100 != dateOfBirth.Year % 100)
            {
                return false;
            }

            // If it passes the length, date of birth, and year checks, it is considered valid
            return true;
        }


        private string GetRoleNameByUserType(int userTypeId)
        {
            return userTypeId switch
            {
                1 => "Administrator",
                2 => "Employee",
                3 => "Member",
                _ => string.Empty,
            };
        }

        [HttpPost]
        [Route("Login")]
        public async Task<ActionResult> Login(LoginViewModel lv)
        {
            var user = await _userManager.FindByNameAsync(lv.Email);

            if (user != null && await _userManager.CheckPasswordAsync(user, lv.Password))
            {
                try
                {
                    var principal = await _claimsPrincipalFactory.CreateAsync(user);
                    return await GenerateJWTToken(user);
                }
                catch (Exception)
                {

                    return StatusCode(StatusCodes.Status500InternalServerError, "Internal Server Error. Please contact support.");
                }
            }
            else
            {
                return NotFound("Incorrect email or password, Please Try Again");
            }
        }

        [HttpGet]
        private async Task<ActionResult> GenerateJWTToken(User user)
        {
            var role = await _userManager.GetRolesAsync(user);
            IdentityOptions _identityOptions = new IdentityOptions();
            // Create JWT Token
            var claims = new List<Claim>
            {
                new Claim(JwtRegisteredClaimNames.Sub, user.Email),
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName),
                // Add user ID claim
                new Claim("userId", user.Id.ToString()),

                new Claim("User_Type_ID", user.User_Type_ID.ToString()),

            };

            if (role.Count() > 0)
            {
                claims.Add(new Claim(_identityOptions.ClaimsIdentity.RoleClaimType, role.FirstOrDefault()));
            }

            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Tokens:Key"]));
            var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

            var token = new JwtSecurityToken(
               issuer:  _configuration["Tokens:Issuer"],
               audience: _configuration["Tokens:Audience"],
               claims: claims,
               signingCredentials: credentials,
               expires: DateTime.UtcNow.AddHours(3)
            );

            return Created("", new
            {

                token = new JwtSecurityTokenHandler().WriteToken(token),
                user = user.UserName,

                userTypeId = user.User_Type_ID,
                // Include user ID in the response
                userId = user.Id
            });
        }

        [HttpPost]
        [Route("ChangePassword")]
        public async Task<IActionResult> ChangePassword(int id, ChangePasswordViewModel cpvm)
        {
            var user = await _userManager.FindByIdAsync(id.ToString());
            if (user == null)
            {
                return NotFound("User not found.");
            }

            var result = await _userManager.ChangePasswordAsync(user, cpvm.CurrentPassword, cpvm.NewPassword);
            if (result.Succeeded)
            {
                return Ok("Password changed successfully.");
            }
            else
            {
                return BadRequest(result.Errors);
            }
        }

        [HttpPut]
        [Route("editUser/{id}")]
        public async Task<IActionResult> EditUser(int id, [FromForm] UpdateUserViewModel uv)
        {
            try
            {
                var user = await _userManager.FindByIdAsync(id.ToString());

                if (user == null)
                {
                    return NotFound("User not found.");
                }

                // Read the form data to get the photo file
                var formCollection = await Request.ReadFormAsync();
                var photo = formCollection.Files.FirstOrDefault();

                user.Name = uv.Name;
                user.Surname = uv.Surname;
                user.Email = uv.Email;
                user.Physical_Address = uv.Physical_Address;
                user.PhoneNumber = uv.PhoneNumber;

                if (photo != null && photo.Length > 0)
                {
                    using (var memoryStream = new MemoryStream())
                    {
                        await photo.CopyToAsync(memoryStream);
                        var fileBytes = memoryStream.ToArray();
                        string base64Image = Convert.ToBase64String(fileBytes);

                        user.Photo = base64Image; // Store the base64 string of the photo
                    }
                }

                // Update the user
                var result = await _userManager.UpdateAsync(user);

                if (result.Succeeded)
                {
                    return Ok("User updated successfully.");
                }
                else
                {
                    return BadRequest(result.Errors);
                }
            }
            catch (Exception)
            {
                return BadRequest("An Error Occurred, Please Try Again");
            }
        }


        [HttpDelete]
        [Route("deleteUser/{id}")]
        public async Task<IActionResult> DeleteUser(int id)
        {
            try
            {
                var user = await _userManager.FindByIdAsync(id.ToString());

                if (user == null)
                {
                    return NotFound("User not found.");
                }

                var result = await _userManager.DeleteAsync(user);

                if (result.Succeeded)
                {
                    return Ok();
                }
                else
                {
                    return StatusCode(StatusCodes.Status500InternalServerError, "Internal Server Error. Please contact support.");
                }
            }
            catch (Exception)
            {

                return BadRequest("An Error Occured, Please Try Again");
            }
        }

        [HttpGet]
        [Route("getAllUsers")]
        public IActionResult GetAllUsers()
        {
            try
            {
                var users = _userManager.Users.ToList();


                if (users == null || users.Count == 0)
                {
                    return NotFound("No users found.");
                }

                return Ok(users);
            }
            catch (Exception)
            {

                return BadRequest("An Error Occured, Please Try Again");
            }
        }

        [HttpGet]
        [Route("getUserById/{id}")]
        public async Task<IActionResult> GetUserById(int id)
        {
            try
            {
                var u = await _appDbContext.Users
                                .Include(u => u.User_Status)
                                .Include(u => u.User_Type)
                                .FirstOrDefaultAsync(u => u.Id == id);

                var user = new
                {
                    u.Id,
                    u.Name,
                    u.Surname,
                    u.Email,
                    u.Physical_Address,
                    u.PhoneNumber,
                    u.Date_of_Birth,
                    UserStatus = u.User_Status.User_Status_Description,
                    UserType = u.User_Type.User_Type_Name,
                    u.Photo,
                    u.ID_Number
                };

                return Ok(user);
            }
            catch (Exception ex)
            {
                // Log the exception for debugging
                Console.WriteLine(ex.Message);
                return BadRequest("An error occurred while fetching user details.");
            }
        }


        [HttpGet("GetMemberByUserId/{userId}")]
        public async Task<ActionResult<Member>> GetMemberByUserId(int userId)
        {
            var member = await _appDbContext.Members.FirstOrDefaultAsync(m => m.User_ID == userId);
            if (member == null)
            {
                return NotFound();
            }
            return Ok(member);
        }

        [HttpGet("employee")]
        public async Task<ActionResult<IEnumerable<EmployeeViewModel>>> GetEmployees()
        {
            var query = await( from e in _appDbContext.Employees
                        join u in _appDbContext.Users on e.User_ID equals u.User_ID
                        select new EmployeeViewModel
                        {
                            employee_ID = e.Employee_ID,
                            employee_name = u.Name
                        }).ToListAsync();

            return query;

        }
        //Roles

        [HttpPost]
        [Route("CreateRole")]
        public async Task<IActionResult> CreateRole(string roleName)
        {
            var role = await _roleManager.FindByNameAsync(roleName);
            if (role == null)
            {
                role = new Role
                {
                    Name = roleName,
                    NormalizedName = roleName.ToUpper(),
                    isEditable = true,
                };

                var result = await _roleManager.CreateAsync(role);
                if (!result.Succeeded) return BadRequest(result.Errors);
            }
            else
            {
                return Forbid("Role already exists.");
            }

            return Ok();
        }

        [HttpPost]
        [Route("AssignRole")]
        public async Task<IActionResult> AssignRole(string emailAddress, string roleName)
        {
            var user = await _userManager.FindByEmailAsync(emailAddress);
            if (user == null) return NotFound();

            var result = await _userManager.AddToRoleAsync(user, roleName);
            if (result.Succeeded) return Ok();

            return BadRequest(result.Errors);
        }


        [HttpPost("ForgotPassword")]
        public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model)
        {
            try
            {
                if (string.IsNullOrEmpty(model.Email))
                {
                    return BadRequest("Email is required.");
                }

                var user = await _userManager.FindByEmailAsync(model.Email);
                if (user == null)
                {
                    return Ok("User does not exist.");
                }

                var token = await _userManager.GeneratePasswordResetTokenAsync(user);
                var resetLink = Url.Action("ResetPassword", "User",
                                           new { token, email = user.Email },
                                           protocol: HttpContext.Request.Scheme);

                await SendResetPasswordEmail(model.Email, resetLink);

                return Ok("Please check your email for password reset instructions.");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error sending reset password email.");
                return StatusCode(StatusCodes.Status500InternalServerError, "Error sending reset password email.");
            }
        }

        [HttpPost("ResetPassword")]
        public async Task<IActionResult> ResetPassword(ResetPasswordViewModel model)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest("Invalid data.");
            }

            var user = await _userManager.FindByEmailAsync(model.Email);
            if (user == null)
            {
                return NotFound("User not found.");
            }

            var result = await _userManager.ResetPasswordAsync(user, model.Token, model.Password);
            if (result.Succeeded)
            {
                return Ok("Password has been reset successfully.");
            }

            return BadRequest("Error while resetting the password.");
        }

        private async Task SendResetPasswordEmail(string email, string resetLink)
        {
            try
            {
                var apiKey = _configuration["SendGrid:ApiKey"];
                var client = new SendGridClient(apiKey);
                var from = new EmailAddress(_configuration["SendGrid:FromEmail"], _configuration["SendGrid:FromName"]);
                var to = new EmailAddress(email);
                var subject = "Reset Password";
                var htmlContent = $"<h4>Reset your password by <a href='{resetLink}'>clicking here</a></h4>";
                var msg = MailHelper.CreateSingleEmail(from, to, subject, null, htmlContent);

                var response = await client.SendEmailAsync(msg);
                if (response.StatusCode != HttpStatusCode.OK && response.StatusCode != HttpStatusCode.Accepted)
                {
                    throw new Exception($"Failed to send email. Status code: {response.StatusCode}");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error sending reset password email.");
                throw;
            }
        }
    }
}
using av_motion_api.Data;
using av_motion_api.Factory;
using av_motion_api.Models;
using av_motion_api.Interfaces;
using av_motion_api.Services;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Logging;
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;

var builder = WebApplication.CreateBuilder(args);

// Configure the app environment
ConfigurationManager configuration = builder.Configuration;

builder.Configuration.SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: false);

builder.Host.ConfigureAppConfiguration((hostingContext, config) =>
{
    config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
    config.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true);
});

// Configure logging
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Logging.AddDebug();

// CORS
if (builder.Environment.IsDevelopment())
{
    builder.Services.AddCors(options =>
    {
        options.AddPolicy("AllowAll", policy =>
        {
            policy.AllowAnyOrigin()
                  .AllowAnyHeader()
                  .AllowAnyMethod();
        });
    });
}

// Add services to the container
builder.Services.AddControllers();

// SQL
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddScoped<IRepository, Repository>();

builder.Services.AddIdentity<User, Role>(options =>
                {
                    options.Password.RequireUppercase = false;
                    options.Password.RequireLowercase = false;
                    options.Password.RequireNonAlphanumeric = false;
                    options.Password.RequireDigit = true;
                    options.User.RequireUniqueEmail = true;
                })
                .AddRoles<Role>()
                .AddEntityFrameworkStores<AppDbContext>()
                .AddDefaultTokenProviders();

builder.Services.AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                })
                .AddCookie()
                .AddJwtBearer(options =>
                {
                    options.TokenValidationParameters = new TokenValidationParameters()
                    {
                        ValidIssuer = builder.Configuration["Tokens:Issuer"],
                        ValidAudience = builder.Configuration["Tokens:Audience"],
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Tokens:Key"]))
                    };
                });

// Configure FormOptions for file uploads
builder.Services.Configure<FormOptions>(o =>
{
    o.ValueLengthLimit = int.MaxValue;
    o.MultipartBodyLengthLimit = int.MaxValue;
    o.MemoryBufferThreshold = int.MaxValue;
});

builder.Services.AddScoped<IUserClaimsPrincipalFactory<User>, AppUserClaimsPrincipalFactory>();

builder.Services.Configure<DataProtectionTokenProviderOptions>(options => options.TokenLifespan = TimeSpan.FromHours(3));

// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

// Use CORS
app.UseCors("AllowAll");

app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Use(async (context, next) =>
{
    var logger = app.Services.GetRequiredService<ILogger<Program>>();
    logger.LogInformation("Handling request: " + context.Request.Path);
    await next.Invoke();
    logger.LogInformation("Finished handling request.");
});

app.Run();
onSubmit() {
    if (this.profileForm.valid) {
      const userId = JSON.parse(localStorage.getItem('User')!).userId;
      this.userService.updateUser(userId, this.profileForm.value).subscribe({
        next: (result) => {
          console.log('User to be updated:', result);
          this.router.navigateByUrl(`/ProfilePage/${userId}`);
          alert('Successfully updated profile');
        },
        error: () => {
          console.error('Error updating user profile:');
          alert('Error updating profile');
        }
      });
    }
  }

onPhotoChange(event: Event): void {
    if (!this.isEditMode) return;

    const input = event.target as HTMLInputElement;
    if (input.files && input.files[0]) {
      const reader = new FileReader();
      reader.onload = (e: any) => {
        this.userProfileImage = e.target.result; // Update the image source
        this.profileForm.patchValue({ photo: e.target.result }); // Update the form control
      };
      reader.readAsDataURL(input.files[0]);
    }
  }
//Method before error
loadRewardTypes(): void {
    this.rewardTypeService.getAllRewardTypes().subscribe((rewardTypes: RewardTypeViewModel[]) => {
      this.rewardTypes = rewardTypes;
      this.filteredRewardTypes = rewardTypes;
    });
  }
  
//Method after error
loadRewardTypes(): void {
    this.rewardTypeService.getAllRewardTypes().subscribe({
      next: (rewardTypes: RewardTypeViewModel[]) => {
        console.log('API Response:', rewardTypes); // Check if the data is an array
        this.rewardTypes = Array.isArray(rewardTypes) ? rewardTypes : [];
        this.filteredRewardTypes = this.rewardTypes;
        console.log('Assigned rewardTypes:', this.rewardTypes); // Confirm assignment
      },
      error: (error) => {
        console.error('Error loading reward types:', error);
      }
    });
  }
using av_motion_api.Data;
using av_motion_api.Factory;
using av_motion_api.Models;
using av_motion_api.Interfaces;
using av_motion_api.Services;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Logging;
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.OpenApi.Models;

var builder = WebApplication.CreateBuilder(args);

// Configure the app environment
ConfigurationManager configuration = builder.Configuration;

builder.Configuration.SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: false);

builder.Host.ConfigureAppConfiguration((hostingContext, config) =>
{
    config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
    config.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true);
});

// Configure logging
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Logging.AddDebug();

// CORS
if (builder.Environment.IsDevelopment())
{
    builder.Services.AddCors(options =>
    {
        options.AddPolicy("AllowAll", policy =>
        {
            policy.AllowAnyOrigin()
                  .AllowAnyHeader()
                  .AllowAnyMethod();
        });
    });
}

// Add services to the container
builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.Preserve;
    });

// SQL
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddScoped<IRepository, Repository>();

builder.Services.AddIdentity<User, Role>(options =>
                {
                    options.Password.RequireUppercase = false;
                    options.Password.RequireLowercase = false;
                    options.Password.RequireNonAlphanumeric = false;
                    options.Password.RequireDigit = true;
                    options.User.RequireUniqueEmail = true;
                })
                .AddRoles<Role>()
                .AddEntityFrameworkStores<AppDbContext>()
                .AddDefaultTokenProviders();

builder.Services.AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                })
                .AddCookie()
                .AddJwtBearer(options =>
                {
                    options.Events = new JwtBearerEvents
                    {
                        OnAuthenticationFailed = context =>
                        {
                            context.Response.Headers.Add("Authentication-Failed", context.Exception.Message);
                            return Task.CompletedTask;
                        },
                        OnTokenValidated = context =>
                        {
                            var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<StartupBase>>();
                            logger.LogInformation("Token validated for user: {0}", context.Principal.Identity.Name);
                            return Task.CompletedTask;
                        }
                    };
                    options.TokenValidationParameters = new TokenValidationParameters()
                    {
                        ValidateIssuer = true,
                        ValidateAudience = true,
                        ValidateLifetime = true,
                        ValidateIssuerSigningKey = true,
                        ValidIssuer = builder.Configuration["Tokens:Issuer"],
                        ValidAudience = builder.Configuration["Tokens:Audience"],
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Tokens:Key"]))
                    };
                });

// Configure FormOptions for file uploads
builder.Services.Configure<FormOptions>(o =>
{
    o.ValueLengthLimit = int.MaxValue;
    o.MultipartBodyLengthLimit = int.MaxValue;
    o.MemoryBufferThreshold = int.MaxValue;
});

builder.Services.AddScoped<IUserClaimsPrincipalFactory<User>, AppUserClaimsPrincipalFactory>();

builder.Services.Configure<DataProtectionTokenProviderOptions>(options => options.TokenLifespan = TimeSpan.FromHours(3));

// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
    // Add JWT Authentication to Swagger
    c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        Description = @"JWT Authorization header using the Bearer scheme. \r\n\r\n 
          Enter 'Bearer' [space] and then your token in the text input below.
          \r\n\r\nExample: 'Bearer 12345abcdef'",
        Name = "Authorization",
        In = ParameterLocation.Header,
        Type = SecuritySchemeType.ApiKey,
        Scheme = "Bearer"
    });

    c.AddSecurityRequirement(new OpenApiSecurityRequirement()
    {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference
                {
                    Type = ReferenceType.SecurityScheme,
                    Id = "Bearer"
                },
                Scheme = "oauth2",
                Name = "Bearer",
                In = ParameterLocation.Header,
            },
            new List<string>()
        }
    });
});

var app = builder.Build();

// Configure the HTTP request pipeline
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

// Use CORS
app.UseCors("AllowAll");

app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Use(async (context, next) =>
{
    var logger = app.Services.GetRequiredService<ILogger<Program>>();
    logger.LogInformation("Handling request: " + context.Request.Path);
    await next.Invoke();
    logger.LogInformation("Finished handling request.");
});

app.Run();
//OrderService
import { HttpHeaders, HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { CartItemViewModel, CartViewModel, Product, WishlistItemViewModel, WishlistViewModel } from '../shared/order';
import { BehaviorSubject, map, Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class OrderService {
  private cartItemsCountSource = new BehaviorSubject<number>(0);
  cartItemsCount = this.cartItemsCountSource.asObservable();

  private wishlistItemsCountSource = new BehaviorSubject<number>(0);
  wishlistItemsCount = this.wishlistItemsCountSource.asObservable();
  
  httpOptions = {
    headers: new HttpHeaders({
      'Content-Type': 'application/json'
    })
  };
  
  constructor(private http: HttpClient) {}
  apiUrl: string = "https://localhost:7185/api/Order";

  //navbar count
  updateCartCount(count: number) {
    this.cartItemsCountSource.next(count);
  }

  loadCartItems(): void {
    this.getCartItems().subscribe({
      next: (items) => {
        const totalCount = items.reduce((sum, item) => sum + item.quantity, 0);
        this.updateCartCount(totalCount);
      },
      error: (err) => {
        console.error('Error loading cart items', err);
      }
    });
  }

  updateWishlistCount(count: number) {
    this.wishlistItemsCountSource.next(count);
  }

  loadWishlistItems(): void {
    this.getWishlistItems().subscribe({
      next: (items) => {
        this.updateWishlistCount(items.length);
      },
      error: (err) => {
        console.error('Error loading wishlist items', err);
      }
    });
  }

  //products
  getAllProducts(): Observable<Product[]> {
    return this.http.get<Product[]>(`${this.apiUrl}/GetAllProducts`, this.httpOptions);
  }

  getProductById(product_id: number): Observable<Product> {
    return this.http.get<Product>(`${this.apiUrl}/GetProductById/${product_id}`, this.httpOptions);
  }

  //cart
  getCartItems(): Observable<CartItemViewModel[]> {
    const token = localStorage.getItem('token');
    console.log('Retrieved token from localStorage:', token); 

    const httpOptionsWithAuth = {
      headers: new HttpHeaders({
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`
      })
    };

    return this.http.get<CartItemViewModel[]>(`${this.apiUrl}/GetCart`, httpOptionsWithAuth);
  }

  addToCart(cartViewModel: { product_ID: number; quantity: number; size: string }): Observable<any> {
    const token = localStorage.getItem('token');
    console.log('Retrieved token from localStorage:', token); 

    const httpOptionsWithAuth = {
      headers: new HttpHeaders({
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`
      })
    };

    return this.http.post<any>(`${this.apiUrl}/AddToCart`, cartViewModel, httpOptionsWithAuth);
  }

  updateCart(cartViewModel: CartViewModel): Observable<any> {
    const token = localStorage.getItem('token');
    console.log('Retrieved token from localStorage:', token); 

    const httpOptionsWithAuth = {
      headers: new HttpHeaders({
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`
      })
    };

    return this.http.put<any>(`${this.apiUrl}/UpdateCart`, cartViewModel, httpOptionsWithAuth);
  }

  removeFromCart(productId: number): Observable<any> {
    const token = localStorage.getItem('token');
    const httpOptionsWithAuth = {
      headers: new HttpHeaders({
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`
      })
    };
    return this.http.delete<any>(`${this.apiUrl}/RemoveFromCart/${productId}`, httpOptionsWithAuth);
  }

  //wishlist
  getWishlistItems(): Observable<WishlistItemViewModel[]> {
    const token = localStorage.getItem('token');
    const httpOptionsWithAuth = {
      headers: new HttpHeaders({
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`
      })
    };
    return this.http.get<WishlistItemViewModel[]>(`${this.apiUrl}/GetWishlist`, httpOptionsWithAuth);
  }

  addToWishlist(wishlistViewModel: WishlistViewModel): Observable<any> {
    const token = localStorage.getItem('token');
    const httpOptionsWithAuth = {
      headers: new HttpHeaders({
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`
      })
    };
    return this.http.post<any>(`${this.apiUrl}/AddToWishlist`, wishlistViewModel, httpOptionsWithAuth);
  }

  removeFromWishlist(productId: number): Observable<any> {
    const token = localStorage.getItem('token');
    const httpOptionsWithAuth = {
      headers: new HttpHeaders({
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`
      })
    };
    return this.http.delete<any>(`${this.apiUrl}/RemoveFromWishlist/${productId}`, httpOptionsWithAuth);
  }

  moveFromWishlistToCart(product_ID: number): Observable<any> {
    const token = localStorage.getItem('token');
    const httpOptionsWithAuth = {
      headers: new HttpHeaders({
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`
      }),
      responseType: 'text' as 'json' // Specify response type as text
    };
    return this.http.post<any>(`${this.apiUrl}/MoveFromWishlistToCart`, { product_ID, quantity: 1 }, httpOptionsWithAuth);
  }

  //payfast
  getTotalAmount(): Observable<number> {
    return this.getCartItems().pipe(
      map(items => items.reduce((sum, item) => sum + (item.unit_Price * item.quantity), 0))
    );
  }

  getUserData(): any {
    // Replace this with your actual implementation to get user data
    return {
      email: 'user@example.com'
    };
  }
}
using av_motion_api.Data;
using av_motion_api.Factory;
using av_motion_api.Models;
using av_motion_api.Interfaces;
using av_motion_api.Services;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Logging;
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using System.Text.Json.Serialization;

var builder = WebApplication.CreateBuilder(args);

// Configure the app environment
ConfigurationManager configuration = builder.Configuration;

builder.Configuration.SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: false);

builder.Host.ConfigureAppConfiguration((hostingContext, config) =>
{
    config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
    config.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true);
});

// Configure logging
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Logging.AddDebug();

// CORS
if (builder.Environment.IsDevelopment())
{
    builder.Services.AddCors(options =>
    {
        options.AddPolicy("AllowAll", policy =>
        {
            policy.AllowAnyOrigin()
                  .AllowAnyHeader()
                  .AllowAnyMethod();
        });
    });
}

// Add services to the container
builder.Services.AddControllers()
                .AddJsonOptions(options =>
                {
                    options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
                    options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
                });

// SQL
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddScoped<IRepository, Repository>();

builder.Services.AddIdentity<User, Role>(options =>
                {
                    options.Password.RequireUppercase = false;
                    options.Password.RequireLowercase = false;
                    options.Password.RequireNonAlphanumeric = false;
                    options.Password.RequireDigit = true;
                    options.User.RequireUniqueEmail = true;
                })
                .AddRoles<Role>()
                .AddEntityFrameworkStores<AppDbContext>()
                .AddDefaultTokenProviders();

builder.Services.AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                })
                .AddCookie()
                .AddJwtBearer(options =>
                {
                    options.TokenValidationParameters = new TokenValidationParameters()
                    {
                        ValidateIssuer = true,
                        ValidateAudience = true,
                        ValidateLifetime = true,
                        ValidateIssuerSigningKey = true,
                        ValidIssuer = builder.Configuration["Tokens:Issuer"],
                        ValidAudience = builder.Configuration["Tokens:Audience"],
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Tokens:Key"]))
                    };
                });

// Configure FormOptions for file uploads
builder.Services.Configure<FormOptions>(o =>
{
    o.ValueLengthLimit = int.MaxValue;
    o.MultipartBodyLengthLimit = int.MaxValue;
    o.MemoryBufferThreshold = int.MaxValue;
});

builder.Services.AddScoped<IUserClaimsPrincipalFactory<User>, AppUserClaimsPrincipalFactory>();

builder.Services.Configure<DataProtectionTokenProviderOptions>(options => options.TokenLifespan = TimeSpan.FromHours(3));

// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

// Use CORS
app.UseCors("AllowAll");

app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Use(async (context, next) =>
{
    var logger = app.Services.GetRequiredService<ILogger<Program>>();
    logger.LogInformation("Handling request: " + context.Request.Path);
    await next.Invoke();
    logger.LogInformation("Finished handling request.");
});

app.Run();
[HttpPost]
[Route("ForgotPassword")]
public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
    if (!ModelState.IsValid)
    {
        return BadRequest("Invalid data.");
    }

    var user = await _userManager.FindByEmailAsync(model.Email);
    if (user == null)
    {
        return NotFound("User not found.");
    }

    var token = await _userManager.GeneratePasswordResetTokenAsync(user);
    var encodedToken = HttpUtility.UrlEncode(token);

    // Send the token to the user's email
    var resetLink = Url.Action("ResetPassword", "Account", new { token = encodedToken, email = model.Email }, Request.Scheme);
    // Add email sending logic here

    return Ok(encodedToken);
}



[HttpPost]
[Route("ResetPassword")]
public async Task<IActionResult> ResetPassword([FromBody] ResetPasswordViewModel model)
{
    if (!ModelState.IsValid)
    {
        return BadRequest("Invalid data.");
    }

    var user = await _userManager.FindByEmailAsync(model.Email);
    if (user == null)
    {
        return NotFound("User not found.");
    }

    var decodedToken = HttpUtility.UrlDecode(model.Token);
    var result = await _userManager.ResetPasswordAsync(user, decodedToken, model.Password);
    if (result.Succeeded)
    {
        return Ok("Password has been reset successfully.");
    }

    // Log errors for debugging
    var errors = string.Join(", ", result.Errors.Select(e => e.Description));
    return BadRequest($"Error while resetting the password: {errors}");
}
using av_motion_api.Data;
using av_motion_api.Interfaces;
using av_motion_api.Models;
using av_motion_api.ViewModels;
using Microsoft.AspNetCore.Mvc;

// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860

namespace av_motion_api.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ProductController : ControllerBase
    {
        private readonly AppDbContext _appContext;
        public readonly IRepository _repository;
        public ProductController(AppDbContext _context, IRepository repository)
        {

            _appContext = _context;
            _repository = repository;
        }
        // GET: api/<ProductController>
        [HttpGet]
        public async Task<ActionResult<IEnumerable<ProductViewModel>>> GetProducts()
        {
            var products = await _repository.GetProducts();

            return Ok(products);
        }

        // GET api/<ProductController>/5
        [HttpGet("{id}")]
        public async Task<ActionResult<ProductViewModel>> GetProduct(int id)
        {
            if (_appContext.Products == null)
            {
                return NotFound();
            }
            var product = await _repository.GetProduct(id);
            if (product == null)
            {
                return NotFound();
            }

            return Ok(product);
        }
        //POST api/<ProductController>
        [HttpPost]
        public async Task<ActionResult<Product>> PostProduct([FromBody] ProductViewModel product)
        {
            var newProduct = new Product()
            {
                Product_Name = product.name,
                Product_Description = product.description,
                Create_Date = DateTime.Now,
                Last_Update_Date = DateTime.Now,
                IsActive = product.IsActive,
                Size = product.Size,
                Product_Category_ID = product.Product_Category_ID,
                Supplier_ID = product.Supplier_ID,

            };



            if (_appContext.Products == null)
            {
                return Problem("Entity set 'AppDbContext.Products'  is null.");
            }
            _appContext.Products.Add(newProduct);
            await _appContext.SaveChangesAsync();

            return Ok(newProduct);
        }


        // PUT api/<ProductController>/5
        [HttpPut("{id}")]
        public async Task<IActionResult> PutProduct(int id, [FromBody] ProductViewModel updatedProduct)
        {
            var existingproduct = await _appContext.Products.FindAsync(id);
            if (existingproduct == null)
            {
                return NotFound();
            }

            existingproduct.Product_Name = updatedProduct.name;
            existingproduct.Product_Description = updatedProduct.description;
            existingproduct.Last_Update_Date = DateTime.Now;
            existingproduct.Product_Category_ID = updatedProduct.Product_Category_ID;
            existingproduct.Size = updatedProduct.Size;
           
            return null;
        }

        // DELETE api/<ProductController>/5
        [HttpPut("hide-product/{id}")]
      
        public async Task<IActionResult> HideProduct(int id)
        {
            if (_appContext.Products == null)
            {
                return NotFound();
            }
            var product = await _appContext.Products.FindAsync(id);
            if (product == null)
            {
                return NotFound();
            }

            product.IsActive = false; 
            await _appContext.SaveChangesAsync();

            return NoContent();
        }

        [HttpPut("unhide-product/{id}")]

        public async Task<IActionResult> UnHideProduct(int id)
        {
            if (_appContext.Products == null)
            {
                return NotFound();
            }
            var product = await _appContext.Products.FindAsync(id);
            if (product == null)
            {
                return NotFound();
            }

            product.IsActive = true;
            await _appContext.SaveChangesAsync();

            return NoContent();
        }

        private bool ProductExists(int id)
        {
            return (_appContext.Products?.Any(e => e.Product_ID == id)).GetValueOrDefault();
        }

    }
}
var Products = new Product[]
{
    new Product { Product_ID = 1, Product_Name = "Gym T-Shirt", Product_Description = "Breathable cotton gym shirt", Product_Img = "gym_tshirt.jpg", Quantity = 50, Unit_Price = 19.99M, Size = "XS", Product_Category_ID = 1, Supplier_ID = 1 },
    new Product { Product_ID = 2, Product_Name = "Running Shorts", Product_Description = "Lightweight running shorts", Product_Img = "running_shorts.jpg", Quantity = 40, Unit_Price = 24.99M, Size = "M", Product_Category_ID = 2, Supplier_ID = 1 },
    new Product { Product_ID = 3, Product_Name = "Hoodie", Product_Description = "Fleece-lined gym hoodie", Product_Img = "hoodie.jpg", Quantity = 30, Unit_Price = 39.99M, Size = "L", Product_Category_ID = 2, Supplier_ID = 1 },
    new Product { Product_ID = 4, Product_Name = "Compression Tights", Product_Description = "High-performance compression tights", Product_Img = "compression_tights.jpg", Quantity = 60, Unit_Price = 29.99M, Size = "S", Product_Category_ID = 2, Supplier_ID = 1 },
    new Product { Product_ID = 5, Product_Name = "Gym Tank Top", Product_Description = "Sleeveless tank for workouts", Product_Img = "tank_top.jpg", Quantity = 70, Unit_Price = 15.99M, Size = "M", Product_Category_ID = 1, Supplier_ID = 1 },
    new Product { Product_ID = 6, Product_Name = "Sweatpants", Product_Description = "Comfortable gym sweatpants", Product_Img = "sweatpants.jpg", Quantity = 50, Unit_Price = 25.99M, Size = "L", Product_Category_ID = 2, Supplier_ID = 1 },
    new Product { Product_ID = 7, Product_Name = "Sports Bra", Product_Description = "Supportive sports bra", Product_Img = "sports_bra.jpg", Quantity = 40, Unit_Price = 19.99M, Size = "S", Product_Category_ID = 1, Supplier_ID = 1 },
    new Product { Product_ID = 8, Product_Name = "Gym Leggings", Product_Description = "High-waisted gym leggings", Product_Img = "gym_leggings.jpg", Quantity = 60, Unit_Price = 34.99M, Size = "M", Product_Category_ID = 2, Supplier_ID = 1 }
};

builder.Entity<Product>().HasData(Products);
//UserDeletionService.cs
using av_motion_api.Data; // Adjust the namespace to match your project
using av_motion_api.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

namespace av_motion_api.Services
{
    public class UserDeletionService : IHostedService, IDisposable
    {
        private readonly IServiceProvider _serviceProvider;
        private Timer _timer;

        public UserDeletionService(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }

        public Task StartAsync(CancellationToken cancellationToken)
        {
            _timer = new Timer(DeleteDeactivatedUsers, null, TimeSpan.Zero, TimeSpan.FromMinutes(0));
            return Task.CompletedTask;
        }

        private void DeleteDeactivatedUsers(object state)
        {
            using (var scope = _serviceProvider.CreateScope())
            {
                var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();
                var userManager = scope.ServiceProvider.GetRequiredService<UserManager<User>>();

                var sixMonthsAgo = DateTime.UtcNow.AddMonths(-6);

                var usersToDelete = context.Users
                    .Where(u => u.User_Status_ID == 2 && u.DeactivatedAt < sixMonthsAgo)
                    .ToList();

                foreach (var user in usersToDelete)
                {
                    userManager.DeleteAsync(user).Wait();
                }

                context.SaveChanges();
            }
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            _timer?.Change(Timeout.Infinite, 0);
            return Task.CompletedTask;
        }

        public void Dispose()
        {
            _timer?.Dispose();
        }
    }
}
//program.cs
builder.Services.AddHostedService<OrderStatusUpdater>();
using av_motion_api.Data;
using av_motion_api.Factory;
using av_motion_api.Models;
using av_motion_api.Interfaces;
using av_motion_api.Services;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Logging;
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using System.Text.Json.Serialization;

var builder = WebApplication.CreateBuilder(args);

// Configure the app environment
ConfigurationManager configuration = builder.Configuration;

builder.Configuration.SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: false);

builder.Host.ConfigureAppConfiguration((hostingContext, config) =>
{
    config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
    config.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true);
});

// Configure logging
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Logging.AddDebug();

// CORS
if (builder.Environment.IsDevelopment())
{
    builder.Services.AddCors(options =>
    {
        options.AddPolicy("AllowAll", policy =>
        {
            policy.AllowAnyOrigin()
                  .AllowAnyHeader()
                  .AllowAnyMethod();
        });
    });
}

// Add services to the container
builder.Services.AddControllers()
                .AddJsonOptions(options =>
                {
                    options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
                    options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
                });

// SQL
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddScoped<IRepository, Repository>();

builder.Services.AddIdentity<User, Role>(options =>
                {
                    options.Password.RequireUppercase = false;
                    options.Password.RequireLowercase = false;
                    options.Password.RequireNonAlphanumeric = false;
                    options.Password.RequireDigit = true;
                    options.User.RequireUniqueEmail = true;
                })
                .AddRoles<Role>()
                .AddEntityFrameworkStores<AppDbContext>()
                .AddDefaultTokenProviders();

builder.Services.AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                })
                .AddCookie()
                .AddJwtBearer(options =>
                {
                    options.TokenValidationParameters = new TokenValidationParameters()
                    {
                        ValidateIssuer = true,
                        ValidateAudience = true,
                        ValidateLifetime = true,
                        ValidateIssuerSigningKey = true,
                        ValidIssuer = builder.Configuration["Tokens:Issuer"],
                        ValidAudience = builder.Configuration["Tokens:Audience"],
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Tokens:Key"]))
                    };
                });

// Configure FormOptions for file uploads
builder.Services.Configure<FormOptions>(o =>
{
    o.ValueLengthLimit = int.MaxValue;
    o.MultipartBodyLengthLimit = int.MaxValue;
    o.MemoryBufferThreshold = int.MaxValue;
});

builder.Services.AddScoped<IUserClaimsPrincipalFactory<User>, AppUserClaimsPrincipalFactory>();

builder.Services.Configure<DataProtectionTokenProviderOptions>(options => options.TokenLifespan = TimeSpan.FromHours(3));


// Register the OrderStatusUpdater hosted service
builder.Services.AddHostedService<OrderStatusUpdater>();
builder.Services.AddHostedService<UserDeletionService>();


// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

// Use CORS
app.UseCors("AllowAll");

app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Use(async (context, next) =>
{
    var logger = app.Services.GetRequiredService<ILogger<Program>>();
    logger.LogInformation("Handling request: " + context.Request.Path);
    await next.Invoke();
    logger.LogInformation("Finished handling request.");
});

app.Run();
//html
<div class="deletion-settings-container">
    <form>
        <div class="form-group">
          <label for="deletionTime">Deletion Time:</label>
          <input type="number" id="deletionTime" [(ngModel)]="deletionTime.value" name="deletionTime" required>
        </div>
      
        <div class="form-group">
          <label for="timeUnit">Time Unit:</label>
          <select id="timeUnit" [(ngModel)]="deletionTime.unit" name="timeUnit">
            <option value="Minutes">Minutes</option>
            <option value="Hours">Hours</option>
            <option value="Days">Days</option>
            <option value="Weeks">Weeks</option>
            <option value="Months">Months</option>
            <option value="Years">Years</option>
          </select>
        </div>
      
        <button type="button" (click)="saveDeletionTime()">Save</button>
      </form>
</div>

  
//ts
import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { UserService } from '../Services/userprofile.service';
import { DeletionSettings } from '../shared/deletionsettings';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Router } from '@angular/router';
import { Location } from '@angular/common';

@Component({
  selector: 'app-deletion-settings',
  standalone: true,
  imports: [CommonModule, FormsModule],
  templateUrl: './deletion-settings.component.html',
  styleUrl: './deletion-settings.component.css'
})
export class DeletionSettingsComponent {
  deletionTime = {
    value: 0,
    unit: 'Minutes'
  };

  constructor(private userService: UserService, private snackBar: MatSnackBar, private router: Router, private location: Location) {}

  saveDeletionTime() {
    if (this.deletionTime.value < 0) {
      console.error('Deletion time value must be non-negative');
      this.snackBar.open('Deletion time value must be non-negative', 'Close', { duration: 5000 });
      return;
    }

    const settings: DeletionSettings = {
      deletionTimeValue: this.deletionTime.value,
      deletionTimeUnit: this.deletionTime.unit
    };
  
    this.userService.updateDeletionTime(settings).subscribe({
      next: (response) => {
        console.log('Deletion time updated successfully', response);
        // You can also add some user feedback here like a success message
        this.snackBar.open('Deletion time updated successfully', 'Close', { duration: 5000 });
        this.router.navigateByUrl(`/users`);
      },
      error: (error) => {
        console.error('Error updating deletion time', error);
        // You can add error handling logic here
        this.snackBar.open('Failed to update deletion time. Please try again', 'Close', { duration: 5000 });
      }
    });
  }
  
  goBack(): void {
    this.location.back();
  }  
}
using av_motion_api.Data; 
using av_motion_api.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;

namespace av_motion_api.Services
{
    public class UserDeletionService : IHostedService, IDisposable
    {
        private readonly IServiceProvider _serviceProvider;
        private readonly IOptionsMonitor<DeletionSettings> _settings;
        private Timer _timer;

        public UserDeletionService(IServiceProvider serviceProvider, IOptionsMonitor<DeletionSettings> settings)
        {
            _serviceProvider = serviceProvider;
            _settings = settings;
        }

        public Task StartAsync(CancellationToken cancellationToken)
        {
            ScheduleDeletionTask();
            _settings.OnChange(settings => ScheduleDeletionTask());
            return Task.CompletedTask;
        }

        private void ScheduleDeletionTask()
        {
            var interval = GetTimeSpan(_settings.CurrentValue.DeletionTimeValue, _settings.CurrentValue.DeletionTimeUnit);
            _timer = new Timer(DeleteDeactivatedUsers, null, TimeSpan.Zero, interval);
        }

        private void DeleteDeactivatedUsers(object state)
        {
            using (var scope = _serviceProvider.CreateScope())
            {
                var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();
                var userManager = scope.ServiceProvider.GetRequiredService<UserManager<User>>();

                var deletionThreshold = DateTime.UtcNow.Subtract(GetTimeSpan(_settings.CurrentValue.DeletionTimeValue, _settings.CurrentValue.DeletionTimeUnit));

                var usersToDelete = context.Users
                    .Where(u => u.User_Status_ID == 2 && u.DeactivatedAt < deletionThreshold)
                    .ToList();

                foreach (var user in usersToDelete)
                {
                    userManager.DeleteAsync(user).Wait();
                }

                context.SaveChanges();
            }
        }

        private TimeSpan GetTimeSpan(int value, string unit)
        {
            return unit.ToLower() switch
            {
                "minutes" => TimeSpan.FromMinutes(value),
                "hours" => TimeSpan.FromHours(value),
                "days" => TimeSpan.FromDays(value),
                "weeks" => TimeSpan.FromDays(value * 7),
                "months" => TimeSpan.FromDays(value * 30), // Approximation
                "years" => TimeSpan.FromDays(value * 365), // Approximation
                _ => TimeSpan.FromMinutes(value),
            };
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            _timer?.Change(Timeout.Infinite, 0);
            return Task.CompletedTask;
        }

        public void Dispose()
        {
            _timer?.Dispose();
        }
    }
}
//html
<app-navbar></app-navbar>
<br>
<br>
<br>
<div class="header-search-container">
    <i class="bi bi-arrow-left-circle header-icon" (click)="goBack()"></i>
    <h2 class="header-title">AV MERCH</h2>
</div>
<br>
<br>
<div class="checkout-container d-flex justify-content-center align-items-center">
    <br>
    <div class="row">
      <div class="col-12">
        <div class="card" style="width: 25rem;">
          <div class="card-header">
            <h3>Order Summary</h3>
          </div>
          <div class="card-body">
            <div *ngFor="let item of cartItems">
              <div>
                {{ item.product_Name }}: <span style="float: right">{{ item.unit_Price | currency: 'R ' }} x {{ item.quantity}}</span>
              </div>
            </div>
            <br>
            <div>
              Total Price: <span style="float: right">{{ totalAmount | currency: 'R ' }}</span>
            </div>
            <br>
            <div>
              <p>Use Discount</p>
              <input type="text" [(ngModel)]="discountCode" placeholder="Discount Code">
              <span style="float: right"><button (click)="applyDiscount()">Apply</button></span>
            </div>
          </div>
          <div class="card-footer">
            <div class="buttom-container">
                <span style="float: left">
                    <button (click)="cancel()" routerLink="/cart" class="btn btn-secondary">Cancel</button>
                </span>
                <span style="float: right">
                    <app-payfast></app-payfast>
                </span>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>

//ts
import { CommonModule } from '@angular/common';
import { Component, OnInit } from '@angular/core';
import { RouterLink } from '@angular/router';
import { NavbarComponent } from './navbar.component';
import { PayfastComponent } from "./payfast.component";
import { OrderService } from '../Services/order.service';
import { CartItemViewModel } from '../shared/order';
import { FormsModule } from '@angular/forms';
import { Location } from '@angular/common';

@Component({
  selector: 'app-checkout',
  standalone: true,
  imports: [CommonModule, FormsModule, RouterLink, NavbarComponent, PayfastComponent],
  templateUrl: './checkout.component.html',
  styleUrl: './checkout.component.css'
})
export class CheckoutComponent implements OnInit{
  cartItems: CartItemViewModel[] = [];
  totalAmount: number = 0;
  discountCode: string = '';

  constructor(private orderService: OrderService, private location:Location) {}

  ngOnInit(): void {
    this.loadCartItems();
  }

  loadCartItems(): void {
    this.orderService.getCartItems().subscribe(items => {
      this.cartItems = items;
      this.calculateTotal();
    });
  }

  calculateTotal(): void {
    this.totalAmount = this.cartItems.reduce((sum, item) => sum + (item.unit_Price * item.quantity), 0);
    // Apply discount logic if needed
  }

  applyDiscount(): void {
    // Apply discount logic
  }

  cancel(): void {
    // Navigate back to cart or home
    
  }
  
  goBack(): void {
    this.location.back();
  }
}

//ts
import { HttpClient } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import { Router } from '@angular/router';
import { Md5 } from 'ts-md5';
import { environment } from '../../environments/environment';
import { OrderService } from '../Services/order.service';
import { MatSnackBar } from '@angular/material/snack-bar';
import { UserService } from '../Services/userprofile.service';
import { CartItemViewModel, OrderViewModel } from '../shared/order';

declare global {
  interface Window {
    payfast_do_onsite_payment: (param1: any, callback: any) => any;
  }
}

@Component({
  selector: 'app-payfast',
  standalone: true,
  imports: [],
  templateUrl: './payfast.component.html',
  styleUrl: './payfast.component.css'
})
export class PayfastComponent implements OnInit {
  memberId!: number;

  constructor(private router : Router, private orderService : OrderService, private userService: UserService, private formBuilder: FormBuilder, private snackBar: MatSnackBar) {
    
  }

  ngOnInit(): void {
    this.fetchMemberId();
  }

  getSignature(data : Map<string, string>) : string {
    let tmp = new URLSearchParams();
    data.forEach((v, k)=> {
      tmp.append(k, v)
    });
    let queryString = tmp.toString();
    let sig = Md5.hashStr(queryString);
    return sig;
  }

  async doOnSitePayment() {
    await this.fetchMemberId();

    let onSiteUserData = new Map<string, string>();
    onSiteUserData.set("merchant_id", "10033427")
    onSiteUserData.set("merchant_key", "mu83ipbgas9p7")

    onSiteUserData.set('return_url', window.location.origin + '/payment-success')
    onSiteUserData.set('cancel_url', window.location.origin + '/payment-cancel')

    // Gather required user data from orderService or other sources
    const userData = this.orderService.getUserData();
    onSiteUserData.set("email_address", userData.email);
    
    // Set amount and item_name
    this.orderService.getTotalAmount().subscribe(amount => {
      onSiteUserData.set('amount', amount.toString());
      onSiteUserData.set('item_name', 'Cart Purchase');

      // Optional passphrase for added security
      onSiteUserData.set('passphrase', 'HelloWorldHello'); // Use if you have a passphrase

      let signature = this.getSignature(onSiteUserData);
      onSiteUserData.set('signature', signature);

      let formData = new FormData();
      onSiteUserData.forEach((val, key) => {
        formData.append(key, val);
      });

      fetch(environment.payfastOnsiteEndpoint, {
        method: 'POST',
        body: formData,
        redirect: 'follow'
      })
      .then(response => response.json())
      .then(respJson => {
          let uuid = respJson['uuid'];
          window.payfast_do_onsite_payment({ 'uuid': uuid }, (res: any) => {
            if (res == true) {            
              this.createOrder();
              this.snackBar.open('Payment Successful', 'Close', { duration: 5000 });          
            } else {
              this.snackBar.open('Payment Failed', 'Close', { duration: 5000 });
              
            }
          });
        })
        .catch(error => {
          console.error('Error processing payment:', error);
          this.router.navigate(['/cancel']);
        });
      });
    }

    //order
    private fetchMemberId() {
      const user = localStorage.getItem('User');
      if (user) {
        const userData = JSON.parse(user);
        this.memberId = userData.userId;
    
        // Optional: Fetch and validate member details if needed
        this.userService.getMemberByUserId(this.memberId).subscribe({
          next: (member) => {
            if (member && member.member_ID) {
              this.memberId = member.member_ID;
              console.log('Member ID:', this.memberId); // Check if this logs the correct ID
            } else {
              console.error('Member ID is undefined in the response');
              this.snackBar.open('Failed to retrieve member information', 'Close', { duration: 5000 });
            }
          },
          error: (error) => {
            console.error('Error fetching member:', error);
            this.snackBar.open('Failed to retrieve member information', 'Close', { duration: 5000 });
          }
        });
      } else {
        this.snackBar.open('User not logged in', 'Close', { duration: 5000 });
        this.router.navigate(['/login']);
      }
    }



  private createOrder() {
    if (this.memberId === undefined) {
      this.snackBar.open('Member ID is not available', 'Close', { duration: 5000 });
      return;
    }
    
    this.orderService.getCartItems().subscribe(cartItems => {
      const order  = this.prepareOrderDetails(cartItems);

      this.orderService.createOrder(order ).subscribe({
        next: (orderResponse) => {
          this.snackBar.open('Order Created Successfully', 'Close', { duration: 5000 });
          this.router.navigate(['/orders']);
          console.log("Order Creation Successful: ", orderResponse)
        },
        error: (error) => {
          console.error('Error creating order:', error);
          this.snackBar.open('Failed to create order', 'Close', { duration: 5000 });
          this.router.navigate(['/cart']);
        }
      });
    });
  }

  private prepareOrderDetails(cartItems: CartItemViewModel[]): OrderViewModel {
    const order: OrderViewModel = {
      order_ID: 0, // ID will be generated by backend
      member_ID: this.memberId,
      order_Date: new Date().toISOString(),
      total_Price: this.calculateTotalPrice(cartItems),
      order_Status_ID: 1, // Ready for Collection
      isCollected: false,
      orderLines: cartItems.map(item => ({
        order_Line_ID: 0, // ID will be generated by backend
        product_ID: item.product_ID,
        product_Name: item.product_Name,
        quantity: item.quantity,
        unit_Price: item.unit_Price
      }))
    };
  
    console.log('Prepared order details:', order);
    return order;
  }

  // Helper method to calculate the total price
  private calculateTotalPrice(cartItems: CartItemViewModel[]): number {
    return cartItems.reduce((total, item) => total + (item.unit_Price * item.quantity), 0);
  }
  
    
  }
//html
<div class="register-wrapper d-flex justify-content-center align-items-center">
  <div class="card box">
    <div class="card-header text-center">
      <h4 class="card-title">Sign-up</h4>
    </div>
    <form class="form" [formGroup]="registerFormGroup" (ngSubmit)="RegisterUser()">
      <div class="card-body">
        <div class="row">
          <div class="col-md-6">
            <div class="form-group">
              <label for="name" class="form-label">Name</label>
              <input type="text" class="form-control" id="name" placeholder="Enter your Name" formControlName="name">
              <div *ngIf="registerFormGroup.controls['name'].invalid && (registerFormGroup.controls['name'].dirty || registerFormGroup.controls['name'].touched)" class="text-danger">
                <div *ngIf="registerFormGroup.controls['name'].errors?.['required']">Name is required.</div>
                <div *ngIf="registerFormGroup.controls['name'].errors?.['minlength']">Name must be at least 2 characters long.</div>
                <div *ngIf="registerFormGroup.controls['name'].errors?.['maxlength']">Name must be at most 20 characters long.</div>
              </div>
            </div>
            <div class="form-group">
              <label for="surname" class="form-label">Surname</label>
              <input type="text" class="form-control" id="surname" placeholder="Enter your Surname" formControlName="surname">
              <div *ngIf="registerFormGroup.controls['surname'].invalid && (registerFormGroup.controls['surname'].dirty || registerFormGroup.controls['surname'].touched)" class="text-danger">
                <div *ngIf="registerFormGroup.controls['surname'].errors?.['required']">Surname is required.</div>
                <div *ngIf="registerFormGroup.controls['surname'].errors?.['minlength']">Surname must be at least 2 characters long.</div>
                <div *ngIf="registerFormGroup.controls['surname'].errors?.['maxlength']">Surname must be at most 20 characters long.</div>
              </div>
            </div>
            <div class="form-group">
              <label for="email" class="form-label">Email Address</label>
              <input type="email" class="form-control" id="email" placeholder="Enter a valid Email address" formControlName="email">
              <div *ngIf="registerFormGroup.controls['email'].invalid && (registerFormGroup.controls['email'].dirty || registerFormGroup.controls['email'].touched)" class="text-danger">
                <div *ngIf="registerFormGroup.controls['email'].errors?.['required']">Email is required.</div>
                <div *ngIf="registerFormGroup.controls['email'].errors?.['email']">Email is invalid.</div>
              </div>
            </div>
            <div class="form-group">
              <label for="password" class="form-label">Password</label>
              <input type="password" class="form-control" id="password" placeholder="Enter between 8 to 15 characters" formControlName="password">
              <div *ngIf="registerFormGroup.controls['password'].invalid && (registerFormGroup.controls['password'].dirty || registerFormGroup.controls['password'].touched)" class="text-danger">
                <div *ngIf="registerFormGroup.controls['password'].errors?.['required']">Password is required.</div>
                <div *ngIf="registerFormGroup.controls['password'].errors?.['minlength']">Password must be at least 8 characters.</div>
                <div *ngIf="registerFormGroup.controls['password'].errors?.['maxlength']">Password cannot be more than 15 characters.</div>
              </div>
            </div>
            <div class="form-group">
              <label for="PhoneNumber" class="form-label">Phone Number</label>
              <input type="text" class="form-control" id="PhoneNumber" placeholder="Enter your Phone Number" formControlName="PhoneNumber">
              <div *ngIf="registerFormGroup.controls['PhoneNumber'].invalid && (registerFormGroup.controls['PhoneNumber'].dirty || registerFormGroup.controls['PhoneNumber'].touched)" class="text-danger">
                <div *ngIf="registerFormGroup.controls['PhoneNumber'].errors?.['required']">Phone Number is required.</div>
                <div *ngIf="registerFormGroup.controls['PhoneNumber'].errors?.['minlength']">Phone Number must be 10 digits.</div>
                <div *ngIf="registerFormGroup.controls['PhoneNumber'].errors?.['maxlength']">Phone Number must be 10 digits.</div>
              </div>
            </div>
          </div>

          <div class="col-md-6">
            <div class="form-group">
              <label for="Date_of_Birth" class="form-label">Date of Birth</label>
              <input type="date" class="form-control" id="Date_of_Birth" placeholder="Enter your Date of Birth" formControlName="Date_of_Birth">
              <div *ngIf="registerFormGroup.controls['Date_of_Birth'].invalid && (registerFormGroup.controls['Date_of_Birth'].dirty || registerFormGroup.controls['Date_of_Birth'].touched)" class="text-danger">
                <div *ngIf="registerFormGroup.controls['Date_of_Birth'].errors?.['required']">Date of Birth is required.</div>
                <div *ngIf="registerFormGroup.controls['Date_of_Birth'].errors?.['futureDate']">Date of birth cannot be a future date.</div>
                <div *ngIf="registerFormGroup.controls['Date_of_Birth'].errors?.['olderThan100']">Date of birth cannot be more than 100 years ago.</div>
                <div *ngIf="registerFormGroup.controls['Date_of_Birth'].errors?.['futureYear']">User cannot be younger than 16.</div>
              </div>
            </div>
            <div class="form-group">
              <label for="Id_Number" class="form-label">ID Number</label>
              <input type="text" class="form-control" id="Id_Number" placeholder="Enter your ID Number" formControlName="Id_Number">
              <div *ngIf="registerFormGroup.controls['Id_Number'].invalid && (registerFormGroup.controls['Id_Number'].dirty || registerFormGroup.controls['Id_Number'].touched)" class="text-danger">
                <div *ngIf="registerFormGroup.controls['Id_Number'].errors?.['required']">ID Number is required.</div>
                <div *ngIf="registerFormGroup.controls['Id_Number'].errors?.['minlength']">ID Number must be 13 digits.</div>
                <div *ngIf="registerFormGroup.controls['Id_Number'].errors?.['maxlength']">ID Number must be 13 digits.</div>
              </div>
            </div>
            <div class="form-group">
              <label for="Physical_Address" class="form-label">Physical Address</label>
              <input type="text" class="form-control" id="Physical_Address" placeholder="Enter your Physical Address" formControlName="Physical_Address">
              <div *ngIf="registerFormGroup.controls['Physical_Address'].invalid && (registerFormGroup.controls['Physical_Address'].dirty || registerFormGroup.controls['Physical_Address'].touched)" class="text-danger">
                <div *ngIf="registerFormGroup.controls['Physical_Address'].errors?.['required']">Physical Address is required.</div>
                <div *ngIf="registerFormGroup.controls['Physical_Address'].errors?.['minlength']">Physical Address must be at least 7 characters long.</div>
                <div *ngIf="registerFormGroup.controls['Physical_Address'].errors?.['maxlength']">Physical Address must be at most 100 characters long.</div>
              </div>
            </div>
            <div class="form-group">
              <label for="Photo" class="form-label">Photo</label>
              <input type="file" class="form-control" id="Photo" (change)="onFileSelected($event)">
              <div *ngIf="registerFormGroup.controls['Photo'].invalid && (registerFormGroup.controls['Photo'].dirty || registerFormGroup.controls['Photo'].touched)" class="text-danger">
                <div *ngIf="registerFormGroup.controls['Photo'].errors?.['required']">Photo is required.</div>
              </div>
            </div>
            <div class="form-group">
              <label for="User_Type_ID" class="form-label">User Type</label>
              <select class="form-control" id="User_Type_ID" formControlName="User_Type_ID">
                <option value="1">Owner</option>
                <option value="2">Employee</option>
                <option value="3">Member</option>
              </select>
              <div *ngIf="registerFormGroup.controls['User_Type_ID'].invalid && (registerFormGroup.controls['User_Type_ID'].dirty || registerFormGroup.controls['User_Type_ID'].touched)" class="text-danger">
                <div *ngIf="registerFormGroup.controls['User_Type_ID'].errors?.['required']">User Type is required.</div>
              </div>
            </div>
          </div>
        </div>
      </div>
      <div class="card-footer d-flex justify-content-between">
        <button type="submit" class="btn btn-primary">Sign-up</button>
        <button type="button" class="btn btn-secondary" (click)="resetForm()">Cancel</button>
      </div>
    </form>
  </div>
</div>

//ts
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { MatSnackBar } from '@angular/material/snack-bar';
import { UserService } from '../../Services/userprofile.service';
import { FlexLayoutModule } from '@angular/flex-layout';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-register',
  standalone: true,
  imports: [ReactiveFormsModule, CommonModule, FlexLayoutModule],
  templateUrl: './register.component.html',
  styleUrls: ['./register.component.css']
})
export class RegisterComponent {
  submitted = false;
  registerFormGroup: FormGroup;
  selectedFile: File | null = null;

  constructor(
    private router: Router,
    private userService: UserService,
    private fb: FormBuilder,
    private snackBar: MatSnackBar
  ) {
    this.registerFormGroup = this.fb.group({
      // email: ['', [Validators.required, Validators.email, Validators.pattern(/^[a-z0-9._%+-]+@gmail\.com$/), Validators.minLength(3), Validators.maxLength(50)]],
      email: ['', [Validators.required, Validators.email]],
      password: ['', [Validators.required, Validators.minLength(8), Validators.maxLength(15)]],
      name: ['', [Validators.required, Validators.minLength(2), Validators.maxLength(20)]],
      surname: ['', [Validators.required, Validators.minLength(2), Validators.maxLength(20)]],
      // PhoneNumber: ['', [Validators.required, Validators.pattern(/^0\d{9}$/)]],
      PhoneNumber: ['', [Validators.required, Validators.minLength(10), Validators.maxLength(10)]],
      Date_of_Birth: ['', [Validators.required, this.validateDateOfBirth]],
      // Id_Number: ['', [Validators.required, Validators.pattern(/^\d{2}(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])\d{7}$/)]],
      Id_Number: ['', [Validators.required, Validators.minLength(13), Validators.maxLength(13)]],
      Physical_Address: ['', [Validators.required, Validators.minLength(7), Validators.maxLength(100)]],
      Photo: ['', Validators.required],
      // User_Status_ID: [1, [Validators.required, Validators.min(1), Validators.max(2)]],
      // User_Type_ID: ['', [Validators.required, Validators.min(1), Validators.max(3)]]
      User_Status_ID: [1, Validators.required],
      User_Type_ID: ['', Validators.required]
    });
  }

  ngOnInit() { }

  validateDateOfBirth(control: any): { [key: string]: any } | null {
    const selectedDate = new Date(control.value);
    const currentDate = new Date();
    const minDate = new Date(currentDate.getFullYear() - 100, currentDate.getMonth(), currentDate.getDate());
    const maxDate = new Date(currentDate.getFullYear() - 16, currentDate.getMonth(), currentDate.getDate());

    if (selectedDate > currentDate) {
      return { 'futureDate': true };
    }

    if (selectedDate < minDate) {
      return { 'olderThan100': true };
    }

    if (selectedDate > maxDate) {
      return { 'futureYear': true };
    }

    return null;
  }

  onFileSelected(event: any) {
    const file = event.target.files[0];
    if (file) {
      this.selectedFile = file;
      this.registerFormGroup.patchValue({ Photo: file });
    } else {
      this.selectedFile = null;
    }
  }

  RegisterUser() {
    this.submitted = true;
  
    if (this.registerFormGroup.valid && this.selectedFile) {
      console.log('Form is valid');
      console.log('Selected file:', this.selectedFile);

      const emailLowerCase = this.registerFormGroup.value.email.toLowerCase();
      this.registerFormGroup.patchValue({ email: emailLowerCase });
  
      const formData: FormData = new FormData();
      formData.append('name', this.registerFormGroup.value.name);
      formData.append('surname', this.registerFormGroup.value.surname);
      formData.append('email', this.registerFormGroup.value.email);
      formData.append('password', this.registerFormGroup.value.password);
      formData.append('PhoneNumber', this.registerFormGroup.value.PhoneNumber);
      formData.append('Date_of_Birth', this.registerFormGroup.value.Date_of_Birth);
      formData.append('Id_Number', this.registerFormGroup.value.Id_Number);
      formData.append('Physical_Address', this.registerFormGroup.value.Physical_Address);
      formData.append('Photo', this.selectedFile, this.selectedFile.name);
      formData.append('User_Status_ID', this.registerFormGroup.value.User_Status_ID);
      formData.append('User_Type_ID', this.registerFormGroup.value.User_Type_ID);
  
      this.userService.RegisterUser(formData).subscribe({
        next: (response) => {
          console.log('Registration Success:', response);
          this.registerFormGroup.reset();
          this.openSnackbar('Your user profile has been created', 'success');
          this.router.navigate(['/login']);
        },
        error: (error) => {
          console.log('Registration Error:', error);
          let errorMessage = 'Registration failed: ';
  
          if (error.error === 'DuplicateEmail') {
            errorMessage += 'This email address is already registered.';
          } else if (error.error === 'DuplicatePhoneNumber') {
            errorMessage += 'This phone number is already registered.';
          } else if (error.error === 'DuplicateDateOfBirth') {
            errorMessage += 'This date of birth is already registered.';
          } else if (error.error === 'DuplicateIdNumber') {
            errorMessage += 'This ID number is already registered.';
          } else if (error.error === 'DuplicatePhoto') {
            errorMessage += 'This photo is already associated with another user.';
          } else {
            errorMessage += 'Unknown error. Please try again later.';
          }
  
          this.openSnackbar(errorMessage, 'danger');
        }
      });
    } else {
      console.log('Form is not valid or file is not selected');
      console.log(this.registerFormGroup.errors);
      console.log(this.registerFormGroup.controls);

      this.openSnackbar('Form is not filled in correctly', 'danger');
    }
  }
  

  openSnackbar(message: string, type: 'success' | 'danger') {
    this.snackBar.open(message, 'Close', {
      duration: 5000,
      panelClass: type === 'success' ? 'snackbar-success' : 'snackbar-danger'
    });
  }

  get f() {
    return this.registerFormGroup.controls;
  }

  resetForm() {
    this.registerFormGroup.reset({
      user_status_id: 1  // Reset user_status_id to default value of 1 (Active)
    });
    this.router.navigate(['/login']);
  }
}

//css
.register-wrapper {
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  background: #f0f4f7; /* Light blue background */
}

.box {
  padding: 60px 50px 40px;
  width: 100%;
  max-width: 800px;
  background: #fff;
  border-radius: 10px;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}

.card-header {
  text-align: center;
  font-weight: 700;
  color: #000; /* Blue color for header text */
}

.card-title {
  font-size: 30px;
  margin: 0;
}

.form-group label {
  color: #000; /* Blue color for form labels */
}

.form-control {
  border: 1px solid #ccc;
  padding: 10px;
  border-radius: 5px;
  transition: border-color 0.3s ease-in-out;
}

.form-control:focus {
  border-color: #000; /* Blue border on focus */
  box-shadow: 0 0 5px rgba(63, 81, 181, 0.2); /* Light blue shadow on focus */
}

.text-danger {
  color: #f44336; /* Red color for error messages */
}

.btn-primary {
  background-color: #4caf50; /* Green background for primary button */
  border-color: #4caf50; /* Green border for primary button */
  color: #fff; /* White text for primary button */
}

.btn-secondary {
  background-color: #f44336; /* Red background for secondary button */
  border-color: #f44336; /* Red border for secondary button */
  color: #fff; /* White text for secondary button */
}

.card-footer {
  display: flex;
  justify-content: space-between;
  padding: 20px;
}

.card-footer .btn {
  width: 48%;
}

/* Snackbar Styles */
.snackbar {
  visibility: visible;
  min-width: 250px;
  margin-left: -125px;
  background-color: #333;
  color: #fff;
  text-align: center;
  border-radius: 2px;
  padding: 16px;
  position: fixed;
  z-index: 1;
  left: 50%;
  bottom: 30px;
  font-size: 17px;
}

.snackbar-success {
  background-color: #4caf50; /* Green for success */
}

.snackbar-danger {
  background-color: #f44336; /* Red for error */
}

.close-snackbar {
  color: white;
  font-weight: bold;
  float: right;
  font-size: 22px;
  line-height: 20px;
  cursor: pointer;
  background: none;
  border: none;
}
//html
<div class="register-wrapper d-flex justify-content-center align-items-center">
    <div class="card box">
      <div class="card-header text-center">
        <h4 class="card-title">Sign-up</h4>
      </div>
      <form class="form" [formGroup]="registerForm" (ngSubmit)="RegisterEmployee()">
        <div class="card-body">
          <div class="row">
            <div class="col-md-6">
              <div class="form-group">
                <label for="Name" class="form-label">Name</label>
                <input type="text" class="form-control" id="Name" placeholder="Enter your Name" formControlName="Name">
                <div *ngIf="registerForm.controls['Name'].invalid && (registerForm.controls['Name'].dirty || registerForm.controls['Name'].touched)" class="text-danger">
                  <div *ngIf="registerForm.controls['Name'].errors?.['required']">Name is required.</div>
                  <div *ngIf="registerForm.controls['Name'].errors?.['minlength']">Name must be at least 2 characters long.</div>
                  <div *ngIf="registerForm.controls['Name'].errors?.['maxlength']">Name must be at most 20 characters long.</div>
                </div>
              </div>
              <div class="form-group">
                <label for="Surname" class="form-label">Surname</label>
                <input type="text" class="form-control" id="Surname" placeholder="Enter your Surname" formControlName="Surname">
                <div *ngIf="registerForm.controls['Surname'].invalid && (registerForm.controls['Surname'].dirty || registerForm.controls['Surname'].touched)" class="text-danger">
                  <div *ngIf="registerForm.controls['Surname'].errors?.['required']">Surname is required.</div>
                  <div *ngIf="registerForm.controls['Surname'].errors?.['minlength']">Surname must be at least 2 characters long.</div>
                  <div *ngIf="registerForm.controls['Surname'].errors?.['maxlength']">Surname must be at most 20 characters long.</div>
                </div>
              </div>
              <div class="form-group">
                <label for="Email" class="form-label">Email Address</label>
                <input type="Email" class="form-control" id="Email" placeholder="Enter a valid Email address" formControlName="Email">
                <div *ngIf="registerForm.controls['Email'].invalid && (registerForm.controls['Email'].dirty || registerForm.controls['Email'].touched)" class="text-danger">
                  <div *ngIf="registerForm.controls['Email'].errors?.['required']">Email is required.</div>
                  <div *ngIf="registerForm.controls['Email'].errors?.['email']">Email is invalid.</div>
                </div>
              </div>
              <div class="form-group">
                <label for="Password" class="form-label">Password</label>
                <input type="Password" class="form-control" id="password" placeholder="Enter between 8 to 15 characters" formControlName="Password">
                <div *ngIf="registerForm.controls['Password'].invalid && (registerForm.controls['Password'].dirty || registerForm.controls['Password'].touched)" class="text-danger">
                  <div *ngIf="registerForm.controls['Password'].errors?.['required']">Password is required.</div>
                  <div *ngIf="registerForm.controls['Password'].errors?.['minlength']">Password must be at least 8 characters.</div>
                  <div *ngIf="registerForm.controls['Password'].errors?.['maxlength']">Password cannot be more than 15 characters.</div>
                </div>
              </div>
              <div class="form-group">
                <label for="PhoneNumber" class="form-label">Phone Number</label>
                <input type="text" class="form-control" id="PhoneNumber" placeholder="Enter your Phone Number" formControlName="PhoneNumber">
                <div *ngIf="registerForm.controls['PhoneNumber'].invalid && (registerForm.controls['PhoneNumber'].dirty || registerForm.controls['PhoneNumber'].touched)" class="text-danger">
                  <div *ngIf="registerForm.controls['PhoneNumber'].errors?.['required']">Phone Number is required.</div>
                  <div *ngIf="registerForm.controls['PhoneNumber'].errors?.['pattern']">Phone Number format invalid.</div>
                </div>
              </div>
            </div>
  
            <div class="col-md-6">
              <div class="form-group">
                <label for="Date_of_Birth" class="form-label">Date of Birth</label>
                <input type="date" class="form-control" id="Date_of_Birth" placeholder="Enter your Date of Birth" formControlName="Date_of_Birth">
                <div *ngIf="registerForm.controls['Date_of_Birth'].invalid && (registerForm.controls['Date_of_Birth'].dirty || registerForm.controls['Date_of_Birth'].touched)" class="text-danger">
                  <div *ngIf="registerForm.controls['Date_of_Birth'].errors?.['required']">Date of Birth is required.</div>
                  <div *ngIf="registerForm.controls['Date_of_Birth'].errors?.['futureDate']">Date of birth cannot be a future date.</div>
                  <div *ngIf="registerForm.controls['Date_of_Birth'].errors?.['olderThan100']">Date of birth cannot be more than 100 years ago.</div>
                  <div *ngIf="registerForm.controls['Date_of_Birth'].errors?.['futureYear']">User cannot be younger than 16.</div>
                </div>
              </div>
              <div class="form-group">
                <label for="Id_Number" class="form-label">ID Number</label>
                <input type="text" class="form-control" id="Id_Number" placeholder="Enter your ID Number" formControlName="Id_Number">
                <div *ngIf="registerForm.controls['Id_Number'].invalid && (registerForm.controls['Id_Number'].dirty || registerForm.controls['Id_Number'].touched)" class="text-danger">
                  <div *ngIf="registerForm.controls['Id_Number'].errors?.['required']">ID Number is required.</div>
                  <div *ngIf="registerForm.controls['Id_Number'].errors?.['pattern']">ID Number must be 13 digits.</div>
                </div>
              </div>
              <div class="form-group">
                <label for="Physical_Address" class="form-label">Physical Address</label>
                <input type="text" class="form-control" id="Physical_Address" placeholder="Enter your Physical Address" formControlName="Physical_Address">
                <div *ngIf="registerForm.controls['Physical_Address'].invalid && (registerForm.controls['Physical_Address'].dirty || registerForm.controls['Physical_Address'].touched)" class="text-danger">
                  <div *ngIf="registerForm.controls['Physical_Address'].errors?.['required']">Physical Address is required.</div>
                  <div *ngIf="registerForm.controls['Physical_Address'].errors?.['minlength']">Physical Address must be at least 7 characters long.</div>
                  <div *ngIf="registerForm.controls['Physical_Address'].errors?.['maxlength']">Physical Address must be at most 100 characters long.</div>
                </div>
              </div>
              <div class="form-group">
                <label for="Photo" class="form-label">Photo</label>
                <input type="file" class="form-control" id="Photo" (change)="onFileChange($event)">
              </div>
            </div>
          </div>
        </div>
        <div class="card-footer d-flex justify-content-between">
          <button type="submit" class="btn btn-primary">Sign-up</button>
          <button type="button" class="btn btn-secondary">Cancel</button>
        </div>
      </form>
    </div>
  </div>
//ts
import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
import { FlexLayoutModule } from '@angular/flex-layout';
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Router } from '@angular/router';
import { UserService } from '../Services/userprofile.service';

@Component({
  selector: 'app-register-employee',
  standalone: true,
  imports: [ReactiveFormsModule, CommonModule, FlexLayoutModule],
  templateUrl: './register-employee.component.html',
  styleUrl: './register-employee.component.css'
})
export class RegisterEmployeeComponent {
  registerForm: FormGroup;
  selectedFile: File | null = null;

  constructor(
    private router: Router,
    private userService: UserService,
    private fb: FormBuilder,
    private snackBar: MatSnackBar
  ) {
    this.registerForm = this.fb.group({
      Name: ['', [Validators.required, Validators.minLength(2), Validators.maxLength(20)]],
      Surname: ['', [Validators.required, Validators.minLength(2), Validators.maxLength(20)]],
      Email: ['', [Validators.required, Validators.email]],
      Physical_Address: ['', [Validators.required, Validators.minLength(7), Validators.maxLength(100)]],
      PhoneNumber: ['', [Validators.required, Validators.pattern('^\\d{10}$')]],
      Password: ['', [Validators.required, Validators.minLength(8), Validators.maxLength(15)]],
      Id_Number: ['', [Validators.required, Validators.pattern('^\\d{13}$')]],
      Date_of_Birth: ['', [Validators.required, this.validateDateOfBirth]],
      User_Status_ID: [1, Validators.required],
      User_Type_ID: [2, Validators.required],
      Employment_Date: [new Date(), Validators.required],
      Hours_Worked: [0, Validators.required],
      Employee_Type_ID: [1, Validators.required],
      Shift_ID: [null]
    });    
  }

  validateDateOfBirth(control: any): { [key: string]: any } | null {
    const selectedDate = new Date(control.value);
    const currentDate = new Date();
    const minDate = new Date(currentDate.getFullYear() - 100, currentDate.getMonth(), currentDate.getDate());
    const maxDate = new Date(currentDate.getFullYear() - 16, currentDate.getMonth(), currentDate.getDate());
  
    if (selectedDate > currentDate) {
      return { 'futureDate': true };
    }
  
    if (selectedDate < minDate) {
      return { 'olderThan100': true };
    }
  
    if (selectedDate > maxDate) {
      return { 'futureYear': true };
    }
  
    return null;
  }
  

  onFileChange(event: any) {
    this.selectedFile = event.target.files[0];
  }

  RegisterEmployee() {
    if (this.registerForm.invalid) {
      return;
    }

    if (!this.selectedFile) {
      this.snackBar.open('Photo is required', 'Close', { duration: 3000 });
      return;
    }

    const formValues = this.registerForm.value;
    formValues.Date_of_Birth = new Date(formValues.Date_of_Birth).toISOString();

    this.userService.registerEmployee(this.registerForm.value, this.selectedFile).subscribe({
      next: (response) => {
        console.log('Registration Success:', response);
        this.snackBar.open('Employee registered successfully', 'Close', { duration: 3000 });
        this.registerForm.reset();
        this.selectedFile = null;
        this.router.navigate(['/login']);
      },
      error: (error) => {
        console.log('Registration Error:', error);
        this.snackBar.open('Failed to register employee', 'Close', { duration: 3000 });
      }
    });
  }
}

//css
.register-wrapper {
    height: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
    background: #f0f4f7; /* Light blue background */
  }
  
  .box {
    padding: 60px 50px 40px;
    width: 100%;
    max-width: 800px;
    background: #fff;
    border-radius: 10px;
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
  }
  
  .card-header {
    text-align: center;
    font-weight: 700;
    color: #000; /* Blue color for header text */
  }
  
  .card-title {
    font-size: 30px;
    margin: 0;
  }
  
  .form-group label {
    color: #000; /* Blue color for form labels */
  }
  
  .form-control {
    border: 1px solid #ccc;
    padding: 10px;
    border-radius: 5px;
    transition: border-color 0.3s ease-in-out;
  }
  
  .form-control:focus {
    border-color: #000; /* Blue border on focus */
    box-shadow: 0 0 5px rgba(63, 81, 181, 0.2); /* Light blue shadow on focus */
  }
  
  .text-danger {
    color: #f44336; /* Red color for error messages */
  }
  
  .btn-primary {
    background-color: #4caf50; /* Green background for primary button */
    border-color: #4caf50; /* Green border for primary button */
    color: #fff; /* White text for primary button */
  }
  
  .btn-secondary {
    background-color: #f44336; /* Red background for secondary button */
    border-color: #f44336; /* Red border for secondary button */
    color: #fff; /* White text for secondary button */
  }
  
  .card-footer {
    display: flex;
    justify-content: space-between;
    padding: 20px;
  }
  
  .card-footer .btn {
    width: 48%;
  }
  
  /* Snackbar Styles */
  .snackbar {
    visibility: visible;
    min-width: 250px;
    margin-left: -125px;
    background-color: #333;
    color: #fff;
    text-align: center;
    border-radius: 2px;
    padding: 16px;
    position: fixed;
    z-index: 1;
    left: 50%;
    bottom: 30px;
    font-size: 17px;
  }
  
  .snackbar-success {
    background-color: #4caf50; /* Green for success */
  }
  
  .snackbar-danger {
    background-color: #f44336; /* Red for error */
  }
  
  .close-snackbar {
    color: white;
    font-weight: bold;
    float: right;
    font-size: 22px;
    line-height: 20px;
    cursor: pointer;
    background: none;
    border: none;
  }
  
//CreateOrder Method
[HttpPost]
[Route("CreateOrder")]
public async Task<IActionResult> CreateOrder([FromBody] OrderViewModel ov)
{
    // Validate input
    if (ov == null || ov.OrderLines == null || !ov.OrderLines.Any())
    {
        return BadRequest("OrderViewModel or OrderLines field is required.");
    }

    // Extract user ID from claims
    var userId = User.FindFirstValue("userId");
    if (userId == null)
    {
        return Unauthorized("User not logged in");
    }

    // Retrieve member
    var member = await _context.Members.FirstOrDefaultAsync(m => m.User_ID == int.Parse(userId));
    if (member == null)
    {
        return Unauthorized("User is not a member");
    }

    // Create and save the order
    var order = new Order
    {
        Member_ID = member.Member_ID,
        Order_Date = DateTime.UtcNow,
        Order_Status_ID = ov.Order_Status_ID, // Use the Order_Status_ID from the view model
        IsCollected = ov.IsCollected,
        Total_Price = ov.Total_Price // Ensure Total_Price is also set
    };

    _context.Orders.Add(order);
    await _context.SaveChangesAsync(); // Save to get Order_ID

    // Add order lines
    var orderLines = ov.OrderLines.Select(ol => new Order_Line
    {
        Product_ID = ol.Product_ID,
        Quantity = ol.Quantity,
        Unit_Price = ol.Unit_Price,
        Order_ID = order.Order_ID // Link to the created order
    }).ToList();

    _context.Order_Lines.AddRange(orderLines);
    await _context.SaveChangesAsync(); // Save order lines

    // Clear the member's cart
    var cart = await _context.Carts
        .Include(c => c.Cart_Items)
        .FirstOrDefaultAsync(c => c.Member_ID == member.Member_ID);

    if (cart != null)
    {
        _context.Cart_Items.RemoveRange(cart.Cart_Items);
        await _context.SaveChangesAsync();
    }

    return Ok(order);
}
[HttpPost]
[Route("MoveFromWishlistToCart")]
public async Task<IActionResult> MoveFromWishlistToCart([FromBody] CartViewModel model)
{
    var userId = User.FindFirstValue("userId");

    if (userId == null)
    {
        return Unauthorized("User not logged in");
    }

    var member = await _context.Members.FirstOrDefaultAsync(m => m.User_ID == int.Parse(userId));
    if (member == null)
    {
        return Unauthorized("User is not a member");
    }

    var wishlist = await _context.Wishlists.FirstOrDefaultAsync(w => w.Member_ID == member.Member_ID);
    if (wishlist == null)
    {
        return NotFound("Wishlist not found");
    }

    var wishlistItem = await _context.Wishlist_Items
        .FirstOrDefaultAsync(w => w.Product_ID == model.Product_ID && w.Wishlist_ID == wishlist.Wishlist_ID);

    if (wishlistItem == null)
    {
        return NotFound("Product not found in the wishlist");
    }

    var cart = await _context.Carts.FirstOrDefaultAsync(c => c.Member_ID == member.Member_ID);
    if (cart == null)
    {
        cart = new Cart { Member_ID = member.Member_ID };
        _context.Carts.Add(cart);
        await _context.SaveChangesAsync();
    }

    var existingCartItem = await _context.Cart_Items
        .FirstOrDefaultAsync(c => c.Product_ID == model.Product_ID && c.Cart_ID == cart.Cart_ID);

    if (existingCartItem != null)
    {
        existingCartItem.Quantity += 1;
        _context.Cart_Items.Update(existingCartItem);
    }
    else
    {
        var cartItem = new Cart_Item
        {
            Product_ID = model.Product_ID,
            Quantity = 1,
            Cart_ID = cart.Cart_ID
        };

        _context.Cart_Items.Add(cartItem);
    }

    _context.Wishlist_Items.Remove(wishlistItem);

    await _context.SaveChangesAsync();

    return Content("Product moved from wishlist to cart", "text/plain");
}
//html
<app-navbar></app-navbar>
<div class="main-container">
    <div class="header-search-container">
        <i class="bi bi-arrow-left-circle header-icon" (click)="goBack()"></i>
        <h2 class="header-title" routerLink="/product-list">AV MERCH</h2>
        <div class="search-bar-container">
            <input type="text" class="form-control search-bar" placeholder="Search products" [(ngModel)]="searchTerm" (input)="filteredProductsBySearch()">
        </div>
    </div>
    
    <!-- Side Navbar -->
    <div class="side-navbar" [ngClass]="{'open': isSidebarOpen}">
      <ul>
        <li (click)="filterProductsByCategory(0)">All</li>
        <li (click)="filterProductsByCategory(1)">Tops</li>
        <li (click)="filterProductsByCategory(2)">Bottoms</li>
      </ul>
    </div>
    
    <!-- Toggle Button -->
    <button class="toggle-btn" (click)="toggleSidebar()">
      <i class="bi" [ngClass]="{'bi-list': !isSidebarOpen, 'bi-x': isSidebarOpen}"></i>
    </button>
    
    <div class="product-list-container">
        <div *ngIf="filteredProducts.length === 0">
            <br>
            <br>
            <h1>"No Products Found"</h1>
        </div>
        <div *ngIf="filteredProducts.length > 0" class="row">
            <div class="col-6" *ngFor="let product of filteredProducts">
                <div class="card" style="width: 18rem;">
                    <div class="card-body">
                        <div class="card-img-container">
                            <a routerLink="/product-item/{{product.product_ID}}">
                                <img [src]="getImageSrc(product.product_Img)" alt= "Product Image" class="card-img-top">
                            </a>
                        </div>
                        <p class="card-text" style="text-align: center;">{{product.product_Name}}</p>
                    </div>
                </div>
                <br>
                <br>
            </div>
        </div>
    </div>
    <br>
</div>

//ts
import { Component } from '@angular/core';
import { OrderService } from '../Services/order.service';
import { Product } from '../shared/order';
import { CommonModule } from '@angular/common';
import { Router, RouterLink } from '@angular/router';
import { NavbarComponent } from './navbar.component';
import { Location } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';

@Component({
  selector: 'app-product-list',
  standalone: true,
  imports: [CommonModule, RouterLink, NavbarComponent, FormsModule],
  templateUrl: './product-list.component.html',
  styleUrl: './product-list.component.css'
})
export class ProductListComponent {
  productImage: string | null = null;
  products:Product[] = [];
  totalCartItems: number = 0;
  filteredProducts: Product[] = [];
  isSidebarOpen: boolean = false;
  searchTerm: string = '';

  constructor(private orderService:OrderService, private location: Location, private router: Router) {}

  ngOnInit(){
    this.GetAllProducts();
    this.orderService.cartItemsCount.subscribe(count => this.totalCartItems = count); // Update cart items count
    this.orderService.loadWishlistItems();
    this.orderService.loadCartItems();
  }

  GetAllProducts(): void {
    this.orderService.getAllProducts().subscribe({
      next: (p) => {
        this.products = p;
        this.filteredProducts = p;   
        console.log(p);        
      },
      error: (err) => {
        console.error('Error fetching products', err);
      }
    });
  }

  filterProductsByCategory(categoryId: number): void {
    if (categoryId === 0) {
      this.filteredProducts = this.products; // Display all products if 'All' is selected
    } else {
      this.filteredProducts = this.products.filter(product => product.product_Category_ID === categoryId);
    }
    this.toggleSidebar(); // Close sidebar after selecting a category
  }
  

  filteredProductsBySearch(): void {
    if (!this.searchTerm) {
      this.filteredProducts = this.products;
    } else {
      const term = this.searchTerm.toLowerCase();
      this.filteredProducts = this.products.filter(product =>
        product.product_Name.toLowerCase().includes(term)
      );
    }
  }

  toggleSidebar(): void {
    this.isSidebarOpen = !this.isSidebarOpen;
  }

  onCartUpdated(quantity: number) {  // Update cart count
    this.totalCartItems += quantity;
    // this.orderService.updateCartCount(this.totalCartItems); // Update the cart count in the service
  }

  getImageSrc(base64String: string): string {
    return `data:image/jpeg;base64,${base64String}`;
  }
  
  goBack() {
    const userTypeId = JSON.parse(localStorage.getItem('User')!).userTypeId;
    const userId = JSON.parse(localStorage.getItem('User')!).userId;
    if (userTypeId === 1) {  // Ensure userTypeID is compared as string
      this.router.navigateByUrl(`/OwnerHome/${userId}`);
    } else if (userTypeId === 2) {
      this.router.navigateByUrl(`/EmployeeHome/${userId}`);
    } else if (userTypeId === 3) {
      this.router.navigateByUrl(`/Home/${userId}`);
    }
  }
}
// Method to export help content to PDF
  exportToPDF(): void {
    const doc = new jsPDF('p', 'mm', 'a4');
    const margin = 10;
    const pageWidth = 210;
    const pageHeight = 295;
  
    const addContentToPDF = (content: string) => {
      const tempContainer = document.createElement('div');
      tempContainer.style.position = 'absolute';
      tempContainer.style.left = '-9999px';
      tempContainer.style.fontSize = '14px'; // Set default font size
      tempContainer.innerHTML = content;
      document.body.appendChild(tempContainer);
  
      html2canvas(tempContainer, {
        scale: 2, // Increase scale for better quality
        useCORS: true,
        logging: true
      }).then(canvas => {
        const imgData = canvas.toDataURL('image/png');
        const imgWidth = pageWidth - 2 * margin;
        const imgHeight = canvas.height * imgWidth / canvas.width;
        let heightLeft = imgHeight;
        let position = 0;
  
        doc.addImage(imgData, 'PNG', margin, position, imgWidth, imgHeight);
        heightLeft -= pageHeight;
  
        while (heightLeft > 0) {
          doc.addPage();
          position = heightLeft - imgHeight;
          doc.addImage(imgData, 'PNG', margin, position, imgWidth, imgHeight);
          heightLeft -= pageHeight;
        }
  
        document.body.removeChild(tempContainer);
        doc.save('help-guide.pdf');
      }).catch(err => {
        console.error('Error capturing content:', err);
        document.body.removeChild(tempContainer);
      });
    };
  
    const chunkSize = 5;
    for (let i = 0; i < this.filteredContent.length; i += chunkSize) {
      const chunk = this.filteredContent.slice(i, i + chunkSize);
      const chunkContent = chunk.map(item => `
        <h5 style="font-size: 16px;">${item.title}</h5>
        <div style="font-size: 14px;">${item.content}</div>
      `).join('');
  
      addContentToPDF(chunkContent);
    }
  }  
// Method to export help content to PDF
  exportToPDF(): void {
    const doc = new jsPDF();
    let y = 10;
    const pageHeight = doc.internal.pageSize.height;
    const margin = 10;

    this.helpContent.forEach(item => {
      if (y + 10 > pageHeight - margin) {
        doc.addPage();
        y = margin;
      }

      doc.setFontSize(14);
      doc.text(item.title, margin, y);
      y += 10;

      doc.setFontSize(12);

      const plainText = this.htmlToPlainText(item.content);
      const splitContent = doc.splitTextToSize(plainText, 180 - 2 * margin);

      splitContent.forEach((line: string) => {
        if (y + 7 > pageHeight - margin) {
          doc.addPage();
          y = margin;
        }
        doc.text(line, margin, y);
        y += 5;
      });

      y += 7; // Add space between sections
    });

    doc.save('help-guide.pdf');
  }

  // Method to convert HTML content to plain text
  htmlToPlainText(html: string): string {
    const doc = new DOMParser().parseFromString(html, 'text/html');
    return doc.body.textContent || '';
  }
//BACKEND
//Controller
using av_motion_api.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq;
using NuGet.Configuration;

namespace av_motion_api.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class DeletionSettingsController : ControllerBase
    {
        private readonly IOptionsMonitor<DeletionSettings> _deletionSettings;
        //private readonly IConfiguration _configuration;
        private readonly IConfigurationRoot _configurationRoot;

        public DeletionSettingsController(IOptionsMonitor<DeletionSettings> deletionSettings, IConfiguration configuration)
        {
            _deletionSettings = deletionSettings;
            //_configuration = configuration;
            _configurationRoot = (IConfigurationRoot)configuration;
        }

        [HttpPost]
        [Route("UpdateDeletionTime")]
        public IActionResult UpdateDeletionTime([FromBody] DeletionSettings settings)
        {
            if (settings.DeletionTimeValue < 0)
            {
                return BadRequest(new { message = "Deletion time value must be non-negative" });
            }

            var configurationFile = "appsettings.Development.json";
            var configurationPath = Path.Combine(Directory.GetCurrentDirectory(), configurationFile);

            var json = System.IO.File.ReadAllText(configurationPath);
            dynamic jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject(json);

            if (jsonObj["DeletionSettings"] == null)
            {
                jsonObj["DeletionSettings"] = new Newtonsoft.Json.Linq.JObject();
            }

            jsonObj["DeletionSettings"]["DeletionTimeValue"] = settings.DeletionTimeValue;
            jsonObj["DeletionSettings"]["DeletionTimeUnit"] = settings.DeletionTimeUnit;

            string output = Newtonsoft.Json.JsonConvert.SerializeObject(jsonObj, Newtonsoft.Json.Formatting.Indented);
            System.IO.File.WriteAllText(configurationPath, output);

            // Reload the configuration
            _configurationRoot.Reload();

            return Ok(new { message = "Deletion time updated successfully" });
        }

        [HttpGet]
        [Route("GetDeletionSettings")]
        public IActionResult GetDeletionSettings()
        {
            var settings = _deletionSettings.CurrentValue;
            if (settings == null)
            {
                return NotFound(new { message = "Deletion settings not found" });
            }
            return Ok(settings);
        }

    }
}

//Model
namespace av_motion_api.Models
{
    public class DeletionSettings
    {
        public int DeletionTimeValue { get; set; }
        public string DeletionTimeUnit { get; set; }
    }
}

//Service
using av_motion_api.Data; 
using av_motion_api.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;

namespace av_motion_api.Services
{
    public class UserDeletionService : IHostedService, IDisposable
    {
        private readonly IServiceProvider _serviceProvider;
        private readonly IOptionsMonitor<DeletionSettings> _settings;
        private Timer _timer;
        private long _remainingIntervals;
        private const long MaxInterval = Int32.MaxValue - 2;

        public UserDeletionService(IServiceProvider serviceProvider, IOptionsMonitor<DeletionSettings> settings)
        {
            _serviceProvider = serviceProvider;
            _settings = settings;
        }

        public Task StartAsync(CancellationToken cancellationToken)
        {
            ScheduleDeletionTask();
            _settings.OnChange(settings => ScheduleDeletionTask());
            return Task.CompletedTask;
        }

        private void ScheduleDeletionTask()
        {
            var interval = GetTimeSpan(_settings.CurrentValue.DeletionTimeValue, _settings.CurrentValue.DeletionTimeUnit);
            _remainingIntervals = (long)Math.Ceiling(interval.TotalMilliseconds / MaxInterval);

            _timer?.Dispose();
            _timer = new Timer(OnTimerElapsed, null, TimeSpan.Zero, TimeSpan.FromMilliseconds(MaxInterval));
        }



        private void OnTimerElapsed(object state)
        {
            if (--_remainingIntervals <= 0)
            {
                DeleteDeactivatedUsers(state);
                ScheduleDeletionTask(); // Reschedule for the next interval
            }
        }

        private void DeleteDeactivatedUsers(object state)
        {
            using (var scope = _serviceProvider.CreateScope())
            {
                var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();
                var userManager = scope.ServiceProvider.GetRequiredService<UserManager<User>>();

                var deletionThreshold = DateTime.UtcNow.Subtract(GetTimeSpan(_settings.CurrentValue.DeletionTimeValue, _settings.CurrentValue.DeletionTimeUnit));

                var usersToDelete = context.Users
                    .Where(u => u.User_Status_ID == 2 && u.DeactivatedAt < deletionThreshold)
                    .ToList();

                foreach (var user in usersToDelete)
                {
                    userManager.DeleteAsync(user).Wait();
                }

                context.SaveChanges();
            }
        }

        private TimeSpan GetTimeSpan(int value, string unit)
        {
            return unit.ToLower() switch
            {
                "minutes" => TimeSpan.FromMinutes(value),
                "hours" => TimeSpan.FromHours(value),
                "days" => TimeSpan.FromDays(value),
                "weeks" => TimeSpan.FromDays(value * 7),
                "months" => TimeSpan.FromDays(value * 30), // Approximation
                "years" => TimeSpan.FromDays(value * 365), // Approximation
                _ => TimeSpan.FromMinutes(value),
            };
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            _timer?.Change(Timeout.Infinite, 0);
            return Task.CompletedTask;
        }

        public void Dispose()
        {
            _timer?.Dispose();
        }
    }
}

//Program.cs
using av_motion_api.Data;
using av_motion_api.Factory;
using av_motion_api.Models;
using av_motion_api.Interfaces;
using av_motion_api.Services;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Logging;
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Configuration; // Ensure this is here
using Microsoft.Extensions.Hosting;

var builder = WebApplication.CreateBuilder(args);

// Configure the app environment
var configuration = builder.Configuration;

builder.Configuration.SetBasePath(Directory.GetCurrentDirectory())
    //.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: false);
    .AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true); // Add reloadOnChange

builder.Host.ConfigureAppConfiguration((hostingContext, config) =>
{
    //config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
    config.AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true); // Add reloadOnChange
    config.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true);
});

// Configure logging
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Logging.AddDebug();

// CORS
if (builder.Environment.IsDevelopment())
{
    builder.Services.AddCors(options =>
    {
        options.AddPolicy("AllowAll", policy =>
        {
            policy.AllowAnyOrigin()
                  .AllowAnyHeader()
                  .AllowAnyMethod();
        });
    });
}

// Add services to the container
builder.Services.AddControllers()
                .AddJsonOptions(options =>
                {
                    options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
                    options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
                });

// SQL
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddScoped<IRepository, Repository>();

builder.Services.AddIdentity<User, Role>(options =>
                {
                    options.Password.RequireUppercase = false;
                    options.Password.RequireLowercase = false;
                    options.Password.RequireNonAlphanumeric = false;
                    options.Password.RequireDigit = true;
                    options.User.RequireUniqueEmail = true;
                })
                .AddRoles<Role>()
                .AddEntityFrameworkStores<AppDbContext>()
                .AddDefaultTokenProviders();

builder.Services.AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                })
                .AddCookie()
                .AddJwtBearer(options =>
                {
                    options.TokenValidationParameters = new TokenValidationParameters()
                    {
                        ValidateIssuer = true,
                        ValidateAudience = true,
                        ValidateLifetime = true,
                        ValidateIssuerSigningKey = true,
                        ValidIssuer = builder.Configuration["Tokens:Issuer"],
                        ValidAudience = builder.Configuration["Tokens:Audience"],
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Tokens:Key"]))
                    };
                });

// Configure FormOptions for file uploads
builder.Services.Configure<FormOptions>(o =>
{
    o.ValueLengthLimit = int.MaxValue;
    o.MultipartBodyLengthLimit = int.MaxValue;
    o.MemoryBufferThreshold = int.MaxValue;
});

builder.Services.AddScoped<IUserClaimsPrincipalFactory<User>, AppUserClaimsPrincipalFactory>();

builder.Services.Configure<DataProtectionTokenProviderOptions>(options => options.TokenLifespan = TimeSpan.FromHours(3));


// Register the OrderStatusUpdater hosted service
builder.Services.AddHostedService<OrderStatusUpdater>();

// Register the DeletionSettings configuration section
builder.Services.Configure<DeletionSettings>(configuration.GetSection("DeletionSettings"));

// Register the UserDeletionService hosted service
builder.Services.AddHostedService<UserDeletionService>();

// Register ContractLinkingService
//builder.Services.AddHostedService<ContractLinkingService>();


// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

// Use CORS
app.UseCors("AllowAll");

app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Use(async (context, next) =>
{
    var logger = app.Services.GetRequiredService<ILogger<Program>>();
    logger.LogInformation("Handling request: " + context.Request.Path);
    await next.Invoke();
    logger.LogInformation("Finished handling request.");
});

app.Run();

//appsettings.Development.json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "DefaultConnection": "Server=DESKTOP-9QI4G7U\\SQLEXPRESS;Database=AV_Motion;Trusted_Connection=True;TrustServerCertificate=True"
  },
  "Tokens": {
    "Key": "hLKjZkF5q2wX3rQ$@1hy+VRv[&)0XhxJ<sk=yUpW{yE5CH@xh",
    "Issuer": "https://localhost:7185",
    "Audience": "http://localhost:4200"
  },
  "SendGrid": {
    "ApiKey": "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",
    "SenderEmail": "datalungeteam43@gmail.com",
    "SenderName": "AVSFitnessAdmin@AVMotion.com",
    "ContractsDirectory": "C:\\Contracts\\AVS_Fitness"
  },
  "DeletionSettings": {
    "DeletionTimeValue": 6,
    "DeletionTimeUnit": "Months"
  }
}

//FRONTEND
//Deletionsettings component
//html
<div class="view-deletion-settings-container">
    <button class="btn btn" (click)="goBack()">            
        <i class="bi bi-arrow-left-circle header-icon"></i>
    </button>
    <h2>Deletion Settings</h2>
    <p><strong>Deletion Time:</strong> {{deletionSettings?.deletionTimeValue}} {{deletionSettings?.deletionTimeUnit}}</p>
    <button mat-raised-button color="primary" (click)="openEditModal()">Update Settings</button>
</div>

<!-- Edit Modal -->
<div class="modal fade" id="editModal" tabindex="-1" aria-labelledby="editModalLabel" aria-hidden="true">
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header">
          <h5 class="modal-title" id="editModalLabel">Update Deletion Time</h5>
        </div>
        <div class="modal-body">
          <form [formGroup]="deletionSettingsForm">
            <div class="form-group">
              <label for="deletionTime">Deletion Time:</label>
              <input type="number" id="deletionTime" formControlName="value" class="form-control" name="deletionTime">
            </div>
            <div class="form-group">
              <label for="timeUnit">Time Unit:</label>
              <select id="timeUnit" formControlName="unit" class="form-control" name="timeUnit">
                <option value="Minutes">Minutes</option>
                <option value="Hours">Hours</option>
                <option value="Days">Days</option>
                <option value="Weeks">Weeks</option>
                <option value="Months">Months</option>
                <option value="Years">Years</option>
              </select>
            </div>
          </form>
        </div>
        <div class="modal-footer">
          <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
          <button type="button" class="btn btn-primary" (click)="saveDeletionTime()">Update</button>
        </div>
      </div>
    </div>
</div>

//ts
import { CommonModule } from '@angular/common';
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
import { UserService } from '../Services/userprofile.service';
import { DeletionSettings } from '../shared/deletionsettings';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Router, RouterLink } from '@angular/router';
import { Location } from '@angular/common';
declare var $: any; 

@Component({
  selector: 'app-deletion-settings',
  standalone: true,
  imports: [CommonModule, ReactiveFormsModule, RouterLink, FormsModule],
  templateUrl: './deletion-settings.component.html',
  styleUrl: './deletion-settings.component.css'
})
export class DeletionSettingsComponent implements OnInit {
  deletionSettings: DeletionSettings | undefined;
  deletionSettingsForm: FormGroup;

  constructor(
    private userService: UserService,
    private fb: FormBuilder,
    private snackBar: MatSnackBar,
    private router: Router,
    private location: Location
  ) {
    this.deletionSettingsForm  = this.fb.group({
      value: ['', [Validators.required, Validators.min(0)]],
      unit: ['', Validators.required]
    });
  }

  ngOnInit(): void {
    this.loadDeletionSettings();
  }

  loadDeletionSettings(): void {
    this.userService.getDeletionSettings().subscribe({
      next: (settings) => {
        this.deletionSettings = settings;
        this.deletionSettingsForm.patchValue({
          value: settings.deletionTimeValue,
          unit: settings.deletionTimeUnit
        });
      },
      error: (error) => {
        console.error('Error fetching deletion settings', error);
      }
    });
  }

  openEditModal(): void {
    $('#editModal').modal('show');
  }

  saveDeletionTime(): void {
    if (this.deletionSettingsForm.valid) {
      const settings: DeletionSettings = {
        deletionTimeValue: this.deletionSettingsForm.value.value,
        deletionTimeUnit: this.deletionSettingsForm.value.unit
      };

      this.userService.updateDeletionTime(settings).subscribe({
        next: (response) => {
          console.log("Deletion time", response )
          this.snackBar.open('Deletion time updated successfully', 'Close', { duration: 5000 });
          this.loadDeletionSettings();
          $('#editModal').modal('hide');
        },
        error: (error) => {
          console.error('Error updating deletion time', error);
          this.snackBar.open('Failed to update deletion time. Please try again', 'Close', { duration: 5000 });
          this.loadDeletionSettings();
          $('#editModal').modal('hide');
        }
      });
    }
  }

  goBack(): void {
    this.location.back();
  }
}

//Service
export class UserService {

  httpOptions = {
    headers: new HttpHeaders({
      'Content-Type': 'application/json'
    })
  };

  constructor(private http: HttpClient) {}
  endPoint: string = "https://localhost:7185/api/";
getDeletionSettings(): Observable<DeletionSettings> {
    return this.http.get<DeletionSettings>(`${this.endPoint}DeletionSettings/GetDeletionSettings`);
  }
  
  updateDeletionTime(settings: DeletionSettings): Observable<any> {
    return this.http.post<any>(`${this.endPoint}DeletionSettings/UpdateDeletionTime`, settings, this.httpOptions);
  }
//model
export interface DeletionSettings {
    deletionTimeValue: number;
    deletionTimeUnit: string;
  }
//routes
  { path:'deletion-settings', component:DeletionSettingsComponent },
-- ================================================
-- Template generated from Template Explorer using:
-- Create Procedure (New Menu).SQL
--
-- Use the Specify Values for Template Parameters 
-- command (Ctrl-Shift-M) to fill in the parameter 
-- values below.
--
-- This block of comments will not be included in
-- the definition of the procedure.
-- ================================================
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:		<Author,,Name>
-- Create date: <Create Date,,>
-- Description:	<Description,,>
-- =============================================
CREATE PROCEDURE <Procedure_Name, sysname, ProcedureName> 
	-- Add the parameters for the stored procedure here
	<@Param1, sysname, @p1> <Datatype_For_Param1, , int> = <Default_Value_For_Param1, , 0>, 
	<@Param2, sysname, @p2> <Datatype_For_Param2, , int> = <Default_Value_For_Param2, , 0>
AS
BEGIN
	-- SET NOCOUNT ON added to prevent extra result sets from
	-- interfering with SELECT statements.
	SET NOCOUNT ON;

    -- Insert statements for procedure here
	SELECT <@Param1, sysname, @p1>, <@Param2, sysname, @p2>
END
GO
//SQL QUERY
CREATE PROCEDURE UpdateUserDeletionSettings
    @DeletionTimeValue INT,
    @DeletionTimeUnit NVARCHAR(20)
AS
BEGIN
    SET NOCOUNT ON;
    
    -- Define the deletion threshold
    DECLARE @DeletionThreshold DATETIME;
    SET @DeletionThreshold = CASE
        WHEN @DeletionTimeUnit = 'Minutes' THEN DATEADD(MINUTE, -@DeletionTimeValue, GETUTCDATE())
        WHEN @DeletionTimeUnit = 'Hours' THEN DATEADD(HOUR, -@DeletionTimeValue, GETUTCDATE())
        WHEN @DeletionTimeUnit = 'Days' THEN DATEADD(DAY, -@DeletionTimeValue, GETUTCDATE())
        WHEN @DeletionTimeUnit = 'Weeks' THEN DATEADD(WEEK, -@DeletionTimeValue, GETUTCDATE())
        WHEN @DeletionTimeUnit = 'Months' THEN DATEADD(MONTH, -@DeletionTimeValue, GETUTCDATE())
        WHEN @DeletionTimeUnit = 'Years' THEN DATEADD(YEAR, -@DeletionTimeValue, GETUTCDATE())
        ELSE GETUTCDATE()
    END;

    -- Delete users based on the threshold
    DELETE FROM dbo.AspNetUsers
    WHERE User_Status_ID = 2 AND DeactivatedAt < @DeletionThreshold;
END

//UserDeletionService
using av_motion_api.Data; 
using av_motion_api.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;

namespace av_motion_api.Services
{
    public class UserDeletionService : IHostedService, IDisposable
    {
        private readonly IServiceProvider _serviceProvider;
        private readonly IOptionsMonitor<DeletionSettings> _settings;
        private Timer _timer;
        private long _remainingIntervals;
        private const long MaxInterval = Int32.MaxValue - 2;

        public UserDeletionService(IServiceProvider serviceProvider, IOptionsMonitor<DeletionSettings> settings)
        {
            _serviceProvider = serviceProvider;
            _settings = settings;
        }

        public Task StartAsync(CancellationToken cancellationToken)
        {
            ScheduleDeletionTask();
            _settings.OnChange(settings => ScheduleDeletionTask());
            return Task.CompletedTask;
        }

        private void ScheduleDeletionTask()
        {
            var interval = GetTimeSpan(_settings.CurrentValue.DeletionTimeValue, _settings.CurrentValue.DeletionTimeUnit);
            _remainingIntervals = (long)Math.Ceiling(interval.TotalMilliseconds / MaxInterval);

            _timer?.Dispose();
            _timer = new Timer(OnTimerElapsed, null, TimeSpan.Zero, TimeSpan.FromMilliseconds(MaxInterval));
        }



        private void OnTimerElapsed(object state)
        {
            if (--_remainingIntervals <= 0)
            {
                DeleteDeactivatedUsers(state);
                ScheduleDeletionTask(); // Reschedule for the next interval
            }
        }

        private void DeleteDeactivatedUsers(object state)
        {
            using (var scope = _serviceProvider.CreateScope())
            {
                var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();

                var deletionThreshold = DateTime.UtcNow.Subtract(GetTimeSpan(_settings.CurrentValue.DeletionTimeValue, _settings.CurrentValue.DeletionTimeUnit));

                var connection = context.Database.GetDbConnection();
                if (connection.State != System.Data.ConnectionState.Open)
                {
                    connection.Open();
                }

                using (var command = connection.CreateCommand())
                {
                    command.CommandText = "UpdateUserDeletionSettings";
                    command.CommandType = System.Data.CommandType.StoredProcedure;
                    command.Parameters.Add(new SqlParameter("@DeletionTimeValue", _settings.CurrentValue.DeletionTimeValue));
                    command.Parameters.Add(new SqlParameter("@DeletionTimeUnit", _settings.CurrentValue.DeletionTimeUnit));

                    command.ExecuteNonQuery();
                }
            }
        }


        private TimeSpan GetTimeSpan(int value, string unit)
        {
            return unit.ToLower() switch
            {
                "minutes" => TimeSpan.FromMinutes(value),
                "hours" => TimeSpan.FromHours(value),
                "days" => TimeSpan.FromDays(value),
                "weeks" => TimeSpan.FromDays(value * 7),
                "months" => TimeSpan.FromDays(value * 30), // Approximation
                "years" => TimeSpan.FromDays(value * 365), // Approximation
                _ => TimeSpan.FromMinutes(value),
            };
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            _timer?.Change(Timeout.Infinite, 0);
            return Task.CompletedTask;
        }

        public void Dispose()
        {
            _timer?.Dispose();
        }
    }
}

//Program.cs --unchanged
using av_motion_api.Data;
using av_motion_api.Factory;
using av_motion_api.Models;
using av_motion_api.Interfaces;
using av_motion_api.Services;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Logging;
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Configuration; // Ensure this is here
using Microsoft.Extensions.Hosting;

var builder = WebApplication.CreateBuilder(args);

// Configure the app environment
var configuration = builder.Configuration;

builder.Configuration.SetBasePath(Directory.GetCurrentDirectory())
    //.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: false);
    .AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true); // Add reloadOnChange

builder.Host.ConfigureAppConfiguration((hostingContext, config) =>
{
    //config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
    config.AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true); // Add reloadOnChange
    config.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true);
});

// Configure logging
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Logging.AddDebug();

// CORS
if (builder.Environment.IsDevelopment())
{
    builder.Services.AddCors(options =>
    {
        options.AddPolicy("AllowAll", policy =>
        {
            policy.AllowAnyOrigin()
                  .AllowAnyHeader()
                  .AllowAnyMethod();
        });
    });
}

// Add services to the container
builder.Services.AddControllers()
                .AddJsonOptions(options =>
                {
                    options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
                    options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
                });

// SQL
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddScoped<IRepository, Repository>();

builder.Services.AddIdentity<User, Role>(options =>
                {
                    options.Password.RequireUppercase = false;
                    options.Password.RequireLowercase = false;
                    options.Password.RequireNonAlphanumeric = false;
                    options.Password.RequireDigit = true;
                    options.User.RequireUniqueEmail = true;
                })
                .AddRoles<Role>()
                .AddEntityFrameworkStores<AppDbContext>()
                .AddDefaultTokenProviders();

builder.Services.AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                })
                .AddCookie()
                .AddJwtBearer(options =>
                {
                    options.TokenValidationParameters = new TokenValidationParameters()
                    {
                        ValidateIssuer = true,
                        ValidateAudience = true,
                        ValidateLifetime = true,
                        ValidateIssuerSigningKey = true,
                        ValidIssuer = builder.Configuration["Tokens:Issuer"],
                        ValidAudience = builder.Configuration["Tokens:Audience"],
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Tokens:Key"]))
                    };
                });

// Configure FormOptions for file uploads
builder.Services.Configure<FormOptions>(o =>
{
    o.ValueLengthLimit = int.MaxValue;
    o.MultipartBodyLengthLimit = int.MaxValue;
    o.MemoryBufferThreshold = int.MaxValue;
});

builder.Services.AddScoped<IUserClaimsPrincipalFactory<User>, AppUserClaimsPrincipalFactory>();

builder.Services.Configure<DataProtectionTokenProviderOptions>(options => options.TokenLifespan = TimeSpan.FromHours(3));


// Register the OrderStatusUpdater hosted service
builder.Services.AddHostedService<OrderStatusUpdater>();

// Register the DeletionSettings configuration section
builder.Services.Configure<DeletionSettings>(configuration.GetSection("DeletionSettings"));

// Register the UserDeletionService hosted service
builder.Services.AddHostedService<UserDeletionService>();

// Register ContractLinkingService
//builder.Services.AddHostedService<ContractLinkingService>();


// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

// Use CORS
app.UseCors("AllowAll");

app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Use(async (context, next) =>
{
    var logger = app.Services.GetRequiredService<ILogger<Program>>();
    logger.LogInformation("Handling request: " + context.Request.Path);
    await next.Invoke();
    logger.LogInformation("Finished handling request.");
});

app.Run();

//appsettings.Development.json --unchanged
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "DefaultConnection": "Server=DESKTOP-9QI4G7U\\SQLEXPRESS;Database=AV_Motion;Trusted_Connection=True;TrustServerCertificate=True"
  },
  "Tokens": {
    "Key": "hLKjZkF5q2wX3rQ$@1hy+VRv[&)0XhxJ<sk=yUpW{yE5CH@xh",
    "Issuer": "https://localhost:7185",
    "Audience": "http://localhost:4200"
  },
  "SendGrid": {
    "ApiKey": "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",
    "SenderEmail": "datalungeteam43@gmail.com",
    "SenderName": "AVSFitnessAdmin@AVMotion.com",
    "ContractsDirectory": "C:\\Contracts\\AVS_Fitness"
  },
  "DeletionSettings": {
    "DeletionTimeValue": 1,
    "DeletionTimeUnit": "Minutes"
  }
}
  
//AppDbContext --Add & run migration
  public DbSet<DeletionSettings> DeletionSettings { get; set; }

  // Configure the DeletionSettings entity
  builder.Entity<DeletionSettings>()
    .HasKey(ds => new { ds.DeletionTimeValue, ds.DeletionTimeUnit });

  builder.Entity<DeletionSettings>()
    .Property(ds => ds.DeletionTimeValue)
    .IsRequired();

  builder.Entity<DeletionSettings>()
    .Property(ds => ds.DeletionTimeUnit)
    .IsRequired()
    .HasMaxLength(50);
//Installations
npm install xlsx file-saver
npm install xlsx file-saver

//payment component
//ts
import { Component } from '@angular/core';
import { PaymentService } from '../Services/payment.service';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Router } from '@angular/router';
import { Location } from '@angular/common';
import * as XLSX from 'xlsx';
import * as FileSaver from 'file-saver';
declare var $: any;

@Component({
  selector: 'app-payment',
  standalone: true,
  imports: [CommonModule, FormsModule, ReactiveFormsModule],
  templateUrl: './payment.component.html',
  styleUrl: './payment.component.css'
})
export class PaymentComponent {
  payments: any[] = [];
  selectedPayment: any | null = null;
  filterPayment: any[] = [];
  searchTerm: string = '';

  constructor(private paymentService: PaymentService, private router: Router, private location: Location, private snackBar: MatSnackBar) {}

  ngOnInit(): void {
    this.loadPayments();
  }

  loadPayments(): void {
    this.paymentService.getPayments().subscribe({
      next: (data) => {
        this.payments = data;
        this.filterPayment = data;
        console.log(data)
      },
      error: (err) => {
        console.error('Error fetching payments:', err);
      }
    });
  }  

  filteredPayments(): void {
    if (!this.searchTerm) {
      this.filterPayment = this.payments;
    } else {
      const term = this.searchTerm.toLowerCase();
      this.filterPayment = this.payments.filter(payment =>
        payment.payment_ID.toString().includes(term) ||
        payment.memberName.toLowerCase().includes(term) ||
        payment.amount.toString().includes(term) ||
        payment.payment_Date.toString().includes(term) ||
        payment.paymentTypeName.toLowerCase().includes(term)
      );
    }
  }
 
  openModal(payment: any): void {
    this.selectedPayment = payment;
    $('#userModal').modal('show');
  }
 
  closeModal(): void {
    $('#userModal').modal('hide');
  }
 
  goBack(): void {
    this.location.back();
  }

  exportToExcel(): void {
    const customHeaders = [
      { header: 'Payment ID', key: 'payment_ID' },
      { header: 'Member Name', key: 'memberName' },
      { header: 'Amount', key: 'amount' },
      { header: 'Payment Date', key: 'payment_Date' },
      { header: 'Payment Type', key: 'paymentTypeName' }
    ];

    const worksheetData = this.filterPayment.map(payment => ({
      payment_ID: payment.payment_ID,
      memberName: payment.memberName,
      amount: payment.amount,
      payment_Date: this.formatDate(payment.payment_Date), // Format the date
      paymentTypeName: payment.paymentTypeName
    }));

    const worksheet = XLSX.utils.json_to_sheet(worksheetData, { header: customHeaders.map(h => h.key) });
    const workbook = { Sheets: { 'Payments': worksheet }, SheetNames: ['Payments'] };

    // Modify column headers
    customHeaders.forEach((header, index) => {
      const col = XLSX.utils.encode_col(index); // Convert 0 -> 'A', 1 -> 'B', etc.
      worksheet[`${col}1`] = { t: 's', v: header.header };
    });

    const excelBuffer: any = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
    this.saveAsExcelFile(excelBuffer, 'payments');
  }

  private formatDate(date: string): string {
    return new Date(date).toISOString().split('T')[0];
  }

  private saveAsExcelFile(buffer: any, fileName: string): void {
    const data: Blob = new Blob([buffer], { type: EXCEL_TYPE });
    FileSaver.saveAs(data, `${fileName}_${new Date().getTime()}.xlsx`);
  }
}

const EXCEL_TYPE = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8';

//html
<br>
<div class="header-search-container">
    <i class="bi bi-arrow-left-circle header-icon" (click)="goBack()"></i>
    <h2 class="header-title">Payment Manager</h2>
    <div class="search-bar-container">
        <input type="text" class="form-control search-bar" placeholder="Search payments" [(ngModel)]="searchTerm" (input)="filteredPayments()">
    </div>
</div>
<br>
<br>
<div class="container">
    <div *ngIf="payments.length === 0">
        <p>No payments have been made.</p>
    </div>
    <div *ngIf="payments.length > 0" class="row">
        <table class="table">
            <thead class="table-dark">
                <tr>
                <th>Payment ID</th>
                <th>Member Name</th>
                <th>Amount</th>
                <th>Payment Date</th>
                <th>Payment Type</th>
                <th>Action</th>
                </tr>
            </thead>
            <tbody class>
                <tr *ngFor="let payment of filterPayment">
                <td>{{ payment.payment_ID }}</td>
                <td>{{ payment.memberName }}</td>
                <td>{{ payment.amount | currency: 'R ' }}</td>
                <td>{{ payment.payment_Date | date: 'short' }}</td>
                <td>{{ payment.paymentTypeName }}</td>
                <td>
                    <button class="btn btn-primary" (click)="openModal(payment)">View</button>
                </td>
                </tr>
            </tbody>
    </table>
    
    <button class="btn btn-success" (click)="exportToExcel()">Export to Excel</button>
    
    <div class="modal fade" id="userModal" tabindex="-1" aria-labelledby="userModalLabel" aria-hidden="true">
        <div class="modal-dialog modal-lg">
          <div class="modal-content" style="text-align: center;">
            <div class="modal-header">
              <h5 class="modal-title" id="userModalLabel"><strong>Payment Details</strong></h5>
            </div>
            <div class="modal-body">
              <p><strong>Payment ID:</strong> {{ selectedPayment?.payment_ID }}</p>
              <p><strong>Member Name:</strong> {{ selectedPayment?.memberName }}</p>
              <p><strong>Amount:</strong> {{ selectedPayment?.amount | currency: 'R ' }}</p>
              <p><strong>Payment Date:</strong> {{ selectedPayment?.payment_Date | date: 'short' }}</p>
              <p><strong>Payment Type:</strong> {{ selectedPayment?.paymentTypeName }}</p>
            </div>
            <div class="modal-footer">
              <button type="button" class="btn btn-secondary" (click)="closeModal()">Close</button>
            </div>
          </div>
        </div>
      </div>
</div> 

//shared
//file-saver.d.ts
declare module 'file-saver';
//controller
using av_motion_api.Data;
using av_motion_api.Models;
using av_motion_api.ViewModels;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace av_motion_api.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class RewardController : ControllerBase
    {
        private readonly AppDbContext _appDbContext;

        public RewardController(AppDbContext appDbContext)
        {
            _appDbContext = appDbContext;
        }

        [HttpGet]
        [Route("getAllRewardTypes")]
        public async Task<IActionResult> GetAllRewardTypes()
        {
            try
            {
                var rewardTypes = await _appDbContext.Reward_Types.ToListAsync();
                return Ok(rewardTypes);
            }
            catch (Exception)
            {
                return BadRequest();
            }
        }

        [HttpGet]
        [Route("getRewardTypeById/{id}")]
        public async Task<IActionResult> GetRewardTypeById(int id)
        {
            try
            {
                var rewardType = await _appDbContext.Reward_Types.FindAsync(id);

                if (rewardType == null)
                {
                    return NotFound();
                }

                return Ok(rewardType);
            }
            catch (Exception)
            {
                return BadRequest();
            }
        }

        [HttpPost]
        [Route("createRewardType")]
        public async Task<IActionResult> CreateRewardType(RewardTypeViewModel rt)
        {
            try
            {
                var existingRewardType = await _appDbContext.Reward_Types
                    .FirstOrDefaultAsync(r => r.Reward_Type_Name == rt.Reward_Type_Name);

                if (existingRewardType != null)
                {
                    return Conflict(new { message = "Reward type already exists." });
                }


                var rewardType = new Reward_Type
                {
                    Reward_Type_Name = rt.Reward_Type_Name,
                    Reward_Criteria = rt.Reward_Criteria
                    
                };
                _appDbContext.Reward_Types.Add(rewardType);
                await _appDbContext.SaveChangesAsync();

                return CreatedAtAction(nameof(GetRewardTypeById), new { id = rewardType.Reward_Type_ID }, rewardType);
            }
            catch (Exception)
            {
                return BadRequest();
            }
        }

        [HttpPut]
        [Route("updateRewardType/{id}")]
        public async Task<IActionResult> UpdateRewardType(int id, [FromBody] RewardTypeViewModel rt)
        {
            try
            {
                var rewardType = await _appDbContext.Reward_Types.FindAsync(id);

                if(rewardType == null)
                {
                    return NotFound();
                }

                var existingRewardType = await _appDbContext.Reward_Types
                    .FirstOrDefaultAsync(r => r.Reward_Type_Name == rt.Reward_Type_Name && r.Reward_Type_ID != id);

                if (existingRewardType != null)
                {
                    return Conflict(new { message = "Reward type already exists." });
                }

                rewardType.Reward_Type_Name = rt.Reward_Type_Name;
                rewardType.Reward_Criteria = rt.Reward_Criteria;

                _appDbContext.Entry(rewardType).State = EntityState.Modified;
                await _appDbContext.SaveChangesAsync();

                return NoContent();
            }
            catch (Exception)
            {
                return BadRequest();
            }
        }

        [HttpDelete]
        [Route("deleteRewardType/{id}")]
        public async Task<IActionResult> DeleteRewardType(int id)
        {
            try
            {
                var rewardType = await _appDbContext.Reward_Types.FindAsync(id);

                if(rewardType == null)
                {
                    return NotFound();
                }

                _appDbContext.Reward_Types.Remove(rewardType);
                await _appDbContext.SaveChangesAsync();

                return NoContent();
            }
            catch (Exception)
            {
                return BadRequest();
            }
        }



        [HttpGet]
        [Route("getRewardById/{id}")]
        public async Task<IActionResult> GetRewardById(int id)
        {
            try
            {
                var reward = await _appDbContext.Rewards.FindAsync(id);

                if (reward == null)
                {
                    return NotFound();
                }

                return Ok(reward);
            }
            catch (Exception)
            {
                return BadRequest();
            }
        }

        [HttpGet]
        [Route("getAllRewards")]
        public async Task<IActionResult> GetAllRewards()
        {
            try
            {
                var rewards = await _appDbContext.Rewards
                                         .Join(_appDbContext.Reward_Types,
                                               reward => reward.Reward_Type_ID,
                                               rewardType => rewardType.Reward_Type_ID,
                                               (reward, rewardType) => new RewardViewModel
                                               {
                                                   Reward_ID = reward.Reward_ID,
                                                   Reward_Issue_Date = reward.Reward_Issue_Date,
                                                   Reward_Type_Name = rewardType.Reward_Type_Name,
                                                   IsPosted = reward.IsPosted
                                               })
                                         .ToListAsync();
                return Ok(rewards);
            }
            catch (Exception)
            {
                return BadRequest();
            }
        }

        [HttpGet]
        [Route("UnredeemedRewards/{memberId}")]
        public async Task<IActionResult> UnredeemedRewards(int memberId)
        {
            try
            {
                var rewards = await _appDbContext.Reward_Members
                    .Where(r => r.Member_ID == memberId && !r.IsRedeemed)
                    .Select(rm => new
                    {
                        rm.Reward_ID,
                        rm.IsRedeemed,
                        RewardTypeName = rm.Reward.Reward_Type.Reward_Type_Name,
                        RewardCriteria = rm.Reward.Reward_Type.Reward_Criteria,
                        RewardIssueDate = rm.Reward.Reward_Issue_Date
                    })
                    .ToListAsync();

                return Ok(rewards);
            }
            catch (Exception ex)
            {
                return BadRequest($"An error occurred: {ex.Message}");
            }
        }

        [HttpPost]
        [Route("redeemReward")]
        public async Task<IActionResult> RedeemReward([FromBody] RewardRedeemViewModel request)
        {
            try
            {
                // Fetch the reward by ID
                var reward = await _appDbContext.Reward_Members
                                                .FirstOrDefaultAsync(r => r.Reward_ID == request.RewardId && r.Member_ID == request.MemberId);

                if (reward == null)
                {
                    return NotFound("Reward not found or not eligible for the member.");
                }

                // Check if the reward is already redeemed
                if (reward.IsRedeemed)
                {
                    return BadRequest("Reward is already redeemed.");
                }

                // Mark the reward as redeemed
                reward.IsRedeemed = true;
                _appDbContext.Entry(reward).State = EntityState.Modified;
                await _appDbContext.SaveChangesAsync();

                return Ok(new { Status = "Success", Message = "Reward redeemed successfully!" });
            }
            catch (Exception)
            {
                return BadRequest("An error occurred while redeeming the reward.");
            }
        }

        [HttpPost]
        [Route("setReward")]
        public async Task<IActionResult> SetReward(RewardSetViewModel r)
        {
            try
            {
                var reward = new Reward
                {
                    Reward_Issue_Date = r.Reward_Issue_Date,
                    Reward_Type_ID = r.Reward_Type_ID,
                    IsPosted = r.IsPosted
                };
                _appDbContext.Rewards.Add(reward);
                await _appDbContext.SaveChangesAsync();

                return CreatedAtAction(nameof(GetRewardById), new { id = reward.Reward_ID }, reward);
            }
            catch (Exception)
            {
                return BadRequest();
            }
        }


        [HttpPost]
        [Route("postReward")]
        public async Task<IActionResult> PostReward([FromBody] RewardPostViewModel request)
        {
            try
            {
                // Fetch the reward by ID
                var reward = await _appDbContext.Rewards
                                                .FirstOrDefaultAsync(r => r.Reward_ID == request.RewardId);

                if (reward == null)
                {
                    return NotFound("Reward not found.");
                }

                // Check if the reward is already posted
                if (reward.IsPosted)
                {
                    return BadRequest("Reward is already posted.");
                }

                // Mark the reward as posted
                reward.IsPosted = true;
                _appDbContext.Entry(reward).State = EntityState.Modified;
                await _appDbContext.SaveChangesAsync();

                // Fetch members who qualify for this reward based on the criteria
                var rewardType = await _appDbContext.Reward_Types.FindAsync(reward.Reward_Type_ID);
                var qualifyingMembers = await GetQualifyingMembers(rewardType.Reward_Criteria);

                // Add qualifying members to the Reward_Member table
                foreach (var member in qualifyingMembers)
                {
                    var rewardMember = new Reward_Member
                    {
                        Member_ID = member.Member_ID,
                        Reward_ID = reward.Reward_ID,
                        IsRedeemed = false
                    };
                    _appDbContext.Reward_Members.Add(rewardMember);
                }
                await _appDbContext.SaveChangesAsync();

                return Ok(new { Status = "Success", Message = "Reward posted successfully!" });
            }
            catch (Exception)
            {
                return BadRequest("An error occurred while posting the reward.");
            }
        }

        //Predined Reward Types Methods
        private async Task<List<Member>> GetQualifyingMembers(string criteria)
        {
            var qualifyingMembers = new List<Member>();

            if (criteria == "Placed a Booking")
            {
                qualifyingMembers = await GetMembersWithAtLeastOneBooking();
            }
            else if (criteria == "Completed 10 Bookings in a Month")
            {
                qualifyingMembers = await GetMembersWithBookingsInMonth(10);
            }
            else if (criteria == "Made 20 Bookings in Last 3 Months")
            {
                qualifyingMembers = await GetFrequentBookers(20, 3);
            }
            else if (criteria == "Member for Over a Year")
            {
                qualifyingMembers = await GetLoyalMembers();
            }

            return qualifyingMembers;
        }

        private async Task<List<Member>> GetMembersWithAtLeastOneBooking()
        {
            var qualifyingMembers = await _appDbContext.Members
                .Where(m => _appDbContext.Bookings
                    .Any(b => b.Member_ID == m.Member_ID))
                .ToListAsync();

            return qualifyingMembers;
        }


        private async Task<List<Member>> GetMembersWithBookingsInMonth(int bookingCount)
        {
            var oneMonthAgo = DateTime.Now.AddMonths(-1);
            var qualifyingMembers = await _appDbContext.Members
                .Where(m => _appDbContext.Bookings
                    .Where(b => b.Member_ID == m.Member_ID)
                    .Join(_appDbContext.Booking_Time_Slots,
                          b => b.Booking_ID,
                          bts => bts.Booking_ID,
                          (b, bts) => bts.Time_Slot_ID)
                    .Join(_appDbContext.Time_Slots,
                          btsId => btsId,
                          ts => ts.Time_Slot_ID,
                          (btsId, ts) => ts)
                    .Count(ts => ts.Slot_Date >= oneMonthAgo) >= bookingCount)
                .ToListAsync();

            return qualifyingMembers;
        }

        private async Task<List<Member>> GetLoyalMembers()
        {
            var oneYearAgo = DateTime.Now.AddYears(-1);
            var qualifyingMembers = await _appDbContext.Members
                .Join(_appDbContext.Contracts,
                      m => m.Contract_ID,
                      c => c.Contract_ID,
                      (m, c) => new { Member = m, Contract = c })
                .Where(mc => mc.Contract.Subscription_Date <= oneYearAgo)
                .Select(mc => mc.Member)
                .ToListAsync();

            return qualifyingMembers;
        }


        private async Task<List<Member>> GetFrequentBookers(int bookingCount, int months)
        {
            var dateLimit = DateTime.Now.AddMonths(-months);
            var qualifyingMembers = await _appDbContext.Members
                .Where(m => _appDbContext.Bookings
                    .Where(b => b.Member_ID == m.Member_ID)
                    .Join(_appDbContext.Booking_Time_Slots,
                          b => b.Booking_ID,
                          bts => bts.Booking_ID,
                          (b, bts) => bts.Time_Slot_ID)
                    .Join(_appDbContext.Time_Slots,
                          btsId => btsId,
                          ts => ts.Time_Slot_ID,
                          (btsId, ts) => ts)
                    .Count(ts => ts.Slot_Date >= dateLimit) >= bookingCount)
                .ToListAsync();

            return qualifyingMembers;
        }
    }
}

//RewardPostViewModel
namespace av_motion_api.ViewModels
{
    public class RewardPostViewModel
    {
        public int RewardId { get; set; }
    }
}
import { HttpClient } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import { Router } from '@angular/router';
import { Md5 } from 'ts-md5';
import { environment } from '../../environments/environment';
import { OrderService } from '../Services/order.service';
import { MatSnackBar } from '@angular/material/snack-bar';
import { UserService } from '../Services/userprofile.service';
import { CartItemViewModel, OrderViewModel } from '../shared/order';
import { PaymentViewModel } from '../shared/payment';
import { PaymentService } from '../Services/payment.service';

declare global {
  interface Window {
    payfast_do_onsite_payment: (param1: any, callback: any) => any;
  }
}

@Component({
  selector: 'app-payfast',
  standalone: true,
  imports: [],
  templateUrl: './payfast.component.html',
  styleUrl: './payfast.component.css'
})
export class PayfastComponent implements OnInit {
  memberId!: number;

  constructor(private router : Router, private orderService : OrderService, private paymentService : PaymentService , private userService: UserService, private formBuilder: FormBuilder, private snackBar: MatSnackBar) {
    
  }

  ngOnInit(): void {
    this.fetchMemberId();
  }

  getSignature(data : Map<string, string>) : string {
    let tmp = new URLSearchParams();
    data.forEach((v, k)=> {
      tmp.append(k, v)
    });
    let queryString = tmp.toString();
    let sig = Md5.hashStr(queryString);
    return sig;
  }

  async doOnSitePayment() {
    await this.fetchMemberId();

    let onSiteUserData = new Map<string, string>();
    onSiteUserData.set("merchant_id", "10033427")
    onSiteUserData.set("merchant_key", "mu83ipbgas9p7")

    onSiteUserData.set('return_url', window.location.origin + '/payment-success')
    onSiteUserData.set('cancel_url', window.location.origin + '/payment-cancel')

    // Gather required user data from orderService or other sources
    const userData = this.orderService.getUserData();
    onSiteUserData.set("email_address", userData.email);
    
    // Set amount and item_name
    this.orderService.getTotalAmount().subscribe(amount => {
      onSiteUserData.set('amount', amount.toString());
      onSiteUserData.set('item_name', 'Cart Purchase');

      // Optional passphrase for added security
      onSiteUserData.set('passphrase', 'HelloWorldHello'); // Use if you have a passphrase

      let signature = this.getSignature(onSiteUserData);
      onSiteUserData.set('signature', signature);

      let formData = new FormData();
      onSiteUserData.forEach((val, key) => {
        formData.append(key, val);
      });

      fetch(environment.payfastOnsiteEndpoint, {
        method: 'POST',
        body: formData,
        redirect: 'follow'
      })
      .then(response => response.json())
      .then(respJson => {
          let uuid = respJson['uuid'];
          window.payfast_do_onsite_payment({ 'uuid': uuid }, (res: any) => {
            if (res == true) {
              this.createOrder().then((orderResponse) => {
                this.createPayment(orderResponse);
                this.snackBar.open('Payment Successful', 'Close', { duration: 5000 });
              });         
            } else {
              this.snackBar.open('Payment Failed', 'Close', { duration: 5000 });              
            }
          });
        })
        .catch(error => {
          console.error('Error processing payment:', error);
          this.router.navigate(['/cancel']);
        });
      });
    }

    //order
    private fetchMemberId() {
      const user = localStorage.getItem('User');
      if (user) {
        const userData = JSON.parse(user);
        this.memberId = userData.userId;
    
        // Optional: Fetch and validate member details if needed
        this.userService.getMemberByUserId(this.memberId).subscribe({
          next: (member) => {
            if (member && member.member_ID) {
              this.memberId = member.member_ID;
              console.log('Member ID:', this.memberId); // Check if this logs the correct ID
            } else {
              console.error('Member ID is undefined in the response');
              this.snackBar.open('Failed to retrieve member information', 'Close', { duration: 5000 });
            }
          },
          error: (error) => {
            console.error('Error fetching member:', error);
            this.snackBar.open('Failed to retrieve member information', 'Close', { duration: 5000 });
          }
        });
      } else {
        this.snackBar.open('User not logged in', 'Close', { duration: 5000 });
        this.router.navigate(['/login']);
      }
    }

    private createOrder(): Promise<OrderViewModel> {
      return new Promise((resolve, reject) => {
        if (this.memberId === undefined) {
          this.snackBar.open('Member ID is not available', 'Close', { duration: 5000 });
          reject('Member ID is not available');
        } else {
          this.orderService.getCartItems().subscribe({
            next: (cartItems) => {
              const order = this.prepareOrderDetails(cartItems);
              this.orderService.createOrder(order).subscribe({
                next: (orderResponse) => {
                  this.snackBar.open('Order Created Successfully', 'Close', { duration: 5000 });
                  resolve(orderResponse);
                },
                error: (error) => {
                  console.error('Error creating order:', error);
                  this.snackBar.open('Failed to create order', 'Close', { duration: 5000 });
                  reject(error);
                }
              });
            },
            error: (error) => {
              console.error('Error fetching cart items:', error);
              this.snackBar.open('Failed to fetch cart items', 'Close', { duration: 5000 });
              reject(error);
            }
          });
        }
      });
    }
    
  private prepareOrderDetails(cartItems: CartItemViewModel[]): OrderViewModel {
    const order: OrderViewModel = {
      order_ID: 0, // ID will be generated by backend
      member_ID: this.memberId,
      order_Date: new Date().toISOString(),
      total_Price: this.calculateTotalPrice(cartItems),
      order_Status_ID: 1, // Ready for Collection
      isCollected: false,
      orderLines: cartItems.map(item => ({
        order_Line_ID: 0, // ID will be generated by backend
        product_ID: item.product_ID,
        product_Name: item.product_Name,
        quantity: item.quantity,
        unit_Price: item.unit_Price
      }))
    };
  
    console.log('Prepared order details:', order);
    return order;
  }

  // Helper method to calculate the total price
  private calculateTotalPrice(cartItems: CartItemViewModel[]): number {
    return cartItems.reduce((total, item) => total + (item.unit_Price * item.quantity), 0);
  }

  private createPayment(order: OrderViewModel) {
    const paymentData: PaymentViewModel = {
      payment_ID: 0,
      amount: order.total_Price,
      payment_Date: new Date().toISOString(),
      order_ID: order.order_ID,
      payment_Type_ID: 1 // Default to 1
    };

    this.paymentService.createPayment(paymentData).subscribe({
      next: (response) => {
        console.log('Payment record created successfully:', response);
        this.router.navigate(['/orders']);
      },
      error: (error) => {
        console.error('Error creating payment record:', error);
      }
    });
  }  
}
//html
<div class="container-fluid" style="margin-top: 60px;">
  <div class="row justify-content-start">
    <!-- Back Button -->
    <div class="col-6">
      <i class="bi bi-arrow-left-circle header-icon" (click)="goBack()"></i>
    </div>

    <!-- My Profile Heading -->
    <div class="col-6">
      <h2 class="text-left">My Profile</h2>
      <span style="float: right;">
        <a class="navbar-brand" href="#" data-bs-toggle="modal" data-bs-target="#helpModal">
            <i class="bi bi-info-circle-fill">Help</i>
          </a>
      </span>
    </div>

    <!-- Left Side Menu -->
    <div class="col-md-3">
      <div routerLink="/orders" *ngIf="userTypeID === 3">
        <h5 class="small-heading">
          <i class="bi bi-cart4"></i>
          Orders
        </h5>
      </div>  
      <br>
      <h5 class="small-heading">
        <i class="bi bi-bell-fill"></i>
        Notifications
      </h5>
      <!-- Notification placeholder -->
      <div class="card mt-3">
        <div class="card-body">
          <div *ngIf="unredeemedRewards.length === 0" class="notification-item">
            <span>No Notifications</span>
          </div>
          <div *ngFor="let reward of unredeemedRewards" class="notification-item">
            <span style="float:left">{{ reward.rewardTypeName }}</span>
            <span style="float: right"><button class="btn btn-sm btn-primary" (click)="openRedeemModal(reward)">Redeem</button></span>
          </div>
        </div>
      </div>
    </div>

    

    <!-- Vertical Line Separator -->
    <div class="col-md-1">
      <div class="vertical-line"></div>
    </div>

    <!-- Right Side Form -->
    <div class="col-md-6">
      <form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
        <h5 class="small-heading">
          <i class="bi bi-house-gear-fill"></i>
          Personal Details
        </h5>
        <div class="text-center mb-3">
          <div class="profile-photo-wrapper">
            <img [src]="userProfileImage" alt="Profile Photo" class="img-fluid rounded-circle profile-photo" *ngIf="userProfileImage">
            <div class="edit-photo-wrapper">
              <a href="#" (click)="enableEditMode($event)" class="edit-link">Edit</a>
              <label [class.disabled-icon]="!isEditMode" for="photoUpload" class="photo-upload-icon">
                <i class="bi bi-camera"></i>
              </label>
            </div>
            <input type="file" id="photoUpload" formControlName="photo" class="d-none" (change)="onPhotoChange($event)" [readonly]="!isEditMode" >
          </div>
        </div>
        

        <br>

      
        <div class="row">
          <div class="col-md-6 mb-3">
            <div class="form-group">
              <label for="name" class="form-label">Name</label>
              <input type="text" class="form-control" id="name" formControlName="name" [readonly]="!isEditMode" [ngClass]="{'disabled-input': !isEditMode}">
              <div *ngIf="profileForm.controls['name'].invalid && (profileForm.controls['name'].dirty || profileForm.controls['name'].touched)" class="alert">
                <div *ngIf="profileForm.controls['name'].errors?.['required']">Name is required.</div>
                <div *ngIf="profileForm.controls['name'].errors?.['minlength']">Name must be at least 2 characters long.</div>
                <div *ngIf="profileForm.controls['name'].errors?.['maxlength']">Name must be at most 20 characters long.</div>
              </div>
            </div>
          </div>

          <div class="col-md-6 mb-3">
            <div class="form-group">            
              <label for="surname" class="form-label">Surname</label>
              <input type="text" class="form-control" id="surname" formControlName="surname" [readonly]="!isEditMode" [ngClass]="{'disabled-input': !isEditMode}">
              <div *ngIf="profileForm.controls['surname'].invalid && (profileForm.controls['surname'].dirty || profileForm.controls['surname'].touched)" class="alert">
                <div *ngIf="profileForm.controls['surname'].errors?.['required']">Surname is required.</div>
                <div *ngIf="profileForm.controls['surname'].errors?.['minlength']">Surname must be at least 2 characters long.</div>
                <div *ngIf="profileForm.controls['surname'].errors?.['maxlength']">Surname must be at most 20 characters long.</div>
              </div>
            </div>
          </div>

          <div class="col-md-6 mb-3">
            <div class="form-group">
              <label for="email" class="form-label">Email Address</label>
              <input type="email" class="form-control" id="email" formControlName="email" [readonly]="!isEditMode" [ngClass]="{'disabled-input': !isEditMode}">
              <div *ngIf="profileForm.controls['email'].invalid && (profileForm.controls['email'].dirty || profileForm.controls['email'].touched)" class="alert">
                <div *ngIf="profileForm.controls['email'].errors?.['required']">Email is required.</div>
                <div *ngIf="profileForm.controls['email'].errors?.['email']">Email is invalid.</div>
              </div>
            </div>
          </div>

          <div class="col-md-6 mb-3">
            <div class="form-group">
              <label for="phoneNumber" class="form-label">Phone Number</label>
              <input type="text" class="form-control" id="phoneNumber" formControlName="phoneNumber" [readonly]="!isEditMode" [ngClass]="{'disabled-input': !isEditMode}">
              <div *ngIf="profileForm.controls['phoneNumber'].invalid && (profileForm.controls['phoneNumber'].dirty || profileForm.controls['phoneNumber'].touched)" class="alert">
                <div *ngIf="profileForm.controls['phoneNumber'].errors?.['required']">Phone Number is required.</div>
                <div *ngIf="profileForm.controls['phoneNumber'].errors?.['minlength']">Phone Number must be a valid 10-digit number.</div>
                <div *ngIf="profileForm.controls['phoneNumber'].errors?.['maxlength']">Phone Number must be a valid 10-digit number.</div>
              </div>
            </div>
          </div>

          <div class="col-md-6 mb-3">
            <div class="form-group">
              <label for="physical_Address" class="form-label">Physical Address</label>
              <input type="text" class="form-control" id="physical_Address" formControlName="physical_Address" [readonly]="!isEditMode" [ngClass]="{'disabled-input': !isEditMode}">
              <div *ngIf="profileForm.controls['physical_Address'].invalid && (profileForm.controls['physical_Address'].dirty || profileForm.controls['physical_Address'].touched)" class="alert">
                <div *ngIf="profileForm.controls['physical_Address'].errors?.['required']">Physical address is required.</div>
                <div *ngIf="profileForm.controls['physical_Address'].errors?.['minlength']">Physical Address must be at least 7 characters long.</div>
                <div *ngIf="profileForm.controls['physical_Address'].errors?.['maxlength']">Physical Address must be at most 100 characters long.</div>
              </div>
            </div>
          </div>
        </div>

        <div class="d-flex justify-content-end">
          <button type="button" class="btn btn-primary me-2" (click)="openSaveModal()" [disabled]="!isEditMode">Save</button>
          <button type="button" class="btn btn-info" (click)="changePassword()" [disabled]="!isEditMode">Change Password</button>
        </div>
      </form>
    </div>
  </div>
</div>

<!-- Save Confirmation Modal -->
<div class="modal fade" id="saveConfirmationModal" tabindex="-1" role="dialog" aria-labelledby="saveConfirmationModalTitle" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="saveConfirmationModalTitle">Save Changes</h5>
      </div>
      <div class="modal-body">
        Are you sure you want to update your profile details?
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" (click)="dismissModal()">Cancel</button>
        <button type="button" class="btn btn-primary" (click)="confirmSave()">Confirm</button>
      </div>
    </div>
  </div>
</div>

<!-- Error Modal -->
<div class="modal fade" id="errorModal" tabindex="-1" role="dialog" aria-labelledby="errorModalTitle" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="errorModalTitle">Error</h5>
      </div>
      <div class="modal-body">
        Please enter a valid input for the following fields:
        <ul id="errorList"></ul>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-primary" (click)="dismissErrorModal()">OK</button>
      </div>
    </div>
  </div>
</div>

<!-- Redeem Reward Modal -->
<div class="modal fade" id="redeemRewardModal" tabindex="-1" role="dialog" aria-labelledby="redeemRewardModalTitle" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="redeemRewardModalTitle">Redeem Reward</h5>
      </div>
      <div class="modal-body">
        Are you sure you want to redeem the reward <strong>{{ selectedReward?.rewardTypeName }}</strong>?
        <br>
        <p>The reward criteria met: <strong>{{ selectedReward?.rewardCriteria }}</strong></p>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" (click)="dismissRedeemModal()">No</button>
        <button type="button" class="btn btn-primary" (click)="confirmRedeem()">Yes</button>
      </div>
    </div>
  </div>
</div>

<!-- Success Modal -->
<div class="modal fade" id="successModal" tabindex="-1" role="dialog" aria-labelledby="successModalTitle" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <strong><h5 class="modal-title" id="successModalTitle">Congratulations</h5></strong>
      </div>
      <div class="modal-body">
        <strong>Congratulations!</strong> You've successfully redeemed this reward <strong>{{ selectedReward?.rewardTypeName }}</strong>. Please contact the gym and provide this code <strong>{{ verificationCode }}</strong> for further instructions.
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" (click)="dismissSuccessModal()">Close</button>
      </div>
    </div>
  </div>
</div>


<!-- help-modal.component.html -->
<div class="modal fade" id="helpModal" tabindex="-1" aria-labelledby="helpModalLabel" aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
        <h5 class="modal-title mx-auto" id="helpModalLabel">Help Guide</h5>
        <div class="search-bar-container">
          <input type="text" class="form-control search-bar" placeholder="Search help" [(ngModel)]="searchTerm" (input)="filterHelpContent()">
        </div>
      </div>
      <div class="modal-body">
        <div *ngFor="let item of filteredContent">
          <h5>{{ item.title }}</h5>
          <p [innerHTML]="item.content"></p>
        </div>
      </div>
    </div>
  </div>
</div>

//ts
import { Component, OnDestroy, OnInit } from '@angular/core';
import { FormGroup, Validators, FormBuilder, FormsModule } from '@angular/forms';
import { UserService } from '../Services/userprofile.service';
import { Router, RouterLink } from '@angular/router';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule } from '@angular/forms';
import { Member, updateUser } from '../shared/update-user';
import { Subscription, catchError } from 'rxjs';
import { RewardRedeemViewModel, UnredeemedRewardModel } from '../shared/reward';
import { RewardService } from '../Services/reward.service';

declare var $: any; // Import jQuery

@Component({
  selector: 'app-profile-page',
  standalone: true,
  imports: [CommonModule, ReactiveFormsModule, RouterLink, FormsModule],
  templateUrl: './profile-page.component.html',
  styleUrls: ['./profile-page.component.css']
})
export class ProfilePageComponent implements OnInit, OnDestroy {
  profileForm: FormGroup;
  user: updateUser | undefined;
  member: Member | undefined;
  isEditMode = false;
  errorMessage = '';  
  userProfileImage: string | null = null;
  photoFile: File | null = null;
  unredeemedRewards: UnredeemedRewardModel[] = [];
  selectedReward: UnredeemedRewardModel | null = null;
  verificationCode: string | null = null;
  userTypeID: number | null = null;
  helpContent: any[] = [];
  filteredContent: any[] = [];
  searchTerm: string = '';

  private userSubscription: Subscription | undefined;
  private memberSubscription: Subscription | undefined;
  private redeemSubscription: Subscription | undefined;
  
  constructor(
    private userService: UserService,
    private rewardService: RewardService,
    private router: Router,
    private fb: FormBuilder
  ) {
    this.profileForm = this.fb.group({
      email: ['', [Validators.required, Validators.email]],
      name: ['', [Validators.required, Validators.minLength(2), Validators.maxLength(20)]],
      surname: ['', [Validators.required, Validators.minLength(2), Validators.maxLength(20)]],
      phoneNumber: ['', [Validators.required, Validators.minLength(10), Validators.maxLength(10)]],
      physical_Address: ['', [Validators.required, Validators.minLength(7), Validators.maxLength(100)]],
      photo: ['']
    });
  }

  ngOnInit(): void {
    const userTypeId = JSON.parse(localStorage.getItem('User') || '{}').userTypeId;
    this.userTypeID = userTypeId;
    console.log('User Type ID',userTypeId);
    const userId = JSON.parse(localStorage.getItem('User') || '{}').userId;
    console.log('User ID from local storage:', userId);
    this.loadUserProfile(userId);
    this.isEditMode = false;    


    // Initialize help content
    this.helpContent = [
      {
        title: 'Profile Page Context-Sensitive Help',
        content: `
          <p><strong>Overview:</strong> The Profile Page allows users to view and update their personal information. This includes details such as username, email, and profile picture.</p>
          <p><strong>Elements and Features:</strong></p>`
      },
      {
        title: '1. Back Button',
        content: `
          <ul>
            <li><strong>Description:</strong> An arrow icon located in the header that allows you to return to the previous page.</li>
            <li><strong>Functionality:</strong> Clicking the back button navigates to the previous page you visited.</li>
            <li><strong>Helpful Tips:</strong>
              <ul>
                <li>Use the back button if you want to return to your previous page without losing your current context.</li>
              </ul>
            </li>
          </ul>`
      },
      {
        title: '2. Header Title',
        content: `
          <ul>
            <li><strong>Description:</strong> Displays the title "Profile" to indicate the purpose of the screen.</li>
            <li><strong>Functionality:</strong> Provides a clear indication of the current screen's functionality.</li>
          </ul>`
      },
      {
        title: '3. Profile Information',
        content: `
          <ul>
            <li><strong>Description:</strong> Displays your personal information such as username, email, and profile picture.</li>
            <li><strong>Functionality:</strong> Allows you to view and edit your personal details.</li>
          </ul>`
      },
      {
        title: '4. Change Password Button',
        content: `
          <ul>
            <li><strong>Description:</strong> A button that navigates you to the Change Password screen.</li>
            <li><strong>Functionality:</strong> Allows you to update your password securely.</li>
          </ul>`
      },
      {
        title: 'Technical Details:',
        content: `
          <ul>
            <li>Dynamic Data: The profile information is dynamically updated based on the data retrieved from the backend.</li>
            <li>Navigation: Utilizes Angular's Router for smooth transitions between different sections of the application.</li>
          </ul>`
      },
      {
        title: 'Common Questions:',
        content: `
          <p><strong>Q:</strong> How do I update my profile information?</p>
          <p><strong>A:</strong> Click on the edit button next to the information you want to update, make your changes, and click save.</p>
          <p><strong>Q:</strong> How do I change my password?</p>
          <p><strong>A:</strong> Click the "Change Password" button to navigate to the Change Password screen.</p>
          <p><strong>Q:</strong> What should I do if I encounter an error?</p>
          <p><strong>A:</strong> Refresh the page or contact support for assistance.</p>`
      },
      {
        title: 'Troubleshooting:',
        content: `
          <p><strong>Problem:</strong> The profile information is not loading.</p>
          <p><strong>Solution:</strong> Ensure you are connected to the internet and logged in. If the problem persists, try refreshing the page or contact technical support.</p>
          <p><strong>Problem:</strong> Unable to update profile information.</p>
          <p><strong>Solution:</strong> Check your internet connection and ensure all required fields are filled out correctly. If the issue continues, contact support.</p>`
      }
    ];

    // Initialize filtered content
    this.filteredContent = [...this.helpContent];
  }

  filterHelpContent(): void {
    const term = this.searchTerm.toLowerCase();
    this.filteredContent = this.helpContent.filter(item =>
      item.title.toLowerCase().includes(term) || item.content.toLowerCase().includes(term)
    );
  }

  ngOnDestroy(): void {
    // Clean up subscriptions
    if (this.userSubscription) {
      this.userSubscription.unsubscribe();
    }
    if (this.memberSubscription) {
      this.memberSubscription.unsubscribe();
    }
    if (this.redeemSubscription) {
      this.redeemSubscription.unsubscribe();
    }
  }

  loadUserProfile(userId: number): void {
    this.userSubscription = this.userService.getUserById(userId).pipe(
      catchError(error => {
        console.error('Error fetching user profile:', error);
        return [];
      })
    ).subscribe({
      next: (result) => {
        console.log('User data received:', result);
        this.user = result; 

        // Patch non-file form values
        this.profileForm.patchValue({
          email: this.user.email,
          name: this.user.name,
          surname: this.user.surname,
          phoneNumber: this.user.phoneNumber,
          physical_Address: this.user.physical_Address,
        });

        // Log photo to debug
        console.log('Photo:', this.user.photo);        
        // Set user profile image
        this.userProfileImage = `data:image/jpeg;base64,${this.user.photo}`;
        console.log('User:', this.user);

        const userTypeId = JSON.parse(localStorage.getItem('User') || '{}').userTypeId;
        this.userTypeID = userTypeId;
        if (this.userTypeID === 3) {
          this.loadMemberProfile(userId);
        }
      },
      complete: () => {
        console.log('getUserById subscription completed');
      }
    });
  }

  loadMemberProfile(userId: number): void {
    this.memberSubscription = this.userService.getMemberByUserId(userId).pipe(
      catchError(error => {
        console.error('Error fetching member profile:', error);
        return [];
      })
    ).subscribe({
      next: (result) => {
        this.member = result;
        if (this.member) {
          console.log('Member data received:', this.member); // Detailed debug statement
          console.log('Member ID:', this.member.member_ID); // Debug statement
          this.loadUnredeemedRewards(this.member.member_ID);
        }
      },
      complete: () => {
        console.log('getMemberByUserId subscription completed');
      }
    });
  }

  clearForm() {
    this.profileForm.reset();
  }

  enableEditMode(event: Event) {
    event.preventDefault();
    this.isEditMode = true;
    this.profileForm.enable();
  }

  openSaveModal() {
    if (this.profileForm.invalid) {
      this.showValidationErrors();
      $('#errorModal').modal('show');
      return;
    }
    $('#saveConfirmationModal').modal('show');
  }

  showValidationErrors() {
    const invalidFields: string[] = [];
    Object.keys(this.profileForm.controls).forEach(key => {
      const controlErrors = this.profileForm.get(key)?.errors;
      if (controlErrors) {
        Object.keys(controlErrors).forEach(errorKey => {
          invalidFields.push(`${key}: ${errorKey}`);
        });
      }
    });
    this.errorMessage = `Please enter a valid input: ${invalidFields.join(', ')}`;
  }

  dismissModal() {
    $('#saveConfirmationModal').modal('hide');
  }

  dismissErrorModal() {
    $('#errorModal').modal('hide');
  }

  confirmSave() {
    this.dismissModal();
    this.onSubmit();
    this.isEditMode = false; // Disable edit mode after confirmation
  }

  onSubmit() {
    if (this.profileForm.valid) {
        const userId = JSON.parse(localStorage.getItem('User')!).userId;
        const formValue = this.profileForm.value;

        // If no new photo is selected, use the existing photo
        let photoFile: File | null = this.photoFile;
        if (!photoFile && this.user?.photo) {
          // Ensure the Base64 string is valid
          const base64Prefix = 'data:image/jpeg;base64,';
          let base64String = this.user.photo;
          if (base64String.startsWith(base64Prefix)) {
            base64String = base64String.replace(base64Prefix, '');
          }
          try {
            const byteString = atob(base64String);
            const arrayBuffer = new ArrayBuffer(byteString.length);
            const intArray = new Uint8Array(arrayBuffer);
            for (let i = 0; i < byteString.length; i++) {
              intArray[i] = byteString.charCodeAt(i);
            }
            const blob = new Blob([intArray], { type: 'image/jpeg' });
            photoFile = new File([blob], 'photo.jpg', { type: 'image/jpeg' });
          } catch (e) {
            console.error('Invalid Base64 string:', e);
          }
        }

        // Create the user object
        const user: updateUser = {
            name: formValue.name,
            surname: formValue.surname,
            email: formValue.email,
            physical_Address: formValue.physical_Address,
            phoneNumber: formValue.phoneNumber,
            photo: this.user?.photo ?? '', // Ensure photo is included even if it's not updated
            user_Type_ID: this.user?.user_Type_ID ?? 0
        };

        this.userService.updateUser(userId, user, photoFile).subscribe({
            next: (result) => {
                console.log('User updated successfully:', result);
                this.router.navigateByUrl(`/ProfilePage/${userId}`);
                alert('Successfully updated profile');
            },
            error: (error) => {
                console.error('Error updating user profile:', error);
                alert('Error updating profile.');
            }
        });
    } else {
        console.warn('Form is invalid:', this.profileForm.errors);
    }
  }

  onPhotoChange(event: Event): void {
    if (!this.isEditMode) return;

    const input = event.target as HTMLInputElement;
    if (input.files && input.files[0]) {
        const file = input.files[0];
        
        // Ensure file is an image
        if (!file.type.startsWith('image/')) {
            console.error('Selected file is not an image.');
            alert('Please select a valid image file.');
            return;
        }

        const reader = new FileReader();

        reader.onload = (e: any) => {
          if (e.target && e.target.result) {
              this.userProfileImage = e.target.result; // Update the image source for preview
              this.photoFile = file; // Set the file for form submission
              console.log('Base64 string of the image:', this.userProfileImage);
          }
      };

        reader.onerror = (e) => {
            console.error('Error reading file:', e);
            alert('There was an error reading the file. Please try again.');
        };

        reader.onabort = (e) => {
            console.warn('File read aborted:', e);
            alert('File read was aborted. Please try again.');
        };

        reader.onloadend = () => {
            console.log('File read completed.');
        };

        try {
            reader.readAsDataURL(file);
            this.photoFile = file; 
        } catch (error) {
            console.error('An error occurred while reading the file:', error);
            alert('An error occurred while reading the file. Please try again.');
        }
    } else {
        console.error('No file selected or input element is invalid.');
    }
  }


  goBack() {
    const userTypeId = JSON.parse(localStorage.getItem('User')!).userTypeId;
    const userId = JSON.parse(localStorage.getItem('User')!).userId;
    if (userTypeId === 1) {  // Ensure userTypeID is compared as string
      this.router.navigateByUrl(`/OwnerHome/${userId}`);
    } else if (userTypeId === 2) {
      this.router.navigateByUrl(`/EmployeeHome/${userId}`);
    } else if (userTypeId === 3) {
      this.router.navigateByUrl(`/Home/${userId}`);
    }
  }

  changePassword() {
    this.router.navigateByUrl('/ChangePasswordPage');
  }

  // Method to load rewards for the current user
  loadUnredeemedRewards(memberId: number): void {
    console.log('Loading unredeemed rewards for member ID:', memberId); 
    this.rewardService.getUnredeemedRewardsForMember(memberId).subscribe({
      next: rewards => {
        this.unredeemedRewards = rewards;
        console.log("reward", rewards)
      },
      error: error => {
        console.error('Error fetching unredeemed rewards:', error);
      },
      complete: () => {
        console.log('Fetching unredeemed rewards completed.');
      }
    });
  }

  // Method to open redeem modal for a reward
  openRedeemModal(reward: UnredeemedRewardModel): void {
    this.selectedReward = reward;
    $('#redeemRewardModal').modal('show');
  }

  // Method to dismiss redeem modal
  dismissRedeemModal(): void {
    $('#redeemRewardModal').modal('hide');
  }

  // Method to confirm redeeming a reward
  confirmRedeem(): void {
    if (!this.selectedReward) {
      return;
    }
    const redeemRequest = new RewardRedeemViewModel();
    redeemRequest.MemberId = this.member?.member_ID ?? 0;
    redeemRequest.RewardId = this.selectedReward.reward_ID;

    // Call backend service to redeem the reward
    this.redeemSubscription = this.rewardService.redeemReward(redeemRequest).subscribe({
      next: () => {
        // Show success modal on successful redemption
        this.verificationCode = this.generateSixDigitCode(); // Generate code when opening modal
        console.log('Generated verification code:', this.verificationCode); 
        $('#successModal').modal('show');
        // Remove redeemed reward from the list
        this.unredeemedRewards = this.unredeemedRewards.filter(r => r.reward_ID !== this.selectedReward?.reward_ID);
      },
      error: (error) => {
        console.error('Error redeeming reward:', error);
        // Handle error
      }
    });
    this.dismissRedeemModal();
  }

  // Method to dismiss success modal
  dismissSuccessModal(): void {
    $('#successModal').modal('hide');
  }

  generateSixDigitCode(): string {
    const code = Math.floor(100000 + Math.random() * 900000).toString();
    return `${code.slice(0, 3)}-${code.slice(3)}`;
  }
}
using av_motion_api.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using System.Net.NetworkInformation;
using System.Reflection.Emit;
using System.Security.Claims;
using System.Xml.Linq;

namespace av_motion_api.Data
{
    public class AppDbContext : IdentityDbContext<User, Role, int>

    {
        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

        //set tables


        public DbSet<Attendance_List> Attendance_Lists { get; set; }
        public DbSet<Audit_Trail> Audit_Trails { get; set; }
        public DbSet<Booking> Bookings { get; set; }
        public DbSet<Booking_Time_Slot> Booking_Time_Slots { get; set; }
        public DbSet<Cart> Carts { get; set; }
        public DbSet<Cart_Item> Cart_Items { get; set; }
        public DbSet<Contract> Contracts { get; set; }
        public DbSet<Contract_History> Contract_History { get; set; }
        public DbSet<Contract_Type> Contract_Types { get; set; }
        public DbSet<DeletionSettings> DeletionSettings { get; set; }
        public DbSet<Discount> Discounts { get; set; }
        public DbSet<Employee> Employees { get; set; }
        public DbSet<Employee_Type> Employee_Types { get; set; }
        public DbSet<Equipment> Equipment { get; set; }
        public DbSet<Inspection> Inspection { get; set; }
        public DbSet<Inspection_Status> Inspection_Status { get; set; }
        public DbSet<Inspection_Type> Inspection_Type { get; set; }
        public DbSet<Inventory> Inventory { get; set; }
        public DbSet<Lesson_Plan> Lesson_Plans { get; set; }

        public DbSet<Lesson_Plan_Workout> lesson_Plan_Workout { get; set; }

        public DbSet<Member> Members { get; set; }
        public DbSet<Order> Orders { get; set; }
        public DbSet<Order_Line> Order_Lines { get; set; }
        public DbSet<Order_Status> Order_Status { get; set; }
        public DbSet<Outstanding_Payment> Outstanding_Payments { get; set; }
        public DbSet<Owner> Owners { get; set; }
        public DbSet<Payment> Payments { get; set; }
        public DbSet<Payment_Method> Payment_Methods { get; set; }
        public DbSet<Payment_Type> Payment_Types { get; set; }
        public DbSet<Price> Prices { get; set; }
        public DbSet<Product> Products { get; set; }
        public DbSet<Product_Category> Product_Categories { get; set; }
        public DbSet<Received_Supplier_Order> Received_Supplier_Orders { get; set; }
        public DbSet<Received_Supplier_Order_Line> Received_Supplier_Order_Lines { get; set; }
        public DbSet<Report> Reports { get; set; }
        public DbSet<Reward> Rewards { get; set; }

        public DbSet<Role> Roles { get; set; }
        public DbSet<Reward_Member> Reward_Members { get; set; }
        public DbSet<Reward_Type> Reward_Types { get; set; }
        public DbSet<Shift> Shifts { get; set; }
        public DbSet<Supplier> Suppliers { get; set; }
        public DbSet<Supplier_Order> Supplier_Orders { get; set; }
        public DbSet<Supplier_Order_Line> Supplier_Order_Lines { get; set; }
        public DbSet<Time_Slot> Time_Slots { get; set; }

        public DbSet<User> Users { get; set; }
        public DbSet<User_Status> Users_Status{ get; set; }

        public DbSet<User_Type> User_Types { get; set; }
        public DbSet<VAT> VAT { get; set; }

        public DbSet<Wishlist> Wishlists { get; set; }
        public DbSet<Wishlist_Item> Wishlist_Items { get; set; }

        public DbSet<Workout_Category> Workout_Category { get; set; }
        public DbSet<Workout> Workout { get; set; }
  
        public DbSet<Write_Off> Write_Offs { get; set; }



        protected override void OnModelCreating(ModelBuilder builder)
        {
            //Renaming of Default asp Tables
            builder.Entity<User>().ToTable("Users");
            builder.Entity<IdentityUserRole<int>>().ToTable("User_Roles");
            builder.Entity<IdentityUserLogin<int>>().ToTable("User_Logins");
            builder.Entity<Role>().ToTable("Roles");
            builder.Entity<IdentityRoleClaim<int>>().ToTable("Role_Claims");
            builder.Entity<IdentityUserClaim<int>>().ToTable("User_Claims");
            builder.Entity<IdentityUserToken<int>>().ToTable("Tokens");

            //Validation fix for database, specifying coulm types to be type decimal
            // builder.Entity<Contract>()
            //.Property(c => c.Initial_Fee)
            //.HasColumnType("decimal(18, 2)");

            builder.Entity<DeletionSettings>()
            .HasKey(ds => new { ds.DeletionTimeValue, ds.DeletionTimeUnit });

            builder.Entity<DeletionSettings>()
            .Property(ds => ds.DeletionTimeValue)
            .IsRequired();

            builder.Entity<DeletionSettings>()
            .Property(ds => ds.DeletionTimeUnit)
            .IsRequired()
            .HasMaxLength(50);

            builder.Entity<VAT>()
            .Property(v => v.VAT_Percentage)
            .HasColumnType("decimal(18, 2)");

            builder.Entity<Order>()
           .Property(o => o.Total_Price)
           .HasColumnType("decimal(18, 2)");

            builder.Entity<Outstanding_Payment>()
           .Property(op => op.Amount_Due)
           .HasColumnType("decimal(18, 2)");

            builder.Entity<Outstanding_Payment>()
           .Property(op => op.Late_Fee)
           .HasColumnType("decimal(18, 2)");

            builder.Entity<Payment>()
           .Property(pay => pay.Amount)
           .HasColumnType("decimal(18, 2)");

            builder.Entity<Price>()
           .Property(pr => pr.Unit_Price)
           .HasColumnType("decimal(18, 2)");

            builder.Entity<Product>()
            .Property(p => p.Unit_Price)
            .HasColumnType("decimal(18, 2)"); // Add this line for Unit_Price

            builder.Entity<Product>()
            .Property(p => p.Purchase_Price)
            .HasColumnType("decimal(18, 2)");

            builder.Entity<Supplier_Order>()
           .Property(so => so.Total_Price)
           .HasColumnType("decimal(18, 2)");

            builder.Entity<Supplier_Order_Line>()
            .Property(sol => sol.Purchase_Price)
            .HasColumnType("decimal(18, 2)");

            builder.Entity<Supplier_Order_Line>()
            .Property(sol => sol.Unit_Price)
            .HasColumnType("decimal(18, 2)");

            builder.Entity<Discount>()
           .Property(d => d.Discount_Percentage)
           .HasColumnType("decimal(18, 2)");

            builder.Entity<Supplier_Order_Line>()
             .HasOne(s => s.Product)
             .WithMany()
             .HasForeignKey(s => s.Product_ID)
             .OnDelete(DeleteBehavior.NoAction);

            builder.Entity<Order_Line>()
           .Property(ol => ol.Unit_Price)
           .HasColumnType("decimal(18,2)");

            //Delete cascade error fix
            //builder.Entity<Payment>()
            //.HasOne(p => p.Payment_Type)
            //.WithMany()
            //.HasForeignKey(p => p.Payment_Type_ID)
            //.OnDelete(DeleteBehavior.NoAction);

            //builder.Entity<Supplier_Order_Line>()
            //.HasOne(s => s.Supplier)
            //.WithMany()
            //.HasForeignKey(s => s.Supplier_ID)
            //.OnDelete(DeleteBehavior.NoAction);

            builder.Entity<Received_Supplier_Order_Line>()
                .HasOne(r => r.Received_Supplier_Order)
                .WithMany(r => r.Received_Supplier_Order_Lines)  // Assuming Received_Supplier_Order has a collection of Received_Supplier_Order_Lines
                .HasForeignKey(r => r.Received_Supplier_Order_ID)
                .OnDelete(DeleteBehavior.NoAction);

            builder.Entity<Received_Supplier_Order_Line>()
                .HasOne(r => r.Supplier_Order_Line)
                .WithMany()  // Assuming Supplier_Order_Line does not need a navigation property for Received_Supplier_Order_Lines
                .HasForeignKey(r => r.Supplier_Order_Line_ID)
                .OnDelete(DeleteBehavior.NoAction);

            builder.Entity<Received_Supplier_Order_Line>()
                .HasOne(r => r.Product)
                .WithMany()  // Assuming Product does not need a navigation property for Received_Supplier_Order_Lines
                .HasForeignKey(r => r.Product_ID)
                .OnDelete(DeleteBehavior.NoAction);


            //builder.Entity<Received_Supplier_Order_Line>()
            //.HasOne(r => r.Product)
            //.WithMany()
            //.HasForeignKey(r => r.Product_ID)
            //.OnDelete(DeleteBehavior.NoAction);

            builder.Entity<Outstanding_Payment>()
            .HasOne(op => op.Member)
            .WithMany()
            .HasForeignKey(op => op.Member_ID)
            .OnDelete(DeleteBehavior.NoAction); 

            builder.Entity<Outstanding_Payment>()
            .HasOne(op => op.Payment)
            .WithMany()
            .HasForeignKey(op => op.Payment_ID)
            .OnDelete(DeleteBehavior.NoAction);


            builder.Entity<Booking>()
            .HasOne(b => b.Member)
            .WithMany()
            .HasForeignKey(b => b.Member_ID)
            .OnDelete(DeleteBehavior.NoAction);

            builder.Entity<Reward_Member>()
            .HasOne(rm => rm.Member)
            .WithMany()
            .HasForeignKey(rm => rm.Member_ID)
            .OnDelete(DeleteBehavior.NoAction);

            builder.Entity<Reward_Member>()
            .HasOne(rm => rm.Reward)
            .WithMany()
            .HasForeignKey(rm => rm.Reward_ID)
            .OnDelete(DeleteBehavior.NoAction);

           builder.Entity<Booking_Time_Slot>()
            .HasOne(bts => bts.Booking)
            .WithMany()
            .HasForeignKey(bts => bts.Booking_ID)
            .OnDelete(DeleteBehavior.NoAction);

           builder.Entity<Booking_Time_Slot>()
            .HasOne(bts => bts.Time_Slot)
            .WithMany()
            .HasForeignKey(bts => bts.Time_Slot_ID)
            .OnDelete(DeleteBehavior.NoAction);

            builder.Entity<Attendance_List>()
            .HasOne(b => b.Time_Slot)
            .WithMany()
            .HasForeignKey(b => b.Time_Slot_ID)
            .OnDelete(DeleteBehavior.NoAction);

            builder.Entity<Lesson_Plan_Workout>()
            .HasOne(lpw => lpw.Workout)
            .WithMany()
            .HasForeignKey(lpw => lpw.Workout_ID)
            .OnDelete(DeleteBehavior.NoAction);

            builder.Entity<Lesson_Plan_Workout>()
            .HasOne(lpw => lpw.Lesson_Plan)
            .WithMany()
            .HasForeignKey(lpw => lpw.Lesson_Plan_ID)
            .OnDelete(DeleteBehavior.NoAction);

            builder.Entity<Workout>()
           .HasOne(w => w.Workout_Category)
           .WithMany()
           .HasForeignKey(w => w.Workout_Category_ID)
           .IsRequired()
           .OnDelete(DeleteBehavior.Restrict);

            base.OnModelCreating(builder);



            //var Contract_Types = new Contract_Type[]
            //{
            //    new Contract_Type { Contract_Type_ID = 1, Contract_Type_Name = "3-Month Membership", Contract_Description = "Three-month gym membership contract" },              
            //};
            //builder.Entity<Contract_Type>().HasData(Contract_Types);



            var Discounts = new Discount[]
            {
                new Discount
                {
                    Discount_ID = 1,
                    Discount_Code = "SPR-123",
                    Discount_Percentage = 10.00m,
                    Discount_Date = new DateTime(2024, 4, 10),
                    End_Date = new DateTime(2024, 4, 10).AddDays(30)
                }
            };
            builder.Entity<Discount>().HasData(Discounts);


            //var Employee_Types = new Employee_Type[]
            //{
            //    new Employee_Type { Employee_Type_ID = 1, Job_Title = "Administrator", Job_Description = "Responsible for managing administrative tasks and operations" }
            //};
            //builder.Entity<Employee_Type>().HasData(Employee_Types);

            var Inspection_Statuses = new Inspection_Status[]
            {
                new Inspection_Status { Inspection_Status_ID = 1, Inspection_Status_Description= "Pending" }


            };
            builder.Entity<Inspection_Status>().HasData(Inspection_Statuses);

            var Inspection_Types = new Inspection_Type[]
            {
                new Inspection_Type { Inspection_Type_ID = 1, Inspection_Type_Name = "Safety Inspection", Inspection_Type_Criteria = "Ensure compliance with safety standards" }
    
            };
            builder.Entity<Inspection_Type>().HasData(Inspection_Types);

            var Membership_Statuses = new Membership_Status[]
            {
                new Membership_Status { Membership_Status_ID = 1, Membership_Status_Description = "Active" }

            };
            builder.Entity<Membership_Status>().HasData(Membership_Statuses);

            var Newsletters = new Newsletter[]
            {
                new Newsletter { Newsletter_ID = 1, Newsletter_Title = "Fitness Tips", Newsletter_Photo = "fitness_tips.jpg", Newsletter_Description = "Stay updated with our latest fitness tips!" }

            };
            builder.Entity<Newsletter>().HasData(Newsletters);

            //var Payment_Methods = new Payment_Method[]
            //{
            //    new Payment_Method { Payment_Method_ID = 1, Payment_Method_Name = "Payfast" }  
            //};
            //builder.Entity<Payment_Method>().HasData(Payment_Methods);

            var Payment_Types = new Payment_Type[]
            {
                new Payment_Type { Payment_Type_ID = 1, Payment_Type_Name = "Payfast" },
                new Payment_Type { Payment_Type_ID = 2, Payment_Type_Name = "EFT" },
                new Payment_Type { Payment_Type_ID = 3, Payment_Type_Name = "Debit Order" }

            };
            builder.Entity<Payment_Type>().HasData(Payment_Types);

            var ProductCategories = new Product_Category[]
            {
                new Product_Category { Product_Category_ID = 1, Category_Name = "Tops", Category_Description = "Gym Tops" },
                new Product_Category { Product_Category_ID = 2, Category_Name = "Bottoms", Category_Description = "Gym Bottoms" }
            };

            builder.Entity<Product_Category>().HasData(ProductCategories);

            var Reports = new Report[]
            {
                new Report { Report_ID = 1, Report_Name = "Monthly Sales Report", Report_Description = "Report summarizing monthly sales data", Generated_Date = new DateTime(2024, 4, 10) }
            };
            builder.Entity<Report>().HasData(Reports);

            var Reward_Types = new Reward_Type[]
            {
                new Reward_Type { Reward_Type_ID = 1, Reward_Type_Name = "Membership Renewal Discount", Reward_Criteria = "Receive a discount on membership renewal after completing a certain number of workouts" }

            };
            builder.Entity<Reward_Type>().HasData(Reward_Types);

            var Suppliers = new Supplier[]
            {
                new Supplier { Supplier_ID = 1, Name = "FitnessGear", Contact_Number = "1234567890", Email_Address = "info@fitnessgear.com", Physical_Address = "123 Fitness Street, Cityville, South Africa" }
            };
            builder.Entity<Supplier>().HasData(Suppliers);


            var userStatus = new User_Status[]
            {
                new User_Status { User_Status_ID = 1, User_Status_Description = "Actived" },
                new User_Status { User_Status_ID = 2, User_Status_Description = "Deactivated" }
            };
            builder.Entity<User_Status>().HasData(userStatus);

            var userTypes = new User_Type[]
    {
                new User_Type { User_Type_ID = 1, User_Type_Name = "Owner" },
                new User_Type { User_Type_ID = 2, User_Type_Name = "Employee" },
                new User_Type { User_Type_ID = 3, User_Type_Name = "Member" }
    };
            builder.Entity<User_Type>().HasData(userTypes);



        //    var Users = new User[]                
        //    {
        //        new User 
        //        {
        //            User_ID = 1,
        //            Id = 1,
        //            Name = "Don",
        //            Surname = "Percival",
        //            ID_Number = "0203057644931",
        //            Email = "DonPercival@gmail.com",
        //            Physical_Address = "456 Oak Avenue",
        //            PhoneNumber = "0734457681",
        //            Photo = "DonProfilePic.jpg",
        //            PasswordHash = "AEWR54Q35H5T4HRGRGQ",
        //            Date_of_Birth = new DateTime(1994,10,11),
        //            User_Type_ID =1,
        //            User_Status_ID =1
        //        },
        //        new User
        //        {
        //            User_ID = 2,
        //            Id = 2,
        //            Name = "Barbra",
        //            Surname = "Gordon",
        //            ID_Number = "1220231231312",
        //            Email = "barbragordon@gmail.com",
        //            Physical_Address = "456 Oak Avenue",
        //            PhoneNumber = "9876543210",
        //            Photo = "barbra_photo.jpg",
        //            PasswordHash = "HJDKL3948SJDF3JSHFD",
        //            Date_of_Birth = new DateTime(1985, 5, 15),
        //            User_Type_ID =2,
        //            User_Status_ID =1
        //        },

        //        new User

        //        {
        //        User_ID = 3,
        //        Id = 3,
        //        Name = "Jane",
        //        Surname = "Smith",
        //        ID_Number = "1220231231312",
        //        Email = "JaneSmith@gmail.com",
        //        Physical_Address = "456 Oak Avenue",
        //        PhoneNumber = "9876543210",
        //        Photo = "jane_smith_photo.jpg",    
        //        PasswordHash = "JKLFSF34JKLRE983JFSD",
        //        Date_of_Birth = new DateTime(1985, 5, 15),
        //        User_Type_ID =3,
        //        User_Status_ID =1
        //        }


        //    };
        //builder.Entity<User>().HasData(Users);








            //var Contracts = new Contract[]
            //{
            //   new Contract { Contract_ID = 1, Subscription_Date = new DateTime(2023, 1, 1), Expiry_Date = new DateTime(2023, 4, 1), Terms_Of_Agreement = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", Approval_Status = true, Approval_Date = new DateTime(2023, 1, 1), Initial_Fee = 100.00m, Contract_Type_ID = 1, Payment_Type_ID = 1 }
            //};
            //builder.Entity<Contract>().HasData(Contracts);


            //var ContractHistories = new Contract_History[]
            //{
            //    new Contract_History { Contract_History_ID = 1, Modification_Date = new DateTime(2023, 5, 15), Previous_Terms = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", Updated_Terms = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut enim ad minim veniam.", Reasons_For_Changes = "To include additional benefits for members.", Contract_ID = 1 },

            //};
            //builder.Entity<Contract_History>().HasData(ContractHistories);



            var VAT = new VAT[]
            {
               new VAT { VAT_ID = 1, VAT_Percentage = 15.00m, VAT_Date = new DateTime(2024, 1, 1) }
            };
            builder.Entity<VAT>().HasData(VAT);


            var Audit_Trails = new Audit_Trail[]
            {
                new Audit_Trail { Audit_Trail_ID = 1, Action = "Action description", Timestamp = DateTime.Now }

            };
            builder.Entity<Audit_Trail>().HasData(Audit_Trails);

            var shifts = new List<Shift>();

            // Default shift (placeholder)
            int defaultShiftId = 1;
            shifts.Add(new Shift
            {
                Shift_ID = defaultShiftId,
                Shift_Number = defaultShiftId,
                Start_Time = new TimeSpan(0, 0, 0),
                End_Time = new TimeSpan(0, 0, 0)
            });

            // Weekdays (Monday - Friday) shifts
            var startTime = new TimeSpan(6, 0, 0);
            var endTime = new TimeSpan(22, 0, 0);
            int shiftId = defaultShiftId + 1; // Start after the default shift ID

            while (startTime < endTime)
            {
                shifts.Add(new Shift
                {
                    Shift_ID = shiftId,
                    Shift_Number = shiftId,
                    Start_Time = startTime,
                    End_Time = startTime.Add(new TimeSpan(2, 0, 0))
                });
                startTime = startTime.Add(new TimeSpan(2, 0, 0));
                shiftId++;
            }

            // Saturday shifts
            startTime = new TimeSpan(6, 0, 0);
            endTime = new TimeSpan(20, 0, 0);

            while (startTime < endTime)
            {
                shifts.Add(new Shift
                {
                    Shift_ID = shiftId,
                    Shift_Number = shiftId,
                    Start_Time = startTime,
                    End_Time = startTime.Add(new TimeSpan(2, 0, 0))
                });
                startTime = startTime.Add(new TimeSpan(2, 0, 0));
                shiftId++;
            }

            // Sunday shifts
            startTime = new TimeSpan(6, 0, 0);
            endTime = new TimeSpan(14, 0, 0);

            while (startTime < endTime)
            {
                shifts.Add(new Shift
                {
                    Shift_ID = shiftId,
                    Shift_Number = shiftId,
                    Start_Time = startTime,
                    End_Time = startTime.Add(new TimeSpan(2, 0, 0))
                });
                startTime = startTime.Add(new TimeSpan(2, 0, 0));
                shiftId++;
            }

            builder.Entity<Shift>().HasData(shifts);


            //var Employees = new Employee[]
            //{
            //   new Employee { Employee_ID = 1, Employment_Date = new DateTime(2024, 4, 12), Employee_Type_ID = 1 , Shift_ID =1, User_ID = 2 }
            //};
            //builder.Entity<Employee>().HasData(Employees);


            //var Members = new Member[]
            //{
            //   new Member { Member_ID = 1, Contract_ID = 1,User_ID = 3 }
               
            //};
            //builder.Entity<Member>().HasData(Members);


            //var AttendanceLists = new Attendance_List[]
            //{

            //    new Attendance_List { Attendance_ID = 1, Number_Of_Bookings = 1, Members_Present = 10, Members_Absent = 5, Time_Slot_ID = 1}
            //};
            //builder.Entity<Attendance_List>().HasData(AttendanceLists);


            //var Bookings = new Booking[]
            //{
            //    new Booking { Booking_ID = 1,  Member_ID = 1}
            //};
            //builder.Entity<Booking>().HasData(Bookings);

            var employeeTypes = new Employee_Type[]
            {
                new Employee_Type
                {
                    Employee_Type_ID = 1,
                    Job_Title = "Admin",
                    Job_Description = "Manages administrative tasks and oversees operations."
                },
                new Employee_Type
                {
                    Employee_Type_ID = 2,
                    Job_Title = "Trainer",
                    Job_Description = "Provides training and fitness guidance to members."
                }
            };
            builder.Entity<Employee_Type>().HasData(employeeTypes);

            var workoutcategories = new Workout_Category[]
            {
                new Workout_Category { Workout_Category_ID = 1, Workout_Category_Name = "Cardio", Workout_Category_Description = "Cardio workouts to improve endurance and burn calories." },
                new Workout_Category { Workout_Category_ID = 2, Workout_Category_Name = "Strength", Workout_Category_Description = "Strength training workouts to build muscle and increase strength." },
                new Workout_Category { Workout_Category_ID = 3, Workout_Category_Name = "Flexibility", Workout_Category_Description = "Flexibility workouts to improve range of motion and reduce injury risk." }
            };
            builder.Entity<Workout_Category>().HasData(workoutcategories);


            var workouts = new Workout[]
            {
                    new Workout
                    {
                        Workout_ID = 1,
                        Workout_Name = "Cardio Blast",
                        Workout_Description = "High-intensity cardio workout to burn calories and improve endurance.",
                        Sets = 4,
                        Reps = 10,
                        Workout_Category_ID = 1
                    },
                    new Workout
                    {
                        Workout_ID = 2,
                        Workout_Name = "Strength Training",
                        Workout_Description = "Build muscle strength and endurance.",
                        Sets = 3,
                        Reps = 12,
                        Workout_Category_ID = 2
                    },
                    new Workout
                    {
                        Workout_ID = 3,
                        Workout_Name = "Flexibility Routine",
                        Workout_Description = "Improve your flexibility with this stretching routine.",
                        Sets = 2,
                        Reps = 15,
                        Workout_Category_ID = 3
                    }
            };
            builder.Entity<Workout>().HasData(workouts);


            var Lesson_Plans = new Lesson_Plan[]
            {
                new Lesson_Plan { Lesson_Plan_ID = 1, Program_Name = "Base", Program_Description = "Base program description", }

            };
            builder.Entity<Lesson_Plan>().HasData(Lesson_Plans);


            var orderStatuses = new Order_Status[]
            {
                new Order_Status { Order_Status_ID = 1, Order_Status_Description = "Ready for Collection" },
                new Order_Status { Order_Status_ID = 2, Order_Status_Description = "Overdue for Collection" },
                new Order_Status { Order_Status_ID = 3, Order_Status_Description = "Collected" },
                new Order_Status { Order_Status_ID = 4, Order_Status_Description = "Late Collection" }
            };
            builder.Entity<Order_Status>().HasData(orderStatuses);


            //var Orders = new Order[]
            //{
            //   new Order { Order_ID = 1, Order_Date = new DateTime(2024, 4, 12), Order_Details = "Example order details", Total_Price = 100.00m, Member_ID = 1, Order_Status_ID = 1 }
            //};
            //builder.Entity<Order>().HasData(Orders);


            //var Outstanding_Payments = new Outstanding_Payment[]
            //{
            //   new Outstanding_Payment { Outstanding_Payment_ID = 1, Due_Date = new DateTime(2024, 4, 12), Amount_Due = 50.00m, Late_Fee = 0.00m, Member_ID = 1, Payment_ID = 1 }
            //};
            //builder.Entity<Outstanding_Payment>().HasData(Outstanding_Payments);


            //var Owners = new Owner[]
            //{
            //   new Owner { Owner_ID = 1, User_ID = 1 }
            //};
            //builder.Entity<Owner>().HasData(Owners);



            //var Payments = new Payment[]
            //{
            //  new Payment { Payment_ID = 1, Amount = 50.00m, Payment_Date = new DateTime(2024, 4, 12) }
            //};
            //builder.Entity<Payment>().HasData(Payments);



            //var Products = new Product[]
            //{
            //    new Product { Product_ID = 1, Product_Name = "Gym T-Shirt", Product_Description = "Breathable cotton gym shirt", Product_Img = "gym_tshirt.jpg", Quantity = 50, Unit_Price = 19.99M, Size = "XS", Product_Category_ID = 1, Supplier_ID = 1 },
            //    new Product { Product_ID = 2, Product_Name = "Running Shorts", Product_Description = "Lightweight running shorts", Product_Img = "running_shorts.jpg", Quantity = 40, Unit_Price = 24.99M, Size = "M", Product_Category_ID = 2, Supplier_ID = 1 },
            //    new Product { Product_ID = 3, Product_Name = "Hoodie", Product_Description = "Fleece-lined gym hoodie", Product_Img = "hoodie.jpg", Quantity = 30, Unit_Price = 39.99M, Size = "L", Product_Category_ID = 2, Supplier_ID = 1 },
            //    new Product { Product_ID = 4, Product_Name = "Compression Tights", Product_Description = "High-performance compression tights", Product_Img = "compression_tights.jpg", Quantity = 60, Unit_Price = 29.99M, Size = "S", Product_Category_ID = 2, Supplier_ID = 1 },
            //    new Product { Product_ID = 5, Product_Name = "Gym Tank Top", Product_Description = "Sleeveless tank for workouts", Product_Img = "tank_top.jpg", Quantity = 70, Unit_Price = 15.99M, Size = "M", Product_Category_ID = 1, Supplier_ID = 1 },
            //    new Product { Product_ID = 6, Product_Name = "Sweatpants", Product_Description = "Comfortable gym sweatpants", Product_Img = "sweatpants.jpg", Quantity = 50, Unit_Price = 25.99M, Size = "L", Product_Category_ID = 2, Supplier_ID = 1 },
            //    new Product { Product_ID = 7, Product_Name = "Sports Bra", Product_Description = "Supportive sports bra", Product_Img = "sports_bra.jpg", Quantity = 40, Unit_Price = 19.99M, Size = "S", Product_Category_ID = 1, Supplier_ID = 1 },
            //    new Product { Product_ID = 8, Product_Name = "Gym Leggings", Product_Description = "High-waisted gym leggings", Product_Img = "gym_leggings.jpg", Quantity = 60, Unit_Price = 34.99M, Size = "M", Product_Category_ID = 2, Supplier_ID = 1 }
            //};

            //builder.Entity<Product>().HasData(Products);


            //var prices = new Price[]
            //{
            //    new Price { Price_ID = 1, Unit_Price = 50.00m, Product_ID = 1 }
            //};
            //builder.Entity<Price>().HasData(prices);


            //var OrderLines = new Order_Line[]
            //{
            //    new Order_Line { Order_Line_ID = 1, Order_ID = 1, Product_ID = 1 }

            //};
            //builder.Entity<Order_Line>().HasData(OrderLines);


            //var Received_Supplier_Orders = new Received_Supplier_Order[]
            //{
            //    new Received_Supplier_Order { Received_Supplier_Order_ID = 1, Supplies_Received_Date = new DateTime(20, 04, 10) }
            //};

            //builder.Entity<Received_Supplier_Order>().HasData(Received_Supplier_Orders);



            //var Received_Supplier_Order_Lines = new Received_Supplier_Order_Line[]
            //{
            //    new Received_Supplier_Order_Line { Received_Supplier_Order_Line_ID = 1,Received_Supplier_Order_ID = 1,Supplier_Order_ID = 1,Product_ID = 1,Received_Supplies_Quantity = 10 }
            //};

            //builder.Entity<Received_Supplier_Order_Line>().HasData(Received_Supplier_Order_Lines);

            var Rewards = new Reward[]
            {
                new Reward { Reward_ID = 1, IsPosted = false, Reward_Issue_Date = new DateTime(2024, 4, 10), Reward_Type_ID = 1 }

            };
            builder.Entity<Reward>().HasData(Rewards);

            //var Reward_Members = new Reward_Member[]
            //{
            //    new Reward_Member{ Reward_Member_ID = 1, IsRedeemed = false, Member_ID = 1, Reward_ID = 1}
            //};
            //builder.Entity<Reward_Member>().HasData(Reward_Members);

            var Roles = new Role[]
            {
                new Role{ Id = 1, Name = "Owner", NormalizedName= "OWNER", isEditable = false},
                new Role{ Id = 2, Name = "Employee", NormalizedName= "EMPLOYEE", isEditable =true},
                new Role{ Id = 3, Name = "Member", NormalizedName= "MEMBER", isEditable =true}
            };
            builder.Entity<Role>().HasData(Roles);


            int claimId = 1;
            //Owner Claims
            //for each admin claim
            var ownerClaims = new Claim[]

            {
                new Claim("Booking Manager", "Create"),
                new Claim("Booking Manager", "Read"),
                new Claim("Booking Manager", "Update"),
                new Claim("Booking Manager", "Delete"),

                new Claim("Equipment Manager", "Create"),
                new Claim("Equipment Manager", "Read"),
                new Claim("Equipment Manager", "Update"),
                new Claim("Equipment Manager", "Delete"),

                new Claim("Employee Manager", "Create"),
                new Claim("Employee Manager", "Read"),
                new Claim("Employee Manager", "Update"),
                new Claim("Employee Manager", "Delete"),

                new Claim("Inventory Manager", "Create"),
                new Claim("Inventory Manager", "Read"),
                new Claim("Inventory  Manager", "Update"),
                new Claim("Inventory Manager", "Delete"),

                new Claim("Gym Manager", "Create"),
                new Claim("Gym Manager", "Read"),
                new Claim("Gym  Manager", "Update"),
                new Claim("Gym Manager", "Delete"),
            };
            //create a refrence of it in the Role Claims table
            foreach (var claim in ownerClaims) 
            {
                builder.Entity<IdentityRoleClaim<int>>().HasData(new IdentityRoleClaim<int>
                { 
                   Id = claimId++,
                   RoleId = Roles[0].Id,
                   ClaimType = claim.Type,
                   ClaimValue = claim.Value
                });
            }

            //Employee Claims , they are admin too but just for separation        
            //for each employee claim
            var employeeClaims = new Claim[]
            {
                new Claim("Booking Manager", "Create"),
                new Claim("Booking Manager", "Read"),
                new Claim("Booking Manager", "Update"),
                new Claim("Booking Manager", "Delete"),

                new Claim("Equipment Manager", "Create"),
                new Claim("Equipment Manager", "Read"),
                new Claim("Equipment Manager", "Update"),
                new Claim("Equipment Manager", "Delete"),

                new Claim("Employee Manager", "Read"),
                new Claim("Employee Manager", "Update"),

                new Claim("Inventory Manager", "Create"),
                new Claim("Inventory Manager", "Read"),
                new Claim("Inventory Manager", "Update"),
                new Claim("Inventory Manager", "Delete"),
            };
            //create a refrence of it in the Role Claims table
            foreach (var claim in employeeClaims)
            {
                builder.Entity<IdentityRoleClaim<int>>().HasData(new IdentityRoleClaim<int>
                {
                    Id = claimId++,
                    RoleId = Roles[1].Id,
                    ClaimType = claim.Type,
                    ClaimValue = claim.Value
                });
            }

            var memberClaims = new Claim[]
            {
                new Claim("Booking Interface", "Create"),
                new Claim("Booking Interface", "Read"),
                new Claim("Booking Interface", "Update"),
                new Claim("Booking Interface", "Delete"),

                new Claim("Profile", "Create"),
                new Claim("Profile", "Read"),
                new Claim("Profile", "Update"),

            };
            //create a refrence of it in the Role Claims table
            foreach (var claim in memberClaims)
            {
                builder.Entity<IdentityRoleClaim<int>>().HasData(new IdentityRoleClaim<int>
                {
                    Id = claimId++,
                    RoleId = Roles[2].Id,
                    ClaimType = claim.Type,
                    ClaimValue = claim.Value
                });
            }

            //var Supplier_Orders = new Supplier_Order[]
            //{
            //    new Supplier_Order { Supplier_Order_ID = 1, Date = new DateTime(2024, 4, 10), Supplier_Order_Details = "Ordered 50 units of dumbbells and 20 yoga mats", Total_Price = 1500.00m, Supplier_ID = 1, Owner_ID = 1 }

            //};
            //builder.Entity<Supplier_Order>().HasData(Supplier_Orders);

            //var Supplier_Order_Lines = new Supplier_Order_Line[]
            //{
            //    new Supplier_Order_Line { Supplier_Order_Line_ID = 1, Supplier_Qauntity = 12, Supplier_ID = 1, Product_ID = 1 }

            //};
            //builder.Entity<Supplier_Order_Line>().HasData(Supplier_Order_Lines);

            //var Inventory = new Inventory[]
            //{
            //    new Inventory { Inventory_ID = 1, Inventory_Item_Category = "Clothes", Inventory_Item_Name = "Men's Dry-fit Tops", Inventory_Item_Quantity = 20, Inventory_Item_Photo = "dry_fit_tops.jpg", Received_Supplier_Order_ID = 1, Supplier_ID = 1 }

            //};
            //builder.Entity<Inventory>().HasData(Inventory);

            var Equipments = new Equipment[]
            {
                
                 new Equipment{ Equipment_ID = 1, Equipment_Name = "Treadmill", Equipment_Description = "A motorized device used for running or walking while staying in one place." }
                
            };
            builder.Entity<Equipment>().HasData(Equipments);

            var Inspections = new Inspection[]
            {
                new Inspection { Inspection_ID = 1, Inspection_Date = new DateTime(2024, 4, 12),Inspection_Notes = "Holes in AVS pants" , Equipment_ID = 1,  Inspection_Type_ID = 1, Inspection_Status_ID = 1 }

            };
            builder.Entity<Inspection>().HasData(Inspections);

            var Booking_Time_Slots = new Booking_Time_Slot[]
            {
                new Booking_Time_Slot{Booking_Time_Slot_ID = 1, Booking_ID = 1,Time_Slot_ID = 1}
            };
            builder.Entity<Booking_Time_Slot>();


            //var Time_Slots = new Time_Slot[]
            //{

            //    new Time_Slot{Time_Slot_ID = 1,Slot_Date =  new DateTime(2024, 4, 12)  , Slot_Time = DateTime.Parse("11:00:00"), Availability = true, Lesson_Plan_ID= 1, Employee_ID=1}

            //};
            //builder.Entity<Time_Slot>().HasData(Time_Slots);


            //var Write_Offs = new Write_Off[]
            //{
            //    new Write_Off { Write_Off_ID = 1, Date = new DateTime(2024, 4, 12), Write_Off_Reason = "Expired items", Inventory_ID = 1 }

            //};
            //builder.Entity<Write_Off>().HasData(Write_Offs);


            var lessonPlanWorkOuts = new Lesson_Plan_Workout[]
            {
                new Lesson_Plan_Workout {Lesson_Plan_Workout_ID =1, Lesson_Plan_ID = 1, Workout_ID = 1}
            };
            builder.Entity<Lesson_Plan_Workout>().HasData(lessonPlanWorkOuts);

        }

    }
}

//controller
namespace av_motion_api.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ProductController : ControllerBase
    {
        private readonly AppDbContext _appContext;
        public readonly IRepository _repository;
        public ProductController(AppDbContext _context, IRepository repository)
        {

            _appContext = _context;
            _repository = repository;
        }
        // GET: api/<ProductController>
        [HttpGet]
        [Route("GetAllProducts")]
        public async Task<IActionResult> GetAllProducts()
        {
            try
            {
                var products = await _appContext.Products.ToListAsync();

                return Ok(products);
            }
            catch (Exception)
            {
                return BadRequest();
            }
        }

        // GET api/<ProductController>/5
        [HttpGet]
        [Route("GetProductById/{id}")]
        public async Task<IActionResult> GetProductById(int id)
        {
            try
            {
                var product = await _appContext.Products.FindAsync(id);
                if (product == null)
                {
                    return NotFound();
                }

                return Ok(product);
            }
            catch (Exception)
            {
                return BadRequest();
            }
        }

        //POST api/<ProductController>
        [HttpPost]
        [DisableRequestSizeLimit]
        [Route("PostProduct")]
        public async Task<IActionResult> PostProduct([FromForm] ProductViewModel product)
        {
            var formCollection = await Request.ReadFormAsync();
            var product_Img = formCollection.Files.FirstOrDefault();

            using (var memoryStream = new MemoryStream())
            {
                await product_Img.CopyToAsync(memoryStream);
                var fileBytes = memoryStream.ToArray();
                string base64Image = Convert.ToBase64String(fileBytes);

                var newProduct = new Product()
                {
                    Product_Name = product.Product_Name,
                    Product_Description = product.Product_Description,
                    Product_Img = base64Image,
                    Quantity = product.Quantity,
                    Unit_Price = product.Unit_Price,
                    Purchase_Price = product.Purchase_Price,
                    Size = product.Size,
                    Product_Category_ID = product.Product_Category_ID,
                };

                if (_appContext.Products == null)
                {
                    return Problem("Entity set 'AppDbContext.Products' is null.");
                }
                _appContext.Products.Add(newProduct);
                await _appContext.SaveChangesAsync();

                return Ok(newProduct);
            }
        }


        // PUT api/<ProductController>/5
        [HttpPut]
        [Route("PutProduct/{id}")]
        public async Task<IActionResult> PutProduct(int id, [FromForm] ProductViewModel updatedProduct)
        {
            var existingProduct = await _appContext.Products.FindAsync(id);
            if (existingProduct == null)
            {
                return NotFound();
            }

            // Read the form data to get the photo file
            var formCollection = await Request.ReadFormAsync();
            var product_Img = formCollection.Files.FirstOrDefault();

            existingProduct.Product_Name = updatedProduct.Product_Name;
            existingProduct.Product_Description = updatedProduct.Product_Description;
            existingProduct.Quantity = updatedProduct.Quantity;
            existingProduct.Unit_Price = updatedProduct.Unit_Price;
            existingProduct.Purchase_Price = updatedProduct.Purchase_Price;
            existingProduct.Size = updatedProduct.Size;
            existingProduct.Product_Category_ID = updatedProduct.Product_Category_ID;

            if (product_Img != null && product_Img.Length > 0)
            {
                using (var memoryStream = new MemoryStream())
                {
                    await product_Img.CopyToAsync(memoryStream);
                    var fileBytes = memoryStream.ToArray();
                    string base64Image = Convert.ToBase64String(fileBytes);

                    existingProduct.Product_Img = base64Image; // Store the base64 string of the photo
                }
            }

            _appContext.Products.Update(existingProduct);
            await _appContext.SaveChangesAsync();

            return Ok(existingProduct);
        }

        // DELETE api/<ProductController>/5
        [HttpDelete]
        [Route("DeleteProduct/{id}")]

        public async Task<IActionResult> DeleteProduct(int id)
        {
            if (_appContext.Products == null)
            {
                return NotFound();
            }
            var product = await _appContext.Products.FindAsync(id);
            if (product == null)
            {
                return NotFound();
            }
            
            _appContext.Products.Remove(product);
            await _appContext.SaveChangesAsync();

            return NoContent();
        }

    }
}

//model
  namespace av_motion_api.Models
{
    public class Product
    {
        [Key]
        public int Product_ID { get; set; }

        [Required, StringLength(100)]
        public string Product_Name { get; set; }

        [Required, StringLength(100)]
        public string Product_Description { get; set; }

        [Required]
        public string Product_Img { get; set; }

        public decimal? Purchase_Price { get; set; }

        [Required]
        public int Quantity { get; set; }

        [Required]
        public decimal Unit_Price { get; set; }

        [Required]
        public string Size { get; set; }

        public int Product_Category_ID { get; set; }

        [ForeignKey(nameof(Product_Category_ID))]
        public Product_Category Product_Category { get; set; }

    }
}

//viewmodel
namespace av_motion_api.ViewModels
{
    public class ProductViewModel
    { 
        public string Product_Name { get; set; }
        public string Product_Description { get; set; }
        public IFormFile Product_Img { get; set; }
        public int Quantity { get; set; }
        public decimal Unit_Price { get; set; }
        public decimal? Purchase_Price { get; set; }
        public string Size { get; set; }
        public int Product_Category_ID { get; set; }
    }
}
//controller
namespace av_motion_api.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class InventoriesController : ControllerBase
    {
        private readonly AppDbContext _appContext;
        public readonly IRepository _repository;

        public InventoriesController(AppDbContext _context, IRepository repository)
        {

            _appContext = _context;
            _repository = repository;
        }
        // GET: api/<InventoriesController>
        [HttpGet]
        public async Task<ActionResult<IEnumerable<InventoryViewModel>>> GetInventories()
        {
            var inventories = await _repository.GetInventoryDetails();

            return Ok(inventories);
        }


        // GET api/<InventoriesController>/5
        [HttpGet("{id}")]
        public async Task<ActionResult<InventoryViewModel>> GetInventoryItem(int id)
        {
            if (_appContext.Inventory == null)
            {
                return NotFound();
            }
            var inventoryItem = await _repository.GetInventoryItem(id);
            if (inventoryItem == null)
            {
                return NotFound();
            }

            return Ok(inventoryItem);
        }

        // POST api/<InventoriesController>
        [HttpPost]
        public async Task<ActionResult<Inventory>> PostInventoryItem([FromBody] InventoryViewModel inventoryItem)
        {

            var item = new Inventory
            {

                Inventory_Item_Category = inventoryItem.category,
                Inventory_Item_Name = inventoryItem.itemName,
                Inventory_Item_Quantity = inventoryItem.quantity,
                Inventory_Item_Photo = inventoryItem.photo,
                Supplier_ID = inventoryItem.supplierID,
                Received_Supplier_Order_ID = inventoryItem.received_supplier_order_id
            };




            if (_appContext.Inventory == null)
            {
                return Problem("Entity set 'AppDbContext.Inventory'  is null.");
            }
            _appContext.Inventory.Add(item);
            await _appContext.SaveChangesAsync();


            return Ok(item);
        }

        // PUT api/<InventoriesController>/5
        [HttpPut("{id}")]
        public async Task<IActionResult> PutInventoryItem(int id, [FromBody] InventoryViewModel inventoryItem)
        {
            if (id != inventoryItem.inventoryID)
            {
                return BadRequest();
            }
            var existingitem = await _appContext.Inventory.FindAsync(id);

            if (existingitem == null)
            {
                return NotFound();
            }

            existingitem.Inventory_Item_Name = inventoryItem.itemName;
            existingitem.Inventory_Item_Category = inventoryItem.category;
            existingitem.Inventory_Item_Quantity = inventoryItem.quantity;
            existingitem.Supplier_ID = inventoryItem.supplierID;
            existingitem.Inventory_Item_Photo = inventoryItem.photo;

          //  _appContext.Entry(inventoryItem).State = EntityState.Modified;

            try
            {
                _appContext.Inventory.Update(existingitem);    
                await _appContext.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!InventoryStatusExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return NoContent();
        }


        // DELETE api/<InventoriesController>/5
        //[HttpDelete("{id}")]
        //public void Delete(int id)
        //{
        //}

        private bool InventoryStatusExists(int id)
        {
            return (_appContext.Inventory?.Any(e => e.Inventory_ID == id)).GetValueOrDefault();
        }
    }
}

//model
namespace av_motion_api.Models
{
    public class Inventory
    {
        [Key]
        public int Inventory_ID { get; set; }

        [Required]
        [StringLength(50)]
        public string Inventory_Item_Category { get; set;}

        [Required]
        [StringLength(50)]

        public string Inventory_Item_Name { get; set; }

        [Required]

        public int Inventory_Item_Quantity { get; set; }

        [Required]
        public string Inventory_Item_Photo { get; set; }

        public int Supplier_ID { get; set; }

        [ForeignKey(nameof(Supplier_ID))]
        public Supplier Supplier { get; set; }

        public int Received_Supplier_Order_ID { get; set; }

        [ForeignKey(nameof(Received_Supplier_Order_ID))]
        public Received_Supplier_Order Received_Supplier_Order { get; set; }


    }
}

//viewmodel
namespace av_motion_api.ViewModels
{
    public class InventoryViewModel
    {
        public int inventoryID { get; set; }
        public string category { get; set; }
        public string itemName { get; set; }
        public int quantity { get; set; }
        public string photo { get; set; }
        public int supplierID { get; set; }
        public string supplierName { get; set; }
        public int received_supplier_order_id { get; set; }
    }
}
//controller
namespace av_motion_api.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class InventoriesController : ControllerBase
    {
        private readonly AppDbContext _appContext;
        public readonly IRepository _repository;

        public InventoriesController(AppDbContext _context, IRepository repository)
        {

            _appContext = _context;
            _repository = repository;
        }
        // GET: api/<InventoriesController>
        [HttpGet]
        public async Task<ActionResult<IEnumerable<InventoryViewModel>>> GetInventories()
        {
            var inventories = await _repository.GetInventoryDetails();

            return Ok(inventories);
        }


        // GET api/<InventoriesController>/5
        [HttpGet("{id}")]
        public async Task<ActionResult<InventoryViewModel>> GetInventoryItem(int id)
        {
            if (_appContext.Inventory == null)
            {
                return NotFound();
            }
            var inventoryItem = await _repository.GetInventoryItem(id);
            if (inventoryItem == null)
            {
                return NotFound();
            }

            return Ok(inventoryItem);
        }

        // POST api/<InventoriesController>
        [HttpPost]
        public async Task<ActionResult<Inventory>> PostInventoryItem([FromBody] InventoryViewModel inventoryItem)
        {

            var item = new Inventory
            {

                Inventory_Item_Category = inventoryItem.category,
                Inventory_Item_Name = inventoryItem.itemName,
                Inventory_Item_Quantity = inventoryItem.quantity,
                Inventory_Item_Photo = inventoryItem.photo,
                Supplier_ID = inventoryItem.supplierID,
                Received_Supplier_Order_ID = inventoryItem.received_supplier_order_id
            };




            if (_appContext.Inventory == null)
            {
                return Problem("Entity set 'AppDbContext.Inventory'  is null.");
            }
            _appContext.Inventory.Add(item);
            await _appContext.SaveChangesAsync();


            return Ok(item);
        }

        // PUT api/<InventoriesController>/5
        [HttpPut("{id}")]
        public async Task<IActionResult> PutInventoryItem(int id, [FromBody] InventoryViewModel inventoryItem)
        {
            if (id != inventoryItem.inventoryID)
            {
                return BadRequest();
            }
            var existingitem = await _appContext.Inventory.FindAsync(id);

            if (existingitem == null)
            {
                return NotFound();
            }

            existingitem.Inventory_Item_Name = inventoryItem.itemName;
            existingitem.Inventory_Item_Category = inventoryItem.category;
            existingitem.Inventory_Item_Quantity = inventoryItem.quantity;
            existingitem.Supplier_ID = inventoryItem.supplierID;
            existingitem.Inventory_Item_Photo = inventoryItem.photo;

          //  _appContext.Entry(inventoryItem).State = EntityState.Modified;

            try
            {
                _appContext.Inventory.Update(existingitem);    
                await _appContext.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!InventoryStatusExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return NoContent();
        }


        // DELETE api/<InventoriesController>/5
        //[HttpDelete("{id}")]
        //public void Delete(int id)
        //{
        //}

        private bool InventoryStatusExists(int id)
        {
            return (_appContext.Inventory?.Any(e => e.Inventory_ID == id)).GetValueOrDefault();
        }
    }
}

//model
namespace av_motion_api.Models
{
    public class Inventory
    {
        [Key]
        public int Inventory_ID { get; set; }

        [Required]
        [StringLength(50)]
        public string Inventory_Item_Category { get; set;}

        [Required]
        [StringLength(50)]

        public string Inventory_Item_Name { get; set; }

        [Required]

        public int Inventory_Item_Quantity { get; set; }

        [Required]
        public string Inventory_Item_Photo { get; set; }

        public int Supplier_ID { get; set; }

        [ForeignKey(nameof(Supplier_ID))]
        public Supplier Supplier { get; set; }

        public int Received_Supplier_Order_ID { get; set; }

        [ForeignKey(nameof(Received_Supplier_Order_ID))]
        public Received_Supplier_Order Received_Supplier_Order { get; set; }


    }
}

//viewmodel
namespace av_motion_api.ViewModels
{
    public class InventoryViewModel
    {
        public int inventoryID { get; set; }
        public string category { get; set; }
        public string itemName { get; set; }
        public int quantity { get; set; }
        public string photo { get; set; }
        public int supplierID { get; set; }
        public string supplierName { get; set; }
        public int received_supplier_order_id { get; set; }
    }
}
//controller
using av_motion_api.Data;
using av_motion_api.Models;
using av_motion_api.ViewModels;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Security.Claims;

// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860

namespace av_motion_api.Controllers
{
    [Route("api/supplier/[controller]")]
    [ApiController]
    public class SupplierController : ControllerBase
    {
        private readonly AppDbContext _appContext;
        private readonly ILogger<SupplierController> _logger;

        public SupplierController(AppDbContext context, ILogger<SupplierController> logger)
        {
            _appContext = context;
            _logger = logger;
        }

        // GET: api/supplier/GetSuppliers
        [HttpGet]
        [Route("GetSuppliers")]
        //[Authorize(Roles = "Administrator")]
        public async Task<ActionResult<IEnumerable<Supplier>>> GetSuppliers()
        {
            var suppliers = await _appContext.Suppliers.ToListAsync();
            return suppliers;
        }

        // GET: api/supplier/GetSupplier/{id}
        [HttpGet]
        [Route("GetSupplier/{id}")]
        //[Authorize(Roles = "Administrator")]
        public async Task<ActionResult<Supplier>> GetSupplier(int id)
        {
            if (_appContext.Suppliers == null)
            {
                return NotFound();
            }
            var supplier = await _appContext.Suppliers.FindAsync(id);
            if (supplier == null)
            {
                return NotFound();
            }
            return supplier;
        }

        // POST: api/supplier/PostSupplier
        [HttpPost]
        [Route("PostSupplier")]
        //[Authorize(Roles = "Administrator")]
        public async Task<ActionResult<Supplier>> PostSupplier([FromBody] Supplier supplier)
        {
            if (_appContext.Suppliers == null)
            {
                return Problem("Entity set 'AppDbContext.Suppliers' is null.");
            }

            var supplierEntity = new Supplier
            {
                Name = supplier.Name,
                Contact_Number = supplier.Contact_Number,
                Email_Address = supplier.Email_Address,
                Physical_Address = supplier.Physical_Address,
            };

            _appContext.Suppliers.Add(supplierEntity);
            await _appContext.SaveChangesAsync();
            return Ok(supplier);
        }

        // PUT: api/supplier/PutSupplier/{id}
        [HttpPut]
        [Route("PutSupplier/{id}")]
        //[Authorize(Roles = "Administrator")]
        public async Task<IActionResult> PutSupplier(int id, [FromBody] SupplierViewModel supplier)
        {
            var supplierEntity = await _appContext.Suppliers.FindAsync(id);

            if (supplierEntity == null)
            {
                return NotFound();
            }

            supplierEntity.Name = supplier.Name;
            supplierEntity.Contact_Number = supplier.Contact_Number;
            supplierEntity.Email_Address = supplier.Email_Address;
            supplierEntity.Physical_Address = supplier.Physical_Address;

            try
            {
                _appContext.Suppliers.Update(supplierEntity);
                await _appContext.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!SupplierExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return NoContent();
        }

        // DELETE: api/supplier/DeleteSupplier/{id}
        [HttpDelete]
        [Route("DeleteSupplier/{id}")]
        //[Authorize(Roles = "Administrator")]
        public async Task<IActionResult> DeleteSupplier(int id)
        {
            if (_appContext.Suppliers == null)
            {
                return NotFound();
            }
            var supplier = await _appContext.Suppliers.FindAsync(id);
            if (supplier == null)
            {
                return NotFound();
            }

            _appContext.Suppliers.Remove(supplier);
            await _appContext.SaveChangesAsync();
            return NoContent();
        }

        private bool SupplierExists(int id)
        {
            return (_appContext.Suppliers?.Any(e => e.Supplier_ID == id)).GetValueOrDefault();
        }

        // GET: api/supplier/GetAllSupplierOrders
        [HttpGet]
        [Route("GetAllSupplierOrders")]
        //[Authorize(Roles = "Administrator")]
        public async Task<IActionResult> GetAllSupplierOrders()
        {
            var orders = await _appContext.Supplier_Orders
                    .Include(o => o.Supplier)
                    .Include(o => o.Owner)
                    .Include(o => o.Supplier_Order_Lines)
                    .ThenInclude(sol => sol.Product)
                    .ToListAsync();

            var supplierOrderViewModels = orders.Select(order => new SupplierOrderViewModel
            {
                Supplier_Order_ID = order.Supplier_Order_ID,
                Date = order.Date,
                Supplier_ID = order.Supplier_ID,
                Supplier_Name = order.Supplier.Name,
                Owner_ID = order.Owner_ID,
                Status = order.Status,
                Supplier_Order_Details = order.Supplier_Order_Details,
                OrderLines = order.Supplier_Order_Lines.Select(sol => new SupplierOrderLineViewModel
                {
                    Supplier_Order_Line_ID = sol.Supplier_Order_Line_ID,
                    Product_ID = sol.Product_ID,
                    Product_Name = sol.Product.Product_Name,
                    Supplier_Quantity = sol.Supplier_Quantity,
                    Purchase_Price = sol.Purchase_Price
                }).ToList(),
                Total_Price = (decimal)order.Supplier_Order_Lines.Sum(sol => sol.Supplier_Quantity * sol.Purchase_Price)
            }).ToList();

            return Ok(supplierOrderViewModels);
        }

        // GET: api/supplier/GetSupplierOrderById/{id}
        [HttpGet]
        [Route("GetSupplierOrderById/{id}")]
        //[Authorize(Roles = "Administrator")]
        public async Task<IActionResult> GetSupplierOrderById(int id)
        {
            try
            {
                var order = await _appContext.Supplier_Orders
                    .Include(o => o.Supplier)
                    .Include(o => o.Owner)
                    .Include(o => o.Supplier_Order_Lines)
                    .ThenInclude(sol => sol.Product)
                    .FirstOrDefaultAsync(o => o.Supplier_Order_ID == id);

                if (order == null)
                {
                    return NotFound("Supplier order not found.");
                }

                return Ok(order);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error fetching supplier order by ID.");
                return StatusCode(StatusCodes.Status500InternalServerError, "An error occurred while processing your request.");
            }
        }

        // GET: api/supplier/GetSupplierOrderDetails/{orderId}
        [HttpGet]
        [Route("GetSupplierOrderDetails/{orderId}")]
        public async Task<IActionResult> GetSupplierOrderDetails(int orderId)
        {
            try
            {
                var orderDetails = await (from sol in _appContext.Supplier_Order_Lines
                                          join p in _appContext.Products on sol.Product_ID equals p.Product_ID
                                          where sol.Supplier_Order_ID == orderId
                                          select new SupplierOrderDetailsViewModel
                                          {
                                              Supplier_Order_Line_ID = sol.Supplier_Order_Line_ID,
                                              Supplier_Order_ID = sol.Supplier_Order_ID,
                                              Product_ID = sol.Product_ID,
                                              Supplier_Quantity = sol.Supplier_Quantity,
                                              Purchase_Price = p.Purchase_Price ?? 0, // Handle nullable Purchase_Price
                                              Total_Price = (sol.Supplier_Quantity * (p.Purchase_Price ?? 0)), // Calculate Total_Price
                                              Product_Name = p.Product_Name
                                          }).ToListAsync();

                if (orderDetails == null || orderDetails.Count == 0)
                {
                    return NotFound("No order details found for the given order ID.");
                }

                return Ok(orderDetails);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error retrieving supplier order details.");
                return StatusCode(StatusCodes.Status500InternalServerError, "An error occurred while processing your request.");
            }
        }

        [HttpGet]
        [Route("GetProductCategories")]
        public async Task<IActionResult> GetProductCategories()
        {
            var categories = await _appContext.Product_Categories.ToListAsync();
            return Ok(categories);
        }

        // Controller to fetch products by category ID
        [HttpGet]
        [Route("GetProductsByCategory/{categoryId}")]
        public async Task<IActionResult> GetProductsByCategory(int categoryId)
        {
            var products = await _appContext.Products.Where(p => p.Product_Category_ID == categoryId).ToListAsync();
            return Ok(products);
        }
        
        [HttpPost]
        [Route("PlaceSupplierOrder")]
        public async Task<IActionResult> PlaceSupplierOrder([FromBody] SupplierOrderViewModel orderVm)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            // Create a new supplier order
            var supplierOrder = new Supplier_Order
            {
                Date = DateTime.UtcNow,
                Supplier_Order_Details = orderVm.Supplier_Order_Details,
                Supplier_ID = orderVm.Supplier_ID,
                Owner_ID = orderVm.Owner_ID,
                Status = orderVm.Status,
                Supplier_Order_Lines = new List<Supplier_Order_Line>()
            };

            decimal totalOrderPrice = 0;

            Console.WriteLine($"Creating Supplier_Order with ID {supplierOrder.Supplier_Order_ID}");
            Console.WriteLine($"OrderLines count: {orderVm.OrderLines?.Count ?? 0}");

            // Process each order line
            foreach (var orderLine in orderVm.OrderLines)
            {

                // Fetch the product details
                var product = await _appContext.Products.FindAsync(orderLine.Product_ID);
                if (product == null)
                {
                    return BadRequest($"Product with ID {orderLine.Product_ID} not found.");
                }

                if (product != null)
                {
                    var purchasePrice = product.Purchase_Price;
                    if (purchasePrice == null)
                    {
                        return BadRequest($"Purchase price for product ID {orderLine.Product_ID} is not set.");
                    }

                    // Create a new supplier order line
                    var supplierOrderLine = new Supplier_Order_Line
                    {
                        Product_ID = orderLine.Product_ID,
                        Supplier_Quantity = orderLine.Supplier_Quantity,
                        Purchase_Price = purchasePrice // Set the purchase price from product
                    };

                    // Add the order line to the supplier order
                    supplierOrder.Supplier_Order_Lines.Add(supplierOrderLine);

                    // Calculate total price
                    totalOrderPrice += orderLine.Supplier_Quantity * (decimal)purchasePrice;
                }

                Console.WriteLine($"Processing OrderLine: Product_ID = {orderLine.Product_ID}, Quantity = {orderLine.Supplier_Quantity}");
            }

            // Set total price on supplier order
            supplierOrder.Total_Price = totalOrderPrice;

            // Save the supplier order to the database
            _appContext.Supplier_Orders.Add(supplierOrder);
            await _appContext.SaveChangesAsync();

            return Ok(supplierOrder);
        }

        // PUT: api/supplier/updateinventory
        [HttpPut]
        [Route("updateinventory")]
        //[Authorize(Roles = "Administrator")]
        public async Task<IActionResult> UpdateInventory([FromBody] UpdateInventoryViewModel updateInventoryVm)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            try
            {
                var receivedSupplierOrder = new Received_Supplier_Order
                {
                    Supplies_Received_Date = updateInventoryVm.Supplies_Received_Date
                };

                _appContext.Received_Supplier_Orders.Add(receivedSupplierOrder);
                await _appContext.SaveChangesAsync();

                foreach (var line in updateInventoryVm.ReceivedOrderLines)
                {
                    var receivedOrderLine = new Received_Supplier_Order_Line
                    {
                        Received_Supplier_Order_ID = receivedSupplierOrder.Received_Supplier_Order_ID,
                        Received_Supplies_Quantity = line.Received_Supplies_Quantity,
                        Supplier_Order_Line_ID = line.Supplier_Order_Line_ID,
                    };

                    var product = await _appContext.Products.FindAsync(line.Product_ID);
                    if (product != null)
                    {
                        product.Quantity += line.Received_Supplies_Quantity;
                        _appContext.Products.Update(product);
                    }

                    _appContext.Received_Supplier_Order_Lines.Add(receivedOrderLine);
                }

                await _appContext.SaveChangesAsync();

                return Ok("Inventory updated successfully.");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error updating inventory.");
                return StatusCode(StatusCodes.Status500InternalServerError, "An error occurred while processing your request.");
            }
        }

        // POST: api/supplier/receivesupplierorder
        [HttpPost]
        [Route("receivesupplierorder")]

        public async Task<IActionResult> ReceiveSupplierOrder([FromBody] ReceivedSupplierOrderViewModel receiveOrderVm)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            var receivedSupplierOrder = new Received_Supplier_Order
            {
                Supplies_Received_Date = receiveOrderVm.Supplies_Received_Date,
                Discrepancies = receiveOrderVm.Discrepancies,
                Accepted = receiveOrderVm.Accepted ?? false,
                Received_Supplier_Order_Lines = new List<Received_Supplier_Order_Line>()
            };

            _appContext.Received_Supplier_Orders.Add(receivedSupplierOrder);
            await _appContext.SaveChangesAsync();

            if (receivedSupplierOrder.Accepted)
            {
                foreach (var line in receiveOrderVm.Received_Supplier_Order_Lines)
                {
                    var product = await _appContext.Products.FindAsync(line.Product_ID);

                    // Debugging and Logging
                    Console.WriteLine($"Processing Product ID: {line.Product_ID}");

                    if (product == null)
                    {
                        Console.WriteLine($"Product ID {line.Product_ID} not found.");
                        return BadRequest($"Product ID {line.Product_ID} not found.");
                    }

                    product.Quantity += line.Received_Supplies_Quantity;
                    _appContext.Products.Update(product);

                    var receivedOrderLine = new Received_Supplier_Order_Line
                    {
                        Received_Supplier_Order_ID = receivedSupplierOrder.Received_Supplier_Order_ID,
                        Received_Supplies_Quantity = line.Received_Supplies_Quantity,
                        Supplier_Order_Line_ID = line.Supplier_Order_Line_ID,
                        Product_ID = line.Product_ID // Ensure this is set
                    };

                    _appContext.Received_Supplier_Order_Lines.Add(receivedOrderLine);
                }

                await _appContext.SaveChangesAsync();
            }

            return Ok(receivedSupplierOrder);
        }

        [HttpPost]
        [Route("UpdateSupplierOrderStatus")]
        public async Task<IActionResult> UpdateSupplierOrderStatus([FromBody] UpdateSupplierOrderStatusViewModel statusUpdateVm)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            // Fetch the specific supplier order entry
            var supplierOrder = await _appContext.Supplier_Orders
                .FirstOrDefaultAsync(o => o.Supplier_Order_ID == statusUpdateVm.Supplier_Order_ID);

            if (supplierOrder == null)
            {
                return BadRequest($"Supplier order with ID {statusUpdateVm.Supplier_Order_ID} not found.");
            }

            // Update the status based on the Accepted boolean
            supplierOrder.Status = statusUpdateVm.Status; // Ensure this is directly assigned

            // Save changes to the database
            await _appContext.SaveChangesAsync();

            return Ok(supplierOrder);
        }
    }
}
//models
public class Supplier_Order
{
    [Key]
    public int Supplier_Order_ID { get; set; }

    //[Required]
    public DateTime Date { get; set; }

    //[Required]
    public string? Supplier_Order_Details { get; set; }

    //[Required]
    public decimal Total_Price { get; set; }

    public int Status { get; set; }

    public int Supplier_ID { get; set; }

    [ForeignKey(nameof(Supplier_ID))]
    public Supplier Supplier { get; set; }

    public int Owner_ID { get; set; }

    [ForeignKey(nameof(Owner_ID))]
    public Owner Owner { get; set; }

    public ICollection<Supplier_Order_Line> Supplier_Order_Lines { get; set; }
}
public class Supplier_Order_Line
{
    [Key]
    public int Supplier_Order_Line_ID { get; set; }

    public int Supplier_Quantity { get; set; }


    public int Product_ID { get; set; }

    [ForeignKey(nameof(Product_ID))]

    public Product Product { get; set; }

    public decimal? Purchase_Price { get; set; } 

    public decimal? Unit_Price { get; set; } 

    public int Supplier_Order_ID { get; set; }

    [ForeignKey(nameof(Supplier_Order_ID))]
    public Supplier_Order Supplier_Order { get; set; } 
}
public class Received_Supplier_Order
{
    [Key]
    public int Received_Supplier_Order_ID { get; set; }

    [Required]
    public DateTime Supplies_Received_Date { get; set; }

    public bool Accepted { get; set; } // Indicates if the order was accepted

    public string? Discrepancies { get; set; }  // Nullable attribute for discrepancies

    public ICollection<Received_Supplier_Order_Line> Received_Supplier_Order_Lines { get; set; }
}
public class Received_Supplier_Order_Line
{
    [Key]
    public int Received_Supplier_Order_Line_ID { get; set; }

    public int Received_Supplier_Order_ID { get; set; }

    [ForeignKey(nameof(Received_Supplier_Order_ID))]

    public Received_Supplier_Order Received_Supplier_Order { get; set; }


    public int Supplier_Order_Line_ID { get; set; }

    [ForeignKey(nameof(Supplier_Order_Line_ID))]

    public Supplier_Order_Line Supplier_Order_Line { get; set; }
  

    public int Product_ID { get; set; }

    [ForeignKey(nameof(Product_ID))]

    public Product Product { get; set; }

    public int Received_Supplies_Quantity { get; set; }
}

//viewmodels
public class SupplierOrderViewModel
{
    public int Supplier_Order_ID { get; set; }
    public DateTime Date { get; set; }
    public string Supplier_Order_Details { get; set; }
    public decimal Total_Price { get; set; }
    public int Supplier_ID { get; set; }
    public string Supplier_Name { get; set; }
    public int Owner_ID { get; set; }
    public int Status { get; set; }
    public ICollection<SupplierOrderLineViewModel> OrderLines { get; set; }
}
public class SupplierOrderLineViewModel
{
    public int Supplier_Order_Line_ID { get; set; }
    public int Product_ID { get; set; }
    public string Product_Name { get; set; }
    public int Supplier_Quantity { get; set; }

    public decimal? Purchase_Price { get; set; }
}
public class UpdateSupplierOrderStatusViewModel
{
    public int Supplier_Order_ID { get; set; }
    public int Status { get; set; } // Boolean to indicate acceptance/rejection
}
public class ReceivedSupplierOrderViewModel
{
    public DateTime Supplies_Received_Date { get; set; }

    public bool? Accepted { get; set; } // Nullable bool to indicate acceptance/rejection

    public string? Discrepancies { get; set; }  // Nullable attribute for discrepancies

    public List<ReceivedSupplierOrderLineViewModel> Received_Supplier_Order_Lines { get; set; }
}

public class ReceivedSupplierOrderLineViewModel
{
    public int Product_ID { get; set; }
    public int Received_Supplies_Quantity { get; set; }
    public int Supplier_Order_Line_ID { get; set; }
}
import { HttpClient } from '@angular/common/http';
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import { Router } from '@angular/router';
import { Md5 } from 'ts-md5';
import { environment } from '../../environments/environment';
import { OrderService } from '../Services/order.service';
import { MatSnackBar } from '@angular/material/snack-bar';
import { UserService } from '../Services/userprofile.service';
import { CartItemViewModel, OrderViewModel } from '../shared/order';
import { PaymentViewModel } from '../shared/payment';
import { PaymentService } from '../Services/payment.service';
import { Subscription } from 'rxjs';

declare global {
  interface Window {
    payfast_do_onsite_payment: (param1: any, callback: any) => any;
  }
}

  @Component({
    selector: 'app-payfast',
    standalone: true,
    imports: [],
    templateUrl: './payfast.component.html',
    styleUrl: './payfast.component.css'
  })
  export class PayfastComponent implements OnInit {
    memberId!: number;
    finalAmount: number = 0;
    private finalAmountSubscription!: Subscription;

    constructor(private router : Router, private orderService : OrderService, private paymentService : PaymentService , private userService: UserService, private formBuilder: FormBuilder, private snackBar: MatSnackBar, private cdr: ChangeDetectorRef) {
      
    }

    ngOnInit(): void {
      this.fetchMemberId();
      this.finalAmountSubscription = this.paymentService.getFinalAmount().subscribe(amount => {
        this.finalAmount = amount;
        console.log("Retrieved final amount from subscription:", this.finalAmount); // Debugging line
        this.cdr.detectChanges(); // Force change detection
      });
    }

    ngOnDestroy(): void {
      if (this.finalAmountSubscription) {
        this.finalAmountSubscription.unsubscribe();
      }
    }

    getSignature(data : Map<string, string>) : string {
      let tmp = new URLSearchParams();
      data.forEach((v, k)=> {
        tmp.append(k, v)
      });
      let queryString = tmp.toString();
      let sig = Md5.hashStr(queryString);
      return sig;
    }

    async doOnSitePayment() {
      await this.fetchMemberId();

      let onSiteUserData = new Map<string, string>();
      onSiteUserData.set("merchant_id", "10033427")
      onSiteUserData.set("merchant_key", "mu83ipbgas9p7")

      onSiteUserData.set('return_url', window.location.origin + '/payment-success')
      onSiteUserData.set('cancel_url', window.location.origin + '/payment-cancel')

      // Gather required user data from orderService or other sources
      const userData = this.orderService.getUserData();
      onSiteUserData.set("email_address", userData.email);
      
      // Set amount and item_name
      onSiteUserData.set('amount', this.finalAmount.toFixed(2)); // Use the final amount from shared service
      onSiteUserData.set('item_name', 'Cart Purchase');

      // Optional passphrase for added security
      onSiteUserData.set('passphrase', 'HelloWorldHello'); // Use if you have a passphrase

      let signature = this.getSignature(onSiteUserData);
      onSiteUserData.set('signature', signature);

      let formData = new FormData();
      onSiteUserData.forEach((val, key) => {
        formData.append(key, val);
      });

      fetch(environment.payfastOnsiteEndpoint, {
        method: 'POST',
        body: formData,
        redirect: 'follow'
      })
      .then(response => response.json())
      .then(respJson => {
          let uuid = respJson['uuid'];
          window.payfast_do_onsite_payment({ 'uuid': uuid }, (res: any) => {
            if (res == true) {
              this.createOrder().then((orderResponse) => {
                this.createPayment(orderResponse);
                this.snackBar.open('Payment Successful', 'Close', { duration: 5000 });
              });         
            } else {
              this.snackBar.open('Payment Failed', 'Close', { duration: 5000 });              
            }
          });
        })
        .catch(error => {
          console.error('Error processing payment:', error);
          this.router.navigate(['/cancel']);
        });
      }

      //order
      private fetchMemberId() {
        const user = localStorage.getItem('User');
        if (user) {
          const userData = JSON.parse(user);
          this.memberId = userData.userId;
      
          // Optional: Fetch and validate member details if needed
          this.userService.getMemberByUserId(this.memberId).subscribe({
            next: (member) => {
              if (member && member.member_ID) {
                this.memberId = member.member_ID;
                console.log('Member ID:', this.memberId); // Check if this logs the correct ID
              } else {
                console.error('Member ID is undefined in the response');
                this.snackBar.open('Failed to retrieve member information', 'Close', { duration: 5000 });
              }
            },
            error: (error) => {
              console.error('Error fetching member:', error);
              this.snackBar.open('Failed to retrieve member information', 'Close', { duration: 5000 });
            }
          });
        } else {
          this.snackBar.open('User not logged in', 'Close', { duration: 5000 });
          this.router.navigate(['/login']);
        }
      }

      private createOrder(): Promise<OrderViewModel> {
        return new Promise((resolve, reject) => {
          if (this.memberId === undefined) {
            this.snackBar.open('Member ID is not available', 'Close', { duration: 5000 });
            reject('Member ID is not available');
          } else {
            this.orderService.getCartItems().subscribe({
              next: (cartItems) => {
                const order = this.prepareOrderDetails(cartItems);
                this.orderService.createOrder(order).subscribe({
                  next: (orderResponse) => {
                    this.snackBar.open('Order Created Successfully', 'Close', { duration: 5000 });
                    resolve(orderResponse);
                  },
                  error: (error) => {
                    console.error('Error creating order:', error);
                    this.snackBar.open('Failed to create order', 'Close', { duration: 5000 });
                    reject(error);
                  }
                });
              },
              error: (error) => {
                console.error('Error fetching cart items:', error);
                this.snackBar.open('Failed to fetch cart items', 'Close', { duration: 5000 });
                reject(error);
              }
            });
          }
        });
      }
      
    private prepareOrderDetails(cartItems: CartItemViewModel[]): OrderViewModel {
      const order: OrderViewModel = {
        order_ID: 0, // ID will be generated by backend
        member_ID: this.memberId,
        order_Date: new Date().toISOString(),
        total_Price: this.finalAmount, // Use the final amount from shared service
        order_Status_ID: 1, // Ready for Collection
        isCollected: false,
        orderLines: cartItems.map(item => ({
          order_Line_ID: 0, // ID will be generated by backend
          product_ID: item.product_ID,
          product_Name: item.product_Name,
          quantity: item.quantity,
          unit_Price: item.unit_Price
        }))
      };
    
      console.log('Prepared order details:', order);
      return order;
    }

    private createPayment(order: OrderViewModel) {
      const paymentData: PaymentViewModel = {
        payment_ID: 0,
        amount: order.total_Price, // Ensure this reflects VAT and discounts
        payment_Date: new Date().toISOString(),
        order_ID: order.order_ID,
        payment_Type_ID: 1 // Default to 1
      };

      this.paymentService.createPayment(paymentData).subscribe({
        next: (response) => {
          console.log('Payment record created successfully:', response);
          this.router.navigate(['/orders']);
        },
        error: (error) => {
          console.error('Error creating payment record:', error);
        }
      });
    }  
  }
npm install xlsx file-saver
npm install ts-md5 --save
npm install xlsx file-saver
npm install ts-md5 --save
npm install xlsx-style
//Rewards and criteria
Booking Initiator (for "Placed a Booking")
Booking Streak (for "Completed 10 Bookings in a Month")
Booking Pro (for "Made 20 Bookings in Last 3 Months")
Payment Pioneer (for "Made a Payment")
First Order Achiever (for "Placed First Order")
High Roller (for "High-Value Order")
Bulk Buyer (for "Large Quantity Order")

//Equipment
1. Treadmill
Description: A popular cardio machine that simulates walking, jogging, or running indoors.
Size: Approximately 80-150 kg
2. Elliptical Trainer
Description: A low-impact cardio machine that combines the motions of stair climbing, walking, and running.
Size: Approximately 70-120 kg
3. Stationary Bike
Description: A bike used for indoor cycling workouts.
Size: Approximately 25-60 kg
4. Rowing Machine
Description: A full-body cardio machine that mimics the motion of rowing a boat.
Size: Approximately 25-50 kg
5. Weight Bench
Description: A versatile piece of equipment used in weight training.
Size: Approximately 15-30 kg
6. Dumbbells
Description: Small, handheld weights used in various strength training exercises.
Size: Varies widely, typically 1-50 kg each
7. Barbell
Description: A long bar that can be loaded with weight plates on each end.
Size: Standard barbells are typically 20 kg (Olympic barbell)
8. Kettlebell
Description: A round weight with a handle used in dynamic exercises.
Size: Typically 4-32 kg
9. Pull-Up Bar
Description: A horizontal bar mounted at a height, used for pull-ups and chin-ups.
Size: Not typically measured in weight; usually around 3-5 kg
10. Medicine Ball
Description: A weighted ball used in various exercises.
Size: Typically 2-10 kg
11. Resistance Bands
Description: Elastic bands used for strength training and rehabilitation.
Size: Not measured in weight; resistance typically ranges from 5-50 kg
12. Leg Press Machine
Description: A machine designed to target the lower body muscles.
Size: Approximately 180-300 kg
13. Smith Machine
Description: A weight training machine consisting of a barbell fixed within steel rails.
Size: Approximately 150-250 kg
14. Cable Machine
Description: A versatile piece of equipment with adjustable pulleys.
Size: Approximately 200-300 kg
15. Lat Pulldown Machine
Description: A machine designed to strengthen the upper back muscles.
Size: Approximately 100-200 kg
16. Leg Curl/Extension Machine
Description: A dual-function machine that targets the hamstrings and quadriceps.
Size: Approximately 80-150 kg
17. Ab Wheel
Description: A small wheel with handles used to perform ab rollouts.
Size: Approximately 1-2 kg
18. Battle Ropes
Description: Heavy ropes used in various dynamic exercises.
Size: Typically 8-12 kg (for a 30-50 ft rope)
19. Punching Bag
Description: A heavy bag used for boxing and martial arts training.
Size: Approximately 20-50 kg
20. Foam Roller
Description: A cylindrical foam tool used for self-myofascial release (SMR).
Size: Not typically measured in weight; usually around 1-2 kg

//products
T-shirt
Description: A versatile, breathable short-sleeved shirt ideal for workouts or casual wear.
Price: R150 – R400

Tank Top
Description: A sleeveless shirt designed for enhanced ventilation and freedom of movement during exercise.
Price: R120 – R300

Sports Bra
Description: A supportive bra designed for comfort and stability during high-impact workouts.
Price: R250 – R600

Hoodie
Description: A warm, hooded sweatshirt perfect for warming up or casual wear.
Price: R350 – R800

Running Shorts
Description: Lightweight shorts with moisture-wicking fabric, designed for running and other cardio activities.
Price: R200 – R500

Casual Shorts
Description: Comfortable shorts suitable for everyday casual wear.
Price: R200 – R450

Gym Shorts
Description: Durable, flexible shorts designed for gym workouts and other physical activities.
Price: R150 – R400

Sweatpants
Description: Comfortable, loose-fitting pants ideal for warm-ups, cool-downs, or casual wear.
Price: R300 – R700

Gym Leggings
Description: Stretchable, body-hugging leggings designed for flexibility and comfort during workouts.
Price: R300 – R600

Compression Tights
Description: Tight-fitting, supportive tights that enhance circulation and muscle recovery during exercise.
Price: R400 – R900

Socks
Description: Standard socks made from soft, breathable material for everyday use.
Price: R50 – R150

Gym Socks
Description: Cushioned, moisture-wicking socks designed for extra comfort during workouts.
Price: R60 – R180

Gloves
Description: General-purpose gloves for protection and comfort during light activities.
Price: R100 – R250

Gym Gloves
Description: Padded gloves designed to protect hands and improve grip during weightlifting or gym exercises.
Price: R150 – R400

Boxing Gloves
Description: Heavily padded gloves designed to protect hands and opponents during boxing training or sparring.
Price: R400 – R1,200

//Member Emails
Password: AVSFitness!
jakerichards4731@
cassandrawusan6@
enochndlovu616@
sashapillay314@
//Stored Procedure
CREATE PROCEDURE GetQualifyingMembers
    @RewardCriteria NVARCHAR(50)
AS
BEGIN
    SET NOCOUNT ON;

    IF @RewardCriteria = 'Placed a Booking'
    BEGIN
        SELECT DISTINCT m.Member_ID
        FROM Members m
        INNER JOIN Bookings b ON b.Member_ID = m.Member_ID;
    END
    ELSE IF @RewardCriteria = 'Completed 10 Bookings in a Month'
    BEGIN
        SELECT DISTINCT m.Member_ID
        FROM Members m
        INNER JOIN Bookings b ON b.Member_ID = m.Member_ID
        INNER JOIN Booking_Time_Slots bts ON b.Booking_ID = bts.Booking_ID
        INNER JOIN Time_Slots ts ON bts.Time_Slot_ID = ts.Time_Slot_ID
        WHERE ts.Slot_Date >= DATEADD(MONTH, -1, GETDATE())
        GROUP BY m.Member_ID
        HAVING COUNT(b.Booking_ID) >= 10;
    END
    ELSE IF @RewardCriteria = 'Made 20 Bookings in Last 3 Months'
    BEGIN
        SELECT DISTINCT m.Member_ID
        FROM Members m
        INNER JOIN Bookings b ON b.Member_ID = m.Member_ID
        INNER JOIN Booking_Time_Slots bts ON b.Booking_ID = bts.Booking_ID
        INNER JOIN Time_Slots ts ON bts.Time_Slot_ID = ts.Time_Slot_ID
        WHERE ts.Slot_Date >= DATEADD(MONTH, -3, GETDATE())
        GROUP BY m.Member_ID
        HAVING COUNT(b.Booking_ID) >= 20;
    END
    -- Add more conditions based on other reward criteria as needed
    ELSE IF @RewardCriteria = 'Placed First Order'
    BEGIN
        SELECT DISTINCT m.Member_ID
        FROM Members m
        WHERE EXISTS (
            SELECT 1 FROM Orders o WHERE o.Member_ID = m.Member_ID AND o.Order_Date = (
                SELECT MIN(Order_Date) FROM Orders o2 WHERE o2.Member_ID = m.Member_ID
            )
        );
    END
    ELSE IF @RewardCriteria = 'Made a Payment'
    BEGIN
        SELECT DISTINCT m.Member_ID
        FROM Members m
        INNER JOIN Payments p ON p.Member_ID = m.Member_ID;
    END
    ELSE IF @RewardCriteria = 'High-Value Order'
    BEGIN
        SELECT DISTINCT m.Member_ID
        FROM Members m
        INNER JOIN Orders o ON o.Member_ID = m.Member_ID
        WHERE o.Total_Amount >= 1000; -- Example threshold, adjust as needed
    END
    ELSE IF @RewardCriteria = 'Large Quantity Order'
    BEGIN
        SELECT DISTINCT m.Member_ID
        FROM Members m
        INNER JOIN Order_Items oi ON oi.Order_ID = (
            SELECT TOP 1 o.Order_ID
            FROM Orders o
            WHERE o.Member_ID = m.Member_ID
            ORDER BY o.Order_Date DESC
        )
        WHERE oi.Quantity >= 10; -- Example threshold, adjust as needed
    END
    ELSE
    BEGIN
        -- Return an empty set if criteria do not match
        SELECT * FROM Members WHERE 1 = 0;
    END
END

//Service
public class QualifyingMembersService : IHostedService, IDisposable
    {
        private readonly IServiceProvider _serviceProvider;
        private Timer _timer;

        public QualifyingMembersService(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }

        public Task StartAsync(CancellationToken cancellationToken)
        {
            _timer = new Timer(UpdateQualifyingMembers, null, TimeSpan.Zero, TimeSpan.FromMinutes(1)); // Check every 1 minute
            return Task.CompletedTask;
        }

        private async void UpdateQualifyingMembers(object state)
        {
            using (var scope = _serviceProvider.CreateScope())
            {
                var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();
                var postedRewards = await context.Rewards
                    .Where(r => r.IsPosted)
                    .ToListAsync();

                foreach (var reward in postedRewards)
                {
                    var rewardType = await context.Reward_Types
                        .FindAsync(reward.Reward_Type_ID);
                    
                    if (rewardType != null)
                    {
                        var qualifyingMembers = await GetQualifyingMembersAsync(rewardType.Reward_Criteria);

                        foreach (var member in qualifyingMembers)
                        {
                            var existingRewardMember = await context.Reward_Members
                                .FirstOrDefaultAsync(rm => rm.Member_ID == member.Member_ID && rm.Reward_ID == reward.Reward_ID);

                            if (existingRewardMember == null)
                            {
                                var rewardMember = new Reward_Member
                                {
                                    Member_ID = member.Member_ID,
                                    Reward_ID = reward.Reward_ID,
                                    IsRedeemed = false
                                };
                                context.Reward_Members.Add(rewardMember);
                            }
                        }
                    }
                }

                await context.SaveChangesAsync();
            }
        }

        public async Task<List<Member>> GetQualifyingMembersAsync(string criteria)
        {
            var criteriaParam = new SqlParameter("@Criteria", criteria);
            return await _serviceProvider.GetRequiredService<AppDbContext>()
                .Members
                .FromSqlRaw("EXEC GetQualifyingMembers @Criteria", criteriaParam)
                .ToListAsync();
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            _timer?.Change(Timeout.Infinite, 0);
            return Task.CompletedTask;
        }

        public void Dispose()
        {
            _timer?.Dispose();
        }
    }

//UpdateQualifyingMembersForReward
private async Task UpdateQualifyingMembersForReward(int rewardId)
{
    // Fetch reward and reward type
    var reward = await _appDbContext.Rewards.FindAsync(rewardId);
    if (reward != null)
    {
        var rewardType = await _appDbContext.Reward_Types.FindAsync(reward.Reward_Type_ID);
        if (rewardType != null)
        {
            // Use QualifyingMembersService to get qualifying members
            var qualifyingMembers = await _qualifyingMembersService.GetQualifyingMembersAsync(rewardType.Reward_Criteria);

            foreach (var member in qualifyingMembers)
            {
                var existingRewardMember = await _appDbContext.Reward_Members
                    .FirstOrDefaultAsync(rm => rm.Member_ID == member.Member_ID && rm.Reward_ID == reward.Reward_ID);

                // Add qualifying members to the Reward_Member table
                if (existingRewardMember == null)
                {
                    var rewardMember = new Reward_Member
                    {
                        Member_ID = member.Member_ID,
                        Reward_ID = reward.Reward_ID,
                        IsRedeemed = false
                    };
                    _appDbContext.Reward_Members.Add(rewardMember);
                }
            }

            await _appDbContext.SaveChangesAsync();
        }
    }
}
Creating an audit trail in SQL involves capturing and recording changes made to the data in a database, along with metadata like who made the change, when it was made, and what the original and new values were. Here are some common methods to create an audit trail in SQL:

### 1. *Using Triggers*
Triggers are a common way to create an audit trail in SQL. A trigger can be set up to fire before or after an INSERT, UPDATE, or DELETE operation. The trigger then logs the changes into an audit table.

#### Example:
Suppose you have a table named employees and you want to audit changes.

1. *Create an Audit Table:*
    sql
    CREATE TABLE employees_audit (
        audit_id INT AUTO_INCREMENT PRIMARY KEY,
        employee_id INT,
        action VARCHAR(10),
        old_value VARCHAR(255),
        new_value VARCHAR(255),
        changed_by VARCHAR(50),
        changed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    );
    

2. **Create a Trigger for UPDATE:**
    sql
    CREATE TRIGGER employees_update_audit
    AFTER UPDATE ON employees
    FOR EACH ROW
    BEGIN
        INSERT INTO employees_audit (employee_id, action, old_value, new_value, changed_by)
        VALUES (
            OLD.employee_id, 
            'UPDATE', 
            OLD.salary, 
            NEW.salary, 
            USER()
        );
    END;
    

This trigger captures changes made to the salary column and stores the old and new values, the action performed, who performed the action, and when.

3. **Create Triggers for INSERT and DELETE:**
    Similar triggers can be created for INSERT and DELETE operations.

### 2. *Change Data Capture (CDC)*
If your database supports Change Data Capture (CDC), you can enable this feature to automatically track changes without creating manual triggers.

- *SQL Server Example:*
    sql
    EXEC sys.sp_cdc_enable_table 
        @source_schema = 'dbo', 
        @source_name = 'employees', 
        @role_name = NULL;
    

- *Oracle Example:*
    Oracle has its own built-in auditing features that can be configured via DBMS packages or directly using the AUDIT command.

### 3. *Manual Logging*
If you want to avoid using triggers, you can manually log changes by writing data into an audit table within your SQL operations.

#### Example:
sql
UPDATE employees 
SET salary = 60000 
WHERE employee_id = 101;

INSERT INTO employees_audit (employee_id, action, old_value, new_value, changed_by)
VALUES (101, 'UPDATE', '50000', '60000', 'admin');


### 4. *Temporal Tables*
Some databases like SQL Server 2016+ support temporal tables, which automatically keep track of historical data. You can enable a table to be temporal, and SQL Server will automatically manage the history.

#### Example:
sql
CREATE TABLE employees (
    employee_id INT PRIMARY KEY,
    salary INT,
    SysStartTime DATETIME2 GENERATED ALWAYS AS ROW START,
    SysEndTime DATETIME2 GENERATED ALWAYS AS ROW END,
    PERIOD FOR SYSTEM_TIME (SysStartTime, SysEndTime)
) WITH (SYSTEM_VERSIONING = ON);


### Best Practices:
- *Minimal Performance Impact:* Triggers can introduce performance overhead, so ensure they're optimized and only track necessary data.
- *Data Integrity:* Ensure that the audit trail can't be easily tampered with by using appropriate permissions.
- *Compliance:* Ensure that your audit trail complies with legal and regulatory requirements for data retention and privacy.

By implementing one or more of these methods, you can effectively create an audit trail in SQL to track changes in your database.
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using av_motion_api.Data; // Adjust the namespace to match your project
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Microsoft.Data.SqlClient;
using av_motion_api.Models;

namespace av_motion_api.Services
{
    public class OrderStatusUpdater : IHostedService, IDisposable
    {
        private Timer _timer;
        private readonly IServiceProvider _serviceProvider;
        private readonly IOptionsMonitor<OverdueSettings> _settings;

        public OrderStatusUpdater(IServiceProvider serviceProvider, IOptionsMonitor<OverdueSettings> settings)
        {
            _serviceProvider = serviceProvider;
            _settings = settings;
        }

        public Task StartAsync(CancellationToken cancellationToken)
        {
            ScheduleOrderStatusUpdate();
            _settings.OnChange(settings => ScheduleOrderStatusUpdate());
            return Task.CompletedTask;
        }

        private void ScheduleOrderStatusUpdate()
        {
            var overdueTimeValue = _settings.CurrentValue.OverdueTimeValue;
            var overdueTimeUnit = _settings.CurrentValue.OverdueTimeUnit;

            if (string.IsNullOrEmpty(overdueTimeUnit))
            {
                // Log error or warning here
                Console.WriteLine("OverdueTimeUnit is null or empty.");
            }

            var interval = GetTimeSpan(overdueTimeValue, overdueTimeUnit);
            _timer?.Dispose();
            _timer = new Timer(UpdateOrderStatuses, null, TimeSpan.Zero, interval);
        }

        private void UpdateOrderStatuses(object state)
        {
            using (var scope = _serviceProvider.CreateScope())
            {
                var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();

                // Calculate the overdue threshold time
                var overdueThreshold = DateTime.UtcNow.Subtract(GetTimeSpan(_settings.CurrentValue.OverdueTimeValue, _settings.CurrentValue.OverdueTimeUnit));

                var connection = context.Database.GetDbConnection();
                if (connection.State != System.Data.ConnectionState.Open)
                {
                    connection.Open();
                }

                using (var command = connection.CreateCommand())
                {
                    command.CommandText = "UpdateOrderStatuses";
                    command.CommandType = System.Data.CommandType.StoredProcedure;

                    // Add the required parameters
                    command.Parameters.Add(new SqlParameter("@OverdueTimeValue", _settings.CurrentValue.OverdueTimeValue));
                    command.Parameters.Add(new SqlParameter("@OverdueTimeUnit", _settings.CurrentValue.OverdueTimeUnit));

                    // Execute the stored procedure
                    command.ExecuteNonQuery();
                }
            }
        }

        private TimeSpan GetTimeSpan(int value, string unit)
        {
            if (string.IsNullOrEmpty(unit))
            {
                throw new ArgumentNullException(nameof(unit), "Unit cannot be null or empty.");
            }

            return unit.ToLower() switch
            {
                "minutes" => TimeSpan.FromMinutes(value),
                "hours" => TimeSpan.FromHours(value),
                "days" => TimeSpan.FromDays(value),
                "weeks" => TimeSpan.FromDays(value * 7),
                "months" => TimeSpan.FromDays(value * 30), // Approximation
                "years" => TimeSpan.FromDays(value * 365), // Approximation
                _ => TimeSpan.FromMinutes(value),
            };
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            _timer?.Change(Timeout.Infinite, 0);
            return Task.CompletedTask;
        }

        public void Dispose()
        {
            _timer?.Dispose();
        }
    }
}

            // Stored Procedures
            builder.HasAnnotation("SqlServer:IncludeProcedures", new
            {
                UpdateUserDeletionSettings = @"
                    CREATE PROCEDURE UpdateUserDeletionSettings
                        @DeletionTimeValue INT,
                        @DeletionTimeUnit NVARCHAR(20)
                    AS
                    BEGIN
                        SET NOCOUNT ON;
                        DECLARE @DeletionThreshold DATETIME;
                        SET @DeletionThreshold = CASE
                            WHEN @DeletionTimeUnit = 'Minutes' THEN DATEADD(MINUTE, -@DeletionTimeValue, GETUTCDATE())
                            WHEN @DeletionTimeUnit = 'Hours' THEN DATEADD(HOUR, -@DeletionTimeValue, GETUTCDATE())
                            WHEN @DeletionTimeUnit = 'Days' THEN DATEADD(DAY, -@DeletionTimeValue, GETUTCDATE())
                            WHEN @DeletionTimeUnit = 'Weeks' THEN DATEADD(WEEK, -@DeletionTimeValue, GETUTCDATE())
                            WHEN @DeletionTimeUnit = 'Months' THEN DATEADD(MONTH, -@DeletionTimeValue, GETUTCDATE())
                            WHEN @DeletionTimeUnit = 'Years' THEN DATEADD(YEAR, -@DeletionTimeValue, GETUTCDATE())
                            ELSE GETUTCDATE()
                        END;

                        DELETE FROM dbo.AspNetUsers
                        WHERE User_Status_ID = 2 AND DeactivatedAt < @DeletionThreshold;
                    END",
                UpdateOrderStatuses = @"
                    CREATE PROCEDURE UpdateOrderStatuses
                        @OverdueTimeValue INT,
                        @OverdueTimeUnit NVARCHAR(20)
                    AS
                    BEGIN
                        SET NOCOUNT ON;
                        DECLARE @OverdueThreshold DATETIME;
                        SET @OverdueThreshold = CASE
                            WHEN @OverdueTimeUnit = 'Minutes' THEN DATEADD(MINUTE, -@OverdueTimeValue, GETUTCDATE())
                            WHEN @OverdueTimeUnit = 'Hours' THEN DATEADD(HOUR, -@OverdueTimeValue, GETUTCDATE())
                            WHEN @OverdueTimeUnit = 'Days' THEN DATEADD(DAY, -@OverdueTimeValue, GETUTCDATE())
                            WHEN @OverdueTimeUnit = 'Weeks' THEN DATEADD(WEEK, -@OverdueTimeValue, GETUTCDATE())
                            WHEN @OverdueTimeUnit = 'Months' THEN DATEADD(MONTH, -@OverdueTimeValue, GETUTCDATE())
                            WHEN @OverdueTimeUnit = 'Years' THEN DATEADD(YEAR, -@OverdueTimeValue, GETUTCDATE())
                            ELSE GETUTCDATE()
                        END;

                        UPDATE dbo.Orders
                        SET Order_Status_ID = CASE
                            WHEN Order_Date <= @OverdueThreshold THEN 2
                            ELSE Order_Status_ID
                        END
                        WHERE IsCollected = 0;
                    END",
                GetQualifyingMembers = @"
                    CREATE PROCEDURE GetQualifyingMembers
                        @RewardCriteria NVARCHAR(50)
                    AS
                    BEGIN
                        SET NOCOUNT ON;
                        IF @RewardCriteria = 'Placed a Booking'
                        BEGIN
                            SELECT DISTINCT m.Member_ID
                            FROM Members m
                            INNER JOIN Bookings b ON b.Member_ID = m.Member_ID;
                        END
                        ELSE IF @RewardCriteria = 'Completed 10 Bookings in a Month'
                        BEGIN
                            SELECT m.Member_ID
                            FROM Members m
                            INNER JOIN Bookings b ON b.Member_ID = m.Member_ID
                            INNER JOIN Booking_Time_Slots bts ON b.Booking_ID = bts.Booking_ID
                            INNER JOIN Time_Slots ts ON bts.Time_Slot_ID = ts.Time_Slot_ID
                            WHERE ts.Slot_Date >= DATEADD(MONTH, -1, GETDATE())
                            GROUP BY m.Member_ID
                            HAVING COUNT(DISTINCT b.Booking_ID) >= 10;
                        END
                        ELSE IF @RewardCriteria = 'Made 20 Bookings in Last 3 Months'
                        BEGIN
                            SELECT m.Member_ID
                            FROM Members m
                            INNER JOIN Bookings b ON b.Member_ID = m.Member_ID
                            INNER JOIN Booking_Time_Slots bts ON b.Booking_ID = bts.Booking_ID
                            INNER JOIN Time_Slots ts ON bts.Time_Slot_ID = ts.Time_Slot_ID
                            WHERE ts.Slot_Date >= DATEADD(MONTH, -3, GETDATE())
                            GROUP BY m.Member_ID
                            HAVING COUNT(DISTINCT b.Booking_ID) >= 20;
                        END
                        ELSE IF @RewardCriteria = 'Placed First Order'
                        BEGIN
                            SELECT m.Member_ID
                            FROM Members m
                            WHERE EXISTS (
                                SELECT 1 
                                FROM Orders o 
                                WHERE o.Member_ID = m.Member_ID
                                AND o.Order_Date = (
                                    SELECT MIN(o2.Order_Date)
                                    FROM Orders o2
                                    WHERE o2.Member_ID = m.Member_ID
                                )
                            );
                        END
                        ELSE IF @RewardCriteria = 'Made a Payment'
                        BEGIN
                            SELECT DISTINCT m.Member_ID
                            FROM Members m
                            INNER JOIN Orders o ON o.Member_ID = m.Member_ID
                            INNER JOIN Payments p ON p.Order_ID = o.Order_ID;
                        END
                        ELSE IF @RewardCriteria = 'High-Value Order'
                        BEGIN
                            SELECT DISTINCT m.Member_ID
                            FROM Members m
                            INNER JOIN Orders o ON o.Member_ID = m.Member_ID
                            WHERE o.Total_Price >= 1000;
                        END
                        ELSE IF @RewardCriteria = 'Large Quantity Order'
                        BEGIN
                            SELECT m.Member_ID
                            FROM Members m
                            WHERE (
                                SELECT SUM(ol.Quantity)
                                FROM Order_Lines ol
                                INNER JOIN Orders o ON ol.Order_ID = o.Order_ID
                                WHERE o.Member_ID = m.Member_ID
                            ) >= 10;
                        END
                        ELSE
                        BEGIN
                            SELECT * FROM Members WHERE 1 = 0;
                        END
                    END"
            });
var ProductTypes = new Product_Type[]
{
  // Types for "Tops" (Category_ID = 1)
  new Product_Type { Type_ID = 1, Type_Name = "Shirts", Category_ID = 1 },  // T-shirts, long sleeves, crop tops
    new Product_Type { Type_ID = 2, Type_Name = "Tank Tops", Category_ID = 1 }, // All tank tops
      new Product_Type { Type_ID = 3, Type_Name = "Hoodies", Category_ID = 1 },   // Sleeveless and regular hoodies
        new Product_Type { Type_ID = 4, Type_Name = "Sweats", Category_ID = 1 },    // Sweatshirts and similar

          // Types for "Bottoms" (Category_ID = 2)
          new Product_Type { Type_ID = 5, Type_Name = "Shorts", Category_ID = 2 },    // All types of shorts
            new Product_Type { Type_ID = 6, Type_Name = "Joggers", Category_ID = 2 },   // Joggers and sweatpants
              new Product_Type { Type_ID = 7, Type_Name = "Leggings", Category_ID = 2 },  // All leggings
                new Product_Type { Type_ID = 8, Type_Name = "Compression Tights", Category_ID = 2 }, // Compression tights

                  // Types for "Gear" (Category_ID = 3)
                  new Product_Type { Type_ID = 9, Type_Name = "Gloves", Category_ID = 3 },    // Weightlifting gloves, workout gloves
                    new Product_Type { Type_ID = 10, Type_Name = "Socks", Category_ID = 3 },    // Athletic socks
                      new Product_Type { Type_ID = 11, Type_Name = "Accessories", Category_ID = 3 } // Wrist wraps, resistance bands, gym bags, etc.
};

builder.Entity<Product_Type>().HasData(ProductTypes);
import { Component } from '@angular/core';
import { PaymentService } from '../Services/payment.service';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Router } from '@angular/router';
import { Location } from '@angular/common';
import * as XLSX from 'xlsx';
import * as FileSaver from 'file-saver';
import { MasterSideNavBarComponent } from '../master-side-nav-bar/master-side-nav-bar.component';
import { SideNavBarComponent } from '../side-nav-bar/side-nav-bar.component';
declare var $: any;

@Component({
  selector: 'app-payment',
  standalone: true,
  imports: [CommonModule, FormsModule, ReactiveFormsModule, MasterSideNavBarComponent, SideNavBarComponent],
  templateUrl: './payment.component.html',
  styleUrl: './payment.component.css'
})
export class PaymentComponent {
  userTypeID: number | null = null;
  payments: any[] = [];
  selectedPayment: any | null = null;
  filterPayment: any[] = [];
  searchTerm: string = '';
  helpContent: any[] = [];
  filteredContent: any[] = [];

  constructor(private paymentService: PaymentService, private router: Router, private location: Location, private snackBar: MatSnackBar) {}

  ngOnInit(): void {
    const userTypeId = JSON.parse(localStorage.getItem('User') || '{}').userTypeId;
    this.userTypeID = userTypeId;

    this.loadPayments();


    // Initialize help content
    this.helpContent = [
      {
        title: 'Payment Manager Page Context-Sensitive Help',
        content: `
          <p><strong>Overview:</strong> The Payment Manager page allows you to view and manage payment records, including searching for specific payments, viewing detailed payment information, and exporting payment data to Excel.</p>
          <p><strong>Page Components:</strong></p>`
      },
      {
        title: '1. Search Bar',
        content: `
          <ul>
            <li><strong>Purpose:</strong> Allows you to search for specific payments by ID, member name, amount, date, or payment type.</li>
            <li><strong>Usage:</strong> Enter your search term into the input field. The list of payments will automatically filter to show matching results based on your search criteria.</li>
          </ul>`
      },
      {
        title: '2. Payment Table',
        content: `
          <ul>
            <li><strong>Purpose:</strong> Displays a list of all payments with their ID, member name, amount, payment date, payment type, and action buttons.</li>
            <li><strong>Usage:</strong> View details of each payment by clicking the "View" button. The table will update based on the search term entered in the search bar.</li>
          </ul>`
      },
      {
        title: '3. Export to Excel Button',
        content: `
          <ul>
            <li><strong>Purpose:</strong> Exports the currently displayed payment data to an Excel file.</li>
            <li><strong>Usage:</strong> Click the "Export to Excel" button to generate and download an Excel file containing the filtered payment data.</li>
          </ul>`
      },
      {
        title: '4. Modal for Payment Details',
        content: `
          <ul>
            <li><strong>Purpose:</strong> Provides detailed information about a selected payment.</li>
            <li><strong>Usage:</strong> Click the "View" button next to a payment in the table to open the modal. The modal will display detailed information including Payment ID, Member Name, Amount, Payment Date, and Payment Type. Click "Close" to dismiss the modal.</li>
          </ul>`
      },
      {
        title: 'Common Questions',
        content: `
          <p><strong>Q:</strong> How do I search for payments?</p>
          <p><strong>A:</strong> Use the search bar at the top of the page. Enter terms such as payment ID, member name, amount, date, or payment type to filter the payments shown in the table.</p>
          <p><strong>Q:</strong> How can I view details of a payment?</p>
          <p><strong>A:</strong> Click the "View" button next to the payment in the table. This will open a modal with detailed information about the selected payment.</p>
          <p><strong>Q:</strong> How do I export payment data to Excel?</p>
          <p><strong>A:</strong> Click the "Export to Excel" button. An Excel file with the filtered payment data will be generated and downloaded to your computer.</p>
          <p><strong>Q:</strong> Why isn't the search working?</p>
          <p><strong>A:</strong> Ensure that you are entering the search terms correctly and that there are payments that match your criteria. Check for any typos or mismatches in the search terms.</p>
          <p><strong>Q:</strong> What should I do if the export to Excel fails?</p>
          <p><strong>A:</strong> Ensure that your browser supports Excel file downloads and that no errors occur during the generation of the file. Verify that the Excel library (XLSX) is correctly integrated and functioning.</p>`
      },
      {
        title: 'Troubleshooting:',
        content: `
          <p><strong>Problem:</strong> The search bar is not filtering results.</p>
          <p><strong>Solution:</strong> Ensure that the search terms are correctly entered and that there are matching payments. Verify that the search functionality is correctly implemented and check for any console errors.</p>
          <p><strong>Problem:</strong> The export to Excel button is not working.</p>
          <p><strong>Solution:</strong> Ensure that the Excel export library (XLSX) is properly integrated and that there are no issues with file generation. Check for network or console errors that might indicate the problem.</p>`
      }
    ];

    // Initialize filtered content
    this.filteredContent = [...this.helpContent];
  }

  filterHelpContent(): void {
    const term = this.searchTerm.toLowerCase();
    this.filteredContent = this.helpContent.filter(item =>
      item.title.toLowerCase().includes(term) || item.content.toLowerCase().includes(term)
    );
  }

  loadPayments(): void {
    this.paymentService.getPayments().subscribe({
      next: (data) => {
        this.payments = data;
        this.filterPayment = data;
        console.log(data)
      },
      error: (err) => {
        console.error('Error fetching payments:', err);
      }
    });
  }  

  filteredPayments(): void {
    if (!this.searchTerm) {
      this.filterPayment = this.payments;
    } else {
      const term = this.searchTerm.toLowerCase();
      this.filterPayment = this.payments.filter(payment =>
        payment.payment_ID.toString().includes(term) ||
        payment.memberName.toLowerCase().includes(term) ||
        payment.amount.toString().includes(term) ||
        payment.payment_Date.toString().includes(term) ||
        payment.paymentTypeName.toLowerCase().includes(term)
      );
    }
  }
 
  openModal(payment: any): void {
    this.selectedPayment = payment;
    $('#userModal').modal('show');
  }
 
  closeModal(): void {
    $('#userModal').modal('hide');
  }
 
  goBack(): void {
    this.location.back();
  }

  exportToExcel(): void {
    const customHeaders = [
      { header: 'Payment ID', key: 'payment_ID' },
      { header: 'Member Name', key: 'memberName' },
      { header: 'Amount', key: 'amount' },
      { header: 'Payment Date', key: 'payment_Date' },
      { header: 'Payment Type', key: 'paymentTypeName' }
    ];

    const worksheetData = this.filterPayment.map(payment => ({
      payment_ID: payment.payment_ID,
      memberName: payment.memberName,
      amount: payment.amount,
      payment_Date: this.formatDate(payment.payment_Date), // Format the date
      paymentTypeName: payment.paymentTypeName
    }));

    const worksheet = XLSX.utils.json_to_sheet(worksheetData, { header: customHeaders.map(h => h.key) });
    const workbook = { Sheets: { 'Payments': worksheet }, SheetNames: ['Payments'] };

    // Modify column headers
    customHeaders.forEach((header, index) => {
      const col = XLSX.utils.encode_col(index); // Convert 0 -> 'A', 1 -> 'B', etc.
      worksheet[`${col}1`] = { t: 's', v: header.header };
    });

    const excelBuffer: any = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
    this.saveAsExcelFile(excelBuffer, 'payments');
  }

  private formatDate(date: string): string {
    return new Date(date).toISOString().split('T')[0];
  }

  private saveAsExcelFile(buffer: any, fileName: string): void {
    const data: Blob = new Blob([buffer], { type: EXCEL_TYPE });
    FileSaver.saveAs(data, `${fileName}_${new Date().getTime()}.xlsx`);
  }
}

const EXCEL_TYPE = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8';
SELECT COUNT(*) AS TableCount
FROM information_schema.tables
WHERE table_type = 'BASE TABLE';

Similiar Collections

Python strftime reference pandas.Period.strftime python - Formatting Quarter time in pandas columns - Stack Overflow python - Pandas: Change day - Stack Overflow python - Check if multiple columns exist in a df - Stack Overflow Pandas DataFrame apply() - sending arguments examples python - How to filter a dataframe of dates by a particular month/day? - Stack Overflow python - replace a value in the entire pandas data frame - Stack Overflow python - Replacing blank values (white space) with NaN in pandas - Stack Overflow python - get list from pandas dataframe column - Stack Overflow python - How to drop rows of Pandas DataFrame whose value in a certain column is NaN - Stack Overflow python - How to drop rows of Pandas DataFrame whose value in a certain column is NaN - Stack Overflow python - How to lowercase a pandas dataframe string column if it has missing values? - Stack Overflow How to Convert Integers to Strings in Pandas DataFrame - Data to Fish How to Convert Integers to Strings in Pandas DataFrame - Data to Fish create a dictionary of two pandas Dataframe columns? - Stack Overflow python - ValueError: No axis named node2 for object type <class 'pandas.core.frame.DataFrame'> - Stack Overflow Python Pandas iterate over rows and access column names - Stack Overflow python - Creating dataframe from a dictionary where entries have different lengths - Stack Overflow python - Deleting DataFrame row in Pandas based on column value - Stack Overflow python - How to check if a column exists in Pandas - Stack Overflow python - Import pandas dataframe column as string not int - Stack Overflow python - What is the most efficient way to create a dictionary of two pandas Dataframe columns? - Stack Overflow Python Loop through Excel sheets, place into one df - Stack Overflow python - How do I get the row count of a Pandas DataFrame? - Stack Overflow python - How to save a new sheet in an existing excel file, using Pandas? - Stack Overflow Python Loop through Excel sheets, place into one df - Stack Overflow How do I select a subset of a DataFrame? — pandas 1.2.4 documentation python - Delete column from pandas DataFrame - Stack Overflow python - Convert list of dictionaries to a pandas DataFrame - Stack Overflow How to Add or Insert Row to Pandas DataFrame? - Python Examples python - Check if a value exists in pandas dataframe index - Stack Overflow python - Set value for particular cell in pandas DataFrame using index - Stack Overflow python - Pandas Dataframe How to cut off float decimal points without rounding? - Stack Overflow python - Pandas: Change day - Stack Overflow python - Clean way to convert quarterly periods to datetime in pandas - Stack Overflow Pandas - Number of Months Between Two Dates - Stack Overflow python - MonthEnd object result in <11 * MonthEnds> instead of number - Stack Overflow python - Extracting the first day of month of a datetime type column in pandas - Stack Overflow
MySQL MULTIPLES INNER JOIN How to Use EXISTS, UNIQUE, DISTINCT, and OVERLAPS in SQL Statements - dummies postgresql - SQL OVERLAPS PostgreSQL Joins: Inner, Outer, Left, Right, Natural with Examples PostgreSQL Joins: A Visual Explanation of PostgreSQL Joins PL/pgSQL Variables ( Format Dates ) The Ultimate Guide to PostgreSQL Date By Examples Data Type Formatting Functions PostgreSQL - How to calculate difference between two timestamps? | TablePlus Date/Time Functions and Operators PostgreSQL - DATEDIFF - Datetime Difference in Seconds, Days, Months, Weeks etc - SQLines CASE Statements in PostgreSQL - DataCamp SQL Optimizations in PostgreSQL: IN vs EXISTS vs ANY/ALL vs JOIN PostgreSQL DESCRIBE TABLE Quick and best way to Compare Two Tables in SQL - DWgeek.com sql - Best way to select random rows PostgreSQL - Stack Overflow PostgreSQL: Documentation: 13: 70.1. Row Estimation Examples Faster PostgreSQL Counting How to Add a Default Value to a Column in PostgreSQL - PopSQL How to Add a Default Value to a Column in PostgreSQL - PopSQL SQL Subquery - Dofactory SQL IN - SQL NOT IN - JournalDev DROP FUNCTION (Transact-SQL) - SQL Server | Microsoft Docs SQL : Multiple Row and Column Subqueries - w3resource PostgreSQL: Documentation: 9.5: CREATE FUNCTION PostgreSQL CREATE FUNCTION By Practical Examples datetime - PHP Sort a multidimensional array by element containing date - Stack Overflow database - Oracle order NULL LAST by default - Stack Overflow PostgreSQL: Documentation: 9.5: Modifying Tables PostgreSQL: Documentation: 14: SELECT postgresql - sql ORDER BY multiple values in specific order? - Stack Overflow How do I get the current unix timestamp from PostgreSQL? - Database Administrators Stack Exchange SQL MAX() with HAVING, WHERE, IN - w3resource linux - Which version of PostgreSQL am I running? - Stack Overflow Copying Data Between Tables in a Postgres Database php - How to remove all numbers from string? - Stack Overflow sql - How to get a list column names and datatypes of a table in PostgreSQL? - Stack Overflow postgresql - How do I remove all spaces from a field in a Postgres database in an update query? - Stack Overflow sql - How to get a list column names and datatypes of a table in PostgreSQL? - Stack Overflow How to change PRIMARY KEY of an existing PostgreSQL table? · GitHub Drop tables w Dependency Tracking ( constraints ) Import CSV File Into PosgreSQL Table How To Import a CSV into a PostgreSQL Database How can I drop all the tables in a PostgreSQL database? - Stack Overflow How can I drop all the tables in a PostgreSQL database? - Stack Overflow PostgreSQL CASE Statements & Examples using WHEN-THEN, if-else and switch | DataCamp PostgreSQL LEFT: Get First N Characters in a String How can I drop all the tables in a PostgreSQL database? - Stack Overflow How can I drop all the tables in a PostgreSQL database? - Stack Overflow PostgreSQL - Copy Table - GeeksforGeeks PostgreSQL BETWEEN Query with Example sql - Postgres Query: finding values that are not numbers - Stack Overflow PostgreSQL UPDATE Join with A Practical Example
Request API Data with JavaScript or PHP (Access a Json data with PHP API) PHPUnit – The PHP Testing Framework phpspec array_column How to get closest date compared to an array of dates in PHP Calculating past and future dates < PHP | The Art of Web PHP: How to check which item in an array is closest to a given number? - Stack Overflow implode php - Calculate difference between two dates using Carbon and Blade php - Create a Laravel Request object on the fly testing - How can I measure the speed of code written in PHP? testing - How can I measure the speed of code written in PHP? What to include in gitignore for a Laravel and PHPStorm project Laravel Chunk Eloquent Method Example - Tuts Make html - How to solve PHP error 'Notice: Array to string conversion in...' - Stack Overflow PHP - Merging two arrays into one array (also Remove Duplicates) - Stack Overflow php - Check if all values in array are the same - Stack Overflow PHP code - 6 lines - codepad php - Convert array of single-element arrays to one a dimensional array - Stack Overflow datetime - PHP Sort a multidimensional array by element containing date - Stack Overflow sql - Division ( / ) not giving my answer in postgresql - Stack Overflow Get current date, given a timezone in PHP? - Stack Overflow php - Get characters after last / in url - Stack Overflow Add space after 7 characters - PHP Coding Help - PHP Freaks php - Laravel Advanced Wheres how to pass variable into function? - Stack Overflow php - How can I manually return or throw a validation error/exception in Laravel? - Stack Overflow php - How to add meta data in laravel api resource - Stack Overflow php - How do I create a webhook? - Stack Overflow Webhooks - Examples | SugarOutfitters Accessing cells - PhpSpreadsheet Documentation Reading and writing to file - PhpSpreadsheet Documentation PHP 7.1: Numbers shown with scientific notation even if explicitely formatted as text · Issue #357 · PHPOffice/PhpSpreadsheet · GitHub How do I install Java on Ubuntu? nginx - How to execute java command from php page with shell_exec() function? - Stack Overflow exec - Executing a java .jar file with php - Stack Overflow Measuring script execution time in PHP - GeeksforGeeks How to CONVERT seconds to minutes? PHP: Check if variable exist but also if has a value equal to something - Stack Overflow How to declare a global variable in php? - Stack Overflow How to zip a whole folder using PHP - Stack Overflow php - Saving file into a prespecified directory using FPDF - Stack Overflow PHP 7.0 get_magic_quotes_runtime error - Stack Overflow How to Create an Object Without Class in PHP ? - GeeksforGeeks Recursion in PHP | PHPenthusiast PHP PDO Insert Tutorial Example - DEV Community PHP Error : Unparenthesized `a ? b : c ? d : e` is deprecate | Laravel.io mysql - Which is faster: multiple single INSERTs or one multiple-row INSERT? - Stack Overflow Display All PHP Errors: Basic & Advanced Usage Need to write at beginning of file with PHP - Stack Overflow Append at the beginning of the file in PHP - Stack Overflow PDO – Insert, update, and delete records in PHP – BrainBell php - How to execute a raw sql query with multiple statement with laravel - Stack Overflow
Clear config cache Eloquent DB::Table RAW Query / WhereNull Laravel Eloquent "IN" Query get single column value in laravel eloquent php - How to use CASE WHEN in Eloquent ORM? - Stack Overflow AND-OR-AND + brackets with Eloquent - Laravel Daily Database: Query Builder - Laravel - The PHP Framework For Web Artisans ( RAW ) Combine Foreach Loop and Eloquent to perform a search | Laravel.io Access Controller method from another controller in Laravel 5 How to Call a controller function in another Controller in Laravel 5 php - Create a Laravel Request object on the fly php - Laravel 5.6 Upgrade caused Logging to break Artisan Console - Laravel - The PHP Framework For Web Artisans What to include in gitignore for a Laravel and PHPStorm project php - Create a Laravel Request object on the fly Process big DB table with chunk() method - Laravel Daily How to insert big data on the laravel? - Stack Overflow php - How can I build a condition based query in Laravel? - Stack Overflow Laravel Chunk Eloquent Method Example - Tuts Make Database: Migrations - Laravel - The PHP Framework For Web Artisans php - Laravel Model Error Handling when Creating - Exception Laravel - Inner Join with Multiple Conditions Example using Query Builder - ItSolutionStuff.com laravel cache disable phpunit code example | Newbedev In PHP, how to check if a multidimensional array is empty? · Humblix php - Laravel firstOrNew how to check if it's first or new? - Stack Overflow get base url laravel 8 Code Example Using gmail smtp via Laravel: Connection could not be established with host smtp.gmail.com [Connection timed out #110] - Stack Overflow php - Get the Last Inserted Id Using Laravel Eloquent - Stack Overflow php - Laravel-5 'LIKE' equivalent (Eloquent) - Stack Overflow Accessing cells - PhpSpreadsheet Documentation How to update chunk records in Laravel php - How to execute external shell commands from laravel controller? - Stack Overflow How to convert php array to laravel collection object 3 Best Laravel Redis examples to make your site load faster How to Create an Object Without Class in PHP ? - GeeksforGeeks Case insensitive search with Eloquent | Laravel.io How to Run Specific Seeder in Laravel? - ItSolutionStuff.com PHP Error : Unparenthesized `a ? b : c ? d : e` is deprecate | Laravel.io How to chunk query results in Laravel php - How to execute a raw sql query with multiple statement with laravel - Stack Overflow
PostgreSQL POSITION() function PostgresQL ANY / SOME Operator ( IN vs ANY ) PostgreSQL Substring - Extracting a substring from a String How to add an auto-incrementing primary key to an existing table, in PostgreSQL PostgreSQL STRING_TO_ARRAY()function mysql FIND_IN_SET equivalent to postgresql PL/pgSQL Variables ( Format Dates ) The Ultimate Guide to PostgreSQL Date By Examples Data Type Formatting Functions PostgreSQL - How to calculate difference between two timestamps? | TablePlus Date/Time Functions and Operators PostgreSQL - DATEDIFF - Datetime Difference in Seconds, Days, Months, Weeks etc - SQLines CASE Statements in PostgreSQL - DataCamp SQL Optimizations in PostgreSQL: IN vs EXISTS vs ANY/ALL vs JOIN PL/pgSQL Variables PostgreSQL: Documentation: 11: CREATE PROCEDURE Reading a Postgres EXPLAIN ANALYZE Query Plan Faster PostgreSQL Counting sql - Fast way to discover the row count of a table in PostgreSQL - Stack Overflow PostgreSQL: Documentation: 9.1: tablefunc PostgreSQL DESCRIBE TABLE Quick and best way to Compare Two Tables in SQL - DWgeek.com sql - Best way to select random rows PostgreSQL - Stack Overflow How to Add a Default Value to a Column in PostgreSQL - PopSQL How to Add a Default Value to a Column in PostgreSQL - PopSQL PL/pgSQL IF Statement PostgreSQL: Documentation: 9.1: Declarations SQL Subquery - Dofactory SQL IN - SQL NOT IN - JournalDev PostgreSQL - IF Statement - GeeksforGeeks How to work with control structures in PostgreSQL stored procedures: Using IF, CASE, and LOOP statements | EDB PL/pgSQL IF Statement How to combine multiple selects in one query - Databases - ( loop reference ) DROP FUNCTION (Transact-SQL) - SQL Server | Microsoft Docs SQL : Multiple Row and Column Subqueries - w3resource PostgreSQL: Documentation: 9.5: CREATE FUNCTION PostgreSQL CREATE FUNCTION By Practical Examples datetime - PHP Sort a multidimensional array by element containing date - Stack Overflow database - Oracle order NULL LAST by default - Stack Overflow PostgreSQL: Documentation: 9.5: Modifying Tables PostgreSQL: Documentation: 14: SELECT PostgreSQL Array: The ANY and Contains trick - Postgres OnLine Journal postgresql - sql ORDER BY multiple values in specific order? - Stack Overflow sql - How to aggregate two PostgreSQL columns to an array separated by brackets - Stack Overflow How do I get the current unix timestamp from PostgreSQL? - Database Administrators Stack Exchange SQL MAX() with HAVING, WHERE, IN - w3resource linux - Which version of PostgreSQL am I running? - Stack Overflow Postgres login: How to log into a Postgresql database | alvinalexander.com Copying Data Between Tables in a Postgres Database PostgreSQL CREATE FUNCTION By Practical Examples php - How to remove all numbers from string? - Stack Overflow sql - How to get a list column names and datatypes of a table in PostgreSQL? - Stack Overflow postgresql - How do I remove all spaces from a field in a Postgres database in an update query? - Stack Overflow sql - How to get a list column names and datatypes of a table in PostgreSQL? - Stack Overflow A Step-by-Step Guide To PostgreSQL Temporary Table How to change PRIMARY KEY of an existing PostgreSQL table? · GitHub PostgreSQL UPDATE Join with A Practical Example PostgreSQL: Documentation: 15: CREATE SEQUENCE How can I drop all the tables in a PostgreSQL database? - Stack Overflow PostgreSQL Show Tables Drop tables w Dependency Tracking ( constraints ) Import CSV File Into PosgreSQL Table How To Import a CSV into a PostgreSQL Database How can I drop all the tables in a PostgreSQL database? - Stack Overflow How can I drop all the tables in a PostgreSQL database? - Stack Overflow PostgreSQL CASE Statements & Examples using WHEN-THEN, if-else and switch | DataCamp PostgreSQL LEFT: Get First N Characters in a String How can I drop all the tables in a PostgreSQL database? - Stack Overflow How can I drop all the tables in a PostgreSQL database? - Stack Overflow postgresql - Binary path in the pgAdmin preferences - Database Administrators Stack Exchange postgresql - Binary path in the pgAdmin preferences - Database Administrators Stack Exchange PostgreSQL - Copy Table - GeeksforGeeks postgresql duplicate key violates unique constraint - Stack Overflow PostgreSQL BETWEEN Query with Example VACUUM FULL - PostgreSQL wiki How To Remove Spaces Between Characters In PostgreSQL? - Database Administrators Stack Exchange sql - Postgres Query: finding values that are not numbers - Stack Overflow PostgreSQL LEFT: Get First N Characters in a String unaccent: Getting rid of umlauts, accents and special characters
כמה עוד נשאר למשלוח חינם גם לעגלה ולצקאאוט הוספת צ'קבוקס לאישור דיוור בצ'קאאוט הסתרת אפשרויות משלוח אחרות כאשר משלוח חינם זמין דילוג על מילוי כתובת במקרה שנבחרה אפשרות איסוף עצמי הוספת צ'קבוקס לאישור דיוור בצ'קאאוט שינוי האפשרויות בתפריט ה-סידור לפי בווקומרס שינוי הטקסט "אזל מהמלאי" הערה אישית לסוף עמוד העגלה הגבלת רכישה לכל המוצרים למקסימום 1 מכל מוצר קבלת שם המוצר לפי ה-ID בעזרת שורטקוד הוספת כפתור וואטסאפ לקנייה בלופ ארכיון מוצרים הפיכה של מיקוד בצ'קאאוט ללא חובה מעבר ישיר לצ'קאאוט בלחיתה על הוספה לסל (דילוג עגלה) התראה לקבלת משלוח חינם בדף עגלת הקניות גרסה 1 התראה לקבלת משלוח חינם בדף עגלת הקניות גרסה 2 קביעה של מחיר הזמנה מינימלי (מוצג בעגלה ובצ'קאאוט) העברת קוד הקופון ל-ORDER REVIEW העברת קוד הקופון ל-ORDER REVIEW Kadence WooCommerce Email Designer קביעת פונט אסיסנט לכל המייל בתוסף מוצרים שאזלו מהמלאי - יופיעו מסומנים באתר, אבל בתחתית הארכיון הוספת כפתור "קנה עכשיו" למוצרים הסתרת אפשרויות משלוח אחרות כאשר משלוח חינם זמין שיטה 2 שינוי סימן מטבע ש"ח ל-ILS להפוך סטטוס הזמנה מ"השהייה" ל"הושלם" באופן אוטומטי תצוגת הנחה באחוזים שינוי טקסט "בחר אפשרויות" במוצרים עם וריאציות חיפוש מוצר לפי מק"ט שינוי תמונת מוצר לפי וריאציה אחרי בחירה של וריאציה אחת במקרה של וריאציות מרובות הנחה קבועה לפי תפקיד בתעריף קבוע הנחה קבועה לפי תפקיד באחוזים הסרה של שדות משלוח לקבצים וירטואליים הסתרת טאבים מעמוד מוצר הצגת תגית "אזל מהמלאי" בלופ המוצרים להפוך שדות ל-לא חובה בצ'קאאוט שינוי טקסט "אזל מהמלאי" לוריאציות שינוי צבע ההודעות המובנות של ווקומרס הצגת ה-ID של קטגוריות המוצרים בעמוד הקטגוריות אזל מהמלאי- שינוי ההודעה, תגית בלופ, הודעה בדף המוצר והוספת אזל מהמלאי על וריאציה הוספת שדה מחיר ספק לדף העריכה שינוי טקסט אזל מהמלאי תמונות מוצר במאונך לצד תמונת המוצר הראשית באלמנטור הוספת כפתור קנה עכשיו לעמוד המוצר בקניה הזו חסכת XX ש''ח לאפשר למנהל חנות לנקות קאש ברוקט לאפשר רק מוצר אחד בעגלת קניות הוספת סימון אריזת מתנה ואזור להוראות בצ'קאאוט של ווקומרס הצגת הנחה במספר (גודל ההנחה) הוספת "אישור תקנון" לדף התשלום הצגת רשימת תכונות המוצר בפרונט שינוי כמות מוצרים בצ'קאאוט ביטול השדות בצ'קאאוט שינוי כותרות ופלייסהולדר של השדות בצ'קאאוט
החלפת טקסט באתר (מתאים גם לתרגום נקודתי) הסרת פונטים של גוגל מתבנית KAVA ביטול התראות במייל על עדכון וורדפרס אוטומטי הוספת תמיכה בקבצי VCF באתר (קבצי איש קשר VCARD) - חלק 1 להחריג קטגוריה מסוימת מתוצאות החיפוש שליפת תוכן של ריפיטר יצירת כפתור שיתוף למובייל זיהוי אלו אלמנטים גורמים לגלילה אופקית התקנת SMTP הגדרת טקסט חלופי לתמונות לפי שם הקובץ הוספת התאמת תוספים לגרסת WP הוספת טור ID למשתמשים הסרת כותרת בתבנית HELLO הסרת תגובות באופן גורף הרשאת SVG חילוץ החלק האחרון של כתובת העמוד הנוכחי חילוץ הסלאג של העמוד חילוץ כתובת העמוד הנוכחי מניעת יצירת תמונות מוקטנות התקנת SMTP הצגת ה-ID של קטגוריות בעמוד הקטגוריות להוריד מתפריט הניהול עמודים הוספת Favicon שונה לכל דף ודף הוספת אפשרות שכפול פוסטים ובכלל (של שמעון סביר) הסרת תגובות באופן גורף 2 בקניה הזו חסכת XX ש''ח חיפוש אלמנטים סוררים, גלישה צדית במובייל שיטה 1 לאפשר רק מוצר אחד בעגלת קניות הצגת הנחה במספר (גודל ההנחה) הוספת "אישור תקנון" לדף התשלום שינוי צבע האדמין לפי סטטוס העמוד/פוסט שינוי צבע אדמין לכולם לפי הסכמות של וורדפרס תצוגת כמות צפיות מתוך הדשבורד של וורדפרס הצגת סוג משתמש בפרונט גלילה אין סופית במדיה שפת הממשק של אלמנטור תואמת לשפת המשתמש אורך תקציר מותאם אישית
הודעת שגיאה מותאמת אישית בטפסים להפוך כל סקשן/עמודה לקליקבילית (לחיצה) - שיטה 1 להפוך כל סקשן/עמודה לקליקבילית (לחיצה) - שיטה 2 שינוי הגבלת הזיכרון בשרת הוספת לינק להורדת מסמך מהאתר במייל הנשלח ללקוח להפוך כל סקשן/עמודה לקליקבילית (לחיצה) - שיטה 3 יצירת כפתור שיתוף למובייל פתיחת דף תודה בטאב חדש בזמן שליחת טופס אלמנטור - טופס בודד בדף פתיחת דף תודה בטאב חדש בזמן שליחת טופס אלמנטור - טפסים מרובים בדף ביי ביי לאריק ג'ונס (חסימת ספאם בטפסים) זיהוי אלו אלמנטים גורמים לגלילה אופקית לייבלים מרחפים בטפסי אלמנטור יצירת אנימציה של "חדשות רצות" בג'ט (marquee) שינוי פונט באופן דינאמי בג'ט פונקציה ששולפת שדות מטא מתוך JET ומאפשרת לשים הכל בתוך שדה SELECT בטופס אלמנטור הוספת קו בין רכיבי התפריט בדסקטופ ולדציה למספרי טלפון בטפסי אלמנטור חיבור שני שדות בטופס לשדה אחד שאיבת נתון מתוך כתובת ה-URL לתוך שדה בטופס וקידוד לעברית מדיה קוורי למובייל Media Query תמונות מוצר במאונך לצד תמונת המוצר הראשית באלמנטור הצגת תאריך עברי פורמט תאריך מותאם אישית תיקון שדה תאריך בטופס אלמנטור במובייל שאיבת פרמטר מתוך הכתובת והזנתו לתוך שדה בטופס (PARAMETER, URL, INPUT) עמודות ברוחב מלא באלמנטור עמודה דביקה בתוך אלמנטור יצירת "צל" אומנותי קוד לסוויצ'ר, שני כפתורים ושני אלמנטים סקריפט לסגירת פופאפ של תפריט לאחר לחיצה על אחד העמודים הוספת כפתור קרא עוד שפת הממשק של אלמנטור תואמת לשפת המשתמש להריץ קוד JS אחרי שטופס אלמנטור נשלח בהצלחה מצב ממורכז לקרוסלת תמונות של אלמנטור לייבלים מרחפים בטפסי פלואנטפורמס שדרוג חוויית העלאת הקבצים באלמנטור
What is the fastest or most elegant way to compute a set difference using Javascript arrays? - Stack Overflow javascript - Class Binding ternary operator - Stack Overflow Class and Style Bindings — Vue.js How to remove an item from an Array in JavaScript javascript - How to create a GUID / UUID - Stack Overflow json - Remove properties from objects (JavaScript) - Stack Overflow javascript - Remove property for all objects in array - Stack Overflow convert array to dictionary javascript Code Example JavaScript: Map an array of objects to a dictionary - DEV Community JavaScript: Map an array of objects to a dictionary - DEV Community javascript - How to replace item in array? - Stack Overflow javascript - How to replace item in array? - Stack Overflow How can I replace Space with %20 in javascript? - Stack Overflow JavaScript: Check If Array Has All Elements From Another Array - Designcise javascript - How to use format() on a moment.js duration? - Stack Overflow javascript - Elegant method to generate array of random dates within two dates - Stack Overflow Compare two dates in JavaScript using moment.js - Poopcode javascript - Can ES6 template literals be substituted at runtime (or reused)? - Stack Overflow javascript - How to check if array is empty or does not exist? - Stack Overflow How To Use .map() to Iterate Through Array Items in JavaScript | DigitalOcean Check if a Value Is Object in JavaScript | Delft Stack vuejs onclick open url in new tab Code Example javascript - Trying to use fetch and pass in mode: no-cors - Stack Overflow Here are the most popular ways to make an HTTP request in JavaScript JavaScript Array Distinct(). Ever wanted to get distinct element - List of object to a Set How to get distinct values from an array of objects in JavaScript? - Stack Overflow Sorting an array by multiple criteria with vanilla JavaScript | Go Make Things javascript - Map and filter an array at the same time - Stack Overflow ecmascript 6 - JavaScript Reduce Array of objects to object dictionary - Stack Overflow javascript - Convert js Array to Dictionary/Hashmap - Stack Overflow javascript - Is there any way to use a numeric type as an object key? - Stack Overflow
Hooks Cheatsheet React Tutorial Testing Overview – React performance Animating Between Views in React | CSS-Tricks - CSS-Tricks Building an Animated Counter with React and CSS - DEV Community Animate When Element is In-View with Framer Motion | by Chad Murobayashi | JavaScript in Plain English Handling Scroll Based Animation in React (2-ways) - Ryosuke react-animate-on-scroll - npm Bring Life to Your Website | Parallax Scrolling using React and CSS - YouTube react-cool-inview: React hook to monitor an element enters or leaves the viewport (or another element) - DEV Community Improve React Performance using Lazy Loading💤 and Suspense | by Chidume Nnamdi 🔥💻🎵🎮 | Bits and Pieces Mithi's Epic React Exercises React Hooks - Understanding Component Re-renders | by Gupta Garuda | Medium reactjs - When to use useImperativeHandle, useLayoutEffect, and useDebugValue - Stack Overflow useEffect vs useLayoutEffect When to useMemo and useCallback useLockBody hook hooks React animate fade on mount How to Make a React Website with Page Transitions using Framer Motion - YouTube Build a React Image Slider Carousel from Scratch Tutorial - YouTube React Router: useParams | by Megan Lo | Geek Culture | Medium useEffect Fetch POST in React | React Tutorial Updating Arrays in State Background Images in React React Context API : A complete guide | by Pulkit Sharma | Medium Code-Splitting – React Lazy Loading React Components (with react.lazy and suspense) | by Nwose Lotanna | Bits and Pieces Lazy loading React components - LogRocket Blog Code Splitting a Redux Application | Pluralsight react.js folder structure | by Vinoth kumar | Medium react boilerplate React State Management best practices for 2021 (aka no Redux) | by Emmanuel Meric de Bellefon | Medium Create an advanced scroll lock React Hook - LogRocket Blog UseOnClickOutside : Custom hook to detect the mouse click on outside (typescript) - Hashnode react topics 8 Awesome React Hooks web-vitals: Essential metrics for a healthy site. scripts explained warnings and dependency errors Learn the useContext Hook in React - Programming with Mosh Learn useReducer Hook in React - Programming with Mosh Guide To Learn useEffect Hook in React - Programming with Mosh Getting Started With Jest - Testing is Fun - Programming with Mosh Building an accessible React Modal component - Programming with Mosh Kent C. Dodds's free React course
SAVED SEARCH DATE FORMAT(page wise) AND LOOP THROUGH EACH DATA || With Pagination || Avoids 4000 data Limt Rest in Suitlet CALL ANOTHER SCRIPT | SUITELET FROM ANY OTHER SCRIPT | SUITELET | ALSO PASSING OF PARAMETERS CALLING RESTLET |FROM SUITELET Where Is The “IF” Statement In A Saved Search Formula? – > script everything Where Is The “IF” Statement In A Saved Search Formula? – > script everything ClientScript Add Sublist Line items etc Saving Record In different way. BFO AND FREEMarkup || advance pdf html template Join In Lookup Field Search || Alternative of saved search Use Saved Serch or Lookup field search for Getting value or id of Fields not visible On UI or xml https://www.xmlvalidation.com/ XML Validate Load Record--->selectLine--->CommitLine--->Save [Set Sublist field Data After Submit] Null Check Custom Check SEND MAIL WITH FILE ATTACHED EXECUTION CONTEXT || runtime.context In BeforeLoad Use This When Trying to Get Value In BeforeLoad Convert String Into JSON Freemarker Below 2.3.31 use ?eval || above use ?eval_json Design Of Full Advance PDF Template using Suitescript PART 1: Design Of Full Advance PDF Template using Suitescript PART 2: Iterate Through Complex Nested JSON Object Freemarker || BFO || Advance PDF HTML Template WORKING JSON OBJ ITERATE FreeMarker get String convert to JSON ,Iterate through it ,Print Values In Table Series Addition Freemarker Using Loop(List) Modified Null Check || Keep Updated One Here Navigations In Netsuite || Records || etc DATE FORMAT Netsuite Javascript Transform Invoice To Credit Memo NULLCHECK Freemarker||BFO|| XML||Template Before Accessing Any Value Refresh Page Without Pop Up | | client script || Reload Page Easy Manner To Format Date || Date to simple Format || MM/DD/YYYY Format ||Simple Date CUSTOM ERROR MESSAGE CREATE HOW TO STOP MAP/REDUCE SCRIPT RUNNING FILTER ON JSON OBJECT SAVED SEARCH CRITERIA LOGIC FOR WITHIN DATE | | WITHIN START DATE END DATE TO GET Line no. also of Error NETSUITE SERVER TIMEZONE : (GMT-05:00) Eastern Time (US & Canada) || Problem resolved for Map/reduce Timezone issue for Start Date in map/reduce || RESOLVED THE ISSUE compare/check date that it lies between two dates or not || Use of .getTime() object of Date TO FIND ALL TASKS WITHIN SO START DATE AND END DATE || SAVED SEARCH CODE WORDS Saved Search Get only one Result || getRange netsuite - SuiteScript 2.0 Add filters to saved search in script - Stack Overflow || Addition in saved search filter MASS DELETE ALL RCORD FROM SAVED SEARCH DATA IN PARAMETER|| ADD SS IN THE DEPLOYMENT PARAMETER SAVED SEARCH DATE COMPARE AND GET RESULT IN DAYS HOURS MINUTES Multiple Formula Columns Comment In Saved Search || Saved search used SQL language for writing formula Logic Set Addressbook Values || Addressbook is a subrecord SuiteQL || N/Query Module || Support SQL query VALIDATE 2 DATES LIE BETWEEN 2 DATES OR NOT || OVERLAPPING CASE ALSO Weeks Within two dates:
Delete Duplication SQL Using Subquery to find salary above average Sorting Rank Number In Sql find nth Salary in SQL sql - Counting rows in the table which have 1 or more missing values - Stack Overflow How to create customized quarter based on a given date column sql sql server - Stack Overflow get total sales per quarter, every year delete duplicate for table without primary key SQL Server REPLACE Function By Practical Examples adding row_number row_id to table find total sales per cateogry each year change empty string into null values Case when to update table update table using replace function How to Use CASE WHEN With SUM() in SQL | LearnSQL.com find each year sale in each city and state, then sum them all using regexp_replace to remove special chracter postgresql find quarter sale in each state, and sum it using unbounded preceding, and using percent rank create delimiter for the phone number regexp_replace to remove special characters using row between to cummulative sum update null values using WHERE filter insert into table command sum cummulative value in SQL SQL Important syntax Auto Increment in SQL read json file on postgresql Moving Average n Moving Total pivot on potgresql Rollup and Cube date function in postgresql select specify part of string using substring change column data type exclude null values in sql how to exclude zero in our counting finding outlier in sql how to import data with identifier (primary key) email and name filtering in sql trimming and removing particular string from sql replace string on sql regexp to find string in SQL answers only about email, identifier, lower, substring, date_part, to_char find percentage of null values in total remove duplicate on both tables v2 any and in operator string function moreee bucket function find perfomance index in sql find top max and top min finding world gdp formula (SUM OVER) find month percentage change find highest height in sql with row_number and subquery JOIN AND UNION offset and limit in sql function and variables using views on postgresql find cummulative sums in postgresql find null and not null percentage find specific strings or number in SQL using case when and CAST function Lpad function in Postgresql and string function in general Regexp function in postgresql regular expressions example in postgresql And FUZZYSTRMATCH updated method of deleting duplicates determine column types
Initiate MS Team Group Chat via PowerApps Save the current users email as a variable Creates variable with a theme colour palette Send an email from the current user Filters a data source based on the current logged in user Patch data fields, including a choice column to a data source Changes the colour of a selected item in a Gallery Filter and search a data source via a search box and dropdown Changes visibility based on the selection of another Gallery - used for "Tabs" Display current users first name Fix: Combobox/Search is empty check not working - Power Platform Community Retrive a user photo from SharePoint Get user photo from office365 connector from gallery Set a variable with the current users first name, from the currentUser variable Extract values from a collection Extract value from combo box Extract vale from combo box and convert to string/text Convert collection to JSON Combo box values to collection Show newly created items first / sort by most recent entry, will only show items created today Validate/Validation text box length and/or combo boxes contain data Text input validation - turns border red Lookup value against a text input and disable or enable displaymode Lookup items from a textbox Sets items to choices drop down or combo box Change text value based on lookup results returns tops 10 results and sorts by most recent created date Sets a variable with spilt text from a link - YouTube in this case Pass a null or empty value from Power Apps to a flow
Linuxteaching | linux console browser javascript Debugging in Visual Studio Code C# - Visual Studio Marketplace C# - Visual Studio Marketplace dotnet-install scripts - .NET CLI | Microsoft Docs dotnet-install scripts - .NET CLI | Microsoft Docs undefined .NET Tutorial | Hello World in 5 minutes Configuration files – Nordic Developer Academy CacheStorage.open() - Web APIs | MDN TransIP API Install .NET Core SDK on Linux | Snap Store .NET Tutorial | Hello World in 5 minutes Creating Your First Application in Python - GeeksforGeeks Riverbank Computing | Download Managing Application Dependencies — Python Packaging User Guide Building your first mobile application using Python | Engineering Education (EngEd) Program | Section Building your first mobile application using Python | Engineering Education (EngEd) Program | Section Building your first mobile application using Python | Engineering Education (EngEd) Program | Section Building your first mobile application using Python | Engineering Education (EngEd) Program | Section ActivePython-2.7 - ActiveState - Builds - ActiveState Platform Installation guidance for SQL Server on Linux - SQL Server | Microsoft Docs Ellabusby2006/Anzelmo2022 Ik wil de PHP-versie updaten van Ubuntu / Debian | TransIP Ik wil de PHP-versie updaten van Ubuntu / Debian | TransIP W3Schools Tryit Editor .NET installeren op Debian - .NET | Microsoft Learn .NET installeren op Debian - .NET | Microsoft Learn .NET installeren op Debian - .NET | Microsoft Learn .NET installeren op Debian - .NET | Microsoft Learn How To Build A Simple Star Rating System - Simon Ugorji | Tealfeed Visual Studio Code language identifiers Running Visual Studio Code on Linux HTML Forms Installeren .NET op Debian - .NET | Microsoft Learn StarCoderEx (AI code generator) - Visual Studio Marketplace Installeren .NET op Linux zonder een pakketbeheerder te gebruiken - .NET | Microsoft Learn ASP.NET Tutorial | Hello World in 5 minutes | .NET Deploy and connect to SQL Server Linux containers - SQL Server | Microsoft Learn Settings Sync in Visual Studio Code Settings Sync in Visual Studio Code TransIP API Monitoring as Code
.NET Tutorial | Hello World in 5 minutes docx2html - npm Running Visual Studio Code on Linux Connect to an ODBC Data Source (SQL Server Import and Export Wizard) - SQL Server Integration Services (SSIS) | Microsoft Docs .NET installeren in Linux zonder pakketbeheer - .NET | Microsoft Docs TransIP API TransIP API TransIP API TransIP API .NET installeren in Alpine - .NET | Microsoft Docs .NET installeren op Ubuntu - .NET | Microsoft Docs .NET installeren op Ubuntu - .NET | Microsoft Docs Geïnstalleerde .NET-versies controleren op Windows, Linux en macOS - .NET | Microsoft Docs Install .NET Core SDK on Linux | Snap Store .NET Tutorial | Hello World in 5 minutes Riverbank Computing | Download Managing Application Dependencies — Python Packaging User Guide Building your first mobile application using Python | Engineering Education (EngEd) Program | Section Building your first mobile application using Python | Engineering Education (EngEd) Program | Section Building your first mobile application using Python | Engineering Education (EngEd) Program | Section Building your first mobile application using Python | Engineering Education (EngEd) Program | Section ActivePython-2.7 - ActiveState - Builds - ActiveState Platform html - How to get mp3 files to play in iPhone Safari web browser? - Stack Overflow Work with review data  |  Google Business Profile APIs  |  Google Developers Javascript save text file - Javascript .NET installeren op Debian - .NET | Microsoft Learn Deploy and connect to SQL Server Linux containers - SQL Server | Microsoft Learn Settings Sync in Visual Studio Code Settings Sync in Visual Studio Code
Working with JSON in Freemarker - Liferay Community Freemarker parse a String as Json - Stack Overflow Online FreeMarker Template Tester Compiler Validate XML files Convert String Into JSON Freemarker Below 2.3.31 use ?eval || above use ?eval_json Working with JSON in Freemarker - Liferay Community Working with JSON in Freemarker - Liferay Community java - Freemarker iterating over hashmap keys - Stack Overflow java - Freemarker iterating over hashmap keys - Stack Overflow FreeMarker get String convert to JSON ,Iterate through it ,Print Values In Table Online FreeMarker Template Tester || freemarker compiler Series Addition Freemarker Using Loop(List) How to Convert a string to number in freemarker template - Stack Overflow javascript - Grouping JSON by values - Stack Overflow DATE FORMAT Netsuite Javascript Freemarkup | | Iterate through nested JSON all Values Using Nested For Loop Nested JSON Iterate Using BFO javascript - Error parsing XHTML: The content of elements must consist of well-formed character data or markup - Stack Overflow NULLCHECK Freemarker||BFO|| XML||Template Before Accessing Any Value ADVANCE PDF HTML TEMPLATE 7 Tips for Becoming a Pro at NetSuite’s Advanced PDF/HTML HTML Tag Center Does Not Work in Advanced PDF/HTML Templates|| align center HTML BFO NOTES Advanced PDF/HTML Template - NetSuite (Ascendion Holdings Inc) Check Template Code Is Very Different || Bill Payment check template Check Template Code Is Very Different || Bill Payment check template Intro to NetSuite Advanced PDF Source Code Mode | Tutorial | Anchor Group NETSUITE GUIDE OVER PDF/HTML TEMPLATE EDITOR suitescript - Ability to choose between multiple PDF templates on a Netsuite transaction form - Stack Overflow BFO DIV tr td etc User Guide|| USEFULL IMPORTANT Border radius in advanced html/pdf templates is not supported? : Netsuite Comma seperated Number With 2 DECIMAL PLACE || FREEMARKER || ADVANCE PDF HTML TEMPLATE
001-hello-world: Hello Image Classification using OpenVINO™ toolkit 002-openvino-api: OpenVINO API tutorial 003-hello-segmentation: Introduction to Segmentation in OpenVINO 004-hello-detection: Introduction to Detection in OpenVINO 101-tensorflow-to-openvino: TensorFlow to OpenVINO Model Conversion Tutorial 102-pytorch-onnx-to-openvino: PyTorch to ONNX and OpenVINO IR Tutorial 103-paddle-onnx-to-openvino: Convert a PaddlePaddle Model to ONNX and OpenVINO IR 104-model-tools: Working with Open Model Zoo Models 210-ct-scan-live-inference: Live Inference and Benchmark CT-scan Data with OpenVINO 201-vision-monodepth: Monodepth Estimation with OpenVINO 210-ct-scan-live-inference: Live Inference and Benchmark CT-scan Data with OpenVINO 401-object-detection-webcam: Live Object Detection with OpenVINO 402-pose-estimation-webcam: Live Human Pose Estimation with OpenVINO 403-action-recognition-webcam: Human Action Recognition with OpenVINO 211-speech-to-text: Speech to Text with OpenVINO 213-question-answering: Interactive Question Answering with OpenVINO 208-optical-character-recognition: Optical Character Recognition (OCR) with OpenVINO 209-handwritten-ocr: Handwritten Chinese and Japanese OCR 405-paddle-ocr-webcam: PaddleOCR with OpenVINO 305-tensorflow-quantization-aware-training: Optimizing TensorFlow models with Neural Network Compression Framework of OpenVINO by 8-bit quantization 302-pytorch-quantization-aware-training: Optimizing PyTorch models with Neural Network Compression Framework of OpenVINO by 8-bit quantization 301-tensorflow-training-openvino: Post-Training Quantization with TensorFlow Classification Model 301-tensorflow-training-openvino: From Training to Deployment with TensorFlow and OpenVINO 204-named-entity-recognition: Named Entity Recognition with OpenVINO
If statement with switch delete duplicates from an array loop and find class Nullish with an Object - basic tranverse classes in a list substring capitalize substring text with elipses array.at() js media query Dynamic Filter - buttons advanced flow converted if statement Add an array to a HTML dataset getElement Function Intersection Observer template intersection Observer Basic Example fetch data, display and filter for of loop - get index get random index value from an array fetch with post method get id value from url debounce for scrolling get element or elements check functions return values from functions indexOf explantion sorting basic using for-of loop for getting index value of iteration import json data into a JS module Splide slider with modal JS change active class if url path matches Pagination / array of arrays FIlter array or return full array price formatting ignores wrapping element on click Create a dummy array with numbers Random Generated Number Dummy array - list items dummy id Limits the amount of text Random Number generator function format date function Remove duplicates from JSON array data Filter Posts from state remove duplicates and create object simple ternary remove transition with propertyName sorting in reverse counting items using reduce splice explanation Declaring Variables Get primitive properties on a number / string etc using proto check an array of objects exists / check using regex Destructuring nested function scope IFFE function basic switch Switch with function passing an object check length of object while loops (for when the number we are iterating over is unknown) Ternary with 2 / 3 conditions for of loop with index array nested ternary operator callbacks Method Borrowing this keyword and arrow functions rest operator - args functions p1 rest operator - args functions p2 for of loop with index and entries callback functions explained version 1 callback functions explained version 2 Form and value IF Element shorthand function as a callback inside a higher order function Using param for symbol remove 'px' from number scrolling behaviour test 1 clearing event listeners new URL applying css vars scroll behaviours testing intersection observer intersection observer with loop JS docs - writing js docs check is key exists call() nested ternary with function forEach with function and ...args using template literals filter duplicates get class by indexOf returning ternary operator remove children from parent bootstrap rows with cols fetching multple data endpoints Promise.all remove file extension Js snippet for aria expanded passes this from a function containing a listener, to another function dynamic function to change icons compare arrays and push to an empty instead of pushing an object to a data array return the Object.entries(data).map Creating Dynamic filtering using JSON Advanced toggling with 3rd param of force Dynamic Key Assignment flatmap for arrays passing key value pairs from one object to another FInding Prototype Methods Returns Items that are true returning the item data from an array of objects sorting via switch statement filtering with checkboxes JS Docs cheatsheat for param types matches Js syles object on a div element bind grouping groupby() checkbox example conditions modal for text links reduce - dynamic toggle class with force returning objects setProperty with CSS variables CONVERT setProperty with CSS variables to object and functions CONVERT setProperty with CSS variables to object and functions version 2 uses an object instead of a switch statement cookie object functions mapping 3 Async Await / Promise resolve reject filter filter out items from array1 based on array2 promise with init function using maps = toggling class on select Any amount of args in a function Mapping with selections
Microsoft Powershell: Delete registry key or values on remote computer | vGeek - Tales from real IT system Administration environment How to Upload Files Over FTP With PowerShell Configure attack surface reduction in Microsoft Defender using Group Policy or PowerShell – 4sysops WinPE: Create bootable media | Microsoft Learn powershell - Can I get the correct date mixing Get-Date and [DateTime]::FromFileTime - Stack Overflow Search-ADAccount (ActiveDirectory) | Microsoft Docs Manual Package Download - PowerShell | Microsoft Docs Get a List of Expired User Accounts in AD Using PowerShell Search-ADAccount (ActiveDirectory) | Microsoft Docs How to Stop an Unresponsive Hyper-V Virtual Machine | Petri IT Knowledgebase Adding PowerShell 7 to WinPE - Deployment Research Send-MailMessage (Microsoft.PowerShell.Utility) - PowerShell | Microsoft Learn How to run a PowerShell script as a Windows service – 4sysops Connect to Exchange Online with PowerShell - The Best Method Find the Full Windows Build Number with PowerShell How to find all the ADFS servers in your environment and run diagnostics against them | Michael de Blok How to find ADFS servers in the environment - Windows Server path - PowerShell script working directory (current location) - Stack Overflow How to get the path of the currently executing script in PowerShell [SOLVED] Unable to Delete Hyper-V VM Checkpoints - Spiceworks Published Applications – Carl Stalhood VMM Reporting | Aidan Finn, IT Pro Use PowerShell to search for string in registry keys and values - Stack Overflow Search for Uninstall Strings - Jose Espitia
INCOME AND SPEND SUMMARY: Monthly regular gift income (DD, CC, PayPal) INCOME AND SPEND SO AGENCT AND PAYROLL INCOME AND SPEND Non Monthly DD income INCOME AND SPEND SUMMARY Donation (50020) Income and Spend Summary Appeal (50050) INCOME AND SPEND SUMMARY:FAV Donation (50060) INCOME AND SPEND SUMMARY: In Memory Of (IMO) (50170) INCOME AND SPEND SUMMARY: Single Fundraised Sales (51040)+Books(51050) INCOME AND SPEND SUMMARY: Single Fundraised Sales (51040)+Books(51050) INCOME AND SPEND SUMMARY: Single Fundraised Sales (51040)+Books(51050) INCOME AND SPEND SUMMARY: Single Fundraised Sales (51040)+Books(51050) INCOME AND SPEND SUMMARY: Raffle ticket sales INCOME AND SPEND SUMMARY:51060 - Community Fundraising INCOME AND SPEND SUMMARY:50130 - Collecting Tins INCOME AND SPEND SUMMARY:50110 - Gift Aid REGULAR GIVING SUMMARY: Monthly regular gift payments CC/PP/DD SINGLE GIFT SUMMARY: Single gift payments (donations only) NEW SUPPORTER INCOME: Single gift payments NEW SUPPORTER INCOME: Monthly regular gift income NEW SUPPORTER INCOME: New monthly regular gifts established (new supporters only) NEW SUPPORTER INCOME: Single gift income (new supporters only) EXISTING BASE: INCOME FROM EXISTING REGULAR INSTRUCTIONS EXISTING BASE Monthly regular gift payments (CC/PP/DD) EXISTING BASE: Existing Non monthly DD income Existing Base other regular giving income EXISTINGBASE Cumulative monthly regular gift payments EXISTING BASE Monthly regular gift income from new regular donor EXISTING BASE single gift donations income - existing supporters EXISTING BASE Single gift donations EXISTING BASE: Single gift income - high value gifts Existing Base: Workplace giving income EXISTING BASE Volunteer fundraising & other cash income (Community Events, FB fundraiser, Lilo) EXISTING BASE: Gift aid income ES ES Monthly cc/pp/dd Income existing ES ES Monthly cc/pp/dd Payments existing Single Single gift under 500 Total Regular giving Existing WORKPLACE GIVING
Windows: 7/Vista/XP/2K tcp tunneling nbtscan: Multiple-OS command line utility: NETBIOS nameserver scanner Linux: Simulating slow internet connection Linux/Ubuntu: Changing gateway address and flush/restart network interface Linux: SSH Tunnel to local machines. Linux: Get my external ip address Linux/Ubuntu: Enable/Disable ping response Linux: Cron: Reset neorouter Linux/Ubuntu: sniff tcp communications (binary output) Liunux/Ubuntu: get a list of apps that are consuming bandwidth Linux/Ubuntu: iptables: block external outgoing ip address Linux/Ubuntu: How to setup pptp vpn server Linux: NGINX: Setup alias Linux: ssh without password Linux: NGINX: Proxy reverse Linux: one way remote sync using unison and ssh Linux: Open ssh port access using a combination (knocking technique) Linux: ssh login for only one user Linux/Ubuntu: Server: Configuring proxies Linux/Ubuntu: Share folder with Windows (via samba) Linux: Get all my local IP addresses (IPv4) Linux/Ubuntu: list ufw firewall rules without enabling it Linux/Ubuntu: Connect windows to shared folder as guest Linux/Ubuntu: Avoid connection from specific ip address without using iptables Linux: Telegram: Send telegram message to channel when user logged via ssh Linux/Ubuntu: nginx: Configuration to send request to another server by servername Modbus/ModPoll Linux/Neorouter: watchdog for neorouter connection Linux: libgdcm: Send dicom images to PACS using gdcmscu. Ubuntu: mount full rw cifs share Linux/Ubuntu: NGINX: Basic authentication access Linux/Ubuntu: Mosquitto: Enable mqtt service Linux: Detect if port is closed from command line Linux: Get internet content from command line using wget Mac OSX: Port redirection Windows 10: Port redirection/Tunneling Python: Telegram Bot API: Ask/Answer question PHP: Post a XML File using PHP without cURL Nginx: Call php without extension PHP: Send compressed data to be used on Javascript (i.e. Json data) PHP: Download file and detect user connection aborted Linux/Proxmox: Enable /dev/net/tun for hamachi PHP: using curl for get and post NGINX: Creating .htpasswd Linux: Tunnel through two hosts
Linux: Free unused cache memory Linux: mounting VirtualBox VDI disk using qemu nbtscan: Multiple-OS command line utility: NETBIOS nameserver scanner Linux: Saving one or more webpages to pdf file Linux: Creating iso image from disk using one line bash command Linux/PostgreSQL: Getting service uptime Linux: Simulating slow internet connection Linux/Ubuntu: Changing gateway address and flush/restart network interface Linux: SSH Tunnel to local machines. Linux: Fast find text in specific files using wild cards Linux: Merging two or more pdf files into one, by using ghostscript Linux: Cron command for deleting old files (older than n days) Linux: Get my external ip address Linux: Get the size of a folder Linux: Get the size of a folder Linux: Get the size of a folder Lazarus/Linux: Connect to SQLServer using ZeosLib component TZConnection Linux/Ubuntu: Get ordered list of all installed packages Linux/Ubuntu: Enable/Disable ping response Linux/DICOM: Very small DICOM Server Linux: Cron: Reset neorouter Linux/Oracle: Startup script Linux/Ubuntu: detect if CD/DVD disk ispresent and is writeable Linux/PHP: Let www-data run other commands (permission) Linux/DICOM: Create DICOM Video Linux: Apply same command for multiple files Linux/Ubuntu: sniff tcp communications (binary output) Linux: rsync: Backup remote folder into local folder Linux: Installing Conquest Dicom Server Linux: Get number of pages of PDF document via command line Linux: split file into pieces and join pieces again Linux/Ubuntu: iptables: block external incoming ip address Liunux/Ubuntu: get a list of apps that are consuming bandwidth Linux/Ubuntu: iptables: block external outgoing ip address Linux/DICOM: dcmtk: Modify data in dicom folder Linux/Docker: save/load container using tgz file (tar.gz) Linx/Ubuntu: solve problem apt-get when proxy authentication is required Docker: Clean all Linux: ImageMagick: convert first page of pdf document to small jpeg preview Linux: Convert pdf to html PostgreSQL: backup/restore remote database with pg_dump Linux/Ubuntu: How to setup pptp vpn server Linux/Xubuntu: Solve HDMI disconnection caused by non-supported resolution Linux: List all users PostgreSQL: Log all queries Linux: NGINX: Setup alias Linux: ssh without password Linux: NGINX: Proxy reverse Linux: one way remote sync using unison and ssh Linux: Deleting files keeping only lastest n-files in specific folder Linux: Open ssh port access using a combination (knocking technique) Linux: Get Memory percentages. Linux: Can not sudo, unkown user root (solution) Linux: PDF: How to control pdf file size Linux: ssh login for only one user Linux: get pid of process who launch another process Linux: PHP: Fix pm.max server reached max children Linux/Ubuntu: Server: Configuring proxies Linux: Compare two files displaying differences Sox: Managing audio recording and playing Linux: VirtualBox: Explore VDI disk without running virtualbox Linux: Get machine unique ID Linux: rsync only files earlier than N days Linux: Create Virtual Filesystem Linux/Ubuntu: Server: Add disks to lvm Python/Ubuntu: connect to sqlserver and oracle Linux/Ubuntu: Share folder with Windows (via samba) Linux: Get all my local IP addresses (IPv4) Linux/Ubuntu: list ufw firewall rules without enabling it Linux/Ubuntu: Connect windows to shared folder as guest Linux: delete tons of files from folder with one command Linux/Ubuntu: Avoid connection from specific ip address without using iptables Linux: Telegram: Send telegram message to channel when user logged via ssh Linux/Ubuntu: Create barcode from command line. Linux: PHP/Python: Install python dependencies for python scripts and run scripts form php Linux/Ubuntu: nginx: Configuration to send request to another server by servername Linux/Ubuntu: Fix Imagemagick "not authorized" exception Linux/Neorouter: watchdog for neorouter connection Linux: libgdcm: Send dicom images to PACS using gdcmscu. Ubuntu: mount full rw cifs share Linux/Ubuntu: NGINX: Basic authentication access PostgreSQL: Backup/Restore database from/to postgres docker. Linux/Ubuntu: Mosquitto: Enable mqtt service Linux/PHP: Connect php7.0 with sqlserver using pdo. Linux: Detect if port is closed from command line Linux: PHP: Run shell command without waiting for output (untested) Linux/Ubuntu: OS Installation date Linux/Ubuntu: Join pdf files from command line using pdftk Linux: Get internet content from command line using wget Linux/PHP/SQL: Ubuntu/Php/Sybase: Connecting sysbase database with php7 Linux/Ubuntu: Solve LC_ALL file not found error Linux/Ubuntu: Run window program with wine headless on server Linux/Docker: List ip addresses of all containers. Linux: sysmon script (memory usage) Linux: Firebird: Create admin user from command line Linux/Firebird: Backup/Restore database Git: Update folder linux/dfm-to-json/docker Linux/Oracle/Docker: 19c-ee Linux/Docker: SQL Server in docker Linux/PHP/Docker: Run docker command from php/web Linux/Docker: Oracle 12-ee docker Linux: Oracle: Backup using expdp Linux/PHP/NGINX: Increase timeout Lazarus/Fastreport: Install on Linux Linux/Ubuntu: fswatch: watch file changes in folder Linux/Docker: SQLServer: mssql-scripter: backup/restore Linux/Ubuntu: Enable/disable screensaver Linux: SQLServer: Detect MDF version Linux/Docker: Oracle install 19c (II) on docker FirebirdSQL: Restore/Backup Linux/NGINX: Redirect to another server/port by domain name Linux/Proxmox: Enable /dev/net/tun for hamachi Linux/Ubuntu: Create sudoer user Linux: PDF-url to text without downloading pdf file Docker: Reset logs Linux: Tunnel through two hosts
Linux/PostgreSQL: Getting service uptime Lazarus/Linux: Connect to SQLServer using ZeosLib component TZConnection Linux/Oracle: Startup script PostgreSQL: backup/restore remote database with pg_dump PostgreSQL: Log all queries PostgreSQL: Create DBLINK PostgreSQL: Database replication Python/Ubuntu: connect to sqlserver and oracle Oracle/SQL: Generate range of dates PostgreSQL: Convert records to json string and revert to records PostgreSQL: Extract function DDL PostgreSQL: Backup/Restore database from/to postgres docker. Linux/PHP: Connect php7.0 with sqlserver using pdo. PostgreSQL: Trigger template PostgreSQL: Count all records in spite of using offset/limit PHP/SQL: Reverse SQL Order in sentence PostgreSQL: Filter using ilike and string with spaces PostgreSQL: Create log table and trigger Postgres: Get string all matches between {} Linux/PHP/SQL: Ubuntu/Php/Sybase: Connecting sysbase database with php7 PostgreSQL: Must know PostgreSQL: Debito, Credito, Saldo PostgreSQL: Count total rows when range is empty PostgreSQL: Extremely fast text search using tsvector PostgreSQL: Create a copy of DB in same host PHP/PostgreSQL: Event Listener Linux: Firebird: Create admin user from command line SQL: CTE Parent-child recursive Windows/Docker/Firebird: Backup remote database Linux/Firebird: Backup/Restore database PostgreSQL: Set search path (schemas) for user Firebird on Docker Firebird: Find holes in sequence (missing number) Linux/Oracle/Docker: 19c-ee Firebird: Create an Array of integers from String Oracle: Change Sys/System user pasword Oracle: Create/drop tablespace Oracle: Create User/Schema Linux: Oracle: Backup using expdp Oracle: Get slow queries Linux: SQLServer: Detect MDF version Linux/Docker: Oracle install 19c (II) on docker FirebirdSQL: Restore/Backup Firebird: Age Calculation using Ymd format Postgresql: Age calculation to Ymd Firebird: Create Range of dates, fraction in days, hours, minutes, months or years Firebird: DATE_TO_CHAR Function (like Oracle's TO_CHAR) MS SQLServer Backup Database MS SQLServer: Detect long duration SQL sentences Firebird: Fast Search Firebird: Code Generator FirebirdSQL 2.x: DATE_TO_CHAR Procedure (like Oracle's TO_CHAR) MSSQLServer: Sequences using Date based prefix format Firebird: Query to get "Range of time" based on unit: days, months, years, etc Firebird: Generate a range of numbers. Firebird 5.x: Config for D7 compatibility.
CSS: Setup a perfect wallpaper using background image CSS: Ellipsis at end of long string in element Javascript: Is my browser on line? CSS: Crossbrowser blur effect Javascript: calculating similarity % between two string Javascript: vanilla and crossbrowser event handler accepting arguments Javascript: convert native browser event to jQuery event. Linux: Convert pdf to html Javascript: Convert proper name to Capital/Title Case Javascript: Disable Back Button - (untested) HTML/CSS: Login Page HTML/CSS: Data column organized as top-down-right structure HTML/CSS: Printing page in letter size Javascript: Get file name from fullpath. HTML/CSS: Header/Body/Footer layout using flex Windows: Chrome: Avoid prompt on custom url calling by changing registry Javascript: Get filename and path from full path filename Javascript: Clone array/object. CSS + FontAwesome: Battery charging animation CSS: spin element HTML/CSS: Switch with input CSS: Transparent event clicks having translucid front div element CSS: Blurry Glass Effect CSS: Grow element size when mouse hover Javascript/jQuery: Input with fixed prefix. Javascript: ProtocolCheck Javascript: Beep Telegram: Chat/html (personal) PHP: php-imagick: Thumbnail from thispersondoesnotexists Javascript: Get host info Javascript: Vanilla async get HTML/CSS: Rotating circle loader PHP: Post JSON object to API without curl Javascript: Post data to new window. CSS/Android Ripple Effect in Pure CSS OSRM/API: OpenStreet Maps Calculate distance between points OSM/OpenLayers: Place marker on coordinates using custom image. PHP: Send compressed data to be used on Javascript (i.e. Json data) PHP/JSignature: Base30 to PNG Javascript: Query Params to JSON Javascript/CSS: Ripple Effect - Vanilla Delphi/UniGUI: Disable load animation PHP: Send basic authentication credentials Delphi: Post JSON to API PHP: Receiving Bearer Authorization token from header Linux/NGINX: Redirect to another server/port by domain name Javascript: Async/Await Web Programming: Fastest way for appending DOM elements CSS: Animated progress bar CSS: Shake element CSS: Align elements like windows explorer icons layout style Javascript: Submit JSON object from form values Javascript: Fetch POST JSON Linux: PDF-url to text without downloading pdf file Javascript/in Browser: Jump to anchor Javascript/NodeJS: Uglify js files CSS: Two of the best readable fonts in web CSS: Dark/Theater background Javascript: Detect inactivity / idle time Svelte: Dynamic component rendering CSS: Responsive Grid NGINX: Creating .htpasswd Javascript: Wait until all rendered Javascript: Print PDF directly having URL Sveltekit: Create component dynamically Sveltekit: Create component dynamically Sveltekit: Import component dynamically CSS: Animation/Gelatine CSS: Set width/height to max available HTML: Waze map embed into webpage Cordova/Mobile: App ejemplo para autenticar con huella digital. Cordova: Fingerprint auth Javascript/HTML: Detect if element if off screen
Javascript: Is my browser on line? Javascript: calculating similarity % between two string Javascript: vanilla and crossbrowser event handler accepting arguments Javascript: convert native browser event to jQuery event. Javascript: Convert proper name to Capital/Title Case Javascript: Disable Back Button - (untested) Javascript: Get file name from fullpath. Javascript: Get filename and path from full path filename Javascript: Clone array/object. Javascript/jQuery: Input with fixed prefix. Javascript: ProtocolCheck Cordova: Fix FCM plugin error Cordova: Call function at main App from inappbrowser. Javascript: Beep Telegram: Chat/html (personal) Javascript: Get host info Javascript: Vanilla async get Javascript: Post data to new window. OSM/OpenLayers: Place marker on coordinates using custom image. Javascript: Query Params to JSON Javascript/CSS: Ripple Effect - Vanilla Vanilla Javascript: Upload file Javascript: Async/Await Web Programming: Fastest way for appending DOM elements Javascript: Submit JSON object from form values Javascript: Fetch POST JSON Javascript/in Browser: Jump to anchor Javascript/NodeJS: Uglify js files Javascript: Detect inactivity / idle time Svelte: Dynamic component rendering Javascript: Change object property position Javascript: Wait until all rendered Javascript: Print PDF directly having URL Replace URL's with Links Javascript: Wait until fonts and images loaded Cordova/Mobile: App ejemplo para autenticar con huella digital. Javascript/HTML: Detect if element if off screen
substr(): It takes two arguments, the starting index and number of characters to slice. substring(): It takes two arguments, the starting index and the stopping index but it doesn't include the character at the stopping index. split(): The split method splits a string at a specified place. includes(): It takes a substring argument and it checks if substring argument exists in the string. includes() returns a boolean. If a substring exist in a string, it returns true, otherwise it returns false. replace(): takes as a parameter the old substring and a new substring. replace(): takes as a parameter the old substring and a new substring. charAt(): Takes index and it returns the value at that index indexOf(): Takes a substring and if the substring exists in a string it returns the first position of the substring if does not exist it returns -1 lastIndexOf(): Takes a substring and if the substring exists in a string it returns the last position of the substring if it does not exist it returns -1 concat(): it takes many substrings and joins them. startsWith: it takes a substring as an argument and it checks if the string starts with that specified substring. It returns a boolean(true or false). endsWith: it takes a substring as an argument and it checks if the string ends with that specified substring. It returns a boolean(true or false). search: it takes a substring as an argument and it returns the index of the first match. The search value can be a string or a regular expression pattern. match: it takes a substring or regular expression pattern as an argument and it returns an array if there is match if not it returns null. Let us see how a regular expression pattern looks like. It starts with / sign and ends with / sign. repeat(): it takes a number as argument and it returns the repeated version of the string. Concatenating array using concat indexOf:To check if an item exist in an array. If it exists it returns the index else it returns -1. lastIndexOf: It gives the position of the last item in the array. If it exist, it returns the index else it returns -1. includes:To check if an item exist in an array. If it exist it returns the true else it returns false. Array.isArray:To check if the data type is an array toString:Converts array to string join: It is used to join the elements of the array, the argument we passed in the join method will be joined in the array and return as a string. By default, it joins with a comma, but we can pass different string parameter which can be joined between the items. Slice: To cut out a multiple items in range. It takes two parameters:starting and ending position. It doesn't include the ending position. Splice: It takes three parameters:Starting position, number of times to be removed and number of items to be added. Push: adding item in the end. To add item to the end of an existing array we use the push method. pop: Removing item in the end shift: Removing one array element in the beginning of the array. unshift: Adding array element in the beginning of the array. for of loop Unlimited number of parameters in regular function Unlimited number of parameters in arrow function Expression functions are anonymous functions. After we create a function without a name and we assign it to a variable. To return a value from the function we should call the variable. Self invoking functions are anonymous functions which do not need to be called to return a value. Arrow Function Object.assign: To copy an object without modifying the original object Object.keys: To get the keys or properties of an object as an array Object.values:To get values of an object as an array Object.entries:To get the keys and values in an array hasOwnProperty: To check if a specific key or property exist in an object forEach: Iterate an array elements. We use forEach only with arrays. It takes a callback function with elements, index parameter and array itself. The index and the array optional. map: Iterate an array elements and modify the array elements. It takes a callback function with elements, index , array parameter and return a new array. Filter: Filter out items which full fill filtering conditions and return a new array reduce: Reduce takes a callback function. The call back function takes accumulator, current, and optional initial value as a parameter and returns a single value. It is a good practice to define an initial value for the accumulator value. If we do not specify this parameter, by default accumulator will get array first value. If our array is an empty array, then Javascript will throw an error every: Check if all the elements are similar in one aspect. It returns boolean find: Return the first element which satisfies the condition findIndex: Return the position of the first element which satisfies the condition some: Check if some of the elements are similar in one aspect. It returns boolean sort: The sort methods arranges the array elements either ascending or descending order. By default, the sort() method sorts values as strings.This works well for string array items but not for numbers. If number values are sorted as strings and it give us wrong result. Sort method modify the original array. use a compare call back function inside the sort method, which return a negative, zero or positive. Whenever we sort objects in an array, we use the object key to compare. Destructing Arrays : If we like to skip on of the values in the array we use additional comma. The comma helps to omit the value at that specific index
GitHub - ChrisTitusTech/winutil How to install CAB file for updates and drivers on Windows 10 - Pureinfotech launch command, longer and shorter Launching a startup program to run as administrator Install Windows Update Powershell Windows and MS office activation windows group policy update Remove all the policies applied to Edge browser. Remove ALL the Group Policy settings that are applied to your Windows system. Uninstall Edge System scan Disable Logon Background Image Automatic login in Windows 10 Windows local account password: Set to infinty This script will extract your Retail product key. ProductKey.vbs Completely Remove Windows 11 Widgets MS Edge: Use secure DNS - Undo 'browser managed by...' Move edge cache to ramdisk The Proxy Auto-Configuration: disable by changing this registry key Nilesoft Shell Disable or enable Windows 10 password expiration Install Windows 11 Without a Microsoft Account Block the W10 to 11 migration attempts or reminders Windows: Know the user Disable Hyper-V in Windows Windows: Disable 8.3 file naming convention Reduce Svchost.exe (Service Host) Process Running in Task Manager Clean Up the WinSxS Folder Prevent 3rd-party Suggested Apps from being pinned to the W11 Start Menu (NTLite) Stop UAC for a specific app Launch Edge browser with command line flags Autounattended Clean Up Component Store (WinSxS folder) Disable account expiry in windows 10 Always show all icons in sys tray Clean the WinSxS folder in Windows Remove windows start menu recommended section and setting ads Enable seconds in systray clock Win 11 Boot And Upgrade FiX KiT v5.0 Remove .cache folder from root directory Export all your drivers To safely remove one of the devices Check and disable Recall in Windows 11 Tablet-optimized taskbar Windows Defender: Disable Permanently and Re-enable Enable Windows 11 Dark Mode via registry Edge browser, backup the flags Windows PowerShell script execution: Turn on or off Microsoft store app installation on LTSC Winscript. Make Windows yours
openssh GitLab.com / GitLab Infrastructure Team / next.gitlab.com · GitLab Use Apple touch icon | webhint documentation How to get GPU Rasterization How to get GPU Rasterization Migrating to Manifest V3 - Chrome Developers Migrating to Manifest V3 - Chrome Developers Manifest - Web Accessible Resources - Chrome Developers chrome.webRequest - Chrome Developers chrome.webRequest - Chrome Developers Cross-site scripting – Wikipedia Cross-site scripting – Wikipedia Cross-site scripting – Wikipedia Cross-site scripting – Wikipedia Cross-site scripting – Wikipedia Cross-site scripting – Wikipedia Export-ModuleMember (Microsoft.PowerShell.Core) - PowerShell | Microsoft Learn Sourcegraph GraphQL API - Sourcegraph docs Winge19/vscode-abl: An extension for VS Code which provides support for the Progress OpenEdge ABL language. https://marketplace.visualstudio.com/items?itemName=chriscamicas.openedge-abl Winge19/vscode-abl: An extension for VS Code which provides support for the Progress OpenEdge ABL language. https://marketplace.visualstudio.com/items?itemName=chriscamicas.openedge-abl Winge19/vscode-abl: An extension for VS Code which provides support for the Progress OpenEdge ABL language. https://marketplace.visualstudio.com/items?itemName=chriscamicas.openedge-abl New File Cache · Actions · GitHub Marketplace Cache · Actions · GitHub Marketplace Winge19/cache: Cache dependencies and build outputs in GitHub Actions Winge19/cache: Cache dependencies and build outputs in GitHub Actions Winge19/cache: Cache dependencies and build outputs in GitHub Actions Winge19/cache: Cache dependencies and build outputs in GitHub Actions Winge19/cache: Cache dependencies and build outputs in GitHub Actions history.state during a bfcache traversal · web-platform-tests/wpt@7d60342 Configure CI/CD pipeline with YAML file - MSIX | Microsoft Learn Configure CI/CD pipeline with YAML file - MSIX | Microsoft Learn Configure CI/CD pipeline with YAML file - MSIX | Microsoft Learn Configure CI/CD pipeline with YAML file - MSIX | Microsoft Learn Configure CI/CD pipeline with YAML file - MSIX | Microsoft Learn Configure CI/CD pipeline with YAML file - MSIX | Microsoft Learn Configure CI/CD pipeline with YAML file - MSIX | Microsoft Learn Configure CI/CD pipeline with YAML file - MSIX | Microsoft Learn Configure CI/CD pipeline with YAML file - MSIX | Microsoft Learn ASP.NET Core 6.0 Blazor Server APP and Working with MySQL DB - CodeProject json Process Herpaderping – Windows Defender Evasion | Pentest Laboratories 0x7c13/Notepads: A modern, lightweight text editor with a minimalist design. Share data - UWP applications | Microsoft Learn What is ie_to_edge_bho_64.dll? What is ie_to_edge_bho_64.dll? What is ie_to_edge_bho_64.dll? What is ie_to_edge_bho_64.dll? Message passing - Chrome Developers
parsing - Parse (split) a string in C++ using string delimiter (standard C++) - Stack Overflow parsing - Parse (split) a string in C++ using string delimiter (standard C++) - Stack Overflow parsing - Parse (split) a string in C++ using string delimiter (standard C++) - Stack Overflow parsing - Parse (split) a string in C++ using string delimiter (standard C++) - Stack Overflow arrays - Convert a hexadecimal to a float and viceversa in C - Stack Overflow arrays - Convert a hexadecimal to a float and viceversa in C - Stack Overflow Why does C++ require breaks in switch statements? - Stack Overflow Why does C++ require breaks in switch statements? - Stack Overflow Why does C++ require breaks in switch statements? - Stack Overflow coding style - Switch statement fall-through...should it be allowed? - Stack Overflow performance - Convert a hexadecimal string to an integer efficiently in C? - Stack Overflow C,C++ ---结构体指针初始化_zlQ_的博客-CSDN博客_c++初始化结构体指针 c++ - C++ 返回局部变量的常引用 - SegmentFault 思否 (23条消息) C++ 去掉const_最后冰吻free的博客-CSDN博客_c++ 去掉const (23条消息) 尾置返回值类型decltype_最后冰吻free的博客-CSDN博客 (23条消息) 变参模板函数_最后冰吻free的博客-CSDN博客_变参模板 (23条消息) 变参表达式_最后冰吻free的博客-CSDN博客 (23条消息) 变参下标_最后冰吻free的博客-CSDN博客 (23条消息) 变参基类_最后冰吻free的博客-CSDN博客 (23条消息) typname 使用_最后冰吻free的博客-CSDN博客 (23条消息) 零初始化_最后冰吻free的博客-CSDN博客 (23条消息) this->使用_最后冰吻free的博客-CSDN博客 (23条消息) 变量模板_最后冰吻free的博客-CSDN博客_变量模板 (23条消息) enable_if使用_最后冰吻free的博客-CSDN博客 (23条消息) 完美转发函数_最后冰吻free的博客-CSDN博客 (23条消息) C++ 函数返回局部变量地址和引用_最后冰吻free的博客-CSDN博客_c++函数返回地址 (23条消息) C++ 函数返回局部变量地址和引用_最后冰吻free的博客-CSDN博客_c++函数返回地址 (23条消息) C++ 函数返回局部变量地址和引用_最后冰吻free的博客-CSDN博客_c++函数返回地址 结构体数组定义时初始化 cJSON的数据结构 C++共用体与结构体区别-C++ union与struct的区别-嗨客网 C++共用体与结构体区别-C++ union与struct的区别-嗨客网 队列的c语言实现_51CTO博客_c语言实现队列 栈的实现 c语言版_51CTO博客_c语言栈的实现以及操作 【专业技术】如何写出优美的C 代码? - 腾讯云开发者社区-腾讯云 【专业技术】如何写出优美的C 代码? - 腾讯云开发者社区-腾讯云 C++ short-C++短整型-C++ short取值范围-嗨客网 C++ short-C++短整型-C++ short取值范围-嗨客网 C++ long long-C++长长整型-C++ long long取值范围-嗨客网 C++ long long-C++长长整型-C++ long long取值范围-嗨客网 C++字符-C++ char-C++字符取值范围-嗨客网 C++枚举enum-C++怎么定义枚举变量-C++枚举的作用-嗨客网 C++三目运算符-C++的三目运算符-C++三目运算符怎么用-什么是三目运算符-嗨客网 C++打印乘法表-嗨客网 C++ while循环打印乘法表-嗨客网 C++ do while循环打印乘法表-嗨客网 (转)sizeof()和_countof()区别 - 榕树下的愿望 - 博客园 详述CRC校验码(附代码)-面包板社区 详述CRC校验码(附代码)-面包板社区 详述CRC校验码(附代码)-面包板社区 C program to convert Hexadecimal to Decimal - Aticleworld Conversion of Hex decimal to integer value using C language (27条消息) vector<char>太慢,自己造一个CharVector_char vector_飞鸟真人的博客-CSDN博客 C++ windows显示器相关信息获取 - 艺文笔记
Q64 Snapshot Array - LeetCode Q63 Reorganize String - LeetCode Q62 Tricky Sorting Cost | Practice | GeeksforGeeks Q62 Minimum Cost To Connect Sticks Q60 PepCoding | Longest Substring With At Most Two Distinct Characters Q59 PepCoding | Line Reflection Q58 Pairs of Non Coinciding Points | Practice | GeeksforGeeks Q57 Avoid Flood in The City - LeetCode Q56 Random Pick with Blacklist - LeetCode Q55 Insert Delete GetRandom O(1) - Duplicates allowed - LeetCode Q55 Insert Delete GetRandom O(1) - Duplicates allowed - LeetCode Q54 Insert Delete GetRandom O(1) - LeetCode Q53 The Skyline Problem - LeetCode Q52 Encode and Decode TinyURL - LeetCode Q51 Maximum Frequency Stack - LeetCode Q50 Brick Wall - LeetCode Q50 Brick Wall - LeetCode Q49 X of a Kind in a Deck of Cards - LeetCode Q48 First Unique Character in a String - LeetCode Q47 Subdomain Visit Count - LeetCode Q46 Powerful Integers - LeetCode Q45 4Sum II - LeetCode Q44 PepCoding | Quadruplet Sum QFind K Pairs with Smallest Sums - LeetCode Q43 PepCoding | Pairs With Given Sum In Two Sorted Matrices Q42 Completing tasks | Practice | GeeksforGeeks Q41 Degree of an Array - LeetCode Q-40 Can Make Arithmetic Progression From Sequence - LeetCode Q39 PepCoding | Double Pair Array Q38 Rabbits in Forest - LeetCode Q-37* Fraction to Recurring Decimal - LeetCode Q36 PepCoding | Pairs With Equal Sum Q35 PepCoding | Count Of Subarrays With Equal Number Of 0s 1s And 2s Q34 PepCoding | Longest Subarray With Equal Number Of 0s 1s And 2s Q-34PepCoding | Pairs With Equal Sum Q33 PepCoding | Count Of Subarrays With Equal Number Of Zeroes And Ones Q32 Contiguous Array - LeetCode Q31 Subarray Sums Divisible by K - LeetCode Q30 PepCoding | Longest Subarray With Sum Divisible By K Q29 Subarray Sum Equals K - LeetCode Q27 Word Pattern - LeetCode Q-26 Isomorphic Strings - LeetCode Q-25 PepCoding | Group Shifted String Q24 Group Anagrams - LeetCode Q23 Valid Anagram - LeetCode Q22 PepCoding | Find Anagram Mappings Q21 PepCoding | K Anagrams Q20 Find All Anagrams in a String - LeetCode Q-19 Binary String With Substrings Representing 1 To N - LeetCode Q-18 PepCoding | Count Of Substrings Having At Most K Unique Characters Q-17 PepCoding | Longest Substring With At Most K Unique Characters Q-16 PepCoding | Maximum Consecutive Ones - 2 Q15 PepCoding | Maximum Consecutive Ones - 1 Q-14 PepCoding | Equivalent Subarrays Q13 PepCoding | Count Of Substrings With Exactly K Unique Characters Q-12 PepCoding | Longest Substring With Exactly K Unique Characters Q-11 PepCoding | Count Of Substrings Having All Unique Characters Q-10 PepCoding | Longest Substring With Non Repeating Characters Q-9 PepCoding | Smallest Substring Of A String Containing All Unique Characters Of Itself Q8 PepCoding | Smallest Substring Of A String Containing All Characters Of Another String | leetcode76 Q-7 PepCoding | Largest Subarray With Contiguous Elements Q-6 PepCoding | Count Of All Subarrays With Zero Sum Q5 PepCoding | Largest Subarray With Zero Sum Q4 PepCoding | Count Distinct Elements In Every Window Of Size K Q-3 PepCoding | Check If An Array Can Be Divided Into Pairs Whose Sum Is Divisible By K Q2 PepCoding | Find Itinerary From Tickets Q1 PepCoding | Number Of Employees Under Every Manager 2653. Sliding Subarray Beauty
DSA 1.8 : Pointers DSA-1.8 : Pointers DSA-1.8 : Pointers DSA 1.8 : Pointers DSA 1.10 : Reference DSA 1.12 - Pointer to structure DSA 1.12 - pointer to structure DSA 1.15 : Paramter passing method : by value DSA 1.15 : Parameter passing method- by address DSA 1.15 : parameter passing method -by reference DSA 1.18 : returning array from a function DSA 1.20 : pointer to structure DSA 1.23 : monolithic program DSA 1.24 : procedural or modular programming DSA 1.25 : procedural programming using structure and functions DSA 1.26 : Object Oriented programming approach DSA 1.30 : template classes DSA 5.52 Recursion using static variable DSA 5.56 : tree recursion DSA 5.58 : Indirect recursion DSA 5.56 : Nested recursion DSA 5.68 : Taylor series using recursion DSA 5.70 : Taylors series using Horner's rule DSA 5.73 : Fibonacci using iteration DSA 5.73 : Fibonacci using recursion DSA 5.73 : Fibonacci using memoization and recursion DSA 5.75 : nCr using recursion DSA 5.76 : Tower of Hanoi DSA 7 : array ADT DSA 7.99 - Delete function in an array DSA 7.102 : Linear Search DSA 146 : C++ class for Diagonal matrix DSA 150 : Lower Triangular matrix Diagonal matrix full code Creation of sparse matrix 175. Display for linked list 176. Recursive display for linked list 178 : counting nodes in a linked list 179: sum of all elements in a linked list 181: find the largest element in the linked list 183: searching for a value in linked list 184: Improve searching in a linked list 186: Inserting a new node in a linked list (logic) 186: Insertion in a linked list (function) 189: Creating a linked list by inserting at end 191: Inserting in a sorted linked list 192: deleting a node from a linked list 195 : check if the linked list is sorted or not 197: Remove duplicates from sorted linked list
Q66 Distinct Echo Substrings - LeetCode 1316 (rabin karp rolling hash -> O(n^2)) Q66 Distinct Echo Substrings - LeetCode 1316(O(n^3) solution) Q65 Interleaving String - LeetCode 97 Q64 Frog Jump - LeetCode 403 Q63 Champagne Tower - LeetCode 799 Q62 Super Ugly Number - LeetCode 313 Q61 Ugly Number 2 - LeetCode 264 Q60 Minimum Insertion Steps to Make a String Palindrome - LeetCode 1316 Q59 Temple Offerings | Practice | GeeksforGeeks Q58 Word Break - LeetCode 139 Q57 Arithmetic Slices II - Subsequence - LeetCode 446 Q56 Arithmetic Slices - LeetCode 413 Q55 Max sum of M non-overlapping subarrays of size K - GeeksforGeeks (tabulization) Q55 Max sum of M non-overlapping subarrays of size K - GeeksforGeeks (memoization) Q54 Maximum Sum of 3 Non-Overlapping Subarrays - LeetCode 689 Q53 Maximum Sum of Two Non-Overlapping Subarrays - LeetCode 1031 Q52 Maximum difference of zeros and ones in binary string | Practice | GeeksforGeeks Q51 Mobile numeric keypad | Practice | GeeksforGeeks Q50 Distinct Transformation LeetCode Playground ( tabulization approach) Q50 - Distinct Transformation- LeetCode Playground ( recursion + memoization approach) Q49 Highway BillBoards - Coding Ninjas Codestudio approach 2 Q49 Highway BillBoards - Coding Ninjas Codestudio (approach 1 LIS) Q48 Knight Probability in Chessboard - LeetCode 688 Q47 Cherry Pickup - LeetCode 741 (recursion approach) Q46 Super Egg Drop - LeetCode 887 Q45 Predict the Winner - LeetCode 486 Q45 Optimal Strategy For A Game | Practice | GeeksforGeeks | leetcode 46 Q44 Largest Sum Subarray of Size at least K | Practice | GeeksforGeeks Q42 Maximum Subarray - LeetCode 53 Q41 Minimum Cost To Make Two Strings Identical | Practice | GeeksforGeeks Q40 Minimum ASCII Delete Sum for Two Strings - LeetCode 712 Q39 Scramble String - LeetCode 87 Q38 Edit Distance - LeetCode 72 Q37 Regular Expression Matching - LeetCode 10 Q36 Wildcard Matching - LeetCode Q35 Longest Repeating Subsequence | Practice | GeeksforGeeks Q34 Longest Common Substring | Practice | GeeksforGeeks Q33 Count Different Palindromic Subsequences - LeetCode 730 Q32 Number of distinct subsequences | Practice | GeeksforGeeks Q31 Longest Palindromic Substring - LeetCode Q30 Count Palindromic Subsequences | Practice | GeeksforGeeks Q29 Longest Palindromic Subsequence - LeetCode 516 Q28 Longest Common Subsequence - LeetCode 1143 Q27 Minimum Score Triangulation of Polygon - LeetCode 1039 Q26 Optimal binary search tree | Practice | GeeksforGeeks Q24 Matrix Chain Multiplication | Practice | GeeksforGeeks Q23 Palindrome Partitioning II - LeetCode 132 Q23 Palindrome Partitioning II - LeetCode - 132 ( n^3 approach) Q22 Palindromic Substrings - LeetCode 647 Q21 Rod Cutting | Practice | GeeksforGeeks Q20 Minimum Score Triangulation of Polygon - LeetCode 1039 Q19 Intersecting Chords in a Circle | Interviewbit Q18 Generate Parentheses - LeetCode 22 Q17 PepCoding | Count Of Valleys And Mountains Q16 Unique Binary Search Trees - LeetCode 96 Q15 Catalan Number Minimum Score of a Path Between Two Cities - 2492 Q14 Perfect Squares - LeetCode Q13 Russian Doll Envelopes - LeetCode (LIS in NlogN - accepted solution) Q13 Russian Doll Envelopes - LeetCode 354 solution1(LIS in O(n^2)) Q12 PepCoding | Maximum Non-overlapping Bridges Q11 Longest Bitonic subsequence | Practice | GeeksforGeeks Q10 Maximum sum increasing subsequence | Practice | GeeksforGeeks Q9 PepCoding | Print All Longest Increasing Subsequences Q8 Longest Increasing Subsequence - LeetCode 300 Q7 2 Keys Keyboard - LeetCode 650 Q-6 PepCoding | Print All Results In 0-1 Knapsack Q5 PepCoding | Print All Paths With Target Sum Subset Q4 PepCoding | Print All Paths With Maximum Gold Q3 PepCoding | Print All Paths With Minimum Cost Q2 Jump Game II - LeetCode 45 Q1 Maximal Square - LeetCode 221 Q67 Longest Increasing Subsequence - LeetCode 300 ( LIS O(nlogn) solution) Q43 K-Concatenation Maximum Sum - LeetCode 1191 Q13 Russian Doll Envelopes - LeetCode 354 ( LIS -> O(nlogn) solution) Q25 Burst Balloons - LeetCode 312
Minimum Score of a Path Between Two Cities - 2492 Number of Operations to Make Network Connected - 1319 Q42 Mother Vertex | Interviewbit (kosraju) Q41 Count Strongly Connected Components (Kosaraju’s Algorithm) Q40 Leetcode 734. Sentence Similarity Q39 Satisfiability of Equality Equations - LeetCode 990 Q38 Redundant Connection II - LeetCode 685 Q37 Redundant Connection - LeetCode 684 Q36 Minimize Malware Spread II - LeetCode 928 Q35 Minimize Malware Spread - LeetCode 924 Q34 Accounts Merge - LeetCode 721 Q33 Minimize Hamming Distance After Swap Operations - LeetCode 1722 Q32 Rank Transform of a Matrix - LeetCode 1632 Q32 Reconstruct Itinerary - leetcode 332 (eularian path && Eularian cycle) Q31 Regions Cut By Slashes - LeetCode 959 Q30 Minimum Spanning Tree | Practice | GeeksforGeeks (kruskal algo) Q29 Number of Islands II - Coding Ninjas (DSU) Q28 Remove Max Number of Edges to Keep Graph Fully Traversable - LeetCode 1579 Q27 Checking Existence of Edge Length Limited Paths - LeetCode 1675 Q26 Network Delay Time - LeetCode 743 Q25 Cheapest Flights Within K Stops - LeetCode 787 Q24 Distance from the Source (Bellman-Ford Algorithm) | Practice | GeeksforGeeks Q23 Connecting Cities With Minimum Cost - Coding Ninjas Q22 Swim in Rising Water - LeetCode 778 Q21 Water Supply In A Village - Coding Ninjas Q20 Minimum Spanning Tree | Practice | GeeksforGeeks(prims algo) Q19 Alien Dictionary | Practice | GeeksforGeeks Q18 Course Schedule - LeetCode 207 (kahn's algorithm) Q17 Minimum edges(0-1 BFS) | Practice | GeeksforGeeks Q17 Minimum edges(0-1 BFS) | Practice | GeeksforGeeks ( using djikstra) Q16 Sliding Puzzle - LeetCode 773 Q15 Bus Routes - LeetCode 815 Q14 Shortest Bridge - LeetCode 934 (without pair class) Q14 Shortest Bridge - LeetCode 934 ( with pair class) Q 13 As Far from Land as Possible - LeetCode 1120 Q12 Rotting Oranges - LeetCode 994 Q11 01 Matrix - LeetCode 542 Q10 Number of Distinct Islands | Practice | GeeksforGeeks Q9 Number of Enclaves - LeetCode 1085 Q8 Coloring A Border - LeetCode 1034 Q7 Unique Paths II - 63 Q6 Unique Paths III - LeetCode 980 Q5 Number of Provinces - LeetCode 547 Q4 Number of Islands - LeetCode 200 Q3 Number of Operations to Make Network Connected - LeetCode 1319 Q2 All Paths From Source to Target - LeetCode 797 Q1 Find if Path Exists in Graph - LeetCode 1971 Q43 is Cycle present in DAG ? GFG ( Topological Sort) Q43 is cycle present in DAG ? GFG (using kahns algo) Q44 Bellman ford | GFG ( Smaller code) Q45 Minimum Cost to Make at Least One Valid Path in a Grid - LeetCode 1368
descargar archivos de la plantilla avanzada desde la consola formulario dinamico yii2 codigo para descargarlo actualizar composer 2.5.5 subir imagenes en frontend/backend ejemplo yii2 validacion mascara de campo ejemplo models/modelo.php validacion mascara de campo ejemplo models/modelo.php aplicación yii2 completa ejemplo de referencia puede que contenga algún error modificar grid(centrar contenido de campos, textos) formato fecha yii2 ejemplo de widget DepDrop en yii2 Configurar la extension redactor en common/config/main.php ejemplo de campo de texto con mascara ejemplo de reglas de validacion en un modelo Adapta el contenido de un campo de widget a su tamaño en Yii2 Para hacer que el contenido de un campo de un widget en Yii2 se adapte automáticamente al tamaño del campo, puedes utilizar algunas de las siguientes técnicas: para hacer el modulo de usuario en yii2 campo ente del sistema de de indicadores sig ejemplo de campo ente con permisologia de usuario: admin y despacho_ ministro ejemplo de escript para estos campos en yii2 ejemplo estado, municipio, parroquia ejemplo de campo estado, municipio, parroquia en el model search usar esto para mostrar los registros en orden invertido cuando un formulario de problemas en un marco en yii2 ejemplo de campo da data para mostrar la lista en el update codigo para arreglar graficos hightchart del sistema indicadores sig para ordenar los registros del gridview en yii2 otro editor de texto como redactor para implementar de manera manual
chapter2-code-1 chapter2-code-2 chapter2-code-3 chapter3-code-1 chapter4-code-1 chapter4-code-2 chapter4-code-3 chapter4-code-4 chapter4-code-5 chapter4-code-6 chapter4-code-7 chapter4-code-8 chapter4-code-9 chapter4-code-10 chapter5-code-1 chapter5-code-2 chapter5-code-3 chapter6-code-1 chapter6-code-2 chapter6-code-3 chapter7-code-1 chapter7-code-2 chapter7-code-3 chapter7-code-4 chapter7-code-5 chapter7-code-6 chapter7-code-7 chapter7-code-8 chapter7-code-9 chapter7-code-10 chapter7-code-11 chapter7-code-12 chapter7-code-13 chapter8-code-1 chapter8-code-2 chapter8-code-3 chapter8-code-4 chapter9-code-1 chapter9-code-2 chapter9-code-3 chapter9-code-4 chapter10-code-1 chapter10-code-2 chapter10-code-3 chapter10-code-4 chapter10-code-5 chapter11-code-1 chapter11-code2 chapter11-code-3 chapter11-code-4 chapter11-code-5 chapter11-code-6 chapter12-code-1 chapter12-code-2 chapter13-code-1 chapter13-code-2 chapter13-code-3 chapter13-code-4 chapter13-code-5 chapter14-code-1 chapter14-code-2 chapter14-code-3 chapter14-code-4 chapter15-code-1 chapter15-code-2 chapter16-code-1 chapter16-code-2 chapter16-code-3 chapter16-code-4 chapter16-code-5 chapter16-code-6 chapter16-code-7 chapter16-code-8 chapter16-code-9 chapter16-code-10 chapter17-code-1 chapter17-code-2 chapter18-code-1 chapter18-code-2 chapter18-code-3 chapter18-code-4 chapter18-code-5 chapter19-code-1 chapter19-code-2 chapter19-code-3 chapter20-code-1 chapter21-code-1 chapter21-code-2 chapter21-code-3 chapter21-code-4 chapter21-code-5 chapter22-code-1 chapter22-code-2 chapter22-code-3 chapter23-code-1 chapter23-code-2 chapter23-code-3 chapter23-code-4 chapter23-code-5 chapter24-code-1 chapter24-code-2 chapter24-code-3 chapter25-code-1 chapter25-code-2 chapter25-code-3 chapter25-code-4 chapter25-code-5 chapter25-code-6 chapter25-code-7 chapter25-code-8 chapter25-code-9 chapter26-code-1 chapter26-code-2 chapter27-code-1 chapter27-code-2 chapter28-code-1 chapter28-code-2 chapter29-code-1 chapter11-code-2 chapter1-code-1