start add Memos
This commit is contained in:
64
app/Http/Controllers/MemosController.php
Normal file
64
app/Http/Controllers/MemosController.php
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Http\Resources\Memo as MemoResource;
|
||||||
|
use App\Models\Memo;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
|
class MemosController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$this->authorize('viewAny', Memo::class);
|
||||||
|
|
||||||
|
return MemoResource::collection(request()->user()->memos);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store()
|
||||||
|
{
|
||||||
|
$this->authorize('create', Memo::class);
|
||||||
|
|
||||||
|
$memo = request()->user()->memos()->create($this->validateData());
|
||||||
|
|
||||||
|
return (new MemoResource($memo))
|
||||||
|
->response()
|
||||||
|
->setStatusCode(Response::HTTP_CREATED);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function show(Memo $memo)
|
||||||
|
{
|
||||||
|
$this->authorize('view', $memo);
|
||||||
|
|
||||||
|
return new MemoResource($memo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(Memo $memo)
|
||||||
|
{
|
||||||
|
$this->authorize('update', $memo);
|
||||||
|
|
||||||
|
$memo->update($this->validateData());
|
||||||
|
|
||||||
|
return (new MemoResource($memo))
|
||||||
|
->response()
|
||||||
|
->setStatusCode(Response::HTTP_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy(Memo $memo)
|
||||||
|
{
|
||||||
|
$this->authorize('delete', $memo);
|
||||||
|
|
||||||
|
$memo->delete();
|
||||||
|
|
||||||
|
return response([], Response::HTTP_NO_CONTENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function validateData()
|
||||||
|
{
|
||||||
|
return request()->validate([
|
||||||
|
'name' => 'required',
|
||||||
|
'memo' => 'required',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
30
app/Http/Resources/Memo.php
Normal file
30
app/Http/Resources/Memo.php
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Resources;
|
||||||
|
|
||||||
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
|
||||||
|
class Memo extends JsonResource
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Transform the resource into an array.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function toArray($request)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'data' => [
|
||||||
|
'memo_id' => $this->id,
|
||||||
|
'name' => $this->name,
|
||||||
|
'memo' => $this->memo,
|
||||||
|
'last_updated' => $this->updated_at->diffForHumans(),
|
||||||
|
//'tags' => TagResource::collection($this->tags),
|
||||||
|
],
|
||||||
|
'links' => [
|
||||||
|
'self' => $this->path(),
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
27
app/Models/Memo.php
Normal file
27
app/Models/Memo.php
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\User;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\MorphToMany;
|
||||||
|
|
||||||
|
class Memo extends Model
|
||||||
|
{
|
||||||
|
protected $guarded = [];
|
||||||
|
|
||||||
|
public function path()
|
||||||
|
{
|
||||||
|
return '/memos/' . $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function user()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// public function tags(): MorphToMany
|
||||||
|
// {
|
||||||
|
// return $this->morphToMany(Tag::class, 'taggable')->withTimestamps()->withPivot('user_id');
|
||||||
|
// }
|
||||||
|
}
|
||||||
94
app/Policies/MemoPolicy.php
Normal file
94
app/Policies/MemoPolicy.php
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Policies;
|
||||||
|
|
||||||
|
use App\Models\Memo;
|
||||||
|
use App\User;
|
||||||
|
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||||
|
|
||||||
|
class MemoPolicy
|
||||||
|
{
|
||||||
|
use HandlesAuthorization;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can view any memos.
|
||||||
|
*
|
||||||
|
* @param \App\User $user
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function viewAny(User $user)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can view the memo.
|
||||||
|
*
|
||||||
|
* @param \App\User $user
|
||||||
|
* @param \App\Models\Memo $memo
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function view(User $user, Memo $memo)
|
||||||
|
{
|
||||||
|
return $user->id == $memo->user_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can create memos.
|
||||||
|
*
|
||||||
|
* @param \App\User $user
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function create(User $user)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can update the memo.
|
||||||
|
*
|
||||||
|
* @param \App\User $user
|
||||||
|
* @param \App\Models\Memo $memo
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function update(User $user, Memo $memo)
|
||||||
|
{
|
||||||
|
return $user->id == $memo->user_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can delete the memo.
|
||||||
|
*
|
||||||
|
* @param \App\User $user
|
||||||
|
* @param \App\Models\Memo $memo
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function delete(User $user, Memo $memo)
|
||||||
|
{
|
||||||
|
return $user->id == $memo->user_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can restore the memo.
|
||||||
|
*
|
||||||
|
* @param \App\User $user
|
||||||
|
* @param \App\Models\Memo $memo
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function restore(User $user, Memo $memo)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can permanently delete the memo.
|
||||||
|
*
|
||||||
|
* @param \App\User $user
|
||||||
|
* @param \App\Models\Memo $memo
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function forceDelete(User $user, Memo $memo)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ class AuthServiceProvider extends ServiceProvider
|
|||||||
protected $policies = [
|
protected $policies = [
|
||||||
// 'App\Model' => 'App\Policies\ModelPolicy',
|
// 'App\Model' => 'App\Policies\ModelPolicy',
|
||||||
'App\User' => 'App\Policies\UserPolicy',
|
'App\User' => 'App\Policies\UserPolicy',
|
||||||
|
'App\Models\Memo' => 'App\Policies\MemoPolicy',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App;
|
namespace App;
|
||||||
|
|
||||||
|
use App\Models\Memo;
|
||||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
@@ -42,4 +43,9 @@ class User extends Authenticatable
|
|||||||
{
|
{
|
||||||
return $this->role === 2;
|
return $this->role === 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function memos()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Memo::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
14
database/factories/MemoFactory.php
Normal file
14
database/factories/MemoFactory.php
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/** @var \Illuminate\Database\Eloquent\Factory $factory */
|
||||||
|
|
||||||
|
use App\Model;
|
||||||
|
use Faker\Generator as Faker;
|
||||||
|
|
||||||
|
$factory->define(\App\Models\Memo::class, function (Faker $faker) {
|
||||||
|
return [
|
||||||
|
'user_id' => factory(\App\User::class),
|
||||||
|
'name' => $faker->words(3, [false]),
|
||||||
|
'memo' => $faker->text($maxNbChars = 200),
|
||||||
|
];
|
||||||
|
});
|
||||||
34
database/migrations/2020_03_22_171113_create_memos_table.php
Normal file
34
database/migrations/2020_03_22_171113_create_memos_table.php
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class CreateMemosTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('memos', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->unsignedBigInteger('user_id');
|
||||||
|
$table->string('name');
|
||||||
|
$table->longText('memo');
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('memos');
|
||||||
|
}
|
||||||
|
}
|
||||||
37
package-lock.json
generated
37
package-lock.json
generated
@@ -1403,7 +1403,6 @@
|
|||||||
"version": "1.0.10",
|
"version": "1.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"sprintf-js": "~1.0.2"
|
"sprintf-js": "~1.0.2"
|
||||||
}
|
}
|
||||||
@@ -3204,8 +3203,7 @@
|
|||||||
"entities": {
|
"entities": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz",
|
||||||
"integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==",
|
"integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"errno": {
|
"errno": {
|
||||||
"version": "0.1.7",
|
"version": "0.1.7",
|
||||||
@@ -5626,6 +5624,14 @@
|
|||||||
"leven": "^3.1.0"
|
"leven": "^3.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"linkify-it": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==",
|
||||||
|
"requires": {
|
||||||
|
"uc.micro": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"loader-runner": {
|
"loader-runner": {
|
||||||
"version": "2.4.0",
|
"version": "2.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz",
|
||||||
@@ -5846,6 +5852,18 @@
|
|||||||
"object-visit": "^1.0.0"
|
"object-visit": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"markdown-it": {
|
||||||
|
"version": "10.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz",
|
||||||
|
"integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==",
|
||||||
|
"requires": {
|
||||||
|
"argparse": "^1.0.7",
|
||||||
|
"entities": "~2.0.0",
|
||||||
|
"linkify-it": "^2.0.0",
|
||||||
|
"mdurl": "^1.0.1",
|
||||||
|
"uc.micro": "^1.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"md5": {
|
"md5": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz",
|
||||||
@@ -5874,6 +5892,11 @@
|
|||||||
"integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==",
|
"integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"mdurl": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4="
|
||||||
|
},
|
||||||
"media-typer": {
|
"media-typer": {
|
||||||
"version": "0.3.0",
|
"version": "0.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||||
@@ -8608,8 +8631,7 @@
|
|||||||
"sprintf-js": {
|
"sprintf-js": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
|
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"ssri": {
|
"ssri": {
|
||||||
"version": "7.1.0",
|
"version": "7.1.0",
|
||||||
@@ -9114,6 +9136,11 @@
|
|||||||
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
|
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"uc.micro": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
|
||||||
|
"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="
|
||||||
|
},
|
||||||
"uglify-js": {
|
"uglify-js": {
|
||||||
"version": "3.4.10",
|
"version": "3.4.10",
|
||||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz",
|
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz",
|
||||||
|
|||||||
@@ -21,5 +21,8 @@
|
|||||||
"vue-router": "^3.1.6",
|
"vue-router": "^3.1.6",
|
||||||
"vue-template-compiler": "^2.6.10",
|
"vue-template-compiler": "^2.6.10",
|
||||||
"vuex": "^3.1.3"
|
"vuex": "^3.1.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"markdown-it": "^10.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
public/css/app.css
vendored
2
public/css/app.css
vendored
@@ -80,7 +80,7 @@ body {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.width-full {
|
.w-100 {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
10008
public/js/app.js
vendored
10008
public/js/app.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<aside>
|
<aside>
|
||||||
Menu
|
<div>Menu</div>
|
||||||
|
<router-link to="/memos">Memos</router-link>
|
||||||
</aside>
|
</aside>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
58
resources/js/components/TextAreaField.vue
Normal file
58
resources/js/components/TextAreaField.vue
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<template>
|
||||||
|
<div class="relative pb-2">
|
||||||
|
<label :for="name" class="form-label absolute pt-1">{{ label }}</label>
|
||||||
|
<textarea :id="name" type="text" v-model="value" @input="updateField()" :class="errorClassObject()" class="form-textarea pt-4 pb-1">
|
||||||
|
{{ placeholder }}
|
||||||
|
</textarea>
|
||||||
|
<p class="text-alert" v-text="errorMessage()">Error Here</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "TextAreaField",
|
||||||
|
props: [
|
||||||
|
'name', 'label', 'placeholder', 'errors', 'data',
|
||||||
|
],
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
value: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
hasError: function () {
|
||||||
|
return this.errors && this.errors[this.name] && this.errors[this.name].length > 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateField: function () {
|
||||||
|
this.clearErrors(this.name)
|
||||||
|
this.$emit('update:field', this.value)
|
||||||
|
},
|
||||||
|
errorMessage: function () {
|
||||||
|
if (this.hasError) {
|
||||||
|
return this.errors[this.name][0]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clearErrors: function () {
|
||||||
|
if (this.hasError) {
|
||||||
|
this.errors[this.name] = null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
errorClassObject: function () {
|
||||||
|
return {
|
||||||
|
'error-field': this.hasError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
data: function (val) {
|
||||||
|
this.value = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
18
resources/js/router.js
vendored
18
resources/js/router.js
vendored
@@ -4,6 +4,10 @@ import Home from "./views/Home"
|
|||||||
import Profil from "./views/User/UserProfil";
|
import Profil from "./views/User/UserProfil";
|
||||||
import DashBoard from "./views/DashBoard";
|
import DashBoard from "./views/DashBoard";
|
||||||
import CssTesteur from "./views/CssTesteur";
|
import CssTesteur from "./views/CssTesteur";
|
||||||
|
import MemoIndex from "./views/Memo/MemoIndex";
|
||||||
|
import MemoCreate from "./views/Memo/MemoCreate";
|
||||||
|
import MemoShow from "./views/Memo/MemoShow";
|
||||||
|
import MemoEdit from "./views/Memo/MemoEdit";
|
||||||
|
|
||||||
Vue.use(VueRouter)
|
Vue.use(VueRouter)
|
||||||
|
|
||||||
@@ -28,5 +32,19 @@ export default new VueRouter({
|
|||||||
path: '/css-testeur', name: 'css-testeur', component: CssTesteur,
|
path: '/css-testeur', name: 'css-testeur', component: CssTesteur,
|
||||||
meta: { title: 'css-testeur'}
|
meta: { title: 'css-testeur'}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
path: '/memos', component: MemoIndex,
|
||||||
|
meta: {title: 'Memos'}
|
||||||
|
}, {
|
||||||
|
path: '/memos/create', component: MemoCreate,
|
||||||
|
meta: {title: 'Add New Memo'}
|
||||||
|
}, {
|
||||||
|
path: '/memos/:id', component: MemoShow,
|
||||||
|
meta: {title: 'Details for Memo'}
|
||||||
|
}, {
|
||||||
|
path: '/memos/:id/edit', component: MemoEdit,
|
||||||
|
meta: {title: 'Edit Memo'}
|
||||||
|
},
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|||||||
49
resources/js/views/Memo/MemoCreate.vue
Executable file
49
resources/js/views/Memo/MemoCreate.vue
Executable file
@@ -0,0 +1,49 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<form @submit.prevent="submitForm">
|
||||||
|
<InputField name="name" label="Memo Title" placeholder="Your Title" @update:field="form.name = $event" :errors="errors" />
|
||||||
|
<TextAreaField name="memo" label="Memo" placeholder="Your Memo" @update:field="form.memo = $event" :errors="errors" />
|
||||||
|
|
||||||
|
<div class="flex-end">
|
||||||
|
<button @click="$router.back()" class="btn-alert mr-3">Cancel</button>
|
||||||
|
<button class="btn-primary">Add New Memo</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import InputField from "../../components/InputField";
|
||||||
|
import TextAreaField from "../../components/TextAreaField";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "MemoCreate",
|
||||||
|
components: {
|
||||||
|
InputField, TextAreaField
|
||||||
|
},
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
form: {
|
||||||
|
'name': '',
|
||||||
|
'memo': '',
|
||||||
|
},
|
||||||
|
errors: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
submitForm: function () {
|
||||||
|
axios.post('/api/memos', this.form)
|
||||||
|
.then(response => {
|
||||||
|
this.$router.push(response.data.links.self)
|
||||||
|
})
|
||||||
|
.catch(errors => {
|
||||||
|
this.errors = errors.response.data.errors
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
65
resources/js/views/Memo/MemoEdit.vue
Executable file
65
resources/js/views/Memo/MemoEdit.vue
Executable file
@@ -0,0 +1,65 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="flex-between">
|
||||||
|
<a href="#" @click="$router.back()" class="link">
|
||||||
|
< Back
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<form @submit.prevent="submitForm">
|
||||||
|
<InputField name="name" :data="form.name" label="Memo Title" placeholder="Your Title" @update:field="form.name = $event" :errors="errors" />
|
||||||
|
<TextAreaField name="memo" :data="form.memo" label="Memo" placeholder="Your Memo" @update:field="form.memo = $event" :errors="errors" />
|
||||||
|
|
||||||
|
<div class="flex-end">
|
||||||
|
<button class="btn-alert mr-3">Cancel</button>
|
||||||
|
<button class="btn-primary">Save</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import InputField from "../../components/InputField";
|
||||||
|
import TextAreaField from "../../components/TextAreaField";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "MemoEdit",
|
||||||
|
components: {
|
||||||
|
InputField, TextAreaField
|
||||||
|
},
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
form: {
|
||||||
|
'name': '',
|
||||||
|
'memo': '',
|
||||||
|
},
|
||||||
|
errors: null,
|
||||||
|
loading: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
submitForm: function () {
|
||||||
|
axios.patch('/api/memos/' + this.$route.params.id, this.form)
|
||||||
|
.then(response => {
|
||||||
|
this.$router.push(response.data.links.self)
|
||||||
|
})
|
||||||
|
.catch(errors => {
|
||||||
|
this.errors = errors.response.data.errors
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
axios.get('/api/memos/' + this.$route.params.id)
|
||||||
|
.then(response => {
|
||||||
|
this.form = response.data.data
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.loading = false
|
||||||
|
if (error.response.status === 404) {
|
||||||
|
this.$router.back()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
46
resources/js/views/Memo/MemoIndex.vue
Executable file
46
resources/js/views/Memo/MemoIndex.vue
Executable file
@@ -0,0 +1,46 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="flex-between flex-center mb-1">
|
||||||
|
<a href="#" @click="$router.back()" class="link">
|
||||||
|
< Back
|
||||||
|
</a>
|
||||||
|
<router-link :to="'/memos/create'" class="btn-primary">Add New Memo</router-link>
|
||||||
|
</div>
|
||||||
|
<div v-if="loading">> Loading...</div>
|
||||||
|
<div v-else>
|
||||||
|
<div v-if="memos.lenght === 0">
|
||||||
|
<p>No memos yet. <router-link to="/memos/create">Get Started ></router-link></p>
|
||||||
|
</div>
|
||||||
|
<div v-for="memo in memos">
|
||||||
|
<router-link :to="'/memos/' + memo.data.memo_id" class="link-large relative flex-center p-2">
|
||||||
|
<h1>{{ memo.data.name }}</h1>
|
||||||
|
<div class="memo-date">{{ memo.data.last_updated }}</div>
|
||||||
|
</router-link>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "MemoIndex",
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
memos: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
axios.get('/api/memos')
|
||||||
|
.then(response => {
|
||||||
|
this.memos = response.data.data
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.loading = false
|
||||||
|
alert('Unable to fetch memos.')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
78
resources/js/views/Memo/MemoShow.vue
Executable file
78
resources/js/views/Memo/MemoShow.vue
Executable file
@@ -0,0 +1,78 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div v-if="loading">> Loading...</div>
|
||||||
|
<div v-else>
|
||||||
|
<div class="flex-between mb-2">
|
||||||
|
<a href="#" @click="$router.back()" class="link">
|
||||||
|
< Back
|
||||||
|
</a>
|
||||||
|
<div class="relative">
|
||||||
|
<router-link :to="'/memos/' + memo.memo_id + '/edit'" class="btn-success mr-1">Edit</router-link>
|
||||||
|
<a href="#" @click="modal = ! modal" class="btn-alert">Delete</a>
|
||||||
|
<div v-if="modal" class="absolute modal mt-2">
|
||||||
|
<p>Are you sure you want to delete this record ?</p>
|
||||||
|
<div class="flex-end flex-center mt-2">
|
||||||
|
<button @click="modal = ! modal" class="btn mr-2">Cancel</button>
|
||||||
|
<button @click="destroy" class="btn-alert-strong">Delete</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="modal" @click="modal = ! modal" class="modal-background"></div>
|
||||||
|
</div>
|
||||||
|
<!-- <TagBox :memo="memo" />-->
|
||||||
|
<p class="title-section pt-3">Memo</p>
|
||||||
|
<h1 class="memo-title">{{ memo.name }}</h1>
|
||||||
|
<p class="memo-style pt-1" v-html="memoMarkdown"> </p>
|
||||||
|
<div class="memo-change my-2 p-1">@last update : {{ memo.last_updated }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// import TagBox from "../Tag/TagBox";
|
||||||
|
let MarkdownIt = require('markdown-it'),
|
||||||
|
md = new MarkdownIt();
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "MemoShow",
|
||||||
|
components: {
|
||||||
|
// TagBox
|
||||||
|
},
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
modal: false,
|
||||||
|
memo: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
memoMarkdown: function () {
|
||||||
|
return md.render(this.memo.memo)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
destroy: function () {
|
||||||
|
axios.delete('/api/memos/' + this.$route.params.id)
|
||||||
|
.then(response => {
|
||||||
|
this.$router.push('/memos')
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
alert('Internal Error, Unable to delete contact.')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
axios.get('/api/memos/' + this.$route.params.id)
|
||||||
|
.then(response => {
|
||||||
|
this.memo = response.data.data
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.loading = false
|
||||||
|
if (error.response.status === 404) {
|
||||||
|
this.$router.push('/memos')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
2
resources/sass/setup/_positions.scss
vendored
2
resources/sass/setup/_positions.scss
vendored
@@ -42,7 +42,7 @@ $base: 1rem;
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.width-full {
|
.w-100{
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,47 +1,37 @@
|
|||||||
@extends('layouts.app')
|
@extends('layouts.app')
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<div class="container">
|
<div class="auth p-2">
|
||||||
<div class="row justify-content-center">
|
<div class="title-page mb-2">{{ __('Reset Password') }}</div>
|
||||||
<div class="col-md-8">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">{{ __('Reset Password') }}</div>
|
|
||||||
|
|
||||||
<div class="card-body">
|
|
||||||
@if (session('status'))
|
|
||||||
<div class="alert alert-success" role="alert">
|
|
||||||
{{ session('status') }}
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
<form method="POST" action="{{ route('password.email') }}">
|
<div class="card-body">
|
||||||
@csrf
|
@if (session('status'))
|
||||||
|
<div class="alert alert-success" role="alert">
|
||||||
<div class="form-group row">
|
{{ session('status') }}
|
||||||
<label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>
|
|
||||||
|
|
||||||
<div class="col-md-6">
|
|
||||||
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
|
|
||||||
|
|
||||||
@error('email')
|
|
||||||
<span class="invalid-feedback" role="alert">
|
|
||||||
<strong>{{ $message }}</strong>
|
|
||||||
</span>
|
|
||||||
@enderror
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group row mb-0">
|
|
||||||
<div class="col-md-6 offset-md-4">
|
|
||||||
<button type="submit" class="btn btn-primary">
|
|
||||||
{{ __('Send Password Reset Link') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
@endif
|
||||||
|
|
||||||
|
<form method="POST" action="{{ route('password.email') }}">
|
||||||
|
@csrf
|
||||||
|
|
||||||
|
<label for="email" class="mb-1">{{ __('E-Mail') }}</label>
|
||||||
|
<div class="mb-2">
|
||||||
|
<input id="email" type="email" class="@error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
|
||||||
|
@error('email')
|
||||||
|
<span class="invalid-feedback" role="alert">
|
||||||
|
<strong>{{ $message }}</strong>
|
||||||
|
</span>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex-between">
|
||||||
|
<button type="submit" class="btn-primary px-3 w-100">
|
||||||
|
{{ __('Send Password Reset Link') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
@@ -1,65 +1,46 @@
|
|||||||
@extends('layouts.app')
|
@extends('layouts.app')
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<div class="container">
|
<div class="auth p-2">
|
||||||
<div class="row justify-content-center">
|
<div class="title-page mb-2">{{ __('Reset Password') }}</div>
|
||||||
<div class="col-md-8">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">{{ __('Reset Password') }}</div>
|
|
||||||
|
|
||||||
<div class="card-body">
|
<form method="POST" action="{{ route('password.update') }}">
|
||||||
<form method="POST" action="{{ route('password.update') }}">
|
@csrf
|
||||||
@csrf
|
|
||||||
|
|
||||||
<input type="hidden" name="token" value="{{ $token }}">
|
<input type="hidden" name="token" value="{{ $token }}">
|
||||||
|
|
||||||
<div class="form-group row">
|
<label for="email" class="mb-1">{{ __('E-Mail') }}</label>
|
||||||
<label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>
|
<div class="mb-2">
|
||||||
|
<input id="email" type="email" class="@error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
|
||||||
<div class="col-md-6">
|
@error('email')
|
||||||
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ $email ?? old('email') }}" required autocomplete="email" autofocus>
|
<span class="invalid-feedback" role="alert">
|
||||||
|
<strong>{{ $message }}</strong>
|
||||||
@error('email')
|
</span>
|
||||||
<span class="invalid-feedback" role="alert">
|
@enderror
|
||||||
<strong>{{ $message }}</strong>
|
|
||||||
</span>
|
|
||||||
@enderror
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group row">
|
|
||||||
<label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>
|
|
||||||
|
|
||||||
<div class="col-md-6">
|
|
||||||
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">
|
|
||||||
|
|
||||||
@error('password')
|
|
||||||
<span class="invalid-feedback" role="alert">
|
|
||||||
<strong>{{ $message }}</strong>
|
|
||||||
</span>
|
|
||||||
@enderror
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group row">
|
|
||||||
<label for="password-confirm" class="col-md-4 col-form-label text-md-right">{{ __('Confirm Password') }}</label>
|
|
||||||
|
|
||||||
<div class="col-md-6">
|
|
||||||
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group row mb-0">
|
|
||||||
<div class="col-md-6 offset-md-4">
|
|
||||||
<button type="submit" class="btn btn-primary">
|
|
||||||
{{ __('Reset Password') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
<label for="password" class="mb-1">{{ __('Password') }}</label>
|
||||||
|
<div class="mb-2">
|
||||||
|
<input id="password" type="password" class="@error('password') is-invalid @enderror" name="password" required autocomplete="current-password">
|
||||||
|
|
||||||
|
@error('password')
|
||||||
|
<span class="invalid-feedback" role="alert">
|
||||||
|
<strong>{{ $message }}</strong>
|
||||||
|
</span>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label for="password-confirm" class="mb-1">{{ __('Confirm Password') }}</label>
|
||||||
|
<div class="mb-2">
|
||||||
|
<input id="password-confirm" type="password" name="password_confirmation" required autocomplete="new-password">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex-between">
|
||||||
|
<button type="submit" class="btn-primary px-3 w-100">
|
||||||
|
{{ __('Reset Password') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ Route::middleware('auth:api')->group(function () {
|
|||||||
Route::apiResources([
|
Route::apiResources([
|
||||||
// '/posts' => 'PostController',
|
// '/posts' => 'PostController',
|
||||||
'/users' => 'UserController',
|
'/users' => 'UserController',
|
||||||
|
'/memos' => 'MemosController',
|
||||||
// '/users/{user}/posts' => 'UserPostController',
|
// '/users/{user}/posts' => 'UserPostController',
|
||||||
// '/friend-request' => 'FriendRequestController',
|
// '/friend-request' => 'FriendRequestController',
|
||||||
]);
|
]);
|
||||||
|
|||||||
273
tests/Feature/MemosTest.php
Normal file
273
tests/Feature/MemosTest.php
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature;
|
||||||
|
|
||||||
|
use App\Models\Memo;
|
||||||
|
use App\User;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Foundation\Testing\WithFaker;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class MemosTest extends TestCase
|
||||||
|
{
|
||||||
|
use RefreshDatabase;
|
||||||
|
|
||||||
|
protected $user;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->actingAs($user = factory(User::class)->create(), 'api');
|
||||||
|
// $this->user = factory(User::class)->create();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function an_unauthenticated_user_should_redirect_to_login()
|
||||||
|
{
|
||||||
|
$response = $this->post('/api/memos', $this->data());
|
||||||
|
|
||||||
|
$response->assertRedirect('/login');
|
||||||
|
$this->assertCount(0, Memo::all());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function an_unauthenticated_user_can_add_a_memo()
|
||||||
|
{
|
||||||
|
$this->withoutExceptionHandling();
|
||||||
|
|
||||||
|
$this->actingAs($user = factory(\App\User::class)->create(), 'api');
|
||||||
|
|
||||||
|
$response = $this->post('/api/memos', $this->data());
|
||||||
|
|
||||||
|
$memo = Memo::first();
|
||||||
|
|
||||||
|
// $this->assertCount(1, Contact::all());
|
||||||
|
$this->assertEquals('Test Name', $memo->name);
|
||||||
|
$this->assertEquals('Test Memo', $memo->memo);
|
||||||
|
|
||||||
|
$response->assertStatus(Response::HTTP_CREATED);
|
||||||
|
$response->assertJson([
|
||||||
|
'data' => [
|
||||||
|
'memo_id' => $memo->id
|
||||||
|
],
|
||||||
|
'links' => [
|
||||||
|
'self' => $memo->path(),
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function memo_name_are_required()
|
||||||
|
{
|
||||||
|
$response = $this->post('/api/memos', array_merge($this->data(), ['name' => '']));
|
||||||
|
|
||||||
|
$response->assertSessionHasErrors('name');
|
||||||
|
$this->assertCount(0, Memo::all());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function memo_are_required()
|
||||||
|
{
|
||||||
|
$response = $this->post('/api/memos', array_merge($this->data(), ['memo' => '']));
|
||||||
|
|
||||||
|
$response->assertSessionHasErrors('memo');
|
||||||
|
$this->assertCount(0, Memo::all());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function a_memo_can_be_retrieved()
|
||||||
|
{
|
||||||
|
$this->actingAs($user = factory(User::class)->create(), 'api');
|
||||||
|
|
||||||
|
$memo = factory(Memo::class)->create(['user_id' => $user->id]);
|
||||||
|
|
||||||
|
$response = $this->get('/api/memos/' . $memo->id );
|
||||||
|
|
||||||
|
$response->assertJson([
|
||||||
|
'data' => [
|
||||||
|
'memo_id' => $memo->id,
|
||||||
|
'name' => $memo->name,
|
||||||
|
'memo' => $memo->memo,
|
||||||
|
'last_updated' => $memo->updated_at->diffForHumans(),
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function a_memo_can_be_patch()
|
||||||
|
{
|
||||||
|
$this->actingAs($user = factory(User::class)->create(), 'api');
|
||||||
|
|
||||||
|
$memo = factory(Memo::class)->create(['user_id' => $user->id]);
|
||||||
|
|
||||||
|
$response = $this->patch('/api/memos/' . $memo->id, $this->data());
|
||||||
|
|
||||||
|
$memo = $memo->fresh();
|
||||||
|
|
||||||
|
$this->assertEquals('Test Memo', $memo->memo);
|
||||||
|
|
||||||
|
$response->assertStatus(Response::HTTP_OK);
|
||||||
|
$response->assertJson([
|
||||||
|
'data' => [
|
||||||
|
'memo_id' => $memo->id
|
||||||
|
],
|
||||||
|
'links' => [
|
||||||
|
'self' => $memo->path(),
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function only_the_owner_can_patch_the_memo()
|
||||||
|
{
|
||||||
|
$memo = factory(Memo::class)->create();
|
||||||
|
|
||||||
|
$anotherUser = factory(User::class)->create();
|
||||||
|
|
||||||
|
$response = $this->patch('/api/memos/' . $memo->id, array_merge($this->data(), ['api_token' => $anotherUser->api_token]));
|
||||||
|
|
||||||
|
$response->assertStatus(Response::HTTP_FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function a_memo_can_be_delete()
|
||||||
|
{
|
||||||
|
$this->actingAs($user = factory(User::class)->create(), 'api');
|
||||||
|
|
||||||
|
$memo = factory(Memo::class)->create(['user_id' => $user->id]);
|
||||||
|
|
||||||
|
$response = $this->delete('/api/memos/' . $memo->id);
|
||||||
|
|
||||||
|
$memo = $memo->fresh();
|
||||||
|
|
||||||
|
$this->assertCount(0, Memo::all());
|
||||||
|
|
||||||
|
$response->assertStatus(Response::HTTP_NO_CONTENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function only_the_owner_can_delete_the_memo()
|
||||||
|
{
|
||||||
|
$memo = factory(Memo::class)->create();
|
||||||
|
|
||||||
|
$anotherUser = factory(User::class)->create();
|
||||||
|
|
||||||
|
$response = $this->delete('/api/memos/' . $memo->id, ['api_token' => $anotherUser->api_token]);
|
||||||
|
|
||||||
|
$response->assertStatus(Response::HTTP_FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function a_list_of_memos_can_be_fetched_for_the_authenticated_user()
|
||||||
|
{
|
||||||
|
$user = factory(User::class)->create();
|
||||||
|
$anotherUser = factory(User::class)->create();
|
||||||
|
|
||||||
|
$memo = factory(Memo::class)->create(['user_id' => $user->id]);
|
||||||
|
$anotherMemo = factory(Memo::class)->create(['user_id' => $anotherUser->id]);
|
||||||
|
|
||||||
|
$response = $this->get('/api/memos');
|
||||||
|
|
||||||
|
$response->assertJsonCount(1)
|
||||||
|
->assertJson( [
|
||||||
|
'data' => [
|
||||||
|
[
|
||||||
|
'data' => [
|
||||||
|
'memo_id' => $memo->id
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function only_the_users_memos_can_be_retrieved()
|
||||||
|
{
|
||||||
|
$this->actingAs($user = factory(User::class)->create(), 'api');
|
||||||
|
|
||||||
|
$memo = factory(Memo::class)->create(['user_id' => $user->id]);
|
||||||
|
|
||||||
|
$anotherUser = factory(User::class)->create();
|
||||||
|
|
||||||
|
$response = $this->get('/api/memos/' . $memo->id );
|
||||||
|
|
||||||
|
$response->assertStatus(Response::HTTP_FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
// public function user_can_add_tag_to_memo()
|
||||||
|
// {
|
||||||
|
// //$this->withoutExceptionHandling();
|
||||||
|
//
|
||||||
|
// $memo = $this->post('/api/memos', $this->data());
|
||||||
|
// $tag = factory(Tag::class)->create(['user_id' => $this->user->id]);
|
||||||
|
//
|
||||||
|
// $response = $this->patch('/api/tag/add-tag/' . $tag->slug, ['api_token' => $this->user->api_token, 'model' => $memo['data']]);
|
||||||
|
//
|
||||||
|
// $response->assertStatus(Response::HTTP_CREATED)
|
||||||
|
// ->assertJson( [
|
||||||
|
// 'data' => [
|
||||||
|
// 'memo_id' => $memo['data']['memo_id'],
|
||||||
|
// 'tags' => [
|
||||||
|
// [
|
||||||
|
// 'data' => [
|
||||||
|
// 'tag_id' => $tag->id,
|
||||||
|
// 'name' => $tag->name,
|
||||||
|
// 'slug' => $tag->slug,
|
||||||
|
// ]
|
||||||
|
// ]
|
||||||
|
// ]
|
||||||
|
// ]
|
||||||
|
// ]
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
// public function user_can_remove_tag_to_memo()
|
||||||
|
// {
|
||||||
|
// //$this->withoutExceptionHandling();
|
||||||
|
//
|
||||||
|
// $memo = $this->post('/api/memos', $this->data());
|
||||||
|
// $tag = factory(Tag::class)->create(['user_id' => $this->user->id]);
|
||||||
|
//
|
||||||
|
// $response = $this->patch('/api/tag/add-tag/' . $tag->slug, ['api_token' => $this->user->api_token, 'model' => $memo['data']]);
|
||||||
|
//
|
||||||
|
// $response->assertStatus(Response::HTTP_CREATED)
|
||||||
|
// ->assertJson( [
|
||||||
|
// 'data' => [
|
||||||
|
// 'memo_id' => $memo['data']['memo_id'],
|
||||||
|
// 'tags' => [
|
||||||
|
// [
|
||||||
|
// 'data' => [
|
||||||
|
// 'tag_id' => $tag->id,
|
||||||
|
// 'name' => $tag->name,
|
||||||
|
// 'slug' => $tag->slug,
|
||||||
|
// ]
|
||||||
|
// ]
|
||||||
|
// ]
|
||||||
|
// ]
|
||||||
|
// ]
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// $response = $this->patch('/api/tag/remove-tag/' . $tag->slug, ['api_token' => $this->user->api_token, 'model' => $memo['data']]);
|
||||||
|
//
|
||||||
|
// $response->assertStatus(Response::HTTP_CREATED)
|
||||||
|
// ->assertJson( [
|
||||||
|
// 'data' => [
|
||||||
|
// 'memo_id' => $memo['data']['memo_id'],
|
||||||
|
// 'tags' => []
|
||||||
|
// ]
|
||||||
|
// ]
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
private function data()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => 'Test Name',
|
||||||
|
'memo' => 'Test Memo',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user