[ci skip] First iteration: port haml into vue
This commit is contained in:
		
							parent
							
								
									23024a70db
								
							
						
					
					
						commit
						51c9f8b6d6
					
				|  | @ -0,0 +1,90 @@ | |||
| <script> | ||||
|   /* globals Flash */ | ||||
|   import { mapGetters, mapActions } from 'vuex'; | ||||
|   import '../../flash'; | ||||
|   import loadingIcon from '../../vue_shared/components/loading_icon.vue'; | ||||
|   import store from '../stores'; | ||||
|   import collapsibleContainer from './collapsible_container.vue'; | ||||
|   import { errorMessages, errorMessagesTypes } from '../constants'; | ||||
| 
 | ||||
|   export default { | ||||
|     name: 'registryListApp', | ||||
|     props: { | ||||
|       endpoint: { | ||||
|         type: String, | ||||
|         required: true | ||||
|       }, | ||||
|     }, | ||||
|     store, | ||||
|     components: { | ||||
|       collapsibleContainer, | ||||
|       loadingIcon, | ||||
|     }, | ||||
|     computed: { | ||||
|       ...mapGetters([ | ||||
|         'isLoading', | ||||
|         'repos', | ||||
|       ]), | ||||
|     }, | ||||
|     methods: { | ||||
|       ...mapActions([ | ||||
|         'setMainEndpoint', | ||||
|         'fetchRepos', | ||||
|         'fetchList', | ||||
|         'deleteRepo', | ||||
|         'deleteRegistry', | ||||
|         'toggleIsLoading', | ||||
|       ]), | ||||
| 
 | ||||
|       fetchRegistryList(repo) { | ||||
|         this.fetchList(repo) | ||||
|           .catch(() => this.showError(errorMessagesTypes.FETCH_REGISTRY)) | ||||
|       }, | ||||
| 
 | ||||
|       deleteRegistry(repo, registry) { | ||||
|         this.deleteRegistry(registry) | ||||
|           .then(() => this.fetchRegistry(repo)) | ||||
|           .catch(() => this.showError(errorMessagesTypes.DELETE_REGISTRY)); | ||||
|       }, | ||||
| 
 | ||||
|       deleteRepository(repo) { | ||||
|         this.deleteRepo(repo) | ||||
|           .then(() => this.fetchRepo()) | ||||
|           .catch(() => this.showError(errorMessagesTypes.DELETE_REPO)); | ||||
|       }, | ||||
| 
 | ||||
|       showError(message){ | ||||
|         Flash(__(errorMessages[message])); | ||||
|       } | ||||
|     }, | ||||
|     created() { | ||||
|       this.setMainEndpoint(this.endpoint); | ||||
|     }, | ||||
|     mounted() { | ||||
|       this.fetchRepos() | ||||
|         .catch(() => this.showError(errorMessagesTypes.FETCH_REPOS)); | ||||
|     } | ||||
|   }; | ||||
| </script> | ||||
| <template> | ||||
|   <div> | ||||
|     <loading-icon | ||||
|       v-if="isLoading" | ||||
|       size="3" | ||||
|       /> | ||||
| 
 | ||||
|     <collapsible-container | ||||
|       v-else-if="!isLoading && repos.length" | ||||
|       v-for="(item, index) in repos" | ||||
|       :key="index" | ||||
|       :repo="item" | ||||
|       @fetchRegistryList="fetchRegistryList" | ||||
|       @deleteRepository="deleteRepository" | ||||
|       @deleteRegistry="deleteRegistry" | ||||
|       /> | ||||
| 
 | ||||
|     <p v-else-if="!isLoading && !repos.length"> | ||||
|       {{__("No container images stored for this project. Add one by following the instructions above")}} | ||||
|     </p> | ||||
|   </div> | ||||
| </template> | ||||
|  | @ -1,24 +1,22 @@ | |||
| <script> | ||||
|   import clipboardButton from '../../vue_shared/components/clipboard_button.vue'; | ||||
|   import loadingIcon from '../../vue_shared/components/loading_icon.vue'; | ||||
|   import tooltip from '../../vue_shared/directives/tooltip'; | ||||
| 
 | ||||
|   export default { | ||||
|     name: 'collapsibeContainerRegisty', | ||||
|     props: { | ||||
|       title: { | ||||
|         type: String, | ||||
|         required: true, | ||||
|       }, | ||||
|       clipboardContent: { | ||||
|         type: String, | ||||
|         required: true, | ||||
|       }, | ||||
|       repoData: { | ||||
|       repo: { | ||||
|         type: Object, | ||||
|         required: true, | ||||
|       }, | ||||
|     }, | ||||
|     components: { | ||||
|       clipboardButton, | ||||
|       loadingIcon, | ||||
|     }, | ||||
|     directives: { | ||||
|       tooltip, | ||||
|     }, | ||||
|     data() { | ||||
|       return { | ||||
|  | @ -26,37 +24,73 @@ | |||
|       }; | ||||
|     }, | ||||
|     methods: { | ||||
|       itemSize(item) { | ||||
|       layers(item) { | ||||
|         const pluralize = gl.text.pluralize('layer', item.layers); | ||||
|         return `${item.size}·${item.layers}${pluralize}`; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|         return `${item.layers} ${pluralize}`; | ||||
|       }, | ||||
|       toggleRepo() { | ||||
|         if (this.isOpen === false) { | ||||
|           // consider not fetching data the second time it is toggled? :fry: | ||||
|           this.$emit('fetchRegistryList', this.repo); | ||||
|         } | ||||
|         this.isOpen = !this.isOpen; | ||||
|       }, | ||||
|       handleDeleteRepository() { | ||||
|         this.$emit('deleteRepository', this.repo) | ||||
|       }, | ||||
|       handleDeleteRegistry(registry) { | ||||
|         this.$emit('deleteRegistry', this.repo, registry); | ||||
|       }, | ||||
|     }, | ||||
|   }; | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <div class="container-image"> | ||||
|     <div class="container-image-head"> | ||||
|       <i | ||||
|         class="fa" | ||||
|         :class="{ | ||||
|           'chevron-left': !isOpen, | ||||
|           'chevron-up': isOpen, | ||||
|         }" | ||||
|         aria-hidden="true"> | ||||
|       </i> | ||||
|       {{title}} | ||||
| 
 | ||||
|       <clipboard-button | ||||
|         :text="" | ||||
|         :title="" | ||||
|       /> | ||||
|     </div> | ||||
|     <div | ||||
|       class="container-image-tags" | ||||
|       :class="{ hide: !isOpen }"> | ||||
|       class="container-image-head"> | ||||
|       <a | ||||
|         role="button" | ||||
|         @click="toggleRepo"> | ||||
|         <i | ||||
|           class="fa" | ||||
|           :class="{ | ||||
|             'fa-chevron-right': !isOpen, | ||||
|             'fa-chevron-up': isOpen, | ||||
|           }" | ||||
|           aria-hidden="true"> | ||||
|         </i> | ||||
|         {{repo.name}} | ||||
|       </a> | ||||
| 
 | ||||
|       <table class="table tags" v-if="true"> | ||||
|       <clipboard-button text="foo" title="bar" /> | ||||
| 
 | ||||
|       <div class="controls hidden-xs pull-right"> | ||||
|         <button | ||||
|           v-if="repo.canDelete" | ||||
|           type="button" | ||||
|           class="btn btn-remove" | ||||
|           :title="__('Remove repository')" | ||||
|           v-tooltip | ||||
|           @click="handleDeleteRepository"> | ||||
|           <i | ||||
|             class="fa fa-trash" | ||||
|             aria-hidden="true"> | ||||
|           </i> | ||||
|         </button> | ||||
|       </div> | ||||
| 
 | ||||
|     </div> | ||||
| 
 | ||||
|     <loading-icon | ||||
|       v-if="repo.isLoading" | ||||
|       /> | ||||
| 
 | ||||
|     <div | ||||
|       v-else-if="!repo.isLoading && isOpen" | ||||
|       class="container-image-tags"> | ||||
| 
 | ||||
|       <table class="table tags" v-if="repo.list.length"> | ||||
|         <thead> | ||||
|           <tr> | ||||
|             <th>{{__("Tag")}}</th> | ||||
|  | @ -71,23 +105,28 @@ | |||
|             v-for="(item, i) in repo.list" | ||||
|             :key="i"> | ||||
|             <td> | ||||
|               {{item.name}} | ||||
| 
 | ||||
|               {{item.tag}} | ||||
| 
 | ||||
|               <clipboard-button | ||||
|                 :title="item.location" | ||||
|                 :text="item.location" | ||||
|                 :title="item.tag" | ||||
|                 :text="item.tag" | ||||
|                 /> | ||||
|             </td> | ||||
|             <td> | ||||
|               <span | ||||
|                 v-tooltip | ||||
|                 :title="item.revision" | ||||
|                 data-placement="bottom" | ||||
|                 > | ||||
|                 {{item.shortRevision}} | ||||
|                 </span> | ||||
|             </td> | ||||
|             <td> | ||||
|               <template v-if="item.size"> | ||||
|                 {{itemSize(item)}} | ||||
|                 {{item.size}} | ||||
|                 · | ||||
|                 {{layers(item)}} | ||||
|               </template> | ||||
|               <div v-else class="light"> | ||||
|                 \- | ||||
|  | @ -103,18 +142,20 @@ | |||
|               </div> | ||||
|             </td> | ||||
| 
 | ||||
|             <td> | ||||
|               <button | ||||
|                 type="button" | ||||
|                 class="btn btn-remove" | ||||
|                 title="Remove tag" | ||||
|                 v-tooltip | ||||
|                 @click="deleteTag(item)"> | ||||
|                 <i | ||||
|                   class="fa fa-trash cred" | ||||
|                   aria-hidden="true"> | ||||
|                 </i> | ||||
|               </button> | ||||
|             <td class="content"> | ||||
|               <div class="controls hidden-xs pull-right"> | ||||
|                 <button | ||||
|                   type="button" | ||||
|                   class="btn btn-remove" | ||||
|                   title="Remove tag" | ||||
|                   v-tooltip | ||||
|                   @click="handleDeleteRegistry(item)"> | ||||
|                   <i | ||||
|                     class="fa fa-trash" | ||||
|                     aria-hidden="true"> | ||||
|                   </i> | ||||
|                 </button> | ||||
|               </div> | ||||
|             </td> | ||||
|           </tr> | ||||
|         </tbody> | ||||
|  | @ -123,9 +164,7 @@ | |||
|         v-else | ||||
|         class="nothing-here-block"> | ||||
|         {{__("No tags in Container Registry for this container image.")}} | ||||
| 
 | ||||
|       </div> | ||||
|     </div> | ||||
| 
 | ||||
|   </div> | ||||
| </template> | ||||
|  |  | |||
|  | @ -0,0 +1,13 @@ | |||
| export const errorMessagesTypes = { | ||||
|   FETCH_REGISTRY: 'FETCH_REGISTRY', | ||||
|   FETCH_REPOS: 'FETCH_REPOS', | ||||
|   DELETE_REPO: 'DELETE_REPO', | ||||
|   DELETE_REGISTRY: 'DELETE_REGISTRY', | ||||
| }; | ||||
| 
 | ||||
| export const errorMessages = { | ||||
|   [errorMessagesTypes.FETCH_REGISTRY]: 'Something went wrong while fetching the registry list.', | ||||
|   [errorMessagesTypes.FETCH_REPOS]: 'Something went wrong while fetching the repositories.', | ||||
|   [errorMessagesTypes.DELETE_REPO]: 'Something went wrong while deleting the repository.', | ||||
|   [errorMessagesTypes.DELETE_REGISTRY]: 'Something went wrong while deleting registry.', | ||||
| }; | ||||
|  | @ -0,0 +1,25 @@ | |||
| import Vue from 'vue'; | ||||
| import Translate from '../vue_shared/translate'; | ||||
| import registryApp from './components/app.vue'; | ||||
| 
 | ||||
| // Vue.use(Translate);
 | ||||
| 
 | ||||
| document.addEventListener('DOMContentLoaded', () => new Vue({ | ||||
|   el: '#js-vue-registry-images', | ||||
|   components: { | ||||
|     registryApp, | ||||
|   }, | ||||
|   data() { | ||||
|     const dataset = document.querySelector(this.$options.el).dataset; | ||||
|     return { | ||||
|       endpoint: dataset.endpoint, | ||||
|     }; | ||||
|   }, | ||||
|   render(createElement) { | ||||
|     return createElement('registry-app', { | ||||
|       props: { | ||||
|         endpoint: this.endpoint, | ||||
|       }, | ||||
|     }); | ||||
|   }, | ||||
| })); | ||||
|  | @ -16,13 +16,13 @@ export const fetchRepos = ({ commit, state }) => { | |||
| }; | ||||
| 
 | ||||
| export const fetchList = ({ commit }, list) => { | ||||
|   commit(types.TOGGLE_IMAGE_LOADING, list); | ||||
|   commit(types.TOGGLE_REGISTRY_LIST_LOADING, list); | ||||
| 
 | ||||
|   return Vue.http.get(list.path) | ||||
|     .then(res => res.json()) | ||||
|     .then((response) => { | ||||
|       commit(types.TOGGLE_IMAGE_LOADING, list); | ||||
|       commit(types.SET_IMAGES_LIST, list, response); | ||||
|       commit(types.TOGGLE_REGISTRY_LIST_LOADING, list); | ||||
|       commit(types.SET_REGISTRY_LIST, list, response); | ||||
|     }); | ||||
| }; | ||||
| 
 | ||||
|  | @ -32,8 +32,11 @@ export const deleteRepo = ({ commit }, repo) => Vue.http.delete(repo.path) | |||
|     commit(types.DELETE_REPO, repo); | ||||
|   }); | ||||
| 
 | ||||
| export const deleteImage = ({ commit }, image) => Vue.http.delete(image.path) | ||||
| export const deleteRegistry = ({ commit }, image) => Vue.http.delete(image.path) | ||||
|   .then(res => res.json()) | ||||
|   .then(() => { | ||||
|     commit(types.DELETE_IMAGE, image); | ||||
|   }); | ||||
| 
 | ||||
| export const setMainEndpoint = ({ commit }, data) => commit(types.SET_MAIN_ENDPOINT, data); | ||||
| export const toggleIsLoading = ({ commit }) => commit(types.TOGGLE_MAIN_LOADING); | ||||
|  |  | |||
|  | @ -0,0 +1,2 @@ | |||
| export const isLoading = state => state.isLoading; | ||||
| export const repos = state => state.repos; | ||||
|  | @ -1,6 +1,7 @@ | |||
| import Vue from 'vue'; | ||||
| import Vuex from 'vuex'; | ||||
| import actions from './actions'; | ||||
| import * as actions from './actions'; | ||||
| import * as getters from './getters'; | ||||
| import mutations from './mutations'; | ||||
| 
 | ||||
| Vue.use(Vuex); | ||||
|  | @ -31,7 +32,8 @@ export default new Vuex.Store({ | |||
|      * } | ||||
|      */ | ||||
|     repos: [], | ||||
|     actions, | ||||
|     mutations, | ||||
|   }, | ||||
|   actions, | ||||
|   getters, | ||||
|   mutations, | ||||
| }); | ||||
|  |  | |||
|  | @ -1,9 +1,10 @@ | |||
| export const SET_MAIN_ENDPOINT = 'SET_MAIN_ENDPOINT'; | ||||
| export const FETCH_REPOS_LIST = 'FETCH_REPOS_LIST'; | ||||
| export const DELETE_REPO = 'DELETE_REPO'; | ||||
| export const SET_REPOS_LIST = 'SET_REPOS_LIST'; | ||||
| export const TOGGLE_MAIN_LOADING = 'TOGGLE_MAIN_LOADING'; | ||||
| 
 | ||||
| export const FETCH_IMAGES_LIST = 'FETCH_IMAGES_LIST'; | ||||
| export const SET_IMAGES_LIST = 'SET_IMAGES_LIST'; | ||||
| export const SET_REGISTRY_LIST = 'SET_REGISTRY_LIST'; | ||||
| export const DELETE_IMAGE = 'DELETE_IMAGE'; | ||||
| export const TOGGLE_IMAGE_LOADING = 'TOGGLE_MAIN_LOADING'; | ||||
| export const TOGGLE_REGISTRY_LIST_LOADING = 'TOGGLE_REGISTRY_LIST_LOADING'; | ||||
|  |  | |||
|  | @ -1,14 +1,22 @@ | |||
| import * as types from './mutation_types'; | ||||
| 
 | ||||
| export default { | ||||
| 
 | ||||
|   [types.SET_MAIN_ENDPOINT](state, endpoint) { | ||||
|     Object.assign(state, { endpoint }); | ||||
|   }, | ||||
| 
 | ||||
|   [types.SET_REPOS_LIST](state, list) { | ||||
|     Object.assign(state, { | ||||
|       repos: list.map(el => ({ | ||||
|         name: el.name, | ||||
|         isLoading: false, | ||||
|         canDelete: !!el.destroy_path, | ||||
|         destroyPath: el.destroy_path, | ||||
|         isLoading: false, | ||||
|         list: [], | ||||
|         location: el.location, | ||||
|         name: el.name, | ||||
|         tagsPath: el.tags_path, | ||||
|         id: el.id, | ||||
|       })), | ||||
|     }); | ||||
|   }, | ||||
|  | @ -17,8 +25,29 @@ export default { | |||
|     Object.assign(state, { isLoading: !state.isLoading }); | ||||
|   }, | ||||
| 
 | ||||
|   [types.SET_IMAGES_LIST](state, image, list) { | ||||
|     const listToUpdate = state.repos.find(el => el.name === image.name); | ||||
|   [types.SET_REGISTRY_LIST](state, repo, list) { | ||||
|     // mock
 | ||||
|     list = [ | ||||
|       { | ||||
|         name: 'centos6', | ||||
|         short_revision: '0b6091a66', | ||||
|         revision: '0b6091a665af68bbbbb36a3e088ec3cd6f35389deebf6d4617042d56722d76fb', | ||||
|         size: 706, | ||||
|         layers: 19, | ||||
|         created_at: 1505828744434, | ||||
|       }, | ||||
|       { | ||||
|         name: 'centos7', | ||||
|         short_revision: 'b118ab5b0', | ||||
|         revision: 'b118ab5b0e90b7cb5127db31d5321ac14961d097516a8e0e72084b6cdc783b43', | ||||
|         size: 679, | ||||
|         layers: 19, | ||||
|         created_at: 1505828744434, | ||||
|       }, | ||||
|     ]; | ||||
| 
 | ||||
|     const listToUpdate = state.repos.find(el => el.id === repo.id); | ||||
| 
 | ||||
|     listToUpdate.list = list.map(element => ({ | ||||
|       tag: element.name, | ||||
|       revision: element.revision, | ||||
|  | @ -31,8 +60,8 @@ export default { | |||
|     })); | ||||
|   }, | ||||
| 
 | ||||
|   [types.TOGGLE_IMAGE_LOADING](state, image) { | ||||
|     const listToUpdate = state.repos.find(el => el.name === image.name); | ||||
|   [types.TOGGLE_REGISTRY_LIST_LOADING](state, list) { | ||||
|     const listToUpdate = state.repos.find(el => el.id === list.id); | ||||
|     listToUpdate.isLoading = !listToUpdate.isLoading; | ||||
|   }, | ||||
| }; | ||||
|  |  | |||
|  | @ -14,11 +14,11 @@ | |||
|       }, | ||||
|     }, | ||||
|     mounted() { | ||||
|       return new Clipboard(this.$refs.btn, { | ||||
|         text: () => { | ||||
|           return this.text; | ||||
|         }, | ||||
|       }); | ||||
|       // return new Clipboard(this.$refs.btn, { | ||||
|       //   text: () => { | ||||
|       //     return this.text; | ||||
|       //   }, | ||||
|       // }); | ||||
|     } | ||||
|   }; | ||||
| </script> | ||||
|  |  | |||
|  | @ -9,6 +9,10 @@ | |||
| .container-image-head { | ||||
|   padding: 0 16px; | ||||
|   line-height: 4em; | ||||
| 
 | ||||
|   &:hover { | ||||
|     text-decoration: underline; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .table.tags { | ||||
|  |  | |||
|  | @ -6,6 +6,37 @@ module Projects | |||
| 
 | ||||
|       def index | ||||
|         @images = project.container_repositories | ||||
| 
 | ||||
|         respond_to do |format| | ||||
|           format.html | ||||
|           format.json do | ||||
|            # render json: @images | ||||
|             render json: [ | ||||
|               { | ||||
|                 name: 'gitlab-org/omnibus-gitlab/foo', | ||||
|                 tags_path: 'foo', | ||||
|                 destroy_path: 'bar', | ||||
|                 location: 'foo', | ||||
|                 id: '134', | ||||
|                 destroy_path: 'bar' | ||||
|               }, | ||||
|               { | ||||
|                 name: 'gitlab-org/omnibus-gitlab', | ||||
|                 tags_path: 'foo', | ||||
|                 destroy_path: 'bar', | ||||
|                 location: 'foo', | ||||
|                 id: '123', | ||||
|               }, | ||||
|               { | ||||
|                 name: 'gitlab-org/omnibus-gitlab/bar', | ||||
|                 tags_path: 'foo', | ||||
|                 destroy_path: 'bar', | ||||
|                 location: 'foo', | ||||
|                 id: '973', | ||||
|               } | ||||
|             ] | ||||
|           end | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       def destroy | ||||
|  |  | |||
|  | @ -52,9 +52,15 @@ | |||
|             #{escape_once(@project.container_registry_url)}/optional-image-name:tag | ||||
|             #{escape_once(@project.container_registry_url)}/optional-name/optional-image-name:tag | ||||
| 
 | ||||
|     - if @images.blank? | ||||
|       %p.settings-message.text-center.append-bottom-default | ||||
|         No container images stored for this project. Add one by following the | ||||
|         instructions above. | ||||
|     - else | ||||
|       = render partial: 'image', collection: @images | ||||
|     #js-vue-registry-images{ data: { endpoint: project_container_registry_index_path(@project, format: :json)}} | ||||
| 
 | ||||
|     = page_specific_javascript_bundle_tag('common_vue') | ||||
|     = page_specific_javascript_bundle_tag('registry_list') | ||||
| 
 | ||||
| 
 | ||||
|     -# - if @images.blank? | ||||
|     -#   %p.settings-message.text-center.append-bottom-default | ||||
|     -#     No container images stored for this project. Add one by following the | ||||
|     -#     instructions above. | ||||
|     -# - else | ||||
|     -#   = render partial: 'image', collection: @images | ||||
|  |  | |||
|  | @ -67,6 +67,7 @@ var config = { | |||
|     prometheus_metrics:   './prometheus_metrics', | ||||
|     protected_branches:   './protected_branches', | ||||
|     protected_tags:       './protected_tags', | ||||
|     registry_list:        './registry/index.js', | ||||
|     repo:                 './repo/index.js', | ||||
|     sidebar:              './sidebar/sidebar_bundle.js', | ||||
|     schedule_form:        './pipeline_schedules/pipeline_schedule_form_bundle.js', | ||||
|  | @ -199,6 +200,7 @@ var config = { | |||
|         'pdf_viewer', | ||||
|         'pipelines', | ||||
|         'pipelines_details', | ||||
|         'registry_list', | ||||
|         'repo', | ||||
|         'schedule_form', | ||||
|         'schedules_index', | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue