application_row.vue 11.5 KB
Newer Older
1
<script>
2
/* eslint-disable vue/require-default-prop */
3
/* eslint-disable @gitlab/vue-require-i18n-strings */
4
import { GlLink, GlModalDirective } from '@gitlab/ui';
E
Enrique Alcántara 已提交
5
import { s__, __, sprintf } from '~/locale';
6 7 8
import eventHub from '../event_hub';
import identicon from '../../vue_shared/components/identicon.vue';
import loadingButton from '../../vue_shared/components/loading_button.vue';
9
import UninstallApplicationButton from './uninstall_application_button.vue';
10
import UninstallApplicationConfirmationModal from './uninstall_application_confirmation_modal.vue';
11

12
import { APPLICATION_STATUS } from '../constants';
13

14 15 16 17
export default {
  components: {
    loadingButton,
    identicon,
18
    GlLink,
19
    UninstallApplicationButton,
20 21 22 23
    UninstallApplicationConfirmationModal,
  },
  directives: {
    GlModalDirective,
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
  },
  props: {
    id: {
      type: String,
      required: true,
    },
    title: {
      type: String,
      required: true,
    },
    titleLink: {
      type: String,
      required: false,
    },
    manageLink: {
      type: String,
      required: false,
    },
    logoUrl: {
      type: String,
      required: false,
      default: null,
    },
    disabled: {
      type: Boolean,
      required: false,
      default: false,
    },
52 53 54 55 56
    uninstallable: {
      type: Boolean,
      required: false,
      default: false,
    },
57 58 59 60 61 62 63 64 65 66 67 68
    status: {
      type: String,
      required: false,
    },
    statusReason: {
      type: String,
      required: false,
    },
    requestReason: {
      type: String,
      required: false,
    },
69 70 71
    installed: {
      type: Boolean,
      required: false,
72
      default: false,
73
    },
74 75 76 77 78
    installFailed: {
      type: Boolean,
      required: false,
      default: false,
    },
79 80 81 82
    installedVia: {
      type: String,
      required: false,
    },
83 84 85 86 87 88 89 90
    version: {
      type: String,
      required: false,
    },
    chartRepo: {
      type: String,
      required: false,
    },
E
Enrique Alcántara 已提交
91
    updateAvailable: {
92 93 94
      type: Boolean,
      required: false,
    },
95 96 97
    updateable: {
      type: Boolean,
      default: true,
98
      required: false,
99
    },
100 101 102 103 104 105 106 107 108 109
    updateSuccessful: {
      type: Boolean,
      required: false,
      default: false,
    },
    updateFailed: {
      type: Boolean,
      required: false,
      default: false,
    },
110 111 112 113 114 115 116 117 118 119
    uninstallFailed: {
      type: Boolean,
      required: false,
      default: false,
    },
    uninstallSuccessful: {
      type: Boolean,
      required: false,
      default: false,
    },
120 121 122 123 124 125 126 127 128 129 130 131 132
    installApplicationRequestParams: {
      type: Object,
      required: false,
      default: () => ({}),
    },
  },
  computed: {
    isUnknownStatus() {
      return !this.isKnownStatus && this.status !== null;
    },
    isKnownStatus() {
      return Object.values(APPLICATION_STATUS).includes(this.status);
    },
133
    isInstalling() {
134
      return this.status === APPLICATION_STATUS.INSTALLING;
135
    },
136 137 138 139 140 141 142
    canInstall() {
      return (
        this.status === APPLICATION_STATUS.NOT_INSTALLABLE ||
        this.status === APPLICATION_STATUS.INSTALLABLE ||
        this.isUnknownStatus
      );
    },
143
    hasLogo() {
144
      return Boolean(this.logoUrl);
145 146 147 148 149 150 151 152
    },
    identiconId() {
      // generate a deterministic integer id for the identicon background
      return this.id.charCodeAt(0);
    },
    rowJsClass() {
      return `js-cluster-application-row-${this.id}`;
    },
153 154 155 156 157 158
    displayUninstallButton() {
      return this.installed && this.uninstallable;
    },
    displayInstallButton() {
      return !this.installed || !this.uninstallable;
    },
159
    installButtonLoading() {
160
      return !this.status || this.isInstalling;
161 162 163 164 165 166 167 168
    },
    installButtonDisabled() {
      // Avoid the potential for the real-time data to say APPLICATION_STATUS.INSTALLABLE but
      // we already made a request to install and are just waiting for the real-time
      // to sync up.
      return (
        ((this.status !== APPLICATION_STATUS.INSTALLABLE &&
          this.status !== APPLICATION_STATUS.ERROR) ||
169
          this.isInstalling) &&
170 171 172 173 174
        this.isKnownStatus
      );
    },
    installButtonLabel() {
      let label;
175
      if (this.canInstall) {
E
Enrique Alcántara 已提交
176
        label = __('Install');
177
      } else if (this.isInstalling) {
E
Enrique Alcántara 已提交
178
        label = __('Installing');
179
      } else if (this.installed) {
E
Enrique Alcántara 已提交
180
        label = __('Installed');
181
      }
182

183 184 185 186 187 188
      return label;
    },
    showManageButton() {
      return this.manageLink && this.status === APPLICATION_STATUS.INSTALLED;
    },
    manageButtonLabel() {
E
Enrique Alcántara 已提交
189
      return __('Manage');
190
    },
191 192 193
    hasError() {
      return this.installFailed || this.uninstallFailed;
    },
194
    generalErrorDescription() {
195 196 197 198 199 200 201 202 203 204 205
      let errorDescription;

      if (this.installFailed) {
        errorDescription = s__('ClusterIntegration|Something went wrong while installing %{title}');
      } else if (this.uninstallFailed) {
        errorDescription = s__(
          'ClusterIntegration|Something went wrong while uninstalling %{title}',
        );
      }

      return sprintf(errorDescription, { title: this.title });
206
    },
207
    versionLabel() {
208
      if (this.updateFailed) {
E
Enrique Alcántara 已提交
209 210 211
        return __('Update failed');
      } else if (this.isUpdating) {
        return __('Updating');
212 213
      }

214
      return this.updateSuccessful ? __('Updated to') : __('Updated');
215
    },
E
Enrique Alcántara 已提交
216
    updateFailureDescription() {
J
jerasmus 已提交
217
      return s__('ClusterIntegration|Update failed. Please check the logs and try again.');
218
    },
E
Enrique Alcántara 已提交
219 220
    updateSuccessDescription() {
      return sprintf(s__('ClusterIntegration|%{title} updated successfully.'), {
221 222 223
        title: this.title,
      });
    },
E
Enrique Alcántara 已提交
224
    updateButtonLabel() {
225
      let label;
E
Enrique Alcántara 已提交
226 227 228 229
      if (this.updateAvailable && !this.updateFailed && !this.isUpdating) {
        label = __('Update');
      } else if (this.isUpdating) {
        label = __('Updating');
230
      } else if (this.updateFailed) {
E
Enrique Alcántara 已提交
231
        label = __('Retry update');
232 233 234 235
      }

      return label;
    },
E
Enrique Alcántara 已提交
236
    isUpdating() {
237
      // Since upgrading is handled asynchronously on the backend we need this check to prevent any delay on the frontend
238
      return this.status === APPLICATION_STATUS.UPDATING;
239
    },
E
Enrique Alcántara 已提交
240
    shouldShowUpdateDetails() {
J
jerasmus 已提交
241
      // This method only returns true when;
E
Enrique Alcántara 已提交
242 243 244
      // Update was successful OR Update failed
      //     AND new update is unavailable AND version information is present.
      return (this.updateSuccessful || this.updateFailed) && !this.updateAvailable && this.version;
J
jerasmus 已提交
245
    },
246 247 248 249 250
    uninstallSuccessDescription() {
      return sprintf(s__('ClusterIntegration|%{title} uninstalled successfully.'), {
        title: this.title,
      });
    },
251 252
  },
  watch: {
253 254
    updateSuccessful(updateSuccessful) {
      if (updateSuccessful) {
E
Enrique Alcántara 已提交
255
        this.$toast.show(this.updateSuccessDescription);
256 257
      }
    },
258 259 260 261 262
    uninstallSuccessful(uninstallSuccessful) {
      if (uninstallSuccessful) {
        this.$toast.show(this.uninstallSuccessDescription);
      }
    },
263 264 265 266 267 268 269 270
  },
  methods: {
    installClicked() {
      eventHub.$emit('installApplication', {
        id: this.id,
        params: this.installApplicationRequestParams,
      });
    },
E
Enrique Alcántara 已提交
271 272
    updateClicked() {
      eventHub.$emit('updateApplication', {
273 274 275 276
        id: this.id,
        params: this.installApplicationRequestParams,
      });
    },
277 278 279 280 281
    uninstallConfirmed() {
      eventHub.$emit('uninstallApplication', {
        id: this.id,
      });
    },
282 283
  },
};
284 285 286 287
</script>

<template>
  <div
288 289
    :class="[
      rowJsClass,
290
      installed && 'cluster-application-installed',
M
Mike Greiling 已提交
291
      disabled && 'cluster-application-disabled',
292 293
    ]"
    class="cluster-application-row gl-responsive-table-row gl-responsive-table-row-col-span"
294
    :data-qa-selector="id"
295
  >
M
Mike Greiling 已提交
296 297
    <div class="gl-responsive-table-row-layout" role="row">
      <div class="table-section append-right-8 section-align-top" role="gridcell">
298 299 300 301 302 303
        <img
          v-if="hasLogo"
          :src="logoUrl"
          :alt="`${title} logo`"
          class="cluster-application-logo avatar s40"
        />
M
Mike Greiling 已提交
304
        <identicon v-else :entity-id="identiconId" :entity-name="title" size-class="s40" />
305
      </div>
M
Mike Greiling 已提交
306
      <div class="table-section cluster-application-description section-wrap" role="gridcell">
307 308 309 310
        <strong>
          <a
            v-if="titleLink"
            :href="titleLink"
311
            target="_blank"
312 313
            rel="noopener noreferrer"
            class="js-cluster-application-title"
314
            >{{ title }}</a
315
          >
316
          <span v-else class="js-cluster-application-title">{{ title }}</span>
317
        </strong>
318 319 320 321 322
        <span
          v-if="installedVia"
          class="js-cluster-application-installed-via"
          v-html="installedVia"
        ></span>
323
        <slot name="description"></slot>
324
        <div v-if="hasError" class="cluster-application-error text-danger prepend-top-10">
325 326 327 328
          <p class="js-cluster-application-general-error-message append-bottom-0">
            {{ generalErrorDescription }}
          </p>
          <ul v-if="statusReason || requestReason">
M
Mike Greiling 已提交
329
            <li v-if="statusReason" class="js-cluster-application-status-error-message">
330 331
              {{ statusReason }}
            </li>
M
Mike Greiling 已提交
332
            <li v-if="requestReason" class="js-cluster-application-request-error-message">
333 334 335 336
              {{ requestReason }}
            </li>
          </ul>
        </div>
337

338 339
        <div v-if="updateable">
          <div
E
Enrique Alcántara 已提交
340 341
            v-if="shouldShowUpdateDetails"
            class="form-text text-muted label p-0 js-cluster-application-update-details"
342
          >
343 344 345 346 347
            {{ versionLabel }}
            <gl-link
              v-if="updateSuccessful"
              :href="chartRepo"
              target="_blank"
E
Enrique Alcántara 已提交
348
              class="js-cluster-application-update-version"
349 350 351
              >chart v{{ version }}</gl-link
            >
          </div>
352

353
          <div
E
Enrique Alcántara 已提交
354 355
            v-if="updateFailed && !isUpdating"
            class="bs-callout bs-callout-danger cluster-application-banner mt-2 mb-0 js-cluster-application-update-details"
356
          >
E
Enrique Alcántara 已提交
357
            {{ updateFailureDescription }}
358 359
          </div>
          <loading-button
E
Enrique Alcántara 已提交
360 361 362 363 364 365
            v-if="updateAvailable || updateFailed || isUpdating"
            class="btn btn-primary js-cluster-application-update-button mt-2"
            :loading="isUpdating"
            :disabled="isUpdating"
            :label="updateButtonLabel"
            @click="updateClicked"
366
          />
367
        </div>
368 369
      </div>
      <div
370
        :class="{ 'section-25': showManageButton, 'section-15': !showManageButton }"
371
        class="table-section table-button-footer section-align-top"
372 373
        role="gridcell"
      >
M
Mike Greiling 已提交
374
        <div v-if="showManageButton" class="btn-group table-action-buttons">
375 376 377
          <a :href="manageLink" :class="{ disabled: disabled }" class="btn">{{
            manageButtonLabel
          }}</a>
378
        </div>
379 380
        <div class="btn-group table-action-buttons">
          <loading-button
381
            v-if="displayInstallButton"
382
            :loading="installButtonLoading"
383
            :disabled="disabled || installButtonDisabled"
384
            :label="installButtonLabel"
385
            class="js-cluster-application-install-button"
386 387
            data-qa-selector="install_button"
            :data-qa-application="id"
388 389
            @click="installClicked"
          />
390 391
          <uninstall-application-button
            v-if="displayUninstallButton"
392 393
            v-gl-modal-directive="'uninstall-' + id"
            :status="status"
394 395
            data-qa-selector="uninstall_button"
            :data-qa-application="id"
396 397
            class="js-cluster-application-uninstall-button"
          />
398 399 400 401 402
          <uninstall-application-confirmation-modal
            :application="id"
            :application-title="title"
            @confirm="uninstallConfirmed()"
          />
403 404 405 406 407
        </div>
      </div>
    </div>
  </div>
</template>