Manage tasks in Vue.js (#666)

* Tasks are now edited inline
* Tasks can be marked as completed
* Tasks can be edited
This commit is contained in:
Régis Freyd
2017-11-29 16:07:37 -05:00
committed by GitHub
parent 84a1461c0f
commit 7e367d67da
41 changed files with 1352 additions and 466 deletions
+10 -3
View File
@@ -1,14 +1,21 @@
UNRELEASED CHANGES:
* Add more usage statistics to reflect latest changes in the DB.
*
RELEASED VERSIONS:
v1.2.0 - 2017-11-29
-------------------
* Add a much better way to manage tasks of a contact
* Tasks can now be mark as completed and can now be edited
* Add more usage statistics to reflect latest changes in the DB
v1.1.0 - 2017-11-26
-------------------
* Add the ability to add multiple contact fields and addresses per contact.
* Add a new Personalization tab under Settings.
* Add the ability to add multiple contact fields and addresses per contact
* Add a new Personalization tab under Settings
v1.0.0 - 2017-11-09
-------------------
@@ -6,7 +6,6 @@ use App\Task;
use Validator;
use App\Contact;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Illuminate\Database\QueryException;
use App\Http\Resources\Task\Task as TaskResource;
use Illuminate\Database\Eloquent\ModelNotFoundException;
@@ -56,10 +55,7 @@ class ApiTaskController extends ApiController
'title' => 'required|max:255',
'description' => 'string|max:1000000',
'completed_at' => 'date',
'status' => [
'required',
Rule::in(['completed', 'inprogress', 'archived']),
],
'completed' => 'boolean|required',
'contact_id' => 'required|integer',
]);
@@ -109,10 +105,7 @@ class ApiTaskController extends ApiController
'title' => 'required|max:255',
'description' => 'string|max:1000000',
'completed_at' => 'date',
'status' => [
'required',
Rule::in(['completed', 'inprogress', 'archived']),
],
'completed' => 'boolean|required',
'contact_id' => 'required|integer',
]);
@@ -6,124 +6,78 @@ use App\Task;
use App\Contact;
use App\Http\Controllers\Controller;
use App\Http\Requests\People\TasksRequest;
use App\Http\Requests\People\TaskToggleRequest;
class TasksController extends Controller
{
/**
* Display a listing of the resource.
*
* @param Contact $contact
* @return \Illuminate\Http\Response
* Get all the tasks of this contact.
*/
public function index(Contact $contact)
public function get(Contact $contact)
{
return view('people.tasks.index')
->withContact($contact);
$tasks = collect([]);
foreach ($contact->tasks as $task) {
$data = [
'id' => $task->id,
'title' => $task->title,
'description' => $task->description,
'completed' => $task->completed,
'completed_at' => \App\Helpers\DateHelper::getShortDate($task->completed_at),
'edit' => false,
];
$tasks->push($data);
}
return $tasks;
}
/**
* Show the form for creating a new resource.
*
* @param Contact $contact
* @return \Illuminate\Http\Response
*/
public function create(Contact $contact)
{
return view('people.tasks.add')
->withContact($contact)
->withTask(new Task);
}
/**
* Store a newly created resource in storage.
*
* @param TasksRequest $request
* @param Contact $contact
* @return \Illuminate\Http\Response
* Store the task.
*/
public function store(TasksRequest $request, Contact $contact)
{
$task = $contact->tasks()->create(
$request->only([
'title',
'description',
])
+ [
'account_id' => $contact->account_id,
'status' => 'inprogress',
]
);
$task = $contact->tasks()->create([
'account_id' => auth()->user()->account->id,
'title' => $request->get('title'),
'description' => ($request->get('description') == '' ? null : $request->get('description')),
]);
$contact->logEvent('task', $task->id, 'create');
return redirect('/people/'.$contact->id)
->with('success', trans('people.tasks_add_success'));
return $task;
}
/**
* Display the specified resource.
*
* @param Contact $contact
* @param Task $task
* @return \Illuminate\Http\Response
*/
public function show(Contact $contact, Task $task)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param Contact $contact
* @param Task $task
* @return \Illuminate\Http\Response
*/
public function edit(Contact $contact, Task $task)
{
//
}
/**
* Update the specified resource in storage.
*
* @param TasksRequest $request
* @param Contact $contact
* @param Task $task
* @return \Illuminate\Http\Response
* Edit the task field.
*/
public function update(TasksRequest $request, Contact $contact, Task $task)
{
$task->update(
$request->only([
'title',
'status',
'description',
'completed_at',
])
+ ['account_id' => $contact->account_id]
);
$task->update([
'title' => $request->get('title'),
'description' => ($request->get('description') == '' ? null : $request->get('description')),
'completed' => $request->get('completed'),
]);
$contact->logEvent('task', $task->id, 'update');
return redirect('/people/'.$contact->id)
->with('success', trans('people.tasks_update_success'));
return $task;
}
/**
* Update the specified resource in storage.
*
* @param TasksRequest $request
* @param Contact $contact
* @param Task $task
* @return \Illuminate\Http\Response
*/
public function toggle(TasksRequest $request, Contact $contact, Task $task)
public function toggle(TaskToggleRequest $request, Contact $contact, Task $task)
{
$task->toggle();
// check if the state of the task has changed
if ($task->completed) {
$task->completed_at = null;
$task->completed = false;
} else {
$task->completed = true;
$task->completed_at = \Carbon\Carbon::now();
}
return redirect('/people/'.$contact->id)
->with('success', trans('people.tasks_complete_success'));
$contact->logEvent('task', $task->id, 'update');
$task->save();
}
/**
@@ -138,8 +92,5 @@ class TasksController extends Controller
$task->delete();
$contact->events()->forObject($task)->get()->each->delete();
return redirect('/people/'.$contact->id)
->with('success', trans('people.tasks_delete_success'));
}
}
+1 -1
View File
@@ -70,7 +70,7 @@ class DashboardController extends Controller
->get();
// Active tasks
$tasks = $account->tasks()->with('contact')->where('status', 'inprogress')->get();
$tasks = $account->tasks()->with('contact')->where('completed', 0)->get();
$data = [
'events' => $events,
@@ -0,0 +1,28 @@
<?php
namespace App\Http\Requests\People;
use Illuminate\Foundation\Http\FormRequest;
class TaskToggleRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [];
}
}
+2 -3
View File
@@ -29,9 +29,8 @@ class TasksRequest extends FormRequest
return [
'title' => 'required|string',
'description' => '',
'completed_at' => 'date|nullable',
'status' => 'in:completed,inprogress,archived|nullable',
'description' => 'nullable',
'completed' => 'required|boolean',
];
}
}
+1 -1
View File
@@ -20,7 +20,7 @@ class Task extends Resource
'object' => 'task',
'title' => $this->title,
'description' => $this->description,
'status' => $this->status,
'completed' => (bool) $this->completed,
'completed_at' => (is_null($this->completed_at) ? null : $this->completed_at->format(config('api.timestamp_format'))),
'account' => [
'id' => $this->account->id,
+13 -31
View File
@@ -26,7 +26,17 @@ class Task extends Model
*
* @var array
*/
protected $dates = ['completed_at'];
protected $dates = ['completed_at', 'archived_at'];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'completed' => 'boolean',
'archived' => 'boolean',
];
/**
* Get the account record associated with the task.
@@ -56,7 +66,7 @@ class Task extends Model
*/
public function scopeCompleted(Builder $query)
{
return $query->where('status', 'completed');
return $query->where('completed', true);
}
/**
@@ -67,34 +77,6 @@ class Task extends Model
*/
public function scopeInProgress(Builder $query)
{
return $query->where('status', 'inprogress');
}
/**
* Toggle task status.
*
* @return static
*/
public function toggle()
{
$this->status = $this->status === 'completed' ? 'inprogress' : 'completed';
$this->save();
return $this;
}
public function getTitle()
{
return $this->title;
}
public function getDescription()
{
return $this->description;
}
public function getCreatedAt()
{
return $this->created_at;
return $query->where('completed', 'false');
}
}
+1 -1
View File
@@ -114,5 +114,5 @@ return [
| bad things will happen.
|
*/
'app_version' => '1.1.0',
'app_version' => '1.2.0',
];
@@ -0,0 +1,28 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class ChangeTasksTableStructure extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('tasks', function (Blueprint $table) {
$table->boolean('completed')->default(0)->after('description');
});
DB::table('tasks')
->where('status', 'completed')
->update(['completed' => 1]);
Schema::table('tasks', function (Blueprint $table) {
$table->dropColumn('status');
});
}
}
+3 -2
View File
@@ -131,11 +131,12 @@ class FakeContentTableSeeder extends Seeder
// tasks
if (rand(1, 2) == 1) {
for ($j = 0; $j < rand(1, 6); $j++) {
for ($j = 0; $j < rand(1, 10); $j++) {
$task = $contact->tasks()->create([
'title' => $faker->realText(rand(40, 100)),
'description' => $faker->realText(rand(100, 1000)),
'status' => (rand(1, 2) == 1 ? 'inprogress' : 'completed'),
'completed' => (rand(1, 2) == 1 ? 0 : 1),
'completed_at' => (rand(1, 2) == 1 ? $faker->dateTimeThisCentury() : null),
'account_id' => $contact->account_id,
]);
+3
View File
@@ -27,5 +27,8 @@
"typeahead.js": "^0.11.1",
"vue": "^2.1.10",
"vue-resource": "^1.0.3"
},
"dependencies": {
"vue-notification": "^1.3.4"
}
}
+1 -8
View File
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 250 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

+852 -4
View File
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -1,4 +1,4 @@
{
"/js/app.js": "/js/app.js?id=2c5543720aeae4a637f3",
"/css/app.css": "/css/app.css?id=5e02cde5d9a2ab0d822d"
"/js/app.js": "/js/app.js?id=94136d94c734ebefee7f",
"/css/app.css": "/css/app.css?id=854a3b68c0071739f027"
}
+6
View File
@@ -16,6 +16,7 @@ require('jQuery-Tags-Input/dist/jquery.tagsinput.min');
//Vue.component('example', require('./components/people/dashboard/kids.vue'));
const Vue = require('vue');
Vue.component(
'passport-clients',
require('./components/passport/Clients.vue')
@@ -42,6 +43,11 @@ Vue.component(
require('./components/people/ContactInformation.vue')
);
Vue.component(
'contact-task',
require('./components/people/Tasks.vue')
);
// Settings
Vue.component(
'contact-field-types',
@@ -0,0 +1,251 @@
<style scoped>
</style>
<template>
<div>
<div>
<img src="/img/people/tasks.svg" class="icon-section icon-tasks">
<h3>
{{ trans('people.section_personal_tasks') }}
<span class="fr f6 pt2" v-if="tasks.length != 0">
<a class="pointer" @click="editMode = true" v-if="!editMode">{{ trans('app.edit') }}</a>
<a class="pointer" @click="editMode = false" v-if="editMode">{{ trans('app.done') }}</a>
</span>
</h3>
</div>
<div v-bind:class="[editMode ? 'bg-washed-yellow b--yellow ba pa2' : '']">
<!-- EMPTY STATE -->
<div v-if="tasks.length == 0 && !addMode" class="tc bg-near-white b--moon-gray pa3">
<p>{{ trans('people.tasks_blank_title') }}</p>
<p><a class="pointer" @click="toggleAddMode">{{ trans('people.tasks_add_task') }}</a></p>
</div>
<!-- LIST OF IN PROGRESS TASKS -->
<ul>
<li v-for="task in inProgress(tasks)">
<input type="checkbox" id="checkbox" v-model="task.completed" @click="toggleComplete(task)" class="mr1">
{{ task.title }} <span class="silver ml3" v-if="task.description">{{ task.description }}</span>
<div v-if="editMode" class="di">
<i class="fa fa-pencil-square-o pointer pr2 ml3 dark-blue" @click="toggleEditMode(task)"></i>
<i class="fa fa-trash-o pointer pr2 dark-blue" @click="trash(task)"></i>
</div>
<!-- EDIT BOX -->
<form class="bg-near-white pa2 br2 mt3 mb3" v-show="task.edit">
<div class="">
<label class="db fw6 lh-copy f6">
{{ trans('people.tasks_form_title') }}
</label>
<input class="pa2 db w-100" type="text" v-model="task.title" @keyup.esc="editMode = false">
</div>
<div class="mt3">
<label class="db fw6 lh-copy f6">
{{ trans('people.tasks_form_description') }}
</label>
<textarea class="pa2 db w-100" type="text" v-model="task.description" @keyup.esc="editMode = false"></textarea>
</div>
<div class="lh-copy mt3">
<a @click.prevent="update(task)" class="btn btn-primary">{{ trans('app.update') }}</a>
<a class="btn" @click="toggleEditMode(task)">{{ trans('app.cancel') }}</a>
</div>
</form>
</li>
</ul>
<!-- ADD TASK TO ENTER ADD MODE -->
<div v-if="!updateMode && !addMode && tasks.length != 0" class="bg-near-white pa2 br2 mt3 mb3">
<a class="pointer" @click="toggleAddMode">{{ trans('people.tasks_add_task') }}</a>
</div>
<!-- ADD A TASK VIEW -->
<div v-if="addMode">
<form class="bg-near-white pa2 br2 mt3 mb3">
<div class="">
<label class="db fw6 lh-copy f6">
{{ trans('people.tasks_form_title') }}
</label>
<input class="pa2 db w-100" type="text" v-model="createForm.title" @keyup.esc="addMode = false">
</div>
<div class="mt3">
<label class="db fw6 lh-copy f6">
{{ trans('people.tasks_form_description') }}
</label>
<textarea class="pa2 db w-100" type="text" v-model="createForm.description" @keyup.esc="addMode = false"></textarea>
</div>
<div class="lh-copy mt3">
<a @click.prevent="store" class="btn btn-primary">{{ trans('app.add') }}</a>
<a class="btn" @click="addMode = false">{{ trans('app.cancel') }}</a>
</div>
</form>
</div>
<!-- LIST OF COMPLETED TASKS -->
<ul>
<li v-for="task in completed(tasks)" class="f6">
<input type="checkbox" id="checkbox" v-model="task.completed" @click="toggleComplete(task)" class="mr1">
<span class="light-silver mr1">{{ task.completed_at }}</span> <span class="moon-gray">{{ task.title }}</span> <span class="silver ml3" v-if="task.description">{{ task.description }}</span>
<div v-if="editMode" class="di">
<i class="fa fa-trash-o pointer pr2 ml3 dark-blue" @click="trash(task)"></i>
</div>
</li>
</ul>
</div>
</div>
</template>
<script>
export default {
/*
* The component's data.
*/
data() {
return {
tasks: [],
updateMode: false,
addMode: false,
editMode: false,
createForm: {
title: '',
description: '',
completed: 0
},
updateForm: {
id: 0,
title: '',
description: '',
completed: 0
},
};
},
/**
* Prepare the component (Vue 1.x).
*/
ready() {
this.prepareComponent();
},
/**
* Prepare the component (Vue 2.x).
*/
mounted() {
this.prepareComponent();
},
props: ['contactId'],
methods: {
/**
* Prepare the component.
*/
prepareComponent() {
this.getTasks();
},
reinitialize() {
this.createForm.title = '';
this.createForm.description = '';
},
completed: function (tasks) {
return tasks.filter(function (task) {
return task.completed === true
})
},
inProgress: function (tasks) {
return tasks.filter(function (task) {
return task.completed === false
})
},
toggleAddMode() {
this.addMode = true;
this.reinitialize();
},
toggleEditMode(task) {
Vue.set(task, 'edit', !task.edit);
this.updateForm.id = task.id;
this.updateForm.title = task.title;
this.updateForm.description = task.description;
},
getTasks() {
axios.get('/people/' + this.contactId + '/tasks')
.then(response => {
this.tasks = response.data;
});
},
store() {
this.persistClient(
'post', '/people/' + this.contactId + '/tasks',
this.createForm
);
this.addMode = false;
},
toggleComplete(task) {
axios.post('/people/' + this.contactId + '/tasks/' + task.id + '/toggle')
.then(response => {
this.getTasks();
});
},
update(task) {
this.updateForm.id = task.id;
this.updateForm.title = task.title;
this.updateForm.description = task.description;
this.updateForm.completed = task.completed;
this.persistClient(
'put', '/people/' + this.contactId + '/tasks/' + task.id,
this.updateForm
);
this.updateMode = false;
},
trash(task) {
this.updateForm.id = task.id;
this.persistClient(
'delete', '/people/' + this.contactId + '/tasks/' + task.id,
this.updateForm
);
if (this.tasks.length <= 1) {
this.editMode = false;
}
},
persistClient(method, uri, form) {
form.errors = {};
axios[method](uri, form)
.then(response => {
this.getTasks();
})
.catch(error => {
if (typeof error.response.data === 'object') {
form.errors = _.flatten(_.toArray(error.response.data));
} else {
form.errors = ['Something went wrong. Please try again.'];
}
});
},
}
}
</script>
-7
View File
@@ -314,13 +314,6 @@
top: 14px;
width: 17px;
}
span {
font-size: 14px;
position: absolute;
right: 0;
top: 5px;
}
}
.sidebar {
+3 -13
View File
@@ -193,20 +193,10 @@ return [
'kids_unlink_confirmation' => 'Opravdu chcete smazat tento vztah? Dítě nebude smazáno - jen údaje o vztahu mezi oběma kontakty.',
// tasks
'tasks_desc' => 'Udržovat přehled o detailech pro :name',
'tasks_blank_title' => 'Zdá se, že zatím nemáte žádné úkoly pro :name',
'tasks_blank_add_activity' => 'Přidat úkol',
'tasks_add_title_page' => 'Přidat úkol pro kontakt :name',
'tasks_add_title' => 'Na jaký úkol chcete být upozorněni?',
'tasks_add_optional_comment' => 'Komentář (volitelné)',
'tasks_add_cta' => 'Přidat úkol',
'tasks_add_success' => 'Úkol byl úspěšně přidán',
'tasks_delete' => 'Smazat',
'tasks_reactivate' => 'Reaktivovat',
'tasks_mark_complete' => 'Označit jako hotové',
'tasks_blank_title' => 'You don\'t have any tasks yet.',
'tasks_form_title' => 'Title',
'tasks_form_description' => 'Description (optional)',
'tasks_add_task' => 'Přidat úkol',
'tasks_added_on' => 'přidáno :date',
'tasks_delete_confirmation' => 'Opravdu chcete smazat tento úkol?',
'tasks_delete_success' => 'Úkol byl úspěšně smazán',
'tasks_complete_success' => 'Úkol úspěšně změnil svůj stav',
+3 -13
View File
@@ -194,20 +194,10 @@ return [
'kids_unlink_confirmation' => 'Bist Du dir sicher, dass du diese Verbindung lösen möchtest? Das Kind wird nicht gelöscht nur die Verbindung zwischen beiden Kontakten.',
// tasks
'tasks_desc' => 'Behalte Aufgaben für :name im Auge',
'tasks_blank_title' => 'Es scheint als hättest du noch keine Aufgaben für :name',
'tasks_blank_add_activity' => 'Aufgabe hinzufügen',
'tasks_add_title_page' => 'Neue Aufgabe für :name hinzufügen',
'tasks_add_title' => 'An welche Aufgabe möchtest du erinnert werden?',
'tasks_add_optional_comment' => 'Kommentar (optional)',
'tasks_add_cta' => 'Aufgabe hinzufügen',
'tasks_add_success' => 'Die Aufgabe wurde erfolgreich hinzugefügt',
'tasks_delete' => 'Löschen',
'tasks_reactivate' => 'Reaktivieren',
'tasks_mark_complete' => 'Als erledigt markieren',
'tasks_blank_title' => 'You don\'t have any tasks yet.',
'tasks_form_title' => 'Title',
'tasks_form_description' => 'Description (optional)',
'tasks_add_task' => 'Aufgabe hinzufügen',
'tasks_added_on' => 'hinzugefügt am :date',
'tasks_delete_confirmation' => 'Möchtest du diese Aufgabe wirklich löschen?',
'tasks_delete_success' => 'Die Aufgabe wurde erfolgreich gelöscht',
'tasks_complete_success' => 'Der Status der Aufgabe wurder erfolgreich geändert',
+3 -13
View File
@@ -195,20 +195,10 @@ return [
'kids_unlink_confirmation' => 'Are you sure you want to delete this relationship? This kid will not be deleted - only the relationship between the two.',
// tasks
'tasks_desc' => 'Keep tracks of things you need to do for :name',
'tasks_blank_title' => 'It looks like you don\'t have any tasks about :name yet',
'tasks_blank_add_activity' => 'Add task',
'tasks_add_title_page' => 'Add a new task for :name',
'tasks_add_title' => 'What is the task you want to be reminded of?',
'tasks_add_optional_comment' => 'Comment (optional)',
'tasks_add_cta' => 'Add task',
'tasks_add_success' => 'The task has been added successfully',
'tasks_delete' => 'Delete',
'tasks_reactivate' => 'Reactivate',
'tasks_mark_complete' => 'Mark as complete',
'tasks_blank_title' => 'You don\'t have any tasks yet.',
'tasks_form_title' => 'Title',
'tasks_form_description' => 'Description (optional)',
'tasks_add_task' => 'Add a task',
'tasks_added_on' => 'added on :date',
'tasks_delete_confirmation' => 'Are you sure you want to delete this task?',
'tasks_delete_success' => 'The task has been deleted successfully',
'tasks_complete_success' => 'The task has changed status successfully',
+4 -14
View File
@@ -198,20 +198,10 @@ return [
'kids_unlink_confirmation' => 'Are you sure you want to delete this relationship? This kid will not be deleted - only the relationship between the two.',
// tasks
'tasks_desc' => 'Gardez une trace des choses à faire pour :name.',
'tasks_blank_title' => 'Il semble que vous n\'ayez aucune tâche définie pour :name pour le moment.',
'tasks_blank_add_activity' => 'Ajouter une tâche',
'tasks_add_title_page' => 'Ajouter une nouvelle tâche pour :name',
'tasks_add_title' => 'Quelle tâche souhaitez-vous ajouter ?',
'tasks_add_optional_comment' => 'Commentaire (optionnel)',
'tasks_add_cta' => 'Ajouter la tâche',
'tasks_add_success' => 'La tâche a été ajoutée avec succès.',
'tasks_delete' => 'Supprimer',
'tasks_reactivate' => 'Réactivater',
'tasks_mark_complete' => 'Marquer comme complètée',
'tasks_add_task' => 'Ajouter une tâche',
'tasks_added_on' => 'ajoutée le :date',
'tasks_delete_confirmation' => 'Êtes-vous sûr de vouloir supprimer cette tâche ?',
'tasks_blank_title' => 'Vous n\'avez aucune tâche pour le moment.',
'tasks_form_title' => 'Titre',
'tasks_form_description' => 'Description (optionel)',
'tasks_add_task' => 'Ajouter la tâche',
'tasks_delete_success' => 'La tâche a été supprimée avec succès.',
'tasks_complete_success' => 'La tâche a été mise à jour avec succès.',
+2 -12
View File
@@ -187,20 +187,10 @@ return [
'kids_unlink_confirmation' => 'Are you sure you want to delete this relationship? This kid will not be deleted - only the relationship between the two.',
// tasks
'tasks_desc' => 'Tieni traccia delle cose che devi fare per :name',
'tasks_blank_title' => 'Sembra tu non abbia nulla da fare che riguardi :name',
'tasks_blank_add_activity' => 'Aggiungi compito',
'tasks_add_title_page' => 'Aggiungi un nuovo compito che riguarda :name',
'tasks_add_title' => 'Di che compito vuoi ti ricordiamo?',
'tasks_add_optional_comment' => 'Commenti (facoltativi)',
'tasks_add_cta' => 'Aggiungi compito task',
'tasks_add_success' => 'Compito aggiunto',
'tasks_delete' => 'Rimuovi',
'tasks_reactivate' => 'Riattiva',
'tasks_mark_complete' => 'Contrassegna come completa',
'tasks_form_title' => 'Title',
'tasks_form_description' => 'Description (optional)',
'tasks_add_task' => 'Aggiungi compito',
'tasks_added_on' => 'aggiunto il :date',
'tasks_delete_confirmation' => 'Rimuovere questo compito?',
'tasks_delete_success' => 'Compito rimosso',
'tasks_complete_success' => 'Compito completato',
+2 -12
View File
@@ -193,20 +193,10 @@ return [
'kids_unlink_confirmation' => 'Are you sure you want to delete this relationship? This kid will not be deleted - only the relationship between the two.',
// tasks
'tasks_desc' => 'Mantenha na linhas as coisas que você precisa para :name',
'tasks_blank_title' => 'Parece que você não tem nenhuma tarefa para :name ainda',
'tasks_blank_add_activity' => 'Adicionar tarefa',
'tasks_add_title_page' => 'Adicionar uma tarefa para :name',
'tasks_add_title' => 'Qual é a tarefa que você quer lembrar?',
'tasks_add_optional_comment' => 'Comentário (Opcional)',
'tasks_add_cta' => 'Adicionar tarefa',
'tasks_add_success' => 'A tarefa foi adicionada com sucesso',
'tasks_delete' => 'Deletar',
'tasks_reactivate' => 'Reativar',
'tasks_mark_complete' => 'Marque como completo',
'tasks_form_title' => 'Title',
'tasks_form_description' => 'Description (optional)',
'tasks_add_task' => 'Adicionar uma tarefa',
'tasks_added_on' => 'adicionado em :date',
'tasks_delete_confirmation' => 'Você tem certeza de que deseja excluir esta tarefa?',
'tasks_delete_success' => 'A tarefa foi excluída com sucesso',
'tasks_complete_success' => 'O status da tarefa foi alterado com sucesso',
+2 -12
View File
@@ -193,20 +193,10 @@ return [
'kids_unlink_confirmation' => 'Are you sure you want to delete this relationship? This kid will not be deleted - only the relationship between the two.',
// tasks
'tasks_desc' => 'Управляйте Задачами связанными с :name',
'tasks_blank_title' => 'Похоже что у вас пока нет задач связанных с :name',
'tasks_blank_add_activity' => 'Добавить задачу',
'tasks_add_title_page' => 'Добавить новую задачу по :name',
'tasks_add_title' => 'Название',
'tasks_add_optional_comment' => 'Комментарий (не обязательно)',
'tasks_add_cta' => 'Добавить задачу',
'tasks_add_success' => 'Задача была успешно добавлена',
'tasks_delete' => 'Удалить',
'tasks_reactivate' => 'Вернуть',
'tasks_mark_complete' => 'Выполнено',
'tasks_form_title' => 'Title',
'tasks_form_description' => 'Description (optional)',
'tasks_add_task' => 'Добавить задачу',
'tasks_added_on' => 'дата создания: :date',
'tasks_delete_confirmation' => 'Вы уверены что хотите удалить эту задачу?',
'tasks_delete_success' => 'Задача была усрешна удалена',
'tasks_complete_success' => 'Статус задачи был изменён',
+1 -1
View File
@@ -3,7 +3,7 @@
<h3>
{{ trans('people.section_personal_activities') }}
<span><a href="{{ route('activities.add', $contact) }}" class="btn">{{ trans('people.activities_add_activity') }}</a></span>
<span class="fr"><a href="{{ route('activities.add', $contact) }}" class="btn">{{ trans('people.activities_add_activity') }}</a></span>
</h3>
</div>
+2 -2
View File
@@ -118,8 +118,8 @@
<div class="dashboard-item">
<div class="truncate">
<a href="/people/{{ $task->contact_id }}">{{ App\Contact::find($task->contact_id)->getCompleteName(auth()->user()->name_order) }}</a>:
{{ $task->getTitle() }}
{{ $task->getDescription() }}
{{ $task->title }}
{{ $task->description }}
</div>
</div>
@endforeach
@@ -14,6 +14,9 @@
'csrfToken' => csrf_token(),
]); ?>
</script>
<!-- The script below puts all the translation keys in a JS file so we
can reuse it in Vue.js files -->
<script>
window.trans = <?php
// copy all translations from /resources/lang/CURRENT_LOCALE/* to global JS variable
+1 -1
View File
@@ -3,7 +3,7 @@
<h3>
{{ trans('people.call_title') }}
<span>
<span class="fr">
<a href="#logCallModal" class="btn edit-information" data-toggle="modal">{{ trans('people.call_button') }}</a>
</span>
</h3>
+1 -1
View File
@@ -3,7 +3,7 @@
<h3>
{{ trans('people.debt_title') }}
<span>
<span class="fr">
<a href="/people/{{ $contact->id }}/debt/add" class="btn">{{ trans('people.debt_add_cta') }}</a>
</span>
</h3>
+1 -1
View File
@@ -3,7 +3,7 @@
<h3>
{{ trans('people.section_personal_gifts') }}
<span>
<span class="fr">
<a href="/people/{{ $contact->id }}/gifts/add" class="btn">{{ trans('people.gifts_add_gift') }}</a>
</span>
</h3>
+1 -1
View File
@@ -3,7 +3,7 @@
<h3>
{{ trans('people.notes_title') }}
<span>
<span class="fr">
<a href="{{ route('people.notes.add', $contact) }}" class="btn">{{ trans('people.notes_add_one_more') }}</a>
</span>
</h3>
@@ -3,7 +3,7 @@
<h3>
{{ trans('people.section_personal_reminders') }}
<span>
<span class="fr">
<a href="/people/{{ $contact->id }}/reminders/add" class="btn">{{ trans('people.reminders_cta') }}</a>
</span>
</h3>
@@ -1,64 +0,0 @@
@extends('layouts.skeleton')
@section('content')
<div class="people-show">
{{-- Breadcrumb --}}
<div class="breadcrumb">
<div class="{{ Auth::user()->getFluidLayout() }}">
<div class="row">
<div class="col-xs-12">
<ul class="horizontal">
<li>
<a href="/dashboard">{{ trans('app.breadcrumb_dashboard') }}</a>
</li>
<li>
<a href="/people">{{ trans('app.breadcrumb_list_contacts') }}</a>
</li>
<li>
{{ $contact->getCompleteName(auth()->user()->name_order) }}
</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Page header -->
@include('people._header')
<!-- Page content -->
<div class="main-content tasks central-form">
<div class="{{ Auth::user()->getFluidLayout() }}">
<div class="row">
<div class="col-xs-12 col-sm-6 col-sm-offset-3">
<form method="POST" action="/people/{{ $contact->id }}/tasks/store">
{{ csrf_field() }}
<h2>{{ trans('people.tasks_add_title_page', ['name' => $contact->getFirstName()]) }}</h2>
@include('partials.errors')
{{-- First name --}}
<div class="form-group">
<label for="title">{{ trans('people.tasks_add_title') }}</label>
<input type="text" class="form-control" name="title" id="title" value="{{ old('title') ?? $task->description }}" autofocus required>
</div>
<div class="form-group">
<label for="description">{{ trans('people.tasks_add_optional_comment') }}</label>
<textarea class="form-control" id="description" name="description" rows="3">{{ old('description') ?? $task->description }}</textarea>
</div>
<div class="form-group actions">
<button type="submit" class="btn btn-primary">{{ trans('people.tasks_add_cta') }}</button>
<a href="{{ route('people.show', $contact) }}" class="btn btn-secondary">{{ trans('app.cancel') }}</a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
+1 -51
View File
@@ -1,53 +1,3 @@
<div class="col-xs-12 section-title">
<img src="/img/people/tasks.svg" class="icon-section icon-tasks">
<h3>
{{ trans('people.section_personal_tasks') }}
<span>
<a href="/people/{{ $contact->id }}/tasks/add" class="btn">{{ trans('people.tasks_add_task') }}</a>
</span>
</h3>
<contact-task v-bind:contact-id="{!! $contact->id !!}"></contact-task>
</div>
@if ($contact->getTasksInProgress()->count() === 0 and $contact->getCompletedTasks()->count() === 0)
<div class="col-xs-12">
<div class="section-blank">
<h3>{{ trans('people.tasks_blank_title', ['name' => $contact->getFirstName()]) }}</h3>
<a href="/people/{{ $contact->id }}/tasks/add">{{ trans('people.tasks_blank_add_activity') }}</a>
</div>
</div>
@else
<div class="col-xs-12 tasks-list">
<p>{{ trans('people.tasks_desc', ['name' => $contact->getFirstName()]) }}</p>
<ul class="table">
@foreach($contact->tasks as $task)
<li class="table-row">
<div class="table-cell date">
{{ \App\Helpers\DateHelper::getShortDate($task->created_at) }}
</div>
<div class="table-cell">
{{ $task->title }}
</div>
<div class="table-cell list-actions">
<a href="#" onclick="if (confirm('{{ trans('people.tasks_delete_confirmation') }}')) { $(this).closest('.table-row').find('.entry-delete-form').submit(); } return false;">
<i class="fa fa-trash-o" aria-hidden="true"></i>
</a>
</div>
<form method="POST" action="{{ action('Contacts\\TasksController@destroy', compact('contact', 'task')) }}" class="entry-delete-form hidden">
{{ method_field('DELETE') }}
{{ csrf_field() }}
</form>
</li>
@endforeach
</ul>
</div>
@endif
+4 -3
View File
@@ -101,9 +101,10 @@ Route::group(['middleware' => 'auth'], function () {
Route::delete('/people/{contact}/reminders/{reminder}', 'Contacts\\RemindersController@destroy')->name('.reminders.delete');
// Tasks
Route::get('/people/{contact}/tasks/add', 'Contacts\\TasksController@create')->name('.tasks.add');
Route::post('/people/{contact}/tasks/store', 'Contacts\\TasksController@store')->name('.tasks.store');
Route::patch('/people/{contact}/tasks/{task}/toggle', 'Contacts\\TasksController@toggle')->name('.tasks.toggle');
Route::get('/people/{contact}/tasks', 'Contacts\\TasksController@get');
Route::post('/people/{contact}/tasks', 'Contacts\\TasksController@store');
Route::post('/people/{contact}/tasks/{task}/toggle', 'Contacts\\TasksController@toggle');
Route::put('/people/{contact}/tasks/{task}', 'Contacts\\TasksController@update');
Route::delete('/people/{contact}/tasks/{task}', 'Contacts\\TasksController@destroy')->name('.tasks.delete');
// Gifts
+2 -1
View File
@@ -106,10 +106,11 @@ class ContactTest extends FeatureTestCase
$task = [
'title' => $this->faker->sentence(),
'description' => $this->faker->sentence(3),
'completed' => 0,
];
$this->post(
'/people/'.$contact->id.'/tasks/store',
'/people/'.$contact->id.'/tasks',
$task
);
+67
View File
@@ -0,0 +1,67 @@
<?php
namespace Tests\Feature;
use App\Contact;
use Tests\FeatureTestCase;
use Faker\Factory as Faker;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class TaskTest extends FeatureTestCase
{
use DatabaseTransactions;
/**
* Returns an array containing a user object along with
* a contact for that user.
* @return array
*/
private function fetchUser()
{
$user = $this->signIn();
$contact = factory(Contact::class)->create([
'account_id' => $user->account_id,
]);
return [$user, $contact];
}
public function test_user_can_add_a_task()
{
list($user, $contact) = $this->fetchUser();
$faker = Faker::create();
$taskTitle = $faker->realText();
$taskDescription = $faker->realText();
$params = [
'title' => $taskTitle,
'description' => $taskDescription,
'completed' => 0,
];
$response = $this->post('/people/'.$contact->id.'/tasks', $params);
// Assert the note has been added for the correct user.
$params['account_id'] = $user->account_id;
$params['contact_id'] = $contact->id;
$params['title'] = $taskTitle;
$params['description'] = $taskDescription;
$this->assertDatabaseHas('tasks', $params);
// Make sure an event has been created for this action
$eventParams['account_id'] = $user->account_id;
$eventParams['contact_id'] = $contact->id;
$eventParams['object_type'] = 'task';
$eventParams['nature_of_operation'] = 'create';
$this->assertDatabaseHas('events', $eventParams);
// Visit the dashboard and checks that the note event appears on the
// dashboard
$response = $this->get('/dashboard');
$response->assertSee('added a task');
$response->assertSee('<a href="/people/'.$contact->id);
}
}
-85
View File
@@ -1,85 +0,0 @@
<?php
namespace Tests\Unit;
use App\Task;
use Carbon\Carbon;
use Tests\TestCase;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class TaskTest extends TestCase
{
use DatabaseTransactions;
public function testGetTitleReturnsNullIfUndefined()
{
$task = new Task;
$this->assertNull($task->getTitle());
}
public function testGetTitleReturnsTitle()
{
$task = new Task;
$task->title = 'This is a test';
$this->assertEquals(
'This is a test',
$task->getTitle()
);
}
public function testGetDescriptionReturnsNullIfUndefined()
{
$task = new Task;
$this->assertNull($task->getDescription());
}
public function testGetDescriptionReturnsDescription()
{
$task = new Task;
$task->description = 'This is a test';
$this->assertEquals(
'This is a test',
$task->getDescription()
);
}
public function testGetCreatedAtReturnsCarbonObject()
{
$task = factory(\App\Task::class)->make();
$this->assertInstanceOf(Carbon::class, $task->getCreatedAt());
}
public function testToggle()
{
$contact = factory(\App\Contact::class)->create();
$task = factory(\App\Task::class)->make([
'contact_id' => $contact->id,
'status' => 'inprogress',
]);
$this->assertEquals(
'inprogress',
$task->status
);
$task->toggle();
$this->assertEquals(
'completed',
$task->status
);
$task->toggle();
$this->assertEquals(
'inprogress',
$task->status
);
}
}