Explorar el Código

Fix issues with Buefy responsive styles.

- Fix button and input sizing and alignments.
- Make settings tabs responsive.
- Fix toast and modal overlay issues.
- Fix Buefy table top-left/right controls.
- Fix 'New' buttons across pages.
- Fix search and bulk-select controls on subscribers page.
Kailash Nadh hace 3 años
padre
commit
d3f543cb15

+ 118 - 47
frontend/src/assets/style.scss

@@ -95,9 +95,6 @@ section {
     background-color: #efefef;
   }
 
-.page-header {
-  min-height: 60px;
-}
 
 /* Two column sidebar+body layout */
 #app {
@@ -209,6 +206,9 @@ body.is-noscroll {
 .editor {
   margin-bottom: 30px;
 
+  .tox .tox-dialog-wrap {
+    z-index: 950;
+  }
   .tox-tinymce {
     box-shadow: 2px 2px 0 #f3f3f3;
     border: 1px solid #e6e6e6;
@@ -266,8 +266,14 @@ body.is-noscroll {
 }
 
 /* Modal */
-.modal {
-  z-index: 1350; // Needs to get above the TinyMCE modal
+.modal-background {
+  background: rgba(255, 255, 255, 0.7);
+}
+.modal-content, .modal.dialog .modal-card {
+  background: $white;
+  box-shadow: 2px 2px 3px #e4e4e4;
+  border: 1px solid #e5e5e5;
+  padding: 0;
 }
 .modal-card-head {
   display: block;
@@ -279,21 +285,45 @@ body.is-noscroll {
   display: none;
 }
 
+/* Table */
+.b-table .level-left {
+  min-width: 60%;
+  display: block;
+
+  .actions .a {
+    display: inline-block;
+    margin-right: 30px;
+  }
+}
+
 
 /* Fix for input colours */
-.button.is-primary {
-  background: $primary;
+.button {
+  &.is-primary {
+    background: $primary;
+
+    &:hover {
+      background: darken($primary, 15%);
+    }
+    &:disabled {
+      background: $grey-light;
+    }
+  }
 
   &:not(.is-small) {
     height: auto;
     padding: 10px 20px;
   }
+}
 
-  &:hover {
-    background: darken($primary, 15%);
+.has-addons {
+  .controls .button.is-primary {
+    border-top-left-radius: 0;
+    border-bottom-left-radius: 0;
   }
-  &:disabled {
-    background: $grey-light;
+
+  .input:hover {
+    box-shadow: none;
   }
 }
 
@@ -323,7 +353,11 @@ body.is-noscroll {
   border: 1px solid $grey-lighter;
 }
 
-.input {
+.select:not(.is-multiple) {
+  height: auto;
+}
+
+.input, .select select {
   height: auto;
   padding: 10px 12px;
 }
@@ -442,6 +476,13 @@ body.is-noscroll {
   }
 }
 
+/* Page header */
+.page-header {
+  .buttons {
+    justify-content: flex-end;
+  }
+}
+
 /* Dashboard */
 section.dashboard {
   .title {
@@ -496,13 +537,17 @@ section.lists {
 }
 
 /* Subscribers page */
-.subscribers-controls {
-  padding-bottom: 15px;
-}
-.subscribers-bulk {
-  .actions a {
-    display: inline-block;
-    margin-right: 30px;
+.subscribers {
+  .subscribers-controls .buttons {
+    margin-top: 15px;
+  }
+
+  .toggle-advanced {
+    margin-top: 10px;
+  }
+
+  .b-table {
+    margin-top: 25px;
   }
 }
 
@@ -620,13 +665,6 @@ section.analytics {
   }
 }
 
-/* Campaign */
-section.campaign {
-  header .buttons {
-    justify-content: flex-end;
-  }
-}
-
 /* Media gallery */
 .media-files {
   .thumbs {
@@ -805,25 +843,20 @@ section.campaign {
   }
 }
 
-@media screen and (max-width: 1450px) and (min-width: 769px) {
-  section.campaigns {
-    /* Fold the stats labels until the card view */
-    table tbody td {
-      .fields {
-        label {
-          margin: 0;
-          display: block;
-          text-align: left;
-        }
-        p {
-          margin-bottom: 10px !important;
-        }
-      }
+@media screen and (max-width: 1500px) {
+  .b-table {
+    .top.level {
+      display: block;
     }
   }
+  .b-table .level-left {
+    display: block;
+    width: 100%;
+    min-width: 0;
+  }
 }
 
-@media screen and (max-width: 1023px) {
+@media screen and (max-width: 1100px) {
   html, body {
     overflow-x: auto;
   }
@@ -869,20 +902,58 @@ section.campaign {
     }
   }
 
-  td .tags {
-    display: block;
+  .navbar-burger {
+    display: none;
+  }
 
+  td .tags {
     .tag:not(:last-child) {
       margin-right: 0;
     }
   }
-}
 
-@media screen and (max-width: 840px) {
   section.dashboard label {
     min-width: auto;
   }
-  .table-mobile-sort {
-    margin-top: 15px;
+
+  /* Tabs */
+  nav.tabs.is-boxed ul {
+    display: block;
+    text-align: left;
+    flex-grow: 1;
+
+    li {
+      a {
+        justify-content: left;
+        border: 1px solid $grey-lighter !important;
+      }
+
+      &.is-active a {
+        border-bottom: 1px solid $grey-lighter !important;
+        padding-left: 30px;
+      }
+    }
+  }
+
+  /* Table top-left controls */
+  .b-table .level-left {
+    .actions .a {
+      display: block;
+      margin: 0 0 5px 0;
+    }
+  }
+}
+
+@media screen and (max-width: 850px) {
+  .page-header .buttons {
+    display: block;
+  }
+}
+
+/* On big sizes, keep the header buttons small and non-expanded. */
+@media screen and (min-width: 769px) {
+  .page-header .button {
+    display: inline-block;
+    width: auto;
   }
 }

+ 6 - 0
frontend/src/components/ListSelector.vue

@@ -113,5 +113,11 @@ export default {
       this.selectedItems = JSON.parse(JSON.stringify(this.selected));
     },
   },
+
+  mounted() {
+    if (this.selected) {
+      this.selectedItems = JSON.parse(JSON.stringify(this.selected));
+    }
+  },
 };
 </script>

+ 25 - 16
frontend/src/views/Campaign.vue

@@ -1,7 +1,7 @@
 <template>
   <section class="campaign">
-    <header class="columns">
-      <div class="column is-8">
+    <header class="columns page-header">
+      <div class="column is-6">
         <p v-if="isEditing" class="tags">
           <b-tag v-if="isEditing" :class="data.status">
             {{ $t(`campaigns.status.${data.status}`) }}
@@ -18,20 +18,29 @@
         <h4 v-else class="title is-4">{{ $t('campaigns.newCampaign') }}</h4>
       </div>
 
-      <div class="column">
-        <div class="buttons" v-if="isEditing && canEdit">
-          <b-button @click="onSubmit" :loading="loading.campaigns"
-            type="is-primary" icon-left="content-save-outline" data-cy="btn-save">
-            {{ $t('globals.buttons.saveChanges') }}
-          </b-button>
-          <b-button v-if="canStart" @click="startCampaign" :loading="loading.campaigns"
-            type="is-primary" icon-left="rocket-launch-outline" data-cy="btn-start">
-              {{ $t('campaigns.start') }}
-          </b-button>
-          <b-button v-if="canSchedule" @click="startCampaign" :loading="loading.campaigns"
-            type="is-primary" icon-left="clock-start" data-cy="btn-schedule">
-              {{ $t('campaigns.schedule') }}
-          </b-button>
+      <div class="column is-6">
+        <div class="buttons">
+          <b-field grouped v-if="isEditing && canEdit">
+            <b-field expanded>
+              <b-button  expanded @click="onSubmit" :loading="loading.campaigns"
+                type="is-primary" icon-left="content-save-outline" data-cy="btn-save">
+                {{ $t('globals.buttons.saveChanges') }}
+              </b-button>
+            </b-field>
+            <b-field expanded v-if="canStart">
+              <b-button  expanded @click="startCampaign" :loading="loading.campaigns"
+                type="is-primary" icon-left="rocket-launch-outline" data-cy="btn-start">
+                {{ $t('campaigns.start') }}
+              </b-button>
+            </b-field>
+            <b-field expanded v-if="canSchedule">
+              <b-button  expanded @click="startCampaign"
+                :loading="loading.campaigns"
+                type="is-primary" icon-left="clock-start" data-cy="btn-schedule">
+                {{ $t('campaigns.schedule') }}
+              </b-button>
+            </b-field>
+          </b-field>
         </div>
       </div>
     </header>

+ 28 - 14
frontend/src/views/Campaigns.vue

@@ -1,27 +1,22 @@
 <template>
   <section class="campaigns">
-    <header class="columns">
-      <div class="column is-two-thirds">
+    <header class="columns page-header">
+      <div class="column is-10">
         <h1 class="title is-4">{{ $t('globals.terms.campaigns') }}
           <span v-if="!isNaN(campaigns.total)">({{ campaigns.total }})</span>
         </h1>
       </div>
       <div class="column has-text-right">
-        <b-button :to="{name: 'campaign', params:{id: 'new'}}" tag="router-link"
-          type="is-primary" icon-left="plus" data-cy="btn-new">
-          {{ $t('globals.buttons.new') }}
-        </b-button>
+        <b-field expanded>
+          <b-button expanded :to="{name: 'campaign', params:{id: 'new'}}"
+            tag="router-link" class="btn-new"
+            type="is-primary" icon-left="plus" data-cy="btn-new">
+            {{ $t('globals.buttons.new') }}
+          </b-button>
+        </b-field>
       </div>
     </header>
 
-    <form @submit.prevent="getCampaigns">
-      <b-field grouped>
-          <b-input v-model="queryParams.query" name="query"
-            :placeholder="$t('campaigns.queryPlaceholder')" icon="magnify" ref="query"></b-input>
-          <b-button native-type="submit" type="is-primary" icon-left="magnify"></b-button>
-      </b-field>
-    </form>
-
     <b-table
       :data="campaigns.results"
       :loading="loading.campaigns"
@@ -29,6 +24,25 @@
       paginated backend-pagination pagination-position="both" @page-change="onPageChange"
       :current-page="queryParams.page" :per-page="campaigns.perPage" :total="campaigns.total"
       hoverable backend-sorting @sort="onSort">
+
+      <template #top-left>
+        <div class="columns">
+          <div class="column is-6">
+            <form @submit.prevent="getCampaigns">
+              <div>
+                <b-field>
+                  <b-input v-model="queryParams.query" name="query" expanded
+                    :placeholder="$t('campaigns.queryPlaceholder')" icon="magnify" ref="query" />
+                  <p class="controls">
+                    <b-button native-type="submit" type="is-primary" icon-left="magnify" />
+                  </p>
+                </b-field>
+              </div>
+            </form>
+          </div>
+        </div>
+      </template>
+
       <b-table-column v-slot="props" cell-class="status" field="status"
         :label="$t('globals.fields.status')" width="10%" sortable
         :td-attrs="$utils.tdID" header-class="cy-status">

+ 7 - 0
frontend/src/views/Import.vue

@@ -348,6 +348,13 @@ export default Vue.extend({
 
   mounted() {
     this.pollStatus();
+
+    const ids = this.$utils.parseQueryIDs(this.$route.query.list_id);
+    if (ids.length > 0 && this.lists.results) {
+      this.$nextTick(() => {
+        this.form.lists = this.lists.results.filter((l) => ids.indexOf(l.id) > -1);
+      });
+    }
   },
 });
 </script>

+ 18 - 6
frontend/src/views/Lists.vue

@@ -1,16 +1,19 @@
 <template>
   <section class="lists">
-    <header class="columns">
-      <div class="column is-two-thirds">
+    <header class="columns page-header">
+      <div class="column is-10">
         <h1 class="title is-4">
           {{ $t('globals.terms.lists') }}
           <span v-if="!isNaN(lists.total)">({{ lists.total }})</span>
         </h1>
       </div>
       <div class="column has-text-right">
-        <b-button type="is-primary" icon-left="plus" @click="showNewForm" data-cy="btn-new">
-          {{ $t('globals.buttons.new') }}
-        </b-button>
+        <b-field expanded>
+          <b-button expanded type="is-primary" icon-left="plus" class="btn-new"
+            @click="showNewForm" data-cy="btn-new">
+            {{ $t('globals.buttons.new') }}
+          </b-button>
+        </b-field>
       </div>
     </header>
 
@@ -40,7 +43,7 @@
 
       <b-table-column v-slot="props" field="type" :label="$t('globals.fields.type')"
         header-class="cy-type" sortable>
-        <div>
+        <div class="tags">
           <b-tag :class="props.row.type" :data-cy="`type-${props.row.type}`">
             {{ $t(`lists.types.${props.row.type}`) }}
           </b-tag>
@@ -86,11 +89,20 @@
               <b-icon icon="rocket-launch-outline" size="is-small" />
             </b-tooltip>
           </router-link>
+
           <a href="" @click.prevent="showEditForm(props.row)" data-cy="btn-edit">
             <b-tooltip :label="$t('globals.buttons.edit')" type="is-dark">
               <b-icon icon="pencil-outline" size="is-small" />
             </b-tooltip>
           </a>
+
+          <router-link :to="{name: 'import', query: { list_id: props.row.id }}"
+            data-cy="btn-import">
+            <b-tooltip :label="$t('import.title')" type="is-dark">
+              <b-icon icon="file-upload-outline" size="is-small" />
+            </b-tooltip>
+          </router-link>
+
           <a href="" @click.prevent="deleteList(props.row)" data-cy="btn-delete">
             <b-tooltip :label="$t('globals.buttons.delete')" type="is-dark">
               <b-icon icon="trash-can-outline" size="is-small" />

+ 8 - 6
frontend/src/views/Settings.vue

@@ -1,18 +1,20 @@
 <template>
   <section class="settings">
     <b-loading :is-full-page="true" v-if="loading.settings || isLoading" active />
-    <header class="columns">
+    <header class="columns page-header">
       <div class="column is-half">
         <h1 class="title is-4">{{ $t('settings.title') }}
           <span class="has-text-grey-light">({{ serverConfig.version }})</span>
         </h1>
       </div>
       <div class="column has-text-right">
-        <b-button :disabled="!hasFormChanged"
-          type="is-primary" icon-left="content-save-outline"
-          @click="onSubmit" class="isSaveEnabled" data-cy="btn-save">
-          {{ $t('globals.buttons.save') }}
-        </b-button>
+        <b-field expanded>
+          <b-button expanded :disabled="!hasFormChanged"
+            type="is-primary" icon-left="content-save-outline"
+            @click="onSubmit" class="isSaveEnabled" data-cy="btn-save">
+            {{ $t('globals.buttons.save') }}
+          </b-button>
+        </b-field>
       </div>
     </header>
     <hr />

+ 69 - 76
frontend/src/views/Subscribers.vue

@@ -1,7 +1,7 @@
 <template>
   <section class="subscribers">
-    <header class="columns">
-      <div class="column is-half">
+    <header class="columns page-header">
+      <div class="column is-10">
         <h1 class="title is-4">{{ $t('globals.terms.subscribers') }}
           <span v-if="!isNaN(subscribers.total)">
             (<span data-cy="count">{{ subscribers.total }}</span>)
@@ -12,51 +12,44 @@
         </h1>
       </div>
       <div class="column has-text-right">
-        <b-button type="is-primary" icon-left="plus" @click="showNewForm" data-cy="btn-new">
-          {{ $t('globals.buttons.new') }}
-        </b-button>
+        <b-field expanded>
+          <b-button expanded type="is-primary" icon-left="plus"
+            @click="showNewForm" data-cy="btn-new" class="btn-new">
+            {{ $t('globals.buttons.new') }}
+          </b-button>
+        </b-field>
       </div>
     </header>
 
     <section class="subscribers-controls">
       <div class="columns">
-        <div class="column is-6">
+        <div class="column is-4">
           <form @submit.prevent="onSubmit">
             <div>
-              <b-field grouped>
-                <b-input @input="onSimpleQueryInput" v-model="queryInput"
+              <b-field addons>
+                <b-input @input="onSimpleQueryInput" v-model="queryInput" expanded
                   :placeholder="$t('subscribers.queryPlaceholder')" icon="magnify" ref="query"
                   :disabled="isSearchAdvanced" data-cy="search"></b-input>
-                <b-button native-type="submit" type="is-primary" icon-left="magnify"
-                  :disabled="isSearchAdvanced" data-cy="btn-search"></b-button>
+                <p class="controls">
+                  <b-button native-type="submit" type="is-primary" icon-left="magnify"
+                    :disabled="isSearchAdvanced" data-cy="btn-search"></b-button>
+                </p>
               </b-field>
 
-              <p>
-                <a href="#" @click.prevent="toggleAdvancedSearch" data-cy="btn-advanced-search">
-                  <b-icon icon="cog-outline" size="is-small" />
-                  {{ $t('subscribers.advancedQuery') }}
-                </a>
-              </p>
-
               <div v-if="isSearchAdvanced">
-                <b-field>
-                  <b-input v-model="queryParams.queryExp"
-                    @keydown.native.enter="onAdvancedQueryEnter"
-                    type="textarea" ref="queryExp"
-                    placeholder="subscribers.name LIKE '%user%' or subscribers.status='blocklisted'"
-                    data-cy="query">
-                  </b-input>
-                </b-field>
-                <b-field>
-                  <span class="is-size-6 has-text-grey">
-                    {{ $t('subscribers.advancedQueryHelp') }}.{{ ' ' }}
-                    <a href="https://listmonk.app/docs/querying-and-segmentation"
-                      target="_blank" rel="noopener noreferrer">
-                      {{ $t('globals.buttons.learnMore') }}.
-                    </a>
-                  </span>
-                </b-field>
-
+                <b-input v-model="queryParams.queryExp"
+                  @keydown.native.enter="onAdvancedQueryEnter"
+                  type="textarea" ref="queryExp"
+                  placeholder="subscribers.name LIKE '%user%' or subscribers.status='blocklisted'"
+                  data-cy="query">
+                </b-input>
+                <span class="is-size-6 has-text-grey">
+                  {{ $t('subscribers.advancedQueryHelp') }}.{{ ' ' }}
+                  <a href="https://listmonk.app/docs/querying-and-segmentation"
+                    target="_blank" rel="noopener noreferrer">
+                    {{ $t('globals.buttons.learnMore') }}.
+                  </a>
+                </span>
                 <div class="buttons">
                   <b-button native-type="submit" type="is-primary"
                     icon-left="magnify" data-cy="btn-query">{{ $t('subscribers.query') }}</b-button>
@@ -68,37 +61,13 @@
               </div><!-- advanced query -->
             </div>
           </form>
-        </div><!-- search -->
-
-        <div class="column is-6 subscribers-bulk" v-if="bulk.checked.length > 0">
-          <div>
-            <p>
-              <span class="is-size-5 has-text-weight-semibold">
-                {{ $t('subscribers.numSelected', { num: numSelectedSubscribers }) }}
-              </span>
-              <span v-if="!bulk.all && subscribers.total > subscribers.perPage">
-                &mdash;
-                <a href="" @click.prevent="selectAllSubscribers">
-                  {{ $t('subscribers.selectAll', { num: subscribers.total }) }}
-                </a>
-              </span>
-            </p>
-
-            <p class="actions">
-              <a href='' @click.prevent="showBulkListForm" data-cy="btn-manage-lists">
-                <b-icon icon="format-list-bulleted-square" size="is-small" /> Manage lists
-              </a>
-
-              <a href='' @click.prevent="deleteSubscribers" data-cy="btn-delete-subscribers">
-                <b-icon icon="trash-can-outline" size="is-small" /> Delete
-              </a>
-
-              <a href='' @click.prevent="blocklistSubscribers" data-cy="btn-manage-blocklist">
-                <b-icon icon="account-off-outline" size="is-small" /> Blocklist
-              </a>
-            </p><!-- selection actions //-->
+          <div v-if="!isSearchAdvanced" class="toggle-advanced">
+            <a href="#" @click.prevent="toggleAdvancedSearch" data-cy="btn-advanced-search">
+              <b-icon icon="cog-outline" size="is-small" />
+              {{ $t('subscribers.advancedQuery') }}
+            </a>
           </div>
-        </div>
+        </div><!-- search -->
       </div>
     </section><!-- control -->
 
@@ -110,11 +79,38 @@
       paginated backend-pagination pagination-position="both" @page-change="onPageChange"
       :current-page="queryParams.page" :per-page="subscribers.perPage" :total="subscribers.total"
       hoverable checkable backend-sorting @sort="onSort">
+
         <template #top-left>
-          <a href='' @click.prevent="exportSubscribers">
-            <b-icon icon="cloud-download-outline" size="is-small" /> {{ $t('subscribers.export') }}
-          </a>
+          <div class="actions">
+            <a class="a" href='' @click.prevent="exportSubscribers">
+              <b-icon icon="cloud-download-outline" size="is-small" />
+              {{ $t('subscribers.export') }}
+            </a>
+            <template v-if="bulk.checked.length > 0">
+              <a class="a" href='' @click.prevent="showBulkListForm" data-cy="btn-manage-lists">
+                <b-icon icon="format-list-bulleted-square" size="is-small" /> Manage lists
+              </a>
+              <a class="a" href='' @click.prevent="deleteSubscribers"
+                data-cy="btn-delete-subscribers">
+                <b-icon icon="trash-can-outline" size="is-small" /> Delete
+              </a>
+              <a class="a" href='' @click.prevent="blocklistSubscribers"
+                data-cy="btn-manage-blocklist">
+                <b-icon icon="account-off-outline" size="is-small" /> Blocklist
+              </a>
+              <span class="a">
+                {{ $t('subscribers.numSelected', { num: numSelectedSubscribers }) }}
+                <span v-if="!bulk.all && subscribers.total > subscribers.perPage">
+                  &mdash;
+                  <a href="" @click.prevent="selectAllSubscribers">
+                    {{ $t('subscribers.selectAll', { num: subscribers.total }) }}
+                  </a>
+                </span>
+              </span>
+            </template>
+          </div>
         </template>
+
         <b-table-column v-slot="props" field="status" :label="$t('globals.fields.status')"
           header-class="cy-status" :td-attrs="$utils.tdID" sortable>
           <a :href="`/subscribers/${props.row.id}`"
@@ -265,14 +261,11 @@ export default Vue.extend({
 
       // Toggling to simple search.
       if (!this.isSearchAdvanced) {
-        this.$nextTick(() => {
-          this.queryInput = '';
-          this.queryParams.queryExp = '';
-          this.queryParams.page = 1;
-          this.$refs.query.focus();
-
-          this.querySubscribers();
-        });
+        this.queryInput = '';
+        this.queryParams.queryExp = '';
+        this.queryParams.page = 1;
+        this.querySubscribers();
+        this.$refs.query.focus();
         return;
       }
 

+ 8 - 5
frontend/src/views/Templates.vue

@@ -1,14 +1,17 @@
 <template>
   <section class="templates">
-    <header class="columns">
-      <div class="column is-two-thirds">
+    <header class="columns page-header">
+      <div class="column is-10">
         <h1 class="title is-4">{{ $t('globals.terms.templates') }}
           <span v-if="templates.length > 0">({{ templates.length }})</span></h1>
       </div>
       <div class="column has-text-right">
-        <b-button type="is-primary" icon-left="plus" @click="showNewForm">
-          {{ $t('globals.buttons.new') }}
-        </b-button>
+        <b-field expanded>
+          <b-button expanded type="is-primary" icon-left="plus" class="btn-new"
+            @click="showNewForm">
+            {{ $t('globals.buttons.new') }}
+          </b-button>
+        </b-field>
       </div>
     </header>