From 00089ed589292e3c54fb2ab45bf64542c6d79fc7 Mon Sep 17 00:00:00 2001
From: HamdiBenK <78487156+HamdiBenK@users.noreply.github.com>
Date: Thu, 14 Sep 2023 16:15:12 +0100
Subject: [PATCH] Design maximum participation (#524)

* design maximum participation with the switch check condition

* onInputChange

* onInputChange wip

* progress passing limit to saveForm

---------

Co-authored-by: kachourihassen <hassen.kachouri@esprit.tn>
---
 src/app/campaigns/campaigns.module.ts         |  5 +-
 .../draft-maximum-participation.component.css | 67 +++++++++++++
 ...draft-maximum-participation.component.html | 58 +++++++++++
 ...ft-maximum-participation.component.spec.ts | 25 +++++
 .../draft-maximum-participation.component.ts  | 96 +++++++++++++++++++
 .../edit-campaign.component.html              | 10 +-
 .../edit-campaign/edit-campaign.component.ts  |  7 +-
 .../password-modal.component.ts               |  3 +-
 .../remuneration/remuneration.component.ts    |  3 +-
 .../services/draft-campaign.service.ts        |  1 +
 .../campaigns/services/format-data.service.ts |  4 +-
 .../core/campaigns-list-response.interface.ts |  1 +
 src/app/models/campaign.model.ts              |  3 +-
 src/assets/i18n/en.json                       |  3 +
 src/assets/i18n/fr.json                       |  3 +
 15 files changed, 280 insertions(+), 9 deletions(-)
 create mode 100644 src/app/campaigns/components/draft-maximum-participation/draft-maximum-participation.component.css
 create mode 100644 src/app/campaigns/components/draft-maximum-participation/draft-maximum-participation.component.html
 create mode 100644 src/app/campaigns/components/draft-maximum-participation/draft-maximum-participation.component.spec.ts
 create mode 100644 src/app/campaigns/components/draft-maximum-participation/draft-maximum-participation.component.ts

diff --git a/src/app/campaigns/campaigns.module.ts b/src/app/campaigns/campaigns.module.ts
index 4fc7ce4e7..2c907b1e7 100755
--- a/src/app/campaigns/campaigns.module.ts
+++ b/src/app/campaigns/campaigns.module.ts
@@ -18,7 +18,7 @@ import { ParticiperComponent } from '@app/campaigns/components/participer/partic
 import { PasswordModalComponent } from './components/password-modal/password-modal.component';
 import { TransactionMessageStatusComponent } from '@app/campaigns/components/transaction-message-status/transaction-message-status.component';
 import { NgxTweetModule } from 'ngx-tweet';
-
+import { DraftMaximumParticipationComponent } from '@app/campaigns/components/draft-maximum-participation/draft-maximum-participation.component';
 import { CampaignsSharedUiModule } from './campaigns-shared-ui.module';
 import { NpnSliderModule } from 'npn-slider';
 import { EffectsModule } from '@ngrx/effects';
@@ -56,7 +56,8 @@ import { SharedModule } from '@app/shared/shared.module';
     TransactionMessageStatusComponent,
     MissionsComponent,
     DraftPictureComponent,
-    SocialsComponent
+    SocialsComponent,
+    DraftMaximumParticipationComponent
   ],
   imports: [
     CommonModule,
diff --git a/src/app/campaigns/components/draft-maximum-participation/draft-maximum-participation.component.css b/src/app/campaigns/components/draft-maximum-participation/draft-maximum-participation.component.css
new file mode 100644
index 000000000..75d4cfd4a
--- /dev/null
+++ b/src/app/campaigns/components/draft-maximum-participation/draft-maximum-participation.component.css
@@ -0,0 +1,67 @@
+.title-section {
+    font-style: normal;
+    font-weight: bold;
+    font-size: 22px;
+    line-height: 28px;
+    text-align: center;
+    letter-spacing: 1px;
+    color: #1f2337;
+}
+.label-type {
+    font-style: normal;
+    font-family: 'Poppins';
+    font-weight: 400;
+    font-size: 16px;
+    line-height: 125.9%;
+    letter-spacing: 0.03em;
+    color: #75758f;
+    text-align: center;
+    display: flow;
+    margin-bottom: 25px !important;
+}
+.small-label {
+    font-style: normal;
+    font-weight: 600;
+    font-size: 14px;
+    line-height: 125.9%;
+    letter-spacing: 0.03em;
+    color: #75758F;
+    padding-left: 170px !important;
+}
+.styleForInputMedia {
+    border-style: solid;
+    border-radius: 30px;
+    border-color: #D6D6E8;
+    outline: none;
+    width: 40%;
+    height: 150%;
+    text-align: start;
+    color: #323754;
+    margin-right: 30px !important;
+    position: relative;
+    bottom: 0.4em;
+    text-align: center;
+  }
+
+  /* .cont{
+    display : flex;
+    flex-direction: row;
+  } */
+
+  .test{    display: flex;    justify-content: flex-end;}
+  .left {    flex-grow: 1; }    
+  .right {        display: flex;        align-items: center;     }
+
+
+  /* Hide the spinner controls for number input */
+input[type="number"]::-webkit-inner-spin-button,
+input[type="number"]::-webkit-outer-spin-button {
+  -webkit-appearance: none;
+  appearance: none;
+  margin: 0;
+}
+
+/* Hide the spinner controls for Firefox */
+input[type="number"] {
+  -moz-appearance: textfield;
+}
diff --git a/src/app/campaigns/components/draft-maximum-participation/draft-maximum-participation.component.html b/src/app/campaigns/components/draft-maximum-participation/draft-maximum-participation.component.html
new file mode 100644
index 000000000..d3cb1fccc
--- /dev/null
+++ b/src/app/campaigns/components/draft-maximum-participation/draft-maximum-participation.component.html
@@ -0,0 +1,58 @@
+<div class="col-12 px-3 padding-small-version">
+      <div
+        class="col-md-12 col-lg-12 col-xs-12 d-flex justify-content-center pt-4"
+      >
+        <label class="title-section">
+        {{ 'campaign.title_maximum_participation' | translate }}</label>
+      </div>
+      <div class="row d-flex justify-content-center pt-3 mb-1">
+        <div class="col-xl-10 col-md-12 col-lg-10 col-sm-12">
+          <label class="label-type">{{
+            'campaign_sous_titre_max_part' | translate
+          }}</label>
+        </div>
+      </div>
+      <div class="row d-flex">
+        <!-- <div class="cont col-xl-10 col-md-12 col-lg-10 col-sm-12">
+          <p class="small-label mb-0">{{ 'campaign_descrip-max-partic' | translate }}</p>
+          <input
+          type="text"
+          class="styleForInputMedia"
+          aria-describedby="basic-addon2"
+          style="float: right;"
+         
+         
+        />
+          <ui-switch
+                  id="toggle"
+                  
+                >
+                </ui-switch>
+        </div> -->
+
+
+        <div class="test">  
+              <div class="left col-xl-10 col-md-12 col-lg-10 col-sm-12">
+                    <p class="par small-label mb-0">{{ 'campaign_descrip-max-partic' | translate }}</p> 
+                  </div> 
+                     <div class="right">  
+                        <input
+                        type="number"
+                        pattern="[0-9]*"
+                        inputmode="numeric"
+                        class="styleForInputMedia"
+                        aria-describedby="basic-addon2"
+                        [disabled]="!toggleSwitch.checked"
+                        [placeholder]="toggleSwitch.checked ? '' : '-'"  
+                        [(ngModel)]="inputValue"
+                        (input)="onInputChanged()"
+                      />
+                      <ui-switch
+                        id="toggle"
+                        class="button"
+                        #toggleSwitch
+                        (change)="onInputChanged()"
+                      ></ui-switch>
+                      
+                      
+</div>
diff --git a/src/app/campaigns/components/draft-maximum-participation/draft-maximum-participation.component.spec.ts b/src/app/campaigns/components/draft-maximum-participation/draft-maximum-participation.component.spec.ts
new file mode 100644
index 000000000..dd4a762f9
--- /dev/null
+++ b/src/app/campaigns/components/draft-maximum-participation/draft-maximum-participation.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { DraftMaximumParticipationComponent } from './draft-maximum-participation.component';
+
+describe('DraftMaximumParticipationComponent', () => {
+  let component: DraftMaximumParticipationComponent;
+  let fixture: ComponentFixture<DraftMaximumParticipationComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [ DraftMaximumParticipationComponent ]
+    })
+    .compileComponents();
+  });
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(DraftMaximumParticipationComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/campaigns/components/draft-maximum-participation/draft-maximum-participation.component.ts b/src/app/campaigns/components/draft-maximum-participation/draft-maximum-participation.component.ts
new file mode 100644
index 000000000..10c472458
--- /dev/null
+++ b/src/app/campaigns/components/draft-maximum-participation/draft-maximum-participation.component.ts
@@ -0,0 +1,96 @@
+import { Component, OnInit,ViewChild,Output,EventEmitter,OnDestroy } from '@angular/core';
+import { UntypedFormArray, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms';
+import { ActivatedRoute } from '@angular/router';
+import { CampaignHttpApiService } from '@core/services/campaign/campaign.service';
+import { debounceTime, distinctUntilChanged, switchMap, takeUntil, tap } from 'rxjs/operators';
+import { Subject,Subscription } from 'rxjs';
+import { DraftCampaignService } from '@campaigns/services/draft-campaign.service';
+
+
+@Component({
+  selector: 'app-draft-maximum-participation',
+  templateUrl: './draft-maximum-participation.component.html',
+  styleUrls: ['./draft-maximum-participation.component.css']
+})
+
+
+export class DraftMaximumParticipationComponent implements OnInit {
+   
+  private inputValueSubject = new Subject<string>();
+  private inputValueSubscription: Subscription | undefined;
+  private isDestroyed = new Subject();
+
+  form = new UntypedFormGroup({
+    limitParticipation: new UntypedFormControl(0, [Validators.required])
+  });
+  constructor(private route: ActivatedRoute, 
+              private campaignService: CampaignHttpApiService,
+              private service :DraftCampaignService) {
+
+                this.form = new UntypedFormGroup(
+                  {
+                    limitParticipation: new UntypedFormControl(0, {
+                      validators: Validators.required
+                    }),
+                  }
+                );
+  }
+  id! :any;
+  ngOnInit(): void {
+      // Accessing the ID parameter from the URL
+  this.id = this.route.snapshot.paramMap.get('id');
+
+  // Set up the subscription
+  this.inputValueSubscription = this.inputValueSubject
+    .pipe(
+      debounceTime(1000), // Adjust the debounce time as needed (in milliseconds)
+      switchMap((value) => {
+        console.log('SwitchMap triggered with value:', value);
+
+        return this.saveParticipantsNumber(+value)}
+        ) // Convert value to a number and send the API request
+    )
+    .subscribe();
+
+  }
+  inputValue: string = '';
+  @ViewChild('toggleSwitch', { static: true }) toggleSwitch: any;
+onInputChanged() {
+  console.log('Emitting value:', this.inputValue);
+    this.saveForm();
+    !this.toggleSwitch.checked && (this.inputValue = '');  
+    this.form.get('limitParticipation')?.setValue(this.inputValue || 0)
+    return this.campaignService.updateOneById({limitParticipation :this.inputValue || 0},this.id).subscribe((data : any)=>{console.log(data);})
+  }
+
+  private saveParticipantsNumber(limit : number){
+ console.log('here')
+    return this.campaignService.updateOneById({limit},this.id)
+  }
+
+
+  saveForm() {
+    this.form.valueChanges
+      .pipe(
+        debounceTime(500),
+        tap((values: any) => {
+          if (this.id) {
+            this.service.autoSaveFormOnValueChanges({
+              formData: values,
+              id: this.id
+            });
+          }
+        }),
+        takeUntil(this.isDestroyed)
+      )
+      .subscribe();
+  }
+
+  ngOnDestroy(): void {
+    if (this.inputValueSubscription) {
+      this.inputValueSubscription.unsubscribe();
+    }
+    this.isDestroyed.next('');
+    this.isDestroyed.unsubscribe();
+  }
+}
diff --git a/src/app/campaigns/components/edit-campaign/edit-campaign.component.html b/src/app/campaigns/components/edit-campaign/edit-campaign.component.html
index 4db0f23a3..1a67f929d 100755
--- a/src/app/campaigns/components/edit-campaign/edit-campaign.component.html
+++ b/src/app/campaigns/components/edit-campaign/edit-campaign.component.html
@@ -67,7 +67,15 @@
     <div class="row w-100 justify-content-center">
       <hr class="horizontal-line" />
     </div>
-
+   <!------------------------- bloc maximum participation----------------------->
+   <div class="div-block mt-3 pb-3">
+    <app-draft-maximum-participation
+    ></app-draft-maximum-participation>
+  
+  </div>
+  <div class="row w-100 justify-content-center">
+    <hr class="horizontal-line" />
+  </div>
     <!------------------------- bloc kit----------------------->
     <div class="div-block">
       <app-draft-campaign-kit
diff --git a/src/app/campaigns/components/edit-campaign/edit-campaign.component.ts b/src/app/campaigns/components/edit-campaign/edit-campaign.component.ts
index 45782a387..b78fd9efd 100755
--- a/src/app/campaigns/components/edit-campaign/edit-campaign.component.ts
+++ b/src/app/campaigns/components/edit-campaign/edit-campaign.component.ts
@@ -46,7 +46,7 @@ import { TokenStorageService } from '@core/services/tokenStorage/token-storage-s
 import { IApiResponse } from '@app/core/types/rest-api-responses';
 import { ICampaignResponse } from '@app/core/campaigns-list-response.interface';
 import { environment } from '@environments/environment';
-
+import { DraftMaximumParticipationComponent} from '../draft-maximum-participation/draft-maximum-participation.component';
 enum FormStatus {
   Saving = 'saving',
   Saved = 'saved',
@@ -474,4 +474,9 @@ export class EditCampaignComponent implements OnInit, OnDestroy {
       this.modalService.dismissAll(this.useDesktopModal);
     }
   }
+
+  handleInputValueChange(value:any){
+    console.log({value},this.campaignData)
+    this.campaignData.limitParticipation = value;
+  }
 }
diff --git a/src/app/campaigns/components/password-modal/password-modal.component.ts b/src/app/campaigns/components/password-modal/password-modal.component.ts
index bec981b86..bc02ef7c1 100755
--- a/src/app/campaigns/components/password-modal/password-modal.component.ts
+++ b/src/app/campaigns/components/password-modal/password-modal.component.ts
@@ -157,7 +157,8 @@ export class PasswordModalComponent implements OnInit {
       _campaign.pass = this.passwordForm.get('password')?.value;
       /*
       _campaign.ERC20token = ListTokens[this.campaign.currency.name].contract;
-*/
+*/ 
+      _campaign.limit = this.campaign.limitParticipation;
       _campaign.amount = this.campaign?.initialBudget;
       switch (ListTokens[this.campaign.currency.name].type) {
         case 'bep20': {
diff --git a/src/app/campaigns/components/remuneration/remuneration.component.ts b/src/app/campaigns/components/remuneration/remuneration.component.ts
index dfa1cd549..1fbe0f6d4 100755
--- a/src/app/campaigns/components/remuneration/remuneration.component.ts
+++ b/src/app/campaigns/components/remuneration/remuneration.component.ts
@@ -244,7 +244,6 @@ export class RemunerationComponent implements OnInit, OnDestroy {
   
 
   ngOnInit(): void {
-    
     this.cdref.markForCheck();
     this.parentFunction().subscribe();
     this.getUserCrypto();
@@ -1323,7 +1322,7 @@ export class RemunerationComponent implements OnInit, OnDestroy {
       let x: number = +(this.amountUsd.includes(',')
         ? this.amountUsd.replaceAll(',', '')
         : this.amountUsd);
-      if (x <= this.selectedCryptoDetails.total_balance.toFixed(2)) {
+      if (x <= this.selectedCryptoDetails?.total_balance?.toFixed(2)) {
         this.insufficientBalance = false;
       } else {
         this.insufficientBalance = true;
diff --git a/src/app/campaigns/services/draft-campaign.service.ts b/src/app/campaigns/services/draft-campaign.service.ts
index c73d2a92d..cedc1e797 100755
--- a/src/app/campaigns/services/draft-campaign.service.ts
+++ b/src/app/campaigns/services/draft-campaign.service.ts
@@ -84,6 +84,7 @@ export class DraftCampaignService implements OnDestroy {
         const formData = this.formatData.manipulateDataBeforeSend({
           ...campaignData
         });
+
         return { formData, id: values.id };
       }),
       switchMap((values: any) => {
diff --git a/src/app/campaigns/services/format-data.service.ts b/src/app/campaigns/services/format-data.service.ts
index 59286ddd6..1b74b63fe 100755
--- a/src/app/campaigns/services/format-data.service.ts
+++ b/src/app/campaigns/services/format-data.service.ts
@@ -71,6 +71,9 @@ export class FormatDataService {
     if (campaign.hasOwnProperty('startDate')) {
       object.startDate = new Date(campaign.startDate).getTime() / 1000;
     }
+
+    campaign.limitParticipation && (object.limit = campaign.limitParticipation)
+
     if (campaign.hasOwnProperty('remuneration')) {
       // TODO: fix remuneration not sent to backend
       object.remuneration = campaign.remuneration;
@@ -145,7 +148,6 @@ export class FormatDataService {
     if (campaign.hasOwnProperty('missions')) {
       object.missions = campaign.missions;
     }
-
     return object;
   }
 
diff --git a/src/app/core/campaigns-list-response.interface.ts b/src/app/core/campaigns-list-response.interface.ts
index ab6cfbcb3..c0d89c556 100755
--- a/src/app/core/campaigns-list-response.interface.ts
+++ b/src/app/core/campaigns-list-response.interface.ts
@@ -47,4 +47,5 @@ export interface ICampaignResponse {
   file?: string;
   urlPicUser?: string;
   missions?: [];
+  limit:number;  
 }
diff --git a/src/app/models/campaign.model.ts b/src/app/models/campaign.model.ts
index 97c229dd2..691809b23 100755
--- a/src/app/models/campaign.model.ts
+++ b/src/app/models/campaign.model.ts
@@ -38,6 +38,7 @@ export class Campaign {
   ownerId: string;
   urlPicUser: any;
   type: string;
+  limitParticipation:number;
   tokenStorageService!: TokenStorageService;
   missions: [];
   isOwnedByUser = false;
@@ -49,7 +50,7 @@ export class Campaign {
     this.ownerId = data?.idNode || '';
     this.initialBudget = data?.cost || '0';
     this.initialBudgetInUSD = data?.cost_usd || '0';
-
+    this.limitParticipation = data?.limit || 0;
     this.budget = data?.funds
       ? (data?.funds[1] as string)
       : data?.remaining
diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json
index 29e18df78..2634c8cb0 100755
--- a/src/assets/i18n/en.json
+++ b/src/assets/i18n/en.json
@@ -314,9 +314,11 @@
   "campaign.likes": "Like",
   "campaign.shares": "Share",
   "campaign.kit_de_campagne": "AdPool Kit",
+  "campaign.title_maximum_participation": "Maximum participation",
   "campaign_password.confirm": "confirm",
   "campaign.duration": "Duration",
   "campaign.ajout_desc": "Provide documents (audio, video, texts, photos) to your creators to enable them to carry out their missions in the best possible conditions.",
+  "campaign_descrip-max-partic":"Insert the maximum number of participations per individual",
   "campaign.ajout_description": "8MB per file maximum. Maximum 5 files.",
   "campaign.InsufficientBudget": "Insufficient budget",
   "campaign.minfollowers_must_maxfollowers": "Max Followers should be greater than min Followers",
@@ -500,6 +502,7 @@
   "campaign_details.no_summary_msg": "No summary yet!! please add one.",
   "campaign_details.no_description_msg": "No description yet!! please add one.",
   "campaign_details.add_files": "ADD FILES",
+  "campaign_sous_titre_max_part": "Customize participant engagement by setting a maximum limit participation numbers.",
   "campaign_details.add_new_links": "ADD LINKS",
   "campaign_details.campaign_duration_text": "Campaign duration",
   "campaign_details.campaign_kit": "Adpool Kit",
diff --git a/src/assets/i18n/fr.json b/src/assets/i18n/fr.json
index 98631bbac..293e8ac0c 100755
--- a/src/assets/i18n/fr.json
+++ b/src/assets/i18n/fr.json
@@ -634,6 +634,7 @@
   "campaign.shares": "Partages",
   "campaign.reach": "Atteindre le maximum",
   "campaign.kit_de_campagne": "AdPool Kit",
+  "campaign.title_maximum_participation": "Participation maximale",
   "campaign_password.confirm": "confirmer",
   "campaign.edit_page_title": "Editer campagne",
   "campaign.duration_placeholder": "Jours",
@@ -739,6 +740,8 @@
   "campaign_details.no_description_msg": "Pas encore de description !! veuillez en ajouter un.",
   "reject_link": "Rejeté !",
   "campaign_details.add_files": "Ajouter des fichiers",
+  "campaign_sous_titre_max_part": "Personnalisez l’engagement des participants en définissant un nombre maximal de participants.",
+  "campaign_descrip-max-partic":"Insérer le nombre maximum de participations par individu",
   "campaign_details.add_new_links": "Ajouter des Liens",
   "campaign_details.campaign_duration_text": "Durée de la campagne",
   "campaign_details.campaign_kit": "Kit AdPool",