Skip to content
This repository has been archived by the owner on Apr 17, 2023. It is now read-only.

Commit

Permalink
Merge pull request #1434 from vitoravelino/teams-index-vue
Browse files Browse the repository at this point in the history
teams: migrated index page to vue components
  • Loading branch information
mssola committed Oct 5, 2017
2 parents bb20213 + 452ec54 commit 1372853
Show file tree
Hide file tree
Showing 30 changed files with 572 additions and 211 deletions.
87 changes: 87 additions & 0 deletions app/assets/javascripts/modules/teams/components/new-form.js
@@ -0,0 +1,87 @@
import Vue from 'vue';

import { required } from 'vuelidate/lib/validators';

import EventBus from '~/utils/eventbus';
import Alert from '~/shared/components/alert';
import FormMixin from '~/shared/mixins/form';

import TeamsService from '../service';

const { set } = Vue;

export default {
template: '#js-new-team-form-tmpl',

mixins: [FormMixin],

data() {
return {
team: {
name: '',
},
timeout: {
name: null,
},
};
},

methods: {
onSubmit() {
TeamsService.save(this.team).then((response) => {
const team = response.data;

this.toggleForm();
this.$v.$reset();
set(this, 'team', {
name: '',
});

Alert.show(`Team '${team.name}' was created successfully`);
EventBus.$emit('teamCreated', team);
}).catch((response) => {
let errors = response.data;

if (Array.isArray(errors)) {
errors = errors.join('<br />');
}

Alert.show(errors);
});
},
},

validations: {
team: {
name: {
required,
available(value) {
clearTimeout(this.timeout.name);

// required already taking care of this
if (value === '') {
return true;
}

return new Promise((resolve) => {
const searchTeam = () => {
const promise = TeamsService.exists(value);

promise.then((exists) => {
// leave it for the back-end
if (exists === null) {
resolve(true);
}

// if it doesn't exist, valid
resolve(!exists);
});
};

this.timeout.name = setTimeout(searchTeam, 1000);
});
},
},
},
},
};
22 changes: 22 additions & 0 deletions app/assets/javascripts/modules/teams/components/table-row.js
@@ -0,0 +1,22 @@
export default {
template: '#js-team-table-row-tmpl',

props: ['team', 'teamsPath'],

computed: {
scopeClass() {
return `team_${this.team.id}`;
},

teamIcon() {
if (this.team.users_count > 1) {
return 'fa-users';
}
return 'fa-user';
},

teamPath() {
return `${this.teamsPath}/${this.team.id}`;
},
},
};
54 changes: 54 additions & 0 deletions app/assets/javascripts/modules/teams/components/table.js
@@ -0,0 +1,54 @@
import getProperty from 'lodash/get';

import Comparator from '~/utils/comparator';

import TableSortableMixin from '~/shared/mixins/table-sortable';
import TablePaginatedMixin from '~/shared/mixins/table-paginated';

import TeamTableRow from './table-row';

export default {
template: '#js-teams-table-tmpl',

props: {
teams: {
type: Array,
},
teamsPath: {
type: String,
},
prefix: {
type: String,
default: 'tm_',
},
},

mixins: [TableSortableMixin, TablePaginatedMixin],

components: {
TeamTableRow,
},

computed: {
filteredTeams() {
const order = this.sorting.asc ? 1 : -1;
const sortedTeams = [...this.teams];
const sample = sortedTeams[0];
const value = getProperty(sample, this.sorting.by);
const comparator = Comparator.of(value);

// sorting
sortedTeams.sort((a, b) => {
const aValue = getProperty(a, this.sorting.by);
const bValue = getProperty(b, this.sorting.by);

return order * comparator(aValue, bValue);
});

// pagination
const slicedTeams = sortedTeams.slice(this.offset, this.limit * this.currentPage);

return slicedTeams;
},
},
};
15 changes: 1 addition & 14 deletions app/assets/javascripts/modules/teams/index.js
@@ -1,15 +1,2 @@
import './pages/index';
import './pages/show';

import TeamsIndexPage from './pages/index';

const TEAMS_INDEX_ROUTE = 'teams/index';

$(() => {
const $body = $('body');
const route = $body.data('route');

if (route === TEAMS_INDEX_ROUTE) {
// eslint-disable-next-line
new TeamsIndexPage($body);
}
});
57 changes: 44 additions & 13 deletions app/assets/javascripts/modules/teams/pages/index.js
@@ -1,19 +1,50 @@
import BaseComponent from '~/base/component';
import Vue from 'vue';

import TeamsPanel from '../components/teams-panel';
import ToggleLink from '~/shared/components/toggle-link';
import EventBus from '~/utils/eventbus';

const TEAMS_PANEL = '.teams-wrapper';
import NewTeamForm from '../components/new-form';
import TeamsTable from '../components/table';
import TeamsStore from '../store';

// TeamsIndexPage component responsible to instantiate
// the teams index page components and handle interactions.
class TeamsIndexPage extends BaseComponent {
elements() {
this.$teamsPanel = this.$el.find(TEAMS_PANEL);
}
const { set } = Vue;

mount() {
this.teamsPanel = new TeamsPanel(this.$teamsPanel);
$(() => {
if (!$('body[data-route="teams/index"]').length) {
return;
}
}

export default TeamsIndexPage;
// eslint-disable-next-line no-new
new Vue({
el: 'body[data-route="teams/index"] .vue-root',

components: {
ToggleLink,
NewTeamForm,
TeamsTable,
},

data() {
return {
state: TeamsStore.state,
teams: window.teams,
};
},

methods: {
onCreate(team) {
const currentTeams = this.teams;
const teams = [
...currentTeams,
team,
];

set(this, 'teams', teams);
},
},

mounted() {
EventBus.$on('teamCreated', team => this.onCreate(team));
},
});
});
4 changes: 2 additions & 2 deletions app/assets/javascripts/modules/teams/pages/show.js
Expand Up @@ -9,8 +9,8 @@ import NewNamespaceForm from '../../namespaces/components/new-form';
import NamespacesStore from '../../namespaces/store';

// legacy
import TeamDetails from '../components/team-details';
import TeamUsersPanel from '../components/team-users-panel';
import TeamDetails from '../legacy-components/team-details';
import TeamUsersPanel from '../legacy-components/team-users-panel';

const { set } = Vue;

Expand Down
52 changes: 52 additions & 0 deletions app/assets/javascripts/modules/teams/service.js
@@ -0,0 +1,52 @@
import Vue from 'vue';
import VueResource from 'vue-resource';

Vue.use(VueResource);

const customActions = {
teamTypeahead: {
method: 'GET',
url: 'teams/typeahead/{teamName}',
},
};

const resource = Vue.resource('api/v1/teams{/id}', {}, customActions);

function all(params = {}) {
return resource.get({}, params);
}

function get(id) {
return resource.get({ id });
}

function save(team) {
return resource.save({}, team);
}

function searchTeam(teamName) {
return resource.teamTypeahead({ teamName });
}

function exists(value) {
return searchTeam(value)
.then((response) => {
const collection = response.data;

if (Array.isArray(collection)) {
return collection.some(e => e.name === value);
}

// some unexpected response from the api,
// leave it for the back-end validation
return null;
})
.catch(() => null);
}

export default {
get,
all,
save,
exists,
};
10 changes: 10 additions & 0 deletions app/assets/javascripts/modules/teams/store.js
@@ -0,0 +1,10 @@
class TeamsStore {
constructor() {
this.state = {
newFormVisible: false,
editFormVisible: false,
};
}
}

export default new TeamsStore();
31 changes: 5 additions & 26 deletions app/controllers/teams_controller.rb
Expand Up @@ -8,6 +8,11 @@ class TeamsController < ApplicationController
# GET /teams
def index
@teams = policy_scope(Team).page(params[:page])
@teams_serialized = API::Entities::Teams.represent(
@teams,
current_user: current_user,
type: :internal
).to_json
respond_with(@teams)
end

Expand All @@ -26,23 +31,6 @@ def show
)
end

# POST /teams
# POST /teams.json
def create
@team = fetch_team
authorize @team
@team.owners << current_user

if @team.save
@team.create_activity(:create,
owner: current_user,
parameters: { team: @team.name })
respond_with(@team)
else
respond_with @team.errors, status: :unprocessable_entity
end
end

# PATCH/PUT /teams/1
# PATCH/PUT /teams/1.json
def update
Expand Down Expand Up @@ -73,15 +61,6 @@ def all_with_query

private

# Fetch the team to be created from the given parameters.
def fetch_team
team = params.require(:team).permit(:name, :description)

@team = Team.new(name: team["name"])
@team.description = team["description"] if team["description"]
@team
end

def set_team
@team = Team.find(params[:id])
end
Expand Down
20 changes: 20 additions & 0 deletions app/services/teams/create_service.rb
@@ -0,0 +1,20 @@
module Teams
class CreateService < ::BaseService
def execute
@team = Team.new(params)
@team.owners << current_user

create_activity! if @team.save

@team
end

private

def create_activity!
@team.create_activity(:create,
owner: current_user,
parameters: { team: @team.name })
end
end
end

0 comments on commit 1372853

Please sign in to comment.