mirror of https://github.com/doccano/doccano.git
27 changed files with 629 additions and 103 deletions
Split View
Diff Options
-
1Pipfile
-
8README.md
-
28app/server/static/components/annotation.pug
-
66app/server/static/components/annotationMixin.js
-
4docs/project_structure.md
-
99frontend/components/containers/comments/Comment.vue
-
114frontend/components/containers/comments/CommentSection.vue
-
9frontend/components/containers/documents/DocumentList.vue
-
13frontend/components/organisms/annotation/EntityItemBox.vue
-
39frontend/components/organisms/documents/DocumentUploadForm.vue
-
4frontend/components/organisms/layout/TheSideBar.vue
-
6frontend/i18n/en/index.js
-
10frontend/i18n/en/projects/comments.js
-
2frontend/i18n/en/projects/dataset.js
-
2frontend/i18n/en/projects/errors.js
-
3frontend/i18n/en/projects/settings.js
-
2frontend/i18n/en/rules.js
-
8frontend/i18n/index.js
-
13frontend/layouts/annotation.vue
-
18frontend/package-lock.json
-
2frontend/package.json
-
26frontend/pages/projects/_id/comments/index.vue
-
225frontend/pages/projects/_id/settings/index.vue
-
2frontend/rules/index.js
-
10frontend/yarn.lock
-
2nginx/nginx.conf
-
16package-lock.json
@ -0,0 +1,99 @@ |
|||
<template> |
|||
<div> |
|||
<v-timeline-item |
|||
small |
|||
> |
|||
<div class="font-weight-normal"> |
|||
<strong>{{ comment.username }}</strong> @{{ comment.created_at | dateParse('YYYY-MM-DDTHH:mm:ss') | dateFormat('YYYY-MM-DD HH:mm') }} |
|||
<v-tooltip top> |
|||
<template v-slot:activator="{ on, attrs }"> |
|||
<v-btn |
|||
v-if="comment.user == userId" |
|||
icon |
|||
color="green" |
|||
v-bind="attrs" |
|||
v-on="on" |
|||
@click="showEdit=true" |
|||
> |
|||
<v-icon>mdi-comment-edit-outline</v-icon> |
|||
</v-btn> |
|||
</template> |
|||
<span>Edit Comment</span> |
|||
</v-tooltip> |
|||
<v-tooltip top> |
|||
<template v-slot:activator="{ on, attrs }"> |
|||
<v-btn |
|||
v-if="comment.user == userId" |
|||
icon |
|||
color="red" |
|||
v-bind="attrs" |
|||
v-on="on" |
|||
@click="$emit('delete-comment', comment)" |
|||
> |
|||
<v-icon>mdi-delete-outline</v-icon> |
|||
</v-btn> |
|||
</template> |
|||
<span>Delete Comment</span> |
|||
</v-tooltip> |
|||
</div> |
|||
<div v-if="!showEdit"> |
|||
{{ comment.text }} |
|||
</div> |
|||
<div v-else> |
|||
<v-textarea |
|||
v-model="editText" |
|||
solo |
|||
/> |
|||
<div> |
|||
<v-btn |
|||
color="red" |
|||
@click="showEdit=false" |
|||
> |
|||
Close |
|||
</v-btn> |
|||
<v-btn |
|||
color="green" |
|||
@click="updateComment(editText)" |
|||
> |
|||
Update |
|||
</v-btn> |
|||
</div> |
|||
</div> |
|||
</v-timeline-item> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
|
|||
import Vue from 'vue' |
|||
import VueFilterDateFormat from '@vuejs-community/vue-filter-date-format' |
|||
import VueFilterDateParse from '@vuejs-community/vue-filter-date-parse' |
|||
Vue.use(VueFilterDateFormat) |
|||
Vue.use(VueFilterDateParse) |
|||
|
|||
export default { |
|||
name: 'Comment', |
|||
props: { |
|||
comment: { |
|||
required: true, |
|||
type: Object |
|||
}, |
|||
userId: { |
|||
required: true, |
|||
type: Number |
|||
} |
|||
}, |
|||
data() { |
|||
return { |
|||
showEdit: false, |
|||
editText: this.comment.text |
|||
} |
|||
}, |
|||
methods: { |
|||
updateComment(newText) { |
|||
this.showEdit = false |
|||
this.$emit('update-comment', this.comment.id, newText) |
|||
} |
|||
} |
|||
} |
|||
</script> |
@ -0,0 +1,114 @@ |
|||
<template> |
|||
<div> |
|||
<div class="font-weight-bold ml-8 mb-2"> |
|||
{{ this.$t('comments.comments') }} |
|||
</div> |
|||
|
|||
<v-timeline |
|||
align-top |
|||
dense |
|||
> |
|||
<v-timeline-item |
|||
fill-dot |
|||
class="mb-12" |
|||
color="green" |
|||
large |
|||
> |
|||
<v-textarea |
|||
v-model="message" |
|||
outlined |
|||
name="CommentInput" |
|||
:label="this.$t('comments.message')" |
|||
value="" |
|||
/> |
|||
<v-btn |
|||
class="white--text" |
|||
color="green" |
|||
depressed |
|||
:disabled="message.length === 0" |
|||
@click="add" |
|||
> |
|||
{{ this.$t('comments.send') }} |
|||
</v-btn> |
|||
</v-timeline-item> |
|||
<comment |
|||
v-for="comment in comments" |
|||
:key="comment.id" |
|||
:comment="comment" |
|||
:user-id="userId" |
|||
@delete-comment="remove" |
|||
@update-comment="update" |
|||
/> |
|||
</v-timeline> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
|
|||
import { mapActions, mapState, mapMutations } from 'vuex' |
|||
import Comment from './Comment' |
|||
|
|||
export default { |
|||
name: 'CommentSection', |
|||
components: { Comment }, |
|||
fetch() { |
|||
this.getMyUserId() |
|||
}, |
|||
data() { |
|||
return { |
|||
message: '' |
|||
} |
|||
}, |
|||
computed: { |
|||
...mapState('documents', ['items', 'total', 'current', 'selected']), |
|||
...mapState('comments', ['comments', 'userId']) |
|||
}, |
|||
watch: { |
|||
total() { |
|||
this.getCommentList({ |
|||
projectId: this.$route.params.id, |
|||
docId: this.items[this.current].id |
|||
}) |
|||
}, |
|||
current: { |
|||
handler() { |
|||
if (this.total !== 0) { |
|||
this.getCommentList({ |
|||
projectId: this.$route.params.id, |
|||
docId: this.items[this.current].id |
|||
}) |
|||
} |
|||
}, |
|||
immediate: true |
|||
} |
|||
}, |
|||
methods: { |
|||
add() { |
|||
this.addComment({ |
|||
text: this.message, |
|||
projectId: this.$route.params.id, |
|||
docId: this.items[this.current].id |
|||
}) |
|||
this.message = '' |
|||
}, |
|||
remove(comment) { |
|||
this.updateSelectedComments([comment]) |
|||
this.deleteComment({ |
|||
projectId: this.$route.params.id, |
|||
docId: this.items[this.current].id |
|||
}) |
|||
}, |
|||
update(commentId, text) { |
|||
this.updateComment({ |
|||
projectId: this.$route.params.id, |
|||
docId: this.items[this.current].id, |
|||
commentId, |
|||
text |
|||
}) |
|||
}, |
|||
...mapActions('comments', ['addComment', 'getCommentList', 'deleteComment', 'updateComment', 'getMyUserId']), |
|||
...mapMutations('comments', ['updateSelectedComments']) |
|||
} |
|||
} |
|||
|
|||
</script> |
@ -0,0 +1,10 @@ |
|||
export default { |
|||
comments: 'Comments', |
|||
removeComment: 'Remove Comment', |
|||
removePrompt: 'Are you sure you want to remove these comments?', |
|||
commentView: 'View', |
|||
created_at: 'Created at', |
|||
document: 'Document', |
|||
send: 'Send', |
|||
message: 'Message' |
|||
} |
@ -1,5 +1,5 @@ |
|||
export default { |
|||
fileCannotUpload: 'The file could not be uploaded. Maybe invalid format.\n Please check available formats carefully.', |
|||
fileCannotUpload: 'The file(s) could not be uploaded. Maybe invalid format.\n Please check available formats and the following file(s): ', |
|||
labelCannotCreate: 'The label could not be created.\n You cannot use the same label name or shortcut key.', |
|||
invalidUserOrPass: 'Incorrect username or password, or something went wrong.' |
|||
} |
@ -0,0 +1,3 @@ |
|||
export default { |
|||
title: 'Settings' |
|||
} |
@ -0,0 +1,18 @@ |
|||
{ |
|||
"name": "doccano", |
|||
"version": "1.0.0", |
|||
"lockfileVersion": 1, |
|||
"requires": true, |
|||
"dependencies": { |
|||
"@vuejs-community/vue-filter-date-format": { |
|||
"version": "1.6.3", |
|||
"resolved": "https://registry.npmjs.org/@vuejs-community/vue-filter-date-format/-/vue-filter-date-format-1.6.3.tgz", |
|||
"integrity": "sha512-fUjAAI/1qzJPR6AYoK00TBA6/aRYKTXjLY/rSYsWV4G5nwywKfrpOYOJi5exWyzozohyV6nrzliDlWcl32+IPg==" |
|||
}, |
|||
"@vuejs-community/vue-filter-date-parse": { |
|||
"version": "1.1.6", |
|||
"resolved": "https://registry.npmjs.org/@vuejs-community/vue-filter-date-parse/-/vue-filter-date-parse-1.1.6.tgz", |
|||
"integrity": "sha512-OzeiL5wrq+5+sw9hJfcfaIxcG953lpe37Lf0JnRKzVvfBRTHX2t8SIVxtDmgLhSGoGx9B4bGbpv4NaIqeEofXA==" |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,26 @@ |
|||
<template> |
|||
<v-card> |
|||
<v-card-title> |
|||
<comment-deletion-button /> |
|||
</v-card-title> |
|||
<comment-list /> |
|||
</v-card> |
|||
</template> |
|||
|
|||
<script> |
|||
import CommentList from '@/components/containers/comments/CommentList' |
|||
import CommentDeletionButton from '@/components/containers/comments/CommentDeletionButton' |
|||
|
|||
export default { |
|||
layout: 'project', |
|||
|
|||
components: { |
|||
CommentList, |
|||
CommentDeletionButton |
|||
}, |
|||
|
|||
validate({ params }) { |
|||
return /^\d+$/.test(params.id) |
|||
} |
|||
} |
|||
</script> |
@ -0,0 +1,225 @@ |
|||
<template> |
|||
<v-card> |
|||
<v-card-title class="mb-2"> |
|||
<h2>About project</h2> |
|||
</v-card-title> |
|||
<v-card-text v-if="isReady"> |
|||
<v-form |
|||
ref="form" |
|||
v-model="valid" |
|||
> |
|||
<v-row> |
|||
<v-col |
|||
cols="12" |
|||
sm="6" |
|||
> |
|||
<h3>Name</h3> |
|||
<v-text-field |
|||
v-model="project.name" |
|||
label="Add project name" |
|||
:rules="projectNameRules($t('rules.projectNameRules'))" |
|||
:disabled="!edit.name" |
|||
single-line |
|||
/> |
|||
</v-col> |
|||
<v-col |
|||
cols="12" |
|||
sm="6" |
|||
> |
|||
<v-btn |
|||
v-if="!edit.name" |
|||
outlined |
|||
color="grey" |
|||
class="text-capitalize" |
|||
@click="editProject('name')" |
|||
> |
|||
Edit |
|||
</v-btn> |
|||
<v-btn |
|||
v-if="edit.name" |
|||
outlined |
|||
color="primary" |
|||
class="text-capitalize" |
|||
@click="doneEdit()" |
|||
> |
|||
Save |
|||
</v-btn> |
|||
<v-btn |
|||
v-if="edit.name" |
|||
outlined |
|||
color="grey" |
|||
class="text-capitalize" |
|||
@click="cancelEdit()" |
|||
> |
|||
Cancel |
|||
</v-btn> |
|||
</v-col> |
|||
</v-row> |
|||
<v-row> |
|||
<v-col |
|||
cols="12" |
|||
sm="6" |
|||
> |
|||
<h3>Description</h3> |
|||
<v-text-field |
|||
v-model="project.description" |
|||
label="Add description" |
|||
:rules="descriptionRules($t('rules.descriptionRules'))" |
|||
:disabled="!edit.desc" |
|||
single-line |
|||
/> |
|||
</v-col> |
|||
<v-col |
|||
cols="12" |
|||
sm="6" |
|||
> |
|||
<v-btn |
|||
v-if="!edit.desc" |
|||
outlined |
|||
color="grey" |
|||
class="text-capitalize" |
|||
@click="editProject('desc')" |
|||
> |
|||
Edit |
|||
</v-btn> |
|||
<v-btn |
|||
v-if="edit.desc" |
|||
outlined |
|||
color="primary" |
|||
class="text-capitalize" |
|||
@click="doneEdit()" |
|||
> |
|||
Save |
|||
</v-btn> |
|||
<v-btn |
|||
v-if="edit.desc" |
|||
outlined |
|||
color="grey" |
|||
class="text-capitalize" |
|||
@click="cancelEdit()" |
|||
> |
|||
Cancel |
|||
</v-btn> |
|||
</v-col> |
|||
</v-row> |
|||
<v-row> |
|||
<v-col |
|||
cols="12" |
|||
sm="6" |
|||
> |
|||
<h3>Shuffle</h3> |
|||
<v-checkbox |
|||
v-model="project.randomize_document_order" |
|||
:label="$t('overview.randomizeDocOrder')" |
|||
/> |
|||
</v-col> |
|||
</v-row> |
|||
<v-row> |
|||
<v-col |
|||
cols="12" |
|||
sm="6" |
|||
> |
|||
<h3>Collaboration</h3> |
|||
<v-checkbox |
|||
v-model="project.collaborative_annotation" |
|||
:label="$t('overview.shareAnnotations')" |
|||
/> |
|||
</v-col> |
|||
</v-row> |
|||
</v-form> |
|||
</v-card-text> |
|||
</v-card> |
|||
</template> |
|||
|
|||
<script> |
|||
import ProjectService from '@/services/project.service' |
|||
import { projectNameRules, descriptionRules } from '@/rules/index' |
|||
|
|||
export default { |
|||
layout: 'project', |
|||
|
|||
data() { |
|||
return { |
|||
project: {}, |
|||
beforeEditCache: {}, |
|||
edit: { |
|||
name: false, |
|||
desc: false |
|||
}, |
|||
projectNameRules, |
|||
descriptionRules, |
|||
valid: false |
|||
} |
|||
}, |
|||
|
|||
computed: { |
|||
isReady() { |
|||
return !!this.project |
|||
} |
|||
}, |
|||
|
|||
watch: { |
|||
'project.randomize_document_order'() { |
|||
this.doneEdit() |
|||
}, |
|||
'project.collaborative_annotation'() { |
|||
this.doneEdit() |
|||
} |
|||
}, |
|||
|
|||
created() { |
|||
const projectId = this.$route.params.id |
|||
ProjectService.fetchProjectById(projectId) |
|||
.then((response) => { |
|||
this.project = response.data |
|||
}) |
|||
.catch((error) => { |
|||
alert(error) |
|||
}) |
|||
}, |
|||
|
|||
methods: { |
|||
initEdit() { |
|||
Object.keys(this.edit).forEach((v) => { this.edit[v] = false }) |
|||
}, |
|||
|
|||
editProject(name) { |
|||
this.cancelEdit() |
|||
this.edit[name] = true |
|||
Object.assign(this.beforeEditCache, this.project) |
|||
}, |
|||
|
|||
cancelEdit() { |
|||
this.initEdit() |
|||
Object.assign(this.project, this.beforeEditCache) |
|||
}, |
|||
|
|||
doneEdit() { |
|||
if (!this.validate()) { |
|||
this.cancelEdit() |
|||
return |
|||
} |
|||
const projectId = this.$route.params.id |
|||
ProjectService.updateProject(projectId, this.project) |
|||
.then((response) => { |
|||
this.project = response.data |
|||
this.beforeEditCache = {} |
|||
}) |
|||
.catch((error) => { |
|||
alert(error) |
|||
}) |
|||
.finally(() => { |
|||
this.initEdit() |
|||
}) |
|||
}, |
|||
|
|||
validate() { |
|||
return this.$refs.form.validate() |
|||
} |
|||
}, |
|||
|
|||
validate({ params }) { |
|||
return /^\d+$/.test(params.id) |
|||
} |
|||
} |
|||
</script> |
@ -0,0 +1,16 @@ |
|||
{ |
|||
"requires": true, |
|||
"lockfileVersion": 1, |
|||
"dependencies": { |
|||
"@vuejs-community/vue-filter-date-format": { |
|||
"version": "1.6.3", |
|||
"resolved": "https://registry.npmjs.org/@vuejs-community/vue-filter-date-format/-/vue-filter-date-format-1.6.3.tgz", |
|||
"integrity": "sha512-fUjAAI/1qzJPR6AYoK00TBA6/aRYKTXjLY/rSYsWV4G5nwywKfrpOYOJi5exWyzozohyV6nrzliDlWcl32+IPg==" |
|||
}, |
|||
"@vuejs-community/vue-filter-date-parse": { |
|||
"version": "1.1.6", |
|||
"resolved": "https://registry.npmjs.org/@vuejs-community/vue-filter-date-parse/-/vue-filter-date-parse-1.1.6.tgz", |
|||
"integrity": "sha512-OzeiL5wrq+5+sw9hJfcfaIxcG953lpe37Lf0JnRKzVvfBRTHX2t8SIVxtDmgLhSGoGx9B4bGbpv4NaIqeEofXA==" |
|||
} |
|||
} |
|||
} |
Write
Preview
Loading…
Cancel
Save