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",