@@ -0,0 +1,122 @@ | |||||
<template> | |||||
<form id="password-generator-form"> | |||||
<div class="form-group row"> | |||||
<div class="col-lg-6 m-t-1"> | |||||
<label for="pg-email" class="sr-only"> | |||||
{{ $t('passwordgenerator.who_are_you') }} | |||||
</label> | |||||
<input id="pg-email" | |||||
class="form-control" | |||||
type="text" | |||||
placeholder="{{ $t('passwordgenerator.who_are_you') }}" | |||||
value="{{entry.email}}" | |||||
v-model="entry.email" | |||||
autofocus | |||||
autocomplete="off" | |||||
autocorrect="off" | |||||
autocapitalize="none"> | |||||
</div> | |||||
</div> | |||||
<div class="form-group row"> | |||||
<div class="col-lg-12"> | |||||
<label for="pg-site" class="sr-only"> | |||||
{{ $t('passwordgenerator.where_are_you_going') }} | |||||
</label> | |||||
<input id="pg-site" | |||||
class="form-control" | |||||
list="domains" | |||||
type="text" | |||||
placeholder="{{ $t('passwordgenerator.where_are_you_going') }}" | |||||
value="{{entry.site}}" | |||||
v-model="entry.site" | |||||
autocorrect="off" | |||||
autocapitalize="none"> | |||||
<datalist id="domains"> | |||||
<option v-for="domain in domains" v-bind:value="domain"> | |||||
</datalist> | |||||
</div> | |||||
</div> | |||||
<div class="form-group row m-b-0"> | |||||
<div class="col-lg-12"> | |||||
<div class="row"> | |||||
<div class="col-lg-5"> | |||||
<label class="c-input c-checkbox"> | |||||
<input type="checkbox" id="lowercase" value="lowercase" | |||||
v-model="entry.password.settings"> | |||||
<span class="c-indicator"></span> | |||||
{{ $t('passwordgenerator.lowercase_options') }} | |||||
</label> | |||||
</div> | |||||
<div class="col-lg-7"> | |||||
<label class="c-input c-checkbox"> | |||||
<input type="checkbox" id="uppercase" value="uppercase" | |||||
v-model="entry.password.settings"> | |||||
<span class="c-indicator"></span> | |||||
{{ $t('passwordgenerator.uppercase_options') }} | |||||
</label> | |||||
</div> | |||||
</div> | |||||
<div class="row"> | |||||
<div class="col-lg-5"> | |||||
<label class="c-input c-checkbox"> | |||||
<input type="checkbox" id="numbers" value="numbers" | |||||
v-model="entry.password.settings"> | |||||
<span class="c-indicator"></span> | |||||
{{ $t('passwordgenerator.numbers_options') }} | |||||
</label> | |||||
</div> | |||||
<div class="col-lg-7"> | |||||
<label class="c-input c-checkbox"> | |||||
<input type="checkbox" id="symbols" value="symbols" | |||||
v-model="entry.password.settings"> | |||||
<span class="c-indicator"></span> | |||||
{{ $t('passwordgenerator.symbols_options') }} | |||||
</label> | |||||
</div> | |||||
</div> | |||||
<div class="row m-t-1"> | |||||
<div class="col-lg-5 m-b-1"> | |||||
<label for="passwordLength" class="sr-only"> | |||||
{{ $t('passwordgenerator.length') }} | |||||
</label> | |||||
<div class="input-group input-group-sm"> | |||||
<span class="input-group-addon" id="passwordLengthAddon"> | |||||
{{ $t('passwordgenerator.length') }} | |||||
</span> | |||||
<input type="number" class="form-control" id="passwordLength" | |||||
aria-describedby="passwordLengthAddon" | |||||
v-model="entry.password.length" | |||||
value="{{entry.password.length}}" min="6" max="64"> | |||||
</div> | |||||
</div> | |||||
<div class="col-lg-4 m-b-1"> | |||||
<label for="passwordCounter" class="sr-only"> | |||||
{{ $t('passwordgenerator.counter') }} | |||||
</label> | |||||
<div class="input-group input-group-sm"> | |||||
<span class="input-group-addon" id="passwordCounterAddon"> | |||||
{{ $t('passwordgenerator.counter') }} | |||||
</span> | |||||
<input type="number" class="form-control" id="passwordCounter" | |||||
aria-describedby="passwordCounterAddon" | |||||
v-model="entry.password.counter" | |||||
value="{{entry.password.counter}}" min="1" max="100"> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</form> | |||||
</template> | |||||
<script type="text/ecmascript-6"> | |||||
import topDomains from '../../landing-page/PasswordGenerator/top-domains.json'; | |||||
export default { | |||||
data() { | |||||
return { | |||||
domains: topDomains.domains | |||||
}; | |||||
}, | |||||
props: ['entry'] | |||||
}; | |||||
</script> |
@@ -1,13 +1,12 @@ | |||||
<template> | <template> | ||||
<div class="row"> | <div class="row"> | ||||
<div class="col-lg-12"> | |||||
<button type="button" class="btn btn-primary pull-xs-right" data-toggle="modal" | |||||
<div class="col-xs-12 col-md-8 col-lg-6 pull-xs-left pull-md-right"> | |||||
<button type="button" class="btn btn-primary btn-block" data-toggle="modal" | |||||
data-target="#newEntryModal"> | data-target="#newEntryModal"> | ||||
create new password | |||||
{{{ $t('entry.create_new_entry') }}} | |||||
</button> | </button> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<div class="modal fade" id="newEntryModal" tabindex="-1" role="dialog" aria-labelledby="newEntry" | <div class="modal fade" id="newEntryModal" tabindex="-1" role="dialog" aria-labelledby="newEntry" | ||||
aria-hidden="true"> | aria-hidden="true"> | ||||
<div class="modal-dialog" role="document"> | <div class="modal-dialog" role="document"> | ||||
@@ -16,162 +15,52 @@ | |||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> | <button type="button" class="close" data-dismiss="modal" aria-label="Close"> | ||||
<span aria-hidden="true">×</span> | <span aria-hidden="true">×</span> | ||||
</button> | </button> | ||||
<h4 class="modal-title" id="newEntry">Create new password</h4> | |||||
<h4 class="modal-title" id="newEntry">{{{ $t('entry.Create_new_entry') }}}</h4> | |||||
</div> | </div> | ||||
<div class="modal-body text-xs-left"> | <div class="modal-body text-xs-left"> | ||||
<form id="password-generator-form"> | |||||
<div class="form-group row"> | |||||
<div class="col-lg-6 m-t-1"> | |||||
<label for="pg-email" class="sr-only"> | |||||
{{ $t('passwordgenerator.who_are_you') }} | |||||
</label> | |||||
<input id="pg-email" | |||||
class="form-control" | |||||
type="text" | |||||
placeholder="{{ $t('passwordgenerator.who_are_you') }}" | |||||
value="{{email}}" | |||||
v-model="email" | |||||
autofocus | |||||
autocomplete="off" | |||||
autocorrect="off" | |||||
autocapitalize="none"> | |||||
</div> | |||||
</div> | |||||
<div class="form-group row"> | |||||
<div class="col-lg-12"> | |||||
<label for="pg-site" class="sr-only"> | |||||
{{ $t('passwordgenerator.where_are_you_going') }} | |||||
</label> | |||||
<input id="pg-site" | |||||
class="form-control" | |||||
list="domains" | |||||
type="text" | |||||
placeholder="{{ $t('passwordgenerator.where_are_you_going') }}" | |||||
v-model="site" | |||||
autocorrect="off" | |||||
autocapitalize="none"> | |||||
<datalist id="domains"> | |||||
<option v-for="domain in domains" v-bind:value="domain"> | |||||
</datalist> | |||||
</div> | |||||
</div> | |||||
<div class="form-group row m-b-0"> | |||||
<div class="col-lg-12"> | |||||
<div class="row"> | |||||
<div class="col-lg-5"> | |||||
<label class="c-input c-checkbox"> | |||||
<input type="checkbox" id="lowercase" value="lowercase" | |||||
v-model="passwordInfo.settings" checked> | |||||
<span class="c-indicator"></span> | |||||
{{ $t('passwordgenerator.lowercase_options') }} | |||||
</label> | |||||
</div> | |||||
<div class="col-lg-7"> | |||||
<label class="c-input c-checkbox"> | |||||
<input type="checkbox" id="uppercase" value="uppercase" | |||||
v-model="passwordInfo.settings" checked> | |||||
<span class="c-indicator"></span> | |||||
{{ $t('passwordgenerator.uppercase_options') }} | |||||
</label> | |||||
</div> | |||||
</div> | |||||
<div class="row"> | |||||
<div class="col-lg-5"> | |||||
<label class="c-input c-checkbox"> | |||||
<input type="checkbox" id="numbers" value="numbers" | |||||
v-model="passwordInfo.settings" | |||||
checked> | |||||
<span class="c-indicator"></span> | |||||
{{ $t('passwordgenerator.numbers_options') }} | |||||
</label> | |||||
</div> | |||||
<div class="col-lg-7"> | |||||
<label class="c-input c-checkbox"> | |||||
<input type="checkbox" id="symbols" value="symbols" | |||||
v-model="passwordInfo.settings" | |||||
checked> | |||||
<span class="c-indicator"></span> | |||||
{{ $t('passwordgenerator.symbols_options') }} | |||||
</label> | |||||
</div> | |||||
</div> | |||||
<div class="row m-t-1"> | |||||
<div class="col-lg-5 m-b-1"> | |||||
<label for="passwordLength" class="sr-only"> | |||||
{{ $t('passwordgenerator.length') }} | |||||
</label> | |||||
<div class="input-group input-group-sm"> | |||||
<span class="input-group-addon" id="passwordLengthAddon"> | |||||
{{ $t('passwordgenerator.length') }} | |||||
</span> | |||||
<input type="number" class="form-control" id="passwordLength" | |||||
aria-describedby="passwordLengthAddon" | |||||
v-model="passwordInfo.length" | |||||
value="12" min="6" max="64"> | |||||
</div> | |||||
</div> | |||||
<div class="col-lg-4 m-b-1"> | |||||
<label for="passwordCounter" class="sr-only"> | |||||
{{ $t('passwordgenerator.counter') }} | |||||
</label> | |||||
<div class="input-group input-group-sm"> | |||||
<span class="input-group-addon" id="passwordCounterAddon"> | |||||
{{ $t('passwordgenerator.counter') }} | |||||
</span> | |||||
<input type="number" class="form-control" id="passwordCounter" | |||||
aria-describedby="passwordCounterAddon" | |||||
v-model="passwordInfo.counter" | |||||
value="1" min="1" max="100"> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</form> | |||||
<entry-form :entry="entry" v-bind:prop.sync></entry-form> | |||||
</div> | </div> | ||||
<div class="modal-footer"> | <div class="modal-footer"> | ||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button> | |||||
<button type="button" class="btn btn-primary" @click="create()">Create</button> | |||||
<button type="button" class="btn btn-secondary" data-dismiss="modal"> | |||||
{{{ $t('entry.Cancel') }}} | |||||
</button> | |||||
<button type="button" class="btn btn-primary" @click="create()"> | |||||
{{{ $t('entry.Create') }}} | |||||
</button> | |||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
</template> | </template> | ||||
<script type="text/ecmascript-6"> | <script type="text/ecmascript-6"> | ||||
import 'bootstrap/dist/js/umd/modal'; | |||||
import topDomains from '../../landing-page/PasswordGenerator/top-domains.json'; | |||||
import http from '../../services/http'; | import http from '../../services/http'; | ||||
import logging from '../../services/logging'; | import logging from '../../services/logging'; | ||||
import EntryForm from './EntryForm'; | |||||
export default { | export default { | ||||
data() { | data() { | ||||
return { | return { | ||||
email: '', | |||||
site: '', | |||||
passwordInfo: { | |||||
counter: 1, | |||||
length: 12, | |||||
settings: ["lowercase", "uppercase", "numbers", "symbols"] | |||||
}, | |||||
domains: topDomains.domains | |||||
entry: { | |||||
email: '', | |||||
site: '', | |||||
password: { | |||||
counter: 1, | |||||
length: 12, | |||||
settings: ["lowercase", "uppercase", "numbers", "symbols"] | |||||
}, | |||||
} | |||||
}; | }; | ||||
}, | }, | ||||
components: { | |||||
EntryForm, | |||||
}, | |||||
methods: { | methods: { | ||||
create() { | create() { | ||||
var entry = { | |||||
email: this.email, | |||||
site: this.site, | |||||
password: { | |||||
counter: this.passwordInfo.counter, | |||||
length: this.passwordInfo.length, | |||||
settings: this.passwordInfo.settings, | |||||
} | |||||
}; | |||||
if (entry.site === '') { | |||||
if (this.entry.site === '') { | |||||
logging.error(this.$t('entries.site_mandatory')); | logging.error(this.$t('entries.site_mandatory')); | ||||
return; | return; | ||||
} | } | ||||
http.entries.create(entry) | |||||
http.entries.create(this.entry) | |||||
.then(() => { | .then(() => { | ||||
$('#newEntryModal').modal('hide'); | $('#newEntryModal').modal('hide'); | ||||
logging.success(this.$t('entries.entry_created')); | logging.success(this.$t('entries.entry_created')); | ||||
@@ -3,35 +3,63 @@ | |||||
box-shadow: 0 2px 15px rgba(0, 0, 0, 0.30); | box-shadow: 0 2px 15px rgba(0, 0, 0, 0.30); | ||||
cursor: pointer; | cursor: pointer; | ||||
} | } | ||||
.card .edit-icon { | |||||
display: block; | |||||
position: absolute; | |||||
top: 10px; | |||||
right: 10px; | |||||
} | |||||
@media (min-width: 480px) { | |||||
.card .edit-icon { | |||||
display: none; | |||||
} | |||||
.card:hover .edit-icon:hover { | |||||
color: inherit; | |||||
} | |||||
.card:hover .edit-icon { | |||||
display: block; | |||||
position: absolute; | |||||
top: 10px; | |||||
right: 10px; | |||||
} | |||||
} | |||||
</style> | </style> | ||||
<template> | <template> | ||||
<div id="index"> | <div id="index"> | ||||
<div class="container text-xs-center p-t-3"> | |||||
<div class="container text-xs-center p-t-1"> | |||||
<div class="row"> | <div class="row"> | ||||
<div class="col-lg-6 "> | |||||
<div class="col-md-6 p-t-1"> | |||||
<div id="searchEntries bg-card-white"> | <div id="searchEntries bg-card-white"> | ||||
<div class="input-group"> | <div class="input-group"> | ||||
<span class="input-group-addon" id="search-addon"> | <span class="input-group-addon" id="search-addon"> | ||||
<i class="fa fa-search"></i> | <i class="fa fa-search"></i> | ||||
</span> | </span> | ||||
<input type="text" class="form-control" placeholder="search" v-model="search" | |||||
aria-describedby="search-addon" @keyup="filterEntry(search) | debounce 500"> | |||||
<input type="text" class="form-control" placeholder="{{ $t('index.search') }}" | |||||
v-model="search" aria-describedby="search-addon" | |||||
@keyup="filterEntry(search) | debounce 500"> | |||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<div class="col-lg-6"> | |||||
<div class="col-md-6 p-t-1"> | |||||
<new-entry></new-entry> | <new-entry></new-entry> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<div class="row m-t-3"> | |||||
<div class="row m-t-2"> | |||||
<div class="col-lg-12"> | <div class="col-lg-12"> | ||||
<div class="card-columns"> | <div class="card-columns"> | ||||
<div class="card card-block" v-for="entry in entries" @click="openEntry(entry)"> | |||||
<div class="card card-block" v-for="entry in entries" | |||||
v-on:dblclick="copyPassword(entry)"> | |||||
<i class="fa fa-pencil-square-o fa-lg edit-icon text-muted" | |||||
v-on:click="openEntry(entry)"></i> | |||||
<blockquote class="card-blockquote"> | <blockquote class="card-blockquote"> | ||||
<p>{{ entry.site }}</p> | <p>{{ entry.site }}</p> | ||||
<footer> | <footer> | ||||
<small class="text-muted"> | <small class="text-muted"> | ||||
Email / Username : {{ entry.email }} | |||||
{{ entry.email }} | |||||
</small> | </small> | ||||
</footer> | </footer> | ||||
</blockquote> | </blockquote> | ||||
@@ -41,21 +69,21 @@ | |||||
</div> | </div> | ||||
<div class="row m-t-1"> | <div class="row m-t-1"> | ||||
<div class="paginate"> | <div class="paginate"> | ||||
<div class="col-sm-4 text-xs-left"> | |||||
<div class="col-xs-4 text-xs-left"> | |||||
<button class="btn btn-primary btn-sm" v-if="count > limit" | <button class="btn btn-primary btn-sm" v-if="count > limit" | ||||
:disabled="(currentPage*limit >= count)" | :disabled="(currentPage*limit >= count)" | ||||
@click="getPreviousEntries()"> | @click="getPreviousEntries()"> | ||||
précédent | |||||
{{ $t('index.previous') }} | |||||
</button> | </button> | ||||
</div> | </div> | ||||
<div class="col-sm-4 text-xs-center"> | |||||
<div class="col-xs-4 text-xs-center"> | |||||
{{ currentPage }} / {{ numberPages }} | {{ currentPage }} / {{ numberPages }} | ||||
</div> | </div> | ||||
<div class="col-sm-4 text-xs-right"> | |||||
<div class="col-xs-4 text-xs-right"> | |||||
<button class="btn btn-primary btn-sm" v-if="count > limit" | <button class="btn btn-primary btn-sm" v-if="count > limit" | ||||
:disabled="(currentPage===1)" | :disabled="(currentPage===1)" | ||||
@click="getNextEntries()"> | @click="getNextEntries()"> | ||||
suivant | |||||
{{ $t('index.next') }} | |||||
</button> | </button> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
@@ -64,7 +92,8 @@ | |||||
</div> | </div> | ||||
</template> | </template> | ||||
<script type="text/ecmascript-6"> | <script type="text/ecmascript-6"> | ||||
import NewEntry from './Entries/newEntry.vue'; | |||||
import 'bootstrap/dist/js/umd/modal'; | |||||
import NewEntry from './Entries/newEntry'; | |||||
import http from '../services/http'; | import http from '../services/http'; | ||||
export default { | export default { | ||||
data() { | data() { | ||||
@@ -84,7 +113,7 @@ | |||||
this.getEntries(this.limit, this.offset); | this.getEntries(this.limit, this.offset); | ||||
}, | }, | ||||
methods: { | methods: { | ||||
getEntries(limit, offset, search=''){ | |||||
getEntries(limit, offset, search = ''){ | |||||
http.entries.all(limit, offset, search).then((response) => { | http.entries.all(limit, offset, search).then((response) => { | ||||
this.entries = response.data.results; | this.entries = response.data.results; | ||||
this.count = response.data.count; | this.count = response.data.count; | ||||
@@ -101,8 +130,12 @@ | |||||
this.offset = (this.currentPage - 1) * this.limit; | this.offset = (this.currentPage - 1) * this.limit; | ||||
this.getEntries(this.limit, this.offset); | this.getEntries(this.limit, this.offset); | ||||
}, | }, | ||||
copyPassword(entry){ | |||||
alert('password copied !'); | |||||
}, | |||||
openEntry(entry){ | openEntry(entry){ | ||||
this.$router.go(`/app/entries/${entry.id}/`); | |||||
alert(`redirect to /app/entries/${entry.id}/`); | |||||
// this.$router.go(`/app/entries/${entry.id}/`); | |||||
}, | }, | ||||
filterEntry(query){ | filterEntry(query){ | ||||
this.getEntries(this.limit, this.offset, query); | this.getEntries(this.limit, this.offset, query); | ||||