Commit 5bea1a96 authored by Emiliano Balbuena's avatar Emiliano Balbuena
Browse files

(feat): Show warning when campaign cannot be fulfilled

parent f202d154
Loading
Loading
Loading
Loading
+12 −0
Original line number Original line Diff line number Diff line
@@ -344,6 +344,18 @@ m-app {
      font-size: 16px;
      font-size: 16px;
      font-weight: bold;
      font-weight: bold;
      padding: 4px;
      padding: 4px;

      .m-tooltip i.material-icons {
        vertical-align: middle;
        font-size: 1em;
        margin-left: 8px;
        position: relative;
        top: -1px;

        @include m-theme(){
          color: themed($m-red);
        }
      }
    }
    }
  }
  }


+15 −1
Original line number Original line Diff line number Diff line
import { Injectable } from '@angular/core';
import { Injectable } from '@angular/core';
import { Campaign, CampaignDeliveryStatus, CampaignPayment, CampaignType } from './campaigns.type';
import { Campaign, CampaignDeliveryStatus, CampaignPayment, CampaignPreview, CampaignType } from './campaigns.type';
import { Client } from '../../../services/api/client';
import { Client } from '../../../services/api/client';
import isInt from '../../../helpers/is-int';
import isInt from '../../../helpers/is-int';
import getGuidFromUrn from '../../../helpers/get-guid-from-urn';
import getGuidFromUrn from '../../../helpers/get-guid-from-urn';
@@ -84,6 +84,20 @@ export class CampaignsService {
      !!campaign.type;
      !!campaign.type;
  }
  }


  async preview(campaign: Campaign): Promise<CampaignPreview | false> {
    if (
      !campaign.type ||
      !campaign.entity_urns ||
      !campaign.entity_urns.length ||
      !campaign.budget ||
      !campaign.budget_type
    ) {
      return false;
    }

    return (await this.client.post(`api/v2/boost/campaigns/preview`, campaign) as any).preview;
  }

  async prepare(campaign: Campaign): Promise<{ checksum: string, guid: string }> {
  async prepare(campaign: Campaign): Promise<{ checksum: string, guid: string }> {
    if (campaign.entity_urns.length !== 1) {
    if (campaign.entity_urns.length !== 1) {
      throw new Error('Campaigns without a single entity are unsupported');
      throw new Error('Campaigns without a single entity are unsupported');
+4 −0
Original line number Original line Diff line number Diff line
@@ -40,3 +40,7 @@ export type CampaignPayment = {
  txHash: string,
  txHash: string,
  amount: number,
  amount: number,
};
};

export type CampaignPreview = {
  cannot_fulfill_daily?: boolean,
};
+33 −10
Original line number Original line Diff line number Diff line
@@ -24,7 +24,13 @@
        <m-tooltip icon="help" i18n>TBD</m-tooltip>
        <m-tooltip icon="help" i18n>TBD</m-tooltip>
      </label>
      </label>


      <input id="boost-creator__name" type="text" name="name" [(ngModel)]="campaign.name">
      <input
        id="boost-creator__name"
        type="text"
        name="name"
        [ngModel]="campaign.name"
        (ngModelChange)="campaign.name = $event; triggerPreview()"
      >
    </div>
    </div>


    <div class="m-form--field">
    <div class="m-form--field">
@@ -41,7 +47,7 @@
          [class.m-btn--readonly]="isEditing"
          [class.m-btn--readonly]="isEditing"
          [for]="'type__' + type.id"
          [for]="'type__' + type.id"
          [tabindex]="!type.disabled ? 0 : -1"
          [tabindex]="!type.disabled ? 0 : -1"
          (keydown.space)="campaign.type = type.id"
          (keydown.space)="campaign.type = type.id; triggerPreview()"
        >
        >
          <input
          <input
            type="radio"
            type="radio"
@@ -49,7 +55,7 @@
            [value]="type.id"
            [value]="type.id"
            [checked]="type.id === campaign.type"
            [checked]="type.id === campaign.type"
            [disabled]="isEditing || type.disabled"
            [disabled]="isEditing || type.disabled"
            (change)="campaign.type = type.id"
            (change)="campaign.type = type.id; triggerPreview()"
            name="type"
            name="type"
          >
          >


@@ -84,7 +90,8 @@
      <m-boost-campaigns-creator--content-selector
      <m-boost-campaigns-creator--content-selector
        *ngIf="campaign.type === 'newsfeed' || campaign.type === 'content'"
        *ngIf="campaign.type === 'newsfeed' || campaign.type === 'content'"
        [type]="campaign.type"
        [type]="campaign.type"
        [(content)]="campaign.entity_urns"
        [content]="campaign.entity_urns"
        (contentChange)="campaign.entity_urns = $event; triggerPreview()"
        [readonly]="isEditing"
        [readonly]="isEditing"
      ></m-boost-campaigns-creator--content-selector>
      ></m-boost-campaigns-creator--content-selector>
    </div>
    </div>
@@ -109,8 +116,8 @@
          buttonClass="m-boost-campaigns-creator--hashtag-selector-button"
          buttonClass="m-boost-campaigns-creator--hashtag-selector-button"
          [useContent]="true"
          [useContent]="true"
          [tags]="campaign.hashtags"
          [tags]="campaign.hashtags"
          (tagsAdded)="onTagsAdded($event)"
          (tagsAdded)="onTagsAdded($event); triggerPreview()"
          (tagsRemoved)="onTagsRemoved($event)"
          (tagsRemoved)="onTagsRemoved($event); triggerPreview()"
        >
        >
          <i class="material-icons">check</i>
          <i class="material-icons">check</i>
        </m-hashtags-selector>
        </m-hashtags-selector>
@@ -124,7 +131,12 @@
          <m-tooltip icon="help" i18n>TBD</m-tooltip>
          <m-tooltip icon="help" i18n>TBD</m-tooltip>
        </label>
        </label>


        <div class="m-date-selector--input" mdl-datetime-picker [date]="campaign.start" (dateChange)="onStartDateChange($event)">
        <div
          class="m-date-selector--input"
          mdl-datetime-picker
          [date]="campaign.start"
          (dateChange)="onStartDateChange($event); triggerPreview()"
        >
          <input id="boost-creator__start" type="text" placeholder="Now" i18n [value]="campaign.start | date:'MMM dd'" readonly>
          <input id="boost-creator__start" type="text" placeholder="Now" i18n [value]="campaign.start | date:'MMM dd'" readonly>
          <i class="material-icons">keyboard_arrow_down</i>
          <i class="material-icons">keyboard_arrow_down</i>
        </div>
        </div>
@@ -136,7 +148,11 @@
          <m-tooltip icon="help" i18n>TBD</m-tooltip>
          <m-tooltip icon="help" i18n>TBD</m-tooltip>
        </label>
        </label>


        <div class="m-date-selector--input" mdl-datetime-picker [date]="campaign.end" (dateChange)="onEndDateChange($event)">
        <div
          class="m-date-selector--input"
          mdl-datetime-picker [date]="campaign.end"
          (dateChange)="onEndDateChange($event); triggerPreview()"
        >
          <input id="boost-creator__end" type="text" placeholder="End of times" i18n [value]="campaign.end | date:'MMM dd'" readonly>
          <input id="boost-creator__end" type="text" placeholder="End of times" i18n [value]="campaign.end | date:'MMM dd'" readonly>
          <i class="material-icons">keyboard_arrow_down</i>
          <i class="material-icons">keyboard_arrow_down</i>
        </div>
        </div>
@@ -155,7 +171,7 @@
          type="number"
          type="number"
          name="budget"
          name="budget"
          [ngModel]="campaign.budget"
          [ngModel]="campaign.budget"
          (ngModelChange)="campaign.budget = $event; calcImpressions()"
          (ngModelChange)="campaign.budget = $event; triggerPreview()"
        >
        >


        <span>TOKENS</span>
        <span>TOKENS</span>
@@ -169,7 +185,14 @@
      </label>
      </label>


      <div class="m-form--read-only-value">
      <div class="m-form--read-only-value">
        <span>{{campaign.impressions | number}}</span>
        <span>
          {{campaign.impressions | number}}

          <m-tooltip icon="warning" *ngIf="preview?.cannot_fulfill_daily" i18n>
            There might be issues trying to fulfill the impressions per day
            goal for this campaign.
          </m-tooltip>
        </span>
      </div>
      </div>
    </div>
    </div>


+22 −2
Original line number Original line Diff line number Diff line
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from "@angular/core";
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from "@angular/core";
import { ActivatedRoute, Router } from '@angular/router';
import { ActivatedRoute, Router } from '@angular/router';
import { Campaign, CampaignDeliveryStatus, CampaignPayment } from "../campaigns.type";
import { Campaign, CampaignDeliveryStatus, CampaignPayment, CampaignPreview } from "../campaigns.type";
import { CampaignsService } from '../campaigns.service';
import { CampaignsService } from '../campaigns.service';
import { Subscription } from 'rxjs';
import { Subject, Subscription } from 'rxjs';
import { Tag } from '../../../hashtags/types/tag';
import { Tag } from '../../../hashtags/types/tag';
import { CampaignPaymentsService } from '../campaign-payments.service';
import { CampaignPaymentsService } from '../campaign-payments.service';
import { debounceTime } from 'rxjs/operators';


@Component({
@Component({
  providers: [CampaignsService, CampaignPaymentsService],
  providers: [CampaignsService, CampaignPaymentsService],
@@ -24,8 +25,14 @@ export class BoostCampaignsCreatorComponent implements OnInit, OnDestroy {


  currentError: string = '';
  currentError: string = '';


  preview: CampaignPreview = {};

  protected route$: Subscription;
  protected route$: Subscription;


  protected previewSubject: Subject<Campaign> = new Subject();

  protected preview$: Subscription;

  constructor(
  constructor(
    protected service: CampaignsService,
    protected service: CampaignsService,
    protected payments: CampaignPaymentsService,
    protected payments: CampaignPaymentsService,
@@ -48,10 +55,18 @@ export class BoostCampaignsCreatorComponent implements OnInit, OnDestroy {
        this.load();
        this.load();
      }
      }
    });
    });

    this.preview$ = this.previewSubject
      .pipe(debounceTime(600))
      .subscribe(async (campaign: Campaign) => {
        this.preview = (await this.service.preview(campaign)) || {};
        this.detectChanges();
      });
  }
  }


  ngOnDestroy() {
  ngOnDestroy() {
    this.route$.unsubscribe();
    this.route$.unsubscribe();
    this.preview$.unsubscribe();
  }
  }


  createFrom({ type, from }) {
  createFrom({ type, from }) {
@@ -156,6 +171,11 @@ export class BoostCampaignsCreatorComponent implements OnInit, OnDestroy {
    return this.service.getTypes();
    return this.service.getTypes();
  }
  }


  triggerPreview() {
    this.calcImpressions();
    this.previewSubject.next(this.campaign);
  }

  async submit() {
  async submit() {
    if (!this.canSubmit() || this.inProgress) {
    if (!this.canSubmit() || this.inProgress) {
      return;
      return;