Skip to content

Commit

Permalink
feat: add due date for tasks (monicahq/chandler#191)
Browse files Browse the repository at this point in the history
  • Loading branch information
djaiss authored Aug 27, 2022
1 parent 49396f7 commit b54fac2
Show file tree
Hide file tree
Showing 11 changed files with 130 additions and 6 deletions.
2 changes: 2 additions & 0 deletions app/Models/ContactTask.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class ContactTask extends Model
'description',
'completed',
'completed_at',
'due_at',
];

/**
Expand All @@ -43,6 +44,7 @@ class ContactTask extends Model
*/
protected $dates = [
'completed_at',
'due_at',
];

/**
Expand Down
1 change: 1 addition & 0 deletions database/factories/ContactTaskFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public function definition()
'author_name' => $this->faker->name,
'label' => $this->faker->sentence(),
'completed' => false,
'due_at' => $this->faker->dateTimeThisCentury(),
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public function up()
$table->text('description')->nullable();
$table->boolean('completed')->default(false);
$table->datetime('completed_at')->nullable();
$table->datetime('due_at')->nullable();
$table->timestamps();
$table->foreign('contact_id')->references('id')->on('contacts')->onDelete('cascade');
$table->foreign('author_id')->references('id')->on('users')->onDelete('set null');
Expand Down
3 changes: 2 additions & 1 deletion domains/Contact/ManageTasks/Services/CreateContactTask.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public function rules(): array
'contact_id' => 'required|integer|exists:contacts,id',
'label' => 'required|string|max:255',
'description' => 'nullable|string|max:65535',

'due_at' => 'nullable|date_format:Y-m-d',
];
}

Expand Down Expand Up @@ -71,6 +71,7 @@ private function createContactTask(): void
'author_name' => $this->author->name,
'label' => $this->data['label'],
'description' => $this->valueOrNull($this->data, 'description'),
'due_at' => $this->valueOrNull($this->data, 'due_at'),
]);
}

Expand Down
2 changes: 2 additions & 0 deletions domains/Contact/ManageTasks/Services/UpdateContactTask.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public function rules(): array
'contact_task_id' => 'required|integer|exists:contact_tasks,id',
'label' => 'required|string|max:255',
'description' => 'nullable|string|max:65535',
'due_at' => 'nullable|date',
];
}

Expand Down Expand Up @@ -59,6 +60,7 @@ public function execute(array $data): ContactTask

$this->task->label = $data['label'];
$this->task->description = $this->valueOrNull($data, 'description');
$this->task->due_at = $this->valueOrNull($data, 'due_at');
$this->task->save();

$this->contact->last_updated_at = Carbon::now();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use App\Contact\ManageTasks\Web\ViewHelpers\ModuleContactTasksViewHelper;
use App\Http\Controllers\Controller;
use App\Models\Contact;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

Expand All @@ -25,13 +26,19 @@ public function index(Request $request, int $vaultId, int $contactId)

public function store(Request $request, int $vaultId, int $contactId)
{
$dueAt = '';
if ($request->input('due_at')) {
$dueAt = Carbon::parse($request->input('due_at'))->format('Y-m-d');
}

$data = [
'account_id' => Auth::user()->account_id,
'author_id' => Auth::user()->id,
'vault_id' => $vaultId,
'contact_id' => $contactId,
'label' => $request->input('label'),
'description' => null,
'due_at' => $dueAt,
];

$task = (new CreateContactTask())->execute($data);
Expand All @@ -44,6 +51,11 @@ public function store(Request $request, int $vaultId, int $contactId)

public function update(Request $request, int $vaultId, int $contactId, int $taskId)
{
$dueAt = '';
if ($request->input('due_at')) {
$dueAt = Carbon::parse($request->input('due_at'))->format('Y-m-d');
}

$data = [
'account_id' => Auth::user()->account_id,
'author_id' => Auth::user()->id,
Expand All @@ -52,6 +64,7 @@ public function update(Request $request, int $vaultId, int $contactId, int $task
'contact_task_id' => $taskId,
'label' => $request->input('label'),
'description' => null,
'due_at' => $dueAt,
];

$task = (new UpdateContactTask())->execute($data);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ public static function dtoTask(Contact $contact, ContactTask $task, User $user):
'description' => $task->description,
'completed' => $task->completed,
'completed_at' => $task->completed_at ? DateHelper::format($task->completed_at, $user) : null,
'due_at' => $task->due_at ? DateHelper::format($task->due_at, $user) : null,
'due_at_late' => optional($task->due_at)->isPast() ?? false,
'url' => [
'update' => route('contact.task.update', [
'vault' => $contact->vault_id,
Expand Down
2 changes: 1 addition & 1 deletion lang/en/vault.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@

'dashboard_last_updated_contacts_title' => 'Last updated',
'dashboard_reminders_title' => 'Reminders for the next 30 days',
'dashboard_reminders_blank' => 'No planned reminders.',
'dashboard_reminders_blank' => 'No upcoming reminders.',
'dashboard_favorites_title' => 'Favorites',

/***************************************************************
Expand Down
105 changes: 101 additions & 4 deletions resources/js/Shared/Modules/Tasks.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,29 @@
@esc-key-pressed="createTaskModalShown = false" />
</div>

<!-- due date -->
<div class="border-b border-gray-200 p-5">
<div class="flex items-center">
<input
id="reminder"
v-model="form.due_at_checked"
name="reminder"
type="checkbox"
class="focus:ring-3 relative h-4 w-4 rounded border border-gray-300 bg-gray-50 focus:ring-blue-300 dark:border-gray-600 dark:bg-gray-700 dark:ring-offset-gray-800 dark:focus:ring-blue-600"
@click="toggleDueDateModal" />
<label for="reminder" class="ml-2 block cursor-pointer text-sm text-gray-900"> Add a due date </label>
</div>

<!-- task options -->
<div v-if="form.due_at_checked" class="mt-4 ml-4">
<v-date-picker v-model="form.due_at" class="inline-block h-full" :model-config="modelConfig">
<template #default="{ inputValue, inputEvents }">
<input class="rounded border bg-white px-2 py-1" :value="inputValue" v-on="inputEvents" />
</template>
</v-date-picker>
</div>
</div>

<div class="flex justify-between p-5">
<pretty-span :text="$t('app.cancel')" :classes="'mr-3'" @click="createTaskModalShown = false" />
<pretty-button :text="$t('app.save')" :state="loadingState" :icon="'check'" :classes="'save'" />
Expand All @@ -51,16 +74,36 @@
<ul v-if="localTasks.length > 0" class="mb-2 rounded-lg border border-gray-200 bg-white">
<li v-for="task in localTasks" :key="task.id" class="item-list border-b border-gray-200 hover:bg-slate-50">
<div v-if="editedTaskId !== task.id" class="flex items-center justify-between p-3">
<div>
<div class="flex items-center">
<input
:id="task.id"
v-model="task.completed"
:name="task.id"
type="checkbox"
class="focus:ring-3 relative h-4 w-4 rounded border border-gray-300 bg-gray-50 focus:ring-blue-300 dark:border-gray-600 dark:bg-gray-700 dark:ring-offset-gray-800 dark:focus:ring-blue-600"
@change="toggle(task)" />
<label :for="task.id" class="ml-2 cursor-pointer text-gray-900">
<label :for="task.id" class="ml-2 flex cursor-pointer text-gray-900">
{{ task.label }}

<!-- due date -->
<span
v-if="task.due_at"
:class="task.due_at_late ? 'bg-red-400/10 text-red-600' : 'bg-sky-400/10 text-sky-600'"
class="ml-2 flex items-center rounded-full px-2 py-0.5 text-xs font-medium leading-5 dark:text-sky-400">
<svg
xmlns="http://www.w3.org/2000/svg"
class="mr-1 h-3 w-3"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2">
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
<span class="">{{ task.due_at }}</span>
</span>
</label>
</div>

Expand All @@ -84,6 +127,29 @@
@esc-key-pressed="editedTaskId = 0" />
</div>

<!-- due date -->
<div class="border-b border-gray-200 p-5">
<div class="flex items-center">
<input
id="reminder"
v-model="form.due_at_checked"
name="reminder"
type="checkbox"
class="focus:ring-3 relative h-4 w-4 rounded border border-gray-300 bg-gray-50 focus:ring-blue-300 dark:border-gray-600 dark:bg-gray-700 dark:ring-offset-gray-800 dark:focus:ring-blue-600"
@click="toggleDueDateModal" />
<label for="reminder" class="ml-2 block cursor-pointer text-sm text-gray-900"> Add a due date </label>
</div>

<!-- task options -->
<div v-if="form.due_at_checked" class="mt-4 ml-4">
<v-date-picker v-model="form.due_at" class="inline-block h-full" :model-config="modelConfig">
<template #default="{ inputValue, inputEvents }">
<input class="rounded border bg-white px-2 py-1" :value="inputValue" v-on="inputEvents" />
</template>
</v-date-picker>
</div>
</div>

<div class="flex justify-between p-5">
<pretty-span :text="$t('app.cancel')" :classes="'mr-3'" @click="editedTaskId = 0" />
<pretty-button :text="'Update'" :state="loadingState" :icon="'check'" :classes="'save'" />
Expand All @@ -105,16 +171,37 @@
<ul v-for="task in localCompletedTasks" :key="task.id" class="mb-2 rounded-lg border border-gray-200 bg-white">
<li>
<div class="flex items-center justify-between p-3">
<div>
<div class="flex items-center">
<input
:id="task.id"
v-model="task.completed"
:name="task.id"
type="checkbox"
class="focus:ring-3 relative h-4 w-4 rounded border border-gray-300 bg-gray-50 focus:ring-blue-300 dark:border-gray-600 dark:bg-gray-700 dark:ring-offset-gray-800 dark:focus:ring-blue-600"
@change="toggle(task)" />
<label :for="task.id" class="ml-2 cursor-pointer text-gray-900">

<label :for="task.id" class="ml-2 flex cursor-pointer items-center text-gray-900">
{{ task.label }}

<!-- due date -->
<span
v-if="task.due_at"
:class="task.due_at_late ? 'bg-red-400/10' : ''"
class="ml-2 flex items-center rounded-full bg-sky-400/10 px-2 py-0.5 text-xs font-medium leading-5 text-sky-600 dark:text-sky-400">
<svg
xmlns="http://www.w3.org/2000/svg"
class="mr-1 h-3 w-3"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2">
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
<span class="">{{ task.due_at }}</span>
</span>
</label>
</div>

Expand Down Expand Up @@ -166,8 +253,11 @@ export default {
localCompletedTasks: [],
loadingState: '',
editedTaskId: 0,
dueDateShown: false,
form: {
label: '',
due_at: '',
due_at_checked: false,
errors: [],
},
};
Expand All @@ -182,6 +272,7 @@ export default {
this.form.errors = [];
this.form.label = '';
this.createTaskModalShown = true;
this.form.due_at_checked = false;
this.$nextTick(() => {
this.$refs.label.focus();
Expand All @@ -192,12 +283,18 @@ export default {
this.form.errors = [];
this.form.label = task.label;
this.editedTaskId = task.id;
this.form.due_at = task.due_at;
this.form.due_at_checked = task.due_at != '';
this.$nextTick(() => {
this.$refs[`update${task.id}`].focus();
});
},
toggleDueDateModal() {
this.dueDateShown = !this.dueDateShown;
},
getCompleted() {
axios.get(this.data.url.completed).then((response) => {
this.localCompletedTasks = response.data.data;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ private function executeService(User $author, Account $account, Vault $vault, Co
'author_id' => $author->id,
'contact_id' => $contact->id,
'label' => 'birthdate',
'due_at' => '1990-01-01',
];

$contactTask = (new CreateContactTask())->execute($request);
Expand All @@ -98,6 +99,7 @@ private function executeService(User $author, Account $account, Vault $vault, Co
'id' => $contactTask->id,
'contact_id' => $contact->id,
'label' => 'birthdate',
'due_at' => '1990-01-01 00:00:00',
]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public function it_gets_the_data_transfer_object(): void
$user = User::factory()->create();
$task = ContactTask::factory()->create([
'contact_id' => $contact->id,
'due_at' => '2018-01-01',
]);

$collection = ModuleContactTasksViewHelper::dtoTask($contact, $task, $user);
Expand All @@ -73,6 +74,8 @@ public function it_gets_the_data_transfer_object(): void
'description' => $task->description,
'completed' => false,
'completed_at' => null,
'due_at' => 'Jan 01, 2018',
'due_at_late' => false,
'url' => [
'update' => env('APP_URL').'/vaults/'.$contact->vault->id.'/contacts/'.$contact->id.'/tasks/'.$task->id,
'toggle' => env('APP_URL').'/vaults/'.$contact->vault->id.'/contacts/'.$contact->id.'/tasks/'.$task->id.'/toggle',
Expand Down

0 comments on commit b54fac2

Please sign in to comment.