Skip to content

Commit 5862b37

Browse files
authored
Merge pull request doccano#148 from CatalystCode/enhancement/refactor-projects-page-to-vue-single-file-component
Enhancement/Refactor projects page to Vue single file component
2 parents ba9a8cb + 7f83f71 commit 5862b37

10 files changed

Lines changed: 1090 additions & 264 deletions

File tree

app/server/package-lock.json

Lines changed: 744 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/server/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"scripts": {
66
"start": "cross-env HOT_RELOAD=1 DEBUG=1 webpack-dev-server",
77
"build": "webpack",
8-
"lint": "eslint --max-warnings=0 static/js",
8+
"lint": "eslint --max-warnings=0 'static/js/**/*.{js,vue}'",
99
"test": "echo \"Error: no test specified\" && exit 1"
1010
},
1111
"author": "",
@@ -21,10 +21,14 @@
2121
"vue-shortkey": "^3.1.6"
2222
},
2323
"devDependencies": {
24+
"babel-eslint": "^10.0.1",
2425
"cross-env": "^5.2.0",
26+
"css-loader": "^2.1.1",
2527
"eslint": "^5.3.0",
2628
"eslint-config-airbnb-base": "^13.0.0",
2729
"eslint-plugin-import": "^2.13.0",
30+
"eslint-plugin-vue": "^5.2.2",
31+
"vue-template-compiler": "^2.5.16",
2832
"webpack": "^4.12.0",
2933
"webpack-bundle-tracker": "^0.4.2-beta",
3034
"webpack-cli": "^3.2.3",

app/server/static/js/.eslintrc.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ module.exports = {
99
},
1010
extends: [
1111
"airbnb-base",
12+
"plugin:vue/base",
1213
],
1314
rules: {
15+
"no-new": "off",
1416
"no-param-reassign": "off",
1517
"no-plusplus": "off",
1618
"object-shorthand": "off",

app/server/static/js/document_classification.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Vue from 'vue';
22
import annotationMixin from './mixin';
33
import HTTP from './http';
4-
import simpleShortcut from './filter';
4+
import { simpleShortcut } from './filter';
55

66
Vue.use(require('vue-shortkey'), {
77
prevent: ['input', 'textarea'],

app/server/static/js/filter.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,27 @@
1-
export default function simpleShortcut(shortcut) {
1+
export function simpleShortcut(shortcut) {
22
let simplified = shortcut === null ? '' : shortcut;
33
simplified = simplified.replace('ctrl', 'C');
44
simplified = simplified.replace('shift', 'S');
55
simplified = simplified.split(' ').join('-');
66
return simplified;
77
}
8+
9+
export function title(value) {
10+
const string = (value || '').toString();
11+
return string.charAt(0).toUpperCase() + string.slice(1);
12+
}
13+
14+
export function daysAgo(dateStr) {
15+
const updatedAt = new Date(dateStr);
16+
const currentTm = new Date();
17+
18+
// difference between days(ms)
19+
const msDiff = currentTm.getTime() - updatedAt.getTime();
20+
21+
// convert daysDiff(ms) to daysDiff(day)
22+
const daysDiff = Math.floor(msDiff / (1000 * 60 * 60 * 24));
23+
24+
return daysDiff === 1
25+
? `${daysDiff} day ago`
26+
: `${daysDiff} days ago`;
27+
}

app/server/static/js/label.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import Vue from 'vue';
22
import HTTP from './http';
3-
import simpleShortcut from './filter';
3+
import { simpleShortcut } from './filter';
44

55
Vue.filter('simpleShortcut', simpleShortcut);
66

app/server/static/js/projects.js

Lines changed: 4 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1,120 +1,10 @@
11
import Vue from 'vue';
2-
import axios from 'axios';
2+
import Projects from './projects.vue';
33

4-
axios.defaults.xsrfCookieName = 'csrftoken';
5-
axios.defaults.xsrfHeaderName = 'X-CSRFToken';
6-
const baseUrl = window.location.href.split('/').slice(0, 3).join('/');
7-
8-
9-
const vm = new Vue({ // eslint-disable-line no-unused-vars
4+
new Vue({
105
el: '#projects_root',
11-
delimiters: ['[[', ']]'],
12-
data: {
13-
items: [],
14-
isActive: false,
15-
isDelete: false,
16-
project: null,
17-
selected: 'All Project',
18-
projectName: '',
19-
description: '',
20-
projectType: '',
21-
descriptionError: '',
22-
projectTypeError: '',
23-
projectNameError: '',
24-
},
25-
26-
methods: {
27-
28-
deleteProject() {
29-
axios.delete(`${baseUrl}/v1/projects/${this.project.id}`).then(() => {
30-
this.isDelete = false;
31-
const index = this.items.indexOf(this.project);
32-
this.items.splice(index, 1);
33-
});
34-
},
35-
36-
setProject(project) {
37-
this.project = project;
38-
this.isDelete = true;
39-
},
40-
41-
matchType(projectType) {
42-
if (projectType === 'DocumentClassification') {
43-
return this.selected === 'Text Classification';
44-
}
45-
if (projectType === 'SequenceLabeling') {
46-
return this.selected === 'Sequence Labeling';
47-
}
48-
if (projectType === 'Seq2seq') {
49-
return this.selected === 'Seq2seq';
50-
}
51-
return false;
52-
},
53-
54-
getDaysAgo(dateStr) {
55-
const updatedAt = new Date(dateStr);
56-
const currentTm = new Date();
57-
58-
// difference between days(ms)
59-
const msDiff = currentTm.getTime() - updatedAt.getTime();
60-
61-
// convert daysDiff(ms) to daysDiff(day)
62-
const daysDiff = Math.floor(msDiff / (1000 * 60 * 60 * 24));
63-
64-
return daysDiff;
65-
},
66-
67-
create() {
68-
const payload = {
69-
name: this.projectName,
70-
description: this.description,
71-
project_type: this.projectType,
72-
guideline: 'Please write annotation guideline.',
73-
resourcetype: this.resourceType(),
74-
};
75-
axios.post(`${baseUrl}/v1/projects`, payload)
76-
.then((response) => {
77-
window.location = `${baseUrl}/projects/${response.data.id}/docs/create`;
78-
})
79-
.catch((error) => {
80-
this.projectTypeError = '';
81-
this.projectNameError = '';
82-
this.descriptionError = '';
83-
if ('resourcetype' in error.response.data) {
84-
this.projectTypeError = error.response.data.resourcetype;
85-
}
86-
if ('name' in error.response.data) {
87-
this.projectNameError = error.response.data.name[0];
88-
}
89-
if ('description' in error.response.data) {
90-
this.descriptionError = error.response.data.description[0];
91-
}
92-
});
93-
},
94-
95-
resourceType() {
96-
if (this.projectType === 'DocumentClassification') {
97-
return 'TextClassificationProject';
98-
}
99-
if (this.projectType === 'SequenceLabeling') {
100-
return 'SequenceLabelingProject';
101-
}
102-
if (this.projectType === 'Seq2seq') {
103-
return 'Seq2seqProject';
104-
}
105-
return '';
106-
},
107-
},
1086

109-
computed: {
110-
selectedProjects() {
111-
return this.items.filter(item => this.selected === 'All Project' || this.matchType(item.project_type));
112-
},
113-
},
7+
components: { Projects },
1148

115-
created() {
116-
axios.get(`${baseUrl}/v1/projects`).then((response) => {
117-
this.items = response.data;
118-
});
119-
},
9+
template: '<Projects />',
12010
});

0 commit comments

Comments
 (0)