feat: associate a photo to a gift (#3342)
This commit is contained in:
+1
-1
@@ -14,7 +14,7 @@ indent_size = 4
|
||||
[*.blade.php]
|
||||
indent_size = 2
|
||||
|
||||
[*.js]
|
||||
[*.{js,vue}]
|
||||
indent_size = 2
|
||||
|
||||
[*.md]
|
||||
|
||||
+2
-2
@@ -2,11 +2,11 @@
|
||||
|
||||
### New features:
|
||||
|
||||
*
|
||||
* Associate a photo to a gift
|
||||
|
||||
### Enhancements:
|
||||
|
||||
*
|
||||
* Gift are now added and updated inline
|
||||
|
||||
### Fixes:
|
||||
|
||||
|
||||
@@ -6,8 +6,12 @@ use App\Models\Contact\Gift;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Contact\Contact;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use App\Services\Contact\Gift\CreateGift;
|
||||
use App\Services\Contact\Gift\UpdateGift;
|
||||
use App\Services\Contact\Gift\DestroyGift;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use App\Http\Resources\Gift\Gift as GiftResource;
|
||||
use App\Services\Contact\Gift\AssociatePhotoToGift;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
|
||||
class ApiGiftController extends ApiController
|
||||
@@ -23,11 +27,11 @@ class ApiGiftController extends ApiController
|
||||
$gifts = auth()->user()->account->gifts()
|
||||
->orderBy($this->sort, $this->sortDirection)
|
||||
->paginate($this->getLimitPerPage());
|
||||
|
||||
return GiftResource::collection($gifts);
|
||||
} catch (QueryException $e) {
|
||||
return $this->respondInvalidQuery();
|
||||
}
|
||||
|
||||
return GiftResource::collection($gifts);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -41,8 +45,7 @@ class ApiGiftController extends ApiController
|
||||
{
|
||||
try {
|
||||
$gift = Gift::where('account_id', auth()->user()->account_id)
|
||||
->where('id', $id)
|
||||
->firstOrFail();
|
||||
->findOrFail($id);
|
||||
} catch (ModelNotFoundException $e) {
|
||||
return $this->respondNotFound();
|
||||
}
|
||||
@@ -59,21 +62,18 @@ class ApiGiftController extends ApiController
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$isvalid = $this->validateUpdate($request);
|
||||
if ($isvalid !== true) {
|
||||
return $isvalid;
|
||||
}
|
||||
|
||||
try {
|
||||
$gift = Gift::create(
|
||||
$gift = app(CreateGift::class)->execute(
|
||||
$request->except(['account_id'])
|
||||
+ ['account_id' => auth()->user()->account_id]
|
||||
);
|
||||
} catch (QueryException $e) {
|
||||
return $this->respondNotTheRightParameters();
|
||||
}
|
||||
|
||||
return new GiftResource($gift);
|
||||
return new GiftResource($gift);
|
||||
} catch (ModelNotFoundException $e) {
|
||||
return $this->respondNotFound();
|
||||
} catch (ValidationException $e) {
|
||||
return $this->respondValidatorFailed($e->validator);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,86 +87,46 @@ class ApiGiftController extends ApiController
|
||||
public function update(Request $request, $giftId)
|
||||
{
|
||||
try {
|
||||
$gift = Gift::where('account_id', auth()->user()->account_id)
|
||||
->where('id', $giftId)
|
||||
->firstOrFail();
|
||||
$gift = app(UpdateGift::class)->execute(
|
||||
$request->except(['account_id', 'gift_id'])
|
||||
+ [
|
||||
'account_id' => auth()->user()->account_id,
|
||||
'gift_id' => $giftId,
|
||||
]
|
||||
);
|
||||
|
||||
return new GiftResource($gift);
|
||||
} catch (ModelNotFoundException $e) {
|
||||
return $this->respondNotFound();
|
||||
} catch (ValidationException $e) {
|
||||
return $this->respondValidatorFailed($e->validator);
|
||||
}
|
||||
|
||||
$isvalid = $this->validateUpdate($request);
|
||||
if ($isvalid !== true) {
|
||||
return $isvalid;
|
||||
}
|
||||
|
||||
try {
|
||||
$gift->update($request->only([
|
||||
'is_for',
|
||||
'name',
|
||||
'comment',
|
||||
'url',
|
||||
'value',
|
||||
'is_an_idea',
|
||||
'has_been_offered',
|
||||
'date_offered',
|
||||
'contact_id',
|
||||
]));
|
||||
} catch (QueryException $e) {
|
||||
return $this->respondNotTheRightParameters();
|
||||
}
|
||||
|
||||
if (is_null($request->input('is_for'))) {
|
||||
$gift->is_for = null;
|
||||
$gift->save();
|
||||
}
|
||||
|
||||
return new GiftResource($gift);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the request for update.
|
||||
* Associate a photo to the gift.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\JsonResponse|true
|
||||
* @param Request $request
|
||||
* @param int $giftId
|
||||
* @param int $photoId
|
||||
*
|
||||
* @return GiftResource|\Illuminate\Http\JsonResponse
|
||||
*/
|
||||
private function validateUpdate(Request $request)
|
||||
public function associate(Request $request, $giftId, $photoId)
|
||||
{
|
||||
// Validates basic fields to create the entry
|
||||
$validator = Validator::make($request->all(), [
|
||||
'is_for' => 'integer|nullable',
|
||||
'name' => 'required|string|max:255',
|
||||
'comment' => 'string|max:1000000|nullable',
|
||||
'url' => 'string|max:1000000|nullable',
|
||||
'value' => 'string|max:255',
|
||||
'is_an_idea' => 'boolean',
|
||||
'has_been_offered' => 'boolean',
|
||||
'date_offered' => 'date|nullable',
|
||||
'contact_id' => 'required|integer',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return $this->respondValidatorFailed($validator);
|
||||
}
|
||||
|
||||
try {
|
||||
Contact::where('account_id', auth()->user()->account_id)
|
||||
->where('id', $request->input('contact_id'))
|
||||
->firstOrFail();
|
||||
$gift = app(AssociatePhotoToGift::class)->execute([
|
||||
'account_id' => auth()->user()->account_id,
|
||||
'gift_id' => $giftId,
|
||||
'photo_id' => $photoId,
|
||||
]);
|
||||
|
||||
return new GiftResource($gift);
|
||||
} catch (ModelNotFoundException $e) {
|
||||
return $this->respondNotFound();
|
||||
} catch (ValidationException $e) {
|
||||
return $this->respondValidatorFailed($e->validator);
|
||||
}
|
||||
|
||||
if (! is_null($request->input('is_for'))) {
|
||||
try {
|
||||
Contact::where('account_id', auth()->user()->account_id)
|
||||
->where('id', $request->input('is_for'))
|
||||
->firstOrFail();
|
||||
} catch (ModelNotFoundException $e) {
|
||||
return $this->respondNotFound();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,16 +139,17 @@ class ApiGiftController extends ApiController
|
||||
public function destroy(Request $request, $giftId)
|
||||
{
|
||||
try {
|
||||
$gift = Gift::where('account_id', auth()->user()->account_id)
|
||||
->where('id', $giftId)
|
||||
->firstOrFail();
|
||||
app(DestroyGift::class)->execute([
|
||||
'account_id' => auth()->user()->account_id,
|
||||
'gift_id' => $giftId,
|
||||
]);
|
||||
|
||||
return $this->respondObjectDeleted($giftId);
|
||||
} catch (ModelNotFoundException $e) {
|
||||
return $this->respondNotFound();
|
||||
} catch (ValidationException $e) {
|
||||
return $this->respondValidatorFailed($e->validator);
|
||||
}
|
||||
|
||||
$gift->delete();
|
||||
|
||||
return $this->respondObjectDeleted($gift->id);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -200,16 +161,19 @@ class ApiGiftController extends ApiController
|
||||
{
|
||||
try {
|
||||
$contact = Contact::where('account_id', auth()->user()->account_id)
|
||||
->where('id', $contactId)
|
||||
->firstOrFail();
|
||||
->findOrFail($contactId);
|
||||
} catch (ModelNotFoundException $e) {
|
||||
return $this->respondNotFound();
|
||||
}
|
||||
|
||||
$gifts = $contact->gifts()
|
||||
->orderBy($this->sort, $this->sortDirection)
|
||||
->paginate($this->getLimitPerPage());
|
||||
try {
|
||||
$gifts = $contact->gifts()
|
||||
->orderBy($this->sort, $this->sortDirection)
|
||||
->paginate($this->getLimitPerPage());
|
||||
|
||||
return GiftResource::collection($gifts);
|
||||
return GiftResource::collection($gifts);
|
||||
} catch (QueryException $e) {
|
||||
return $this->respondInvalidQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,190 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Contacts;
|
||||
|
||||
use App\Helpers\DateHelper;
|
||||
use App\Helpers\MoneyHelper;
|
||||
use App\Models\Contact\Gift;
|
||||
use App\Models\Contact\Contact;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\People\GiftsRequest;
|
||||
|
||||
class GiftsController extends Controller
|
||||
{
|
||||
/**
|
||||
* List all the gifts for the given contact.
|
||||
*
|
||||
* @param Contact $contact
|
||||
*
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
public function index(Contact $contact)
|
||||
{
|
||||
$giftsCollection = collect([]);
|
||||
$gifts = $contact->gifts()->get();
|
||||
|
||||
foreach ($gifts as $gift) {
|
||||
$value = $gift->value;
|
||||
|
||||
if ($gift->value == '') {
|
||||
$value = 0;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'contact_hash' => $contact->hashID(),
|
||||
'id' => $gift->id,
|
||||
'name' => $gift->name,
|
||||
'recipient_name' => $gift->recipient_name,
|
||||
'comment' => $gift->comment,
|
||||
'url' => $gift->url,
|
||||
'value' => MoneyHelper::format($value),
|
||||
'does_value_exist' => (bool) $value,
|
||||
'is_an_idea' => $gift->is_an_idea,
|
||||
'has_been_offered' => $gift->has_been_offered,
|
||||
'has_been_received' => $gift->has_been_received,
|
||||
'offered_at' => DateHelper::getShortDate($gift->offered_at),
|
||||
'received_at' => DateHelper::getShortDate($gift->received_at),
|
||||
'created_at' => DateHelper::getShortDate($gift->created_at),
|
||||
'edit' => false,
|
||||
'show_comment' => false,
|
||||
];
|
||||
$giftsCollection->push($data);
|
||||
}
|
||||
|
||||
return $giftsCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a gift as being offered.
|
||||
* @param Contact $contact
|
||||
* @param Gift $gift
|
||||
* @return Gift
|
||||
*/
|
||||
public function toggle(Contact $contact, Gift $gift)
|
||||
{
|
||||
$gift->toggle();
|
||||
|
||||
return $gift;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
* @param Contact $contact
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function create(Contact $contact)
|
||||
{
|
||||
$familyRelationships = $contact->getRelationshipsByRelationshipTypeGroup('family');
|
||||
|
||||
return view('people.gifts.add')
|
||||
->withContact($contact)
|
||||
->withFamilyRelationships($familyRelationships)
|
||||
->withGift(new Gift);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param GiftsRequest $request
|
||||
* @param Contact $contact
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(GiftsRequest $request, Contact $contact)
|
||||
{
|
||||
$this->updateOrCreate($request, $contact);
|
||||
|
||||
return redirect()->route('people.show', $contact)
|
||||
->with('success', trans('people.gifts_add_success'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param Contact $contact
|
||||
* @param Gift $gift
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function edit(Contact $contact, Gift $gift)
|
||||
{
|
||||
$familyRelationships = $contact->getRelationshipsByRelationshipTypeGroup('family');
|
||||
|
||||
return view('people.gifts.edit')
|
||||
->withContact($contact)
|
||||
->withFamilyRelationships($familyRelationships)
|
||||
->withGift($gift);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param GiftsRequest $request
|
||||
* @param Contact $contact
|
||||
* @param Gift $gift
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update(GiftsRequest $request, Contact $contact, Gift $gift)
|
||||
{
|
||||
$this->updateOrCreate($request, $contact, $gift);
|
||||
|
||||
return redirect()->route('people.show', $contact)
|
||||
->with('success', trans('people.gifts_update_success'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param Contact $contact
|
||||
* @param Gift $gift
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function destroy(Contact $contact, Gift $gift): void
|
||||
{
|
||||
$gift->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save resource in storage.
|
||||
*
|
||||
* @param GiftsRequest $request
|
||||
* @param Contact $contact
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Model
|
||||
*/
|
||||
public function updateOrCreate(GiftsRequest $request, Contact $contact, Gift $gift = null)
|
||||
{
|
||||
$array =
|
||||
$request->only([
|
||||
'name',
|
||||
'comment',
|
||||
'url',
|
||||
'value',
|
||||
])
|
||||
+ [
|
||||
'account_id' => $contact->account_id,
|
||||
'is_an_idea' => ($request->input('offered') == 'idea' ? 1 : 0),
|
||||
'has_been_offered' => ($request->input('offered') == 'offered' ? 1 : 0),
|
||||
'has_been_received' => ($request->input('offered') == 'received' ? 1 : 0),
|
||||
];
|
||||
|
||||
if (is_null($gift)) {
|
||||
$gift = $contact->gifts()->create($array);
|
||||
} else {
|
||||
$gift->update($array);
|
||||
}
|
||||
|
||||
if ($request->input('has_recipient')
|
||||
&& Contact::where('account_id', auth()->user()->account_id)
|
||||
->find($request->input('recipient')) != null) {
|
||||
$gift->recipient = $request->input('recipient');
|
||||
$gift->save();
|
||||
}
|
||||
|
||||
return $gift;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ namespace App\Http\Resources\Gift;
|
||||
|
||||
use App\Helpers\DateHelper;
|
||||
use Illuminate\Http\Resources\Json\Resource;
|
||||
use App\Http\Resources\Photo\Photo as PhotoResource;
|
||||
use App\Http\Resources\Contact\ContactShort as ContactShortResource;
|
||||
|
||||
class Gift extends Resource
|
||||
@@ -19,18 +20,19 @@ class Gift extends Resource
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'object' => 'gift',
|
||||
'is_for' => new ContactShortResource($this->recipient),
|
||||
'name' => $this->name,
|
||||
'comment' => $this->comment,
|
||||
'url' => $this->url,
|
||||
'value' => $this->value,
|
||||
'is_an_idea' => $this->is_an_idea,
|
||||
'has_been_offered' => $this->has_been_offered,
|
||||
'date_offered' => $this->date_offered,
|
||||
'amount' => $this->value,
|
||||
'amount_with_currency' => $this->amount,
|
||||
'status' => $this->status,
|
||||
'date' => $this->date ? $this->date->format('Y-m-d') : null,
|
||||
'recipient' => new ContactShortResource($this->recipient),
|
||||
'photos' => PhotoResource::collection($this->photos),
|
||||
'contact' => new ContactShortResource($this->contact),
|
||||
'account' => [
|
||||
'id' => $this->account_id,
|
||||
],
|
||||
'contact' => new ContactShortResource($this->contact),
|
||||
'created_at' => DateHelper::getTimestamp($this->created_at),
|
||||
'updated_at' => DateHelper::getTimestamp($this->updated_at),
|
||||
];
|
||||
|
||||
@@ -36,6 +36,7 @@ class ExportAccountAsSQL
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$tempFileName = null;
|
||||
try {
|
||||
$tempFileName = app(ExportAccount::class)
|
||||
->execute([
|
||||
|
||||
@@ -9,7 +9,7 @@ use App\Models\ModelBindingHasherWithContact as Model;
|
||||
/**
|
||||
* @property Account $account
|
||||
* @property Contact $contact
|
||||
* @method static Builder due()s
|
||||
* @method static Builder due()
|
||||
* @method static Builder owed()
|
||||
* @method static Builder inProgress()
|
||||
*/
|
||||
|
||||
+21
-23
@@ -2,11 +2,14 @@
|
||||
|
||||
namespace App\Models\Contact;
|
||||
|
||||
use App\Helpers\MoneyHelper;
|
||||
use App\Models\Account\Photo;
|
||||
use App\Models\Account\Account;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use App\Models\ModelBindingWithContact as Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
/**
|
||||
* @property Account $account
|
||||
@@ -30,8 +33,7 @@ class Gift extends Model
|
||||
* @var array
|
||||
*/
|
||||
protected $dates = [
|
||||
'offered_at',
|
||||
'received_at',
|
||||
'date',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -40,9 +42,6 @@ class Gift extends Model
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'is_an_idea' => 'boolean',
|
||||
'has_been_offered' => 'boolean',
|
||||
'has_been_received' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -75,6 +74,16 @@ class Gift extends Model
|
||||
return $this->hasOne(Contact::class, 'id', 'is_for');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the photos record associated with the gift.
|
||||
*
|
||||
* @return BelongsToMany
|
||||
*/
|
||||
public function photos()
|
||||
{
|
||||
return $this->belongsToMany(Photo::class)->withTimestamps();
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit results to already offered gifts.
|
||||
*
|
||||
@@ -83,7 +92,7 @@ class Gift extends Model
|
||||
*/
|
||||
public function scopeOffered(Builder $query)
|
||||
{
|
||||
return $query->where('has_been_offered', 1);
|
||||
return $query->where('status', 'offered');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,7 +103,7 @@ class Gift extends Model
|
||||
*/
|
||||
public function scopeIsIdea(Builder $query)
|
||||
{
|
||||
return $query->where('is_an_idea', 1);
|
||||
return $query->where('status', 'idea');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,23 +145,12 @@ class Gift extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle a gift between the idea and offered state.
|
||||
* @return void
|
||||
* Get amount with currency.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toggle()
|
||||
public function getAmountAttribute(): string
|
||||
{
|
||||
$this->has_been_received = false;
|
||||
|
||||
if ($this->is_an_idea == 1) {
|
||||
$this->is_an_idea = false;
|
||||
$this->has_been_offered = true;
|
||||
$this->save();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->is_an_idea = true;
|
||||
$this->has_been_offered = false;
|
||||
$this->save();
|
||||
return $this->value ? MoneyHelper::format($this->value) : '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class PhotoGift extends Model
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -123,6 +123,10 @@ class AppServiceProvider extends ServiceProvider
|
||||
\App\Services\Contact\Conversation\UpdateMessage::class => \App\Services\Contact\Conversation\UpdateMessage::class,
|
||||
\App\Services\Contact\Document\DestroyDocument::class => \App\Services\Contact\Document\DestroyDocument::class,
|
||||
\App\Services\Contact\Document\UploadDocument::class => \App\Services\Contact\Document\UploadDocument::class,
|
||||
\App\Services\Contact\Gift\AssociatePhotoToGift::class => \App\Services\Contact\Gift\AssociatePhotoToGift::class,
|
||||
\App\Services\Contact\Gift\CreateGift::class => \App\Services\Contact\Gift\CreateGift::class,
|
||||
\App\Services\Contact\Gift\DestroyGift::class => \App\Services\Contact\Gift\DestroyGift::class,
|
||||
\App\Services\Contact\Gift\UpdateGift::class => \App\Services\Contact\Gift\UpdateGift::class,
|
||||
\App\Services\Contact\LifeEvent\CreateLifeEvent::class => \App\Services\Contact\LifeEvent\CreateLifeEvent::class,
|
||||
\App\Services\Contact\LifeEvent\DestroyLifeEvent::class => \App\Services\Contact\LifeEvent\DestroyLifeEvent::class,
|
||||
\App\Services\Contact\LifeEvent\UpdateLifeEvent::class => \App\Services\Contact\LifeEvent\UpdateLifeEvent::class,
|
||||
|
||||
@@ -676,16 +676,12 @@ SET FOREIGN_KEY_CHECKS=0;
|
||||
'id',
|
||||
'account_id',
|
||||
'contact_id',
|
||||
'is_for',
|
||||
'name',
|
||||
'comment',
|
||||
'url',
|
||||
'value',
|
||||
'is_an_idea',
|
||||
'has_been_offered',
|
||||
'has_been_received',
|
||||
'offered_at',
|
||||
'received_at',
|
||||
'status',
|
||||
'date',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
];
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Contact\Gift;
|
||||
|
||||
use App\Models\Contact\Gift;
|
||||
use App\Models\Account\Photo;
|
||||
use App\Services\BaseService;
|
||||
|
||||
class AssociatePhotoToGift extends BaseService
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the service.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'account_id' => 'required|integer|exists:accounts,id',
|
||||
'photo_id' => 'required|integer|exists:photos,id',
|
||||
'gift_id' => 'required|integer|exists:gifts,id',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Link a photo to a gift.
|
||||
*
|
||||
* @param array $data
|
||||
*/
|
||||
public function execute(array $data)
|
||||
{
|
||||
$this->validate($data);
|
||||
|
||||
$photo = Photo::where('account_id', $data['account_id'])
|
||||
->findOrFail($data['photo_id']);
|
||||
|
||||
$gift = Gift::where('account_id', $data['account_id'])
|
||||
->findOrFail($data['gift_id']);
|
||||
|
||||
$gift->photos()->syncWithoutDetaching([$photo->id]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Contact\Gift;
|
||||
|
||||
use App\Models\Contact\Gift;
|
||||
use App\Services\BaseService;
|
||||
use App\Models\Contact\Contact;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class CreateGift extends BaseService
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the service.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'account_id' => 'required|integer|exists:accounts,id',
|
||||
'contact_id' => 'required|integer|exists:contacts,id',
|
||||
'name' => 'required|string|max:255',
|
||||
'status' => [
|
||||
'required',
|
||||
Rule::in([
|
||||
'idea',
|
||||
'offered',
|
||||
'received',
|
||||
]),
|
||||
],
|
||||
'comment' => 'string|max:1000000|nullable',
|
||||
'url' => 'string|max:1000000|nullable',
|
||||
'amount' => 'numeric|nullable',
|
||||
'date' => 'date|nullable',
|
||||
'recipient_id' => 'integer|nullable|exists:contacts,id',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a tag.
|
||||
*
|
||||
* @param array $data
|
||||
* @return Gift
|
||||
*/
|
||||
public function execute(array $data): Gift
|
||||
{
|
||||
$this->validate($data);
|
||||
|
||||
Contact::where('account_id', $data['account_id'])
|
||||
->findOrFail($data['contact_id']);
|
||||
|
||||
if (isset($data['recipient_id'])) {
|
||||
Contact::where('account_id', $data['account_id'])
|
||||
->findOrFail($data['recipient_id']);
|
||||
}
|
||||
|
||||
$array = [
|
||||
'account_id' => $data['account_id'],
|
||||
'contact_id' => $data['contact_id'],
|
||||
'name' => $data['name'],
|
||||
'status' => $data['status'],
|
||||
'comment' => $this->nullOrvalue($data, 'comment'),
|
||||
'url' => $this->nullOrvalue($data, 'url'),
|
||||
'value' => $this->nullOrvalue($data, 'amount'),
|
||||
'date' => $this->nullOrvalue($data, 'date'),
|
||||
'recipient' => $this->nullOrvalue($data, 'recipient_id'),
|
||||
];
|
||||
|
||||
return Gift::create($array);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Contact\Gift;
|
||||
|
||||
use App\Models\Contact\Gift;
|
||||
use App\Services\BaseService;
|
||||
|
||||
class DestroyGift extends BaseService
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the service.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'account_id' => 'required|integer|exists:accounts,id',
|
||||
'gift_id' => 'required|integer|exists:gifts,id',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a gift.
|
||||
*
|
||||
* @param array $data
|
||||
* @return bool
|
||||
*/
|
||||
public function execute(array $data)
|
||||
{
|
||||
$this->validate($data);
|
||||
|
||||
$gift = Gift::where('account_id', $data['account_id'])
|
||||
->findOrFail($data['gift_id']);
|
||||
|
||||
$gift->photos()->detach();
|
||||
|
||||
$gift->delete();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Contact\Gift;
|
||||
|
||||
use App\Models\Contact\Gift;
|
||||
use App\Services\BaseService;
|
||||
use App\Models\Contact\Contact;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class UpdateGift extends BaseService
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the service.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'account_id' => 'required|integer|exists:accounts,id',
|
||||
'gift_id' => 'required|integer|exists:gifts,id',
|
||||
'contact_id' => 'required|integer|exists:contacts,id',
|
||||
'name' => 'required|string|max:255',
|
||||
'status' => [
|
||||
'required',
|
||||
Rule::in([
|
||||
'idea',
|
||||
'offered',
|
||||
'received',
|
||||
]),
|
||||
],
|
||||
'comment' => 'string|max:1000000|nullable',
|
||||
'url' => 'string|max:1000000|nullable',
|
||||
'amount' => 'numeric|nullable',
|
||||
'date' => 'date|nullable',
|
||||
'recipient_id' => 'integer|nullable|exists:contacts,id',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a gift.
|
||||
*
|
||||
* @param array $data
|
||||
* @return Gift
|
||||
*/
|
||||
public function execute(array $data): Gift
|
||||
{
|
||||
$this->validate($data);
|
||||
|
||||
$gift = Gift::where('account_id', $data['account_id'])
|
||||
->findOrFail($data['gift_id']);
|
||||
|
||||
Contact::where('account_id', $data['account_id'])
|
||||
->findOrFail($data['contact_id']);
|
||||
|
||||
if (isset($data['recipient_id'])) {
|
||||
Contact::where('account_id', $data['account_id'])
|
||||
->findOrFail($data['recipient_id']);
|
||||
}
|
||||
|
||||
$array = [
|
||||
'contact_id' => $data['contact_id'],
|
||||
'name' => $data['name'],
|
||||
'status' => $data['status'],
|
||||
'comment' => $this->nullOrvalue($data, 'comment'),
|
||||
'url' => $this->nullOrvalue($data, 'url'),
|
||||
'value' => $this->nullOrvalue($data, 'amount'),
|
||||
'date' => $this->nullOrvalue($data, 'date'),
|
||||
'recipient' => $this->nullOrvalue($data, 'recipient_id'),
|
||||
];
|
||||
|
||||
return tap($gift)
|
||||
->update($array);
|
||||
}
|
||||
}
|
||||
@@ -131,6 +131,7 @@ $factory->define(App\Models\Contact\Gift::class, function (Faker\Generator $fake
|
||||
'account_id' => $data['account_id'],
|
||||
])->id;
|
||||
},
|
||||
'status' => 'idea',
|
||||
'created_at' => \App\Helpers\DateHelper::parseDateTime($faker->dateTimeThisCentury()),
|
||||
];
|
||||
});
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Contact\Gift;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class ChangeGiftStatus extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('gifts', function (Blueprint $table) {
|
||||
$table->unsignedInteger('is_for')->change();
|
||||
$table->string('status', 8)->after('has_been_received')->default('idea');
|
||||
$table->datetime('date')->after('status')->nullable();
|
||||
|
||||
$table->foreign('is_for')->references('id')->on('contacts')->onDelete('set null');
|
||||
});
|
||||
|
||||
Gift::chunk(500, function ($gifts) {
|
||||
foreach ($gifts as $gift) {
|
||||
$gift->status = $gift->has_been_offered === 1 ? 'offered' :
|
||||
($gift->has_been_received === 1 ? 'received' : 'idea');
|
||||
$gift->save();
|
||||
}
|
||||
});
|
||||
|
||||
Schema::table('gifts', function (Blueprint $table) {
|
||||
$table->dropColumn('is_an_idea');
|
||||
$table->dropColumn('has_been_offered');
|
||||
$table->dropColumn('has_been_received');
|
||||
$table->dropColumn('offered_at');
|
||||
$table->dropColumn('received_at');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddPhotoGift extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('gift_photo', function (Blueprint $table) {
|
||||
$table->unsignedInteger('photo_id');
|
||||
$table->unsignedInteger('gift_id');
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('photo_id')->references('id')->on('photos')->onDelete('cascade');
|
||||
$table->foreign('gift_id')->references('id')->on('gifts')->onDelete('cascade');
|
||||
|
||||
$table->primary(['photo_id', 'gift_id']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('gift_photo');
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ use App\Helpers\CountriesHelper;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Models\Contact\LifeEventType;
|
||||
use App\Models\Contact\ContactFieldType;
|
||||
use App\Services\Contact\Gift\CreateGift;
|
||||
use App\Services\Contact\Tag\AssociateTag;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use App\Services\Contact\Address\CreateAddress;
|
||||
@@ -325,7 +326,7 @@ class FakeContentTableSeeder extends Seeder
|
||||
{
|
||||
if (rand(1, 2) == 1) {
|
||||
for ($j = 0; $j < rand(1, 6); $j++) {
|
||||
$debt = $this->contact->debts()->create([
|
||||
$this->contact->debts()->create([
|
||||
'in_debt' => (rand(1, 2) == 1 ? 'yes' : 'no'),
|
||||
'amount' => rand(321, 39391),
|
||||
'reason' => $this->faker->realText(rand(100, 1000)),
|
||||
@@ -340,15 +341,14 @@ class FakeContentTableSeeder extends Seeder
|
||||
{
|
||||
if (rand(1, 2) == 1) {
|
||||
for ($j = 0; $j < rand(1, 31); $j++) {
|
||||
$gift = $this->contact->gifts()->create([
|
||||
|
||||
app(CreateGift::class)->execute([
|
||||
'account_id' => $this->contact->account_id,
|
||||
'contact_id' => $this->contact->id,
|
||||
'status' => (rand(1, 3) == 1 ? 'offered' : 'idea'),
|
||||
'name' => $this->faker->realText(rand(10, 100)),
|
||||
'comment' => $this->faker->realText(rand(1000, 5000)),
|
||||
'url' => $this->faker->url,
|
||||
'value' => rand(12, 120),
|
||||
'account_id' => $this->contact->account_id,
|
||||
'is_an_idea' => true,
|
||||
'has_been_offered' => false,
|
||||
'amount' => rand(12, 120),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,5 +55,10 @@
|
||||
<server name="LOG_CHANNEL" value="testing"/>
|
||||
<server name="DAV_ENABLED" value="true"/>
|
||||
<server name="REQUIRES_SUBSCRIPTION" value="false"/>
|
||||
<server name="MAIL_DRIVER" value="smtp"/>
|
||||
<server name="MAIL_HOST" value=""/>
|
||||
<server name="MAIL_PORT" value=""/>
|
||||
<server name="MAIL_ENCRYPTION" value=""/>
|
||||
<server name="APP_EMAIL_NEW_USERS_NOTIFICATION" value=""/>
|
||||
</php>
|
||||
</phpunit>
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
{
|
||||
"entries": [
|
||||
{
|
||||
"date": "Jan 04, 2020",
|
||||
"title": "New feature: associate a photo to a gift",
|
||||
"description": "Adding a gift is now done inline, and you can associate a photo with it, to remember what you offered. "
|
||||
},
|
||||
{
|
||||
"date": "Dec 22, 2019",
|
||||
"title": "Enhancement: add emotions to activities",
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 126 KiB |
Vendored
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"/js/manifest.js": "/js/manifest.js?id=7db827d654313dce4250",
|
||||
"/js/vendor.js": "/js/vendor.js?id=40b38586d672dc60fd66",
|
||||
"/js/app.js": "/js/app.js?id=b44a02e3915441821e28",
|
||||
"/js/vendor.js": "/js/vendor.js?id=add29da7fca54527d87a",
|
||||
"/js/app.js": "/js/app.js?id=f8e3246e1aa973c9616e",
|
||||
"/css/app-ltr.css": "/css/app-ltr.css?id=ff41d6c39e7fba5cf37e",
|
||||
"/css/app-rtl.css": "/css/app-rtl.css?id=ee3f6d81111cdc99f35d",
|
||||
"/css/stripe.css": "/css/stripe.css?id=76c70a7b11ae5f38a725",
|
||||
|
||||
Vendored
+1
-11
@@ -168,7 +168,7 @@ Vue.component(
|
||||
|
||||
Vue.component(
|
||||
'contact-gift',
|
||||
require('./components/people/Gifts.vue').default
|
||||
require('./components/people/gifts/Gifts.vue').default
|
||||
);
|
||||
|
||||
Vue.component(
|
||||
@@ -206,16 +206,6 @@ Vue.component(
|
||||
require('./components/people/activity/ActivityList.vue').default
|
||||
);
|
||||
|
||||
Vue.component(
|
||||
'create-activity',
|
||||
require('./components/people/activity/CreateActivity.vue').default
|
||||
);
|
||||
|
||||
Vue.component(
|
||||
'activity-type-list',
|
||||
require('./components/people/activity/ActivityTypeList.vue').default
|
||||
);
|
||||
|
||||
Vue.component(
|
||||
'document-list',
|
||||
require('./components/people/document/DocumentList.vue').default
|
||||
|
||||
@@ -1,17 +1,25 @@
|
||||
<template>
|
||||
<div v-if="errors.length > 0" class="alert alert-danger">
|
||||
<div v-if="apierror || errors.length > 0" class="alert alert-danger">
|
||||
<p>{{ $t('app.error_title') }}</p>
|
||||
<br />
|
||||
<p v-if="errors[0] != 'The given data was invalid.'">
|
||||
{{ errors[0] }}
|
||||
</p>
|
||||
<template v-if="display(errors[1])">
|
||||
<ul v-for="errorsList in errors[1]" :key="errorsList.id">
|
||||
<li v-for="error in errorsList" :key="error.id">
|
||||
<template v-if="apierror">
|
||||
<ul>
|
||||
<li v-for="error in errors[0].message" :key="error.id">
|
||||
▪️ {{ error }}
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
<template v-else>
|
||||
<p v-if="errors[0] != 'The given data was invalid.'">
|
||||
{{ errors[0] }}
|
||||
</p>
|
||||
<template v-if="display(errors[1])">
|
||||
<ul v-for="errorsList in errors[1]" :key="errorsList.id">
|
||||
<li v-for="error in errorsList" :key="error.id">
|
||||
▪️ {{ error }}
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -19,12 +27,15 @@
|
||||
export default {
|
||||
props: {
|
||||
errors: {
|
||||
type: Array,
|
||||
default: function () {
|
||||
return [];
|
||||
}
|
||||
type: [Array, Object],
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
apierror() {
|
||||
return _.isObject(this.errors[0]) && this.errors[0].error_code !== undefined;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
display($val) {
|
||||
return _.isObject($val);
|
||||
|
||||
@@ -26,9 +26,11 @@ input:focus {
|
||||
autofocus
|
||||
:required="required"
|
||||
:name="id"
|
||||
:placeholder="placeholder"
|
||||
:class="inputClass"
|
||||
:style="inputStyle"
|
||||
:value="value"
|
||||
:maxlength="maxlength"
|
||||
@input="event => { $emit('input', event.target.value) }"
|
||||
@keyup.enter="event => { $emit('submit', event.target.value) }"
|
||||
/>
|
||||
@@ -51,6 +53,10 @@ export default {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
@@ -67,6 +73,10 @@ export default {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
maxlength: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
|
||||
@@ -13,9 +13,11 @@
|
||||
:color="inputColor"
|
||||
:value="value"
|
||||
:disabled="disabled"
|
||||
:required="required"
|
||||
@change="event => { $emit('change', event) }"
|
||||
>
|
||||
<slot></slot>
|
||||
<slot slot="extra" name="inputextra"></slot>
|
||||
</p-input>
|
||||
</div>
|
||||
<div class="pointer" @click="select()">
|
||||
@@ -58,6 +60,10 @@ export default {
|
||||
type: [String, Array],
|
||||
default: ''
|
||||
},
|
||||
fullClass: {
|
||||
type: [String, Array],
|
||||
default: ''
|
||||
},
|
||||
dclass: {
|
||||
type: [String, Array],
|
||||
default: ''
|
||||
@@ -69,6 +75,10 @@ export default {
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
@@ -80,7 +90,7 @@ export default {
|
||||
return 'input';
|
||||
},
|
||||
inputClass() {
|
||||
return [this.iclass, 'p-default', this.$options.input_iclass];
|
||||
return this.fullClass != '' ? this.fullClass : [this.iclass, 'p-default', this.$options.input_iclass];
|
||||
},
|
||||
inputColor() {
|
||||
return this.color != '' ? this.color : 'primary-o';
|
||||
|
||||
@@ -20,4 +20,4 @@ let radio = {
|
||||
};
|
||||
|
||||
export default radio;
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@@ -85,8 +85,8 @@
|
||||
{{ props.row.complete_name }}
|
||||
</span>
|
||||
<svg class="relative" style="top: 5px" width="23" height="22" viewBox="0 0 23 22"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M14.6053 7.404L14.7224 7.68675L15.0275 7.7111C16.7206 7.84628 18.1486 7.92359 19.2895 7.98536C19.9026 8.01855 20.4327 8.04725 20.8765 8.07803C21.5288 8.12327 21.9886 8.17235 22.2913 8.24003C22.3371 8.25027 22.3765 8.26035 22.4102 8.27001C22.3896 8.29619 22.3651 8.32572 22.336 8.35877C22.1328 8.58945 21.7914 8.89714 21.2913 9.31475C20.9474 9.60184 20.5299 9.93955 20.0459 10.331C19.1625 11.0455 18.0577 11.9391 16.7762 13.0302L16.543 13.2288L16.614 13.5267C17.0045 15.1663 17.3689 16.5406 17.6601 17.6391C17.819 18.2381 17.956 18.7552 18.0638 19.1886C18.2209 19.8206 18.3149 20.2704 18.3428 20.5768C18.347 20.6222 18.3493 20.6616 18.3505 20.6957C18.3176 20.6838 18.2798 20.6688 18.2367 20.6502C17.9532 20.5277 17.5539 20.2981 17.0014 19.9522C16.6264 19.7175 16.1833 19.4306 15.671 19.099C14.7143 18.4797 13.5162 17.7042 12.0695 16.8199L11.8087 16.6604L11.5478 16.82C10.0753 17.7209 8.86032 18.5085 7.89223 19.136C7.3851 19.4648 6.94572 19.7496 6.57253 19.9838C6.01576 20.3332 5.61353 20.5656 5.32808 20.6899C5.28721 20.7077 5.25111 20.7222 5.21941 20.7339C5.22088 20.7009 5.22355 20.663 5.22783 20.6197C5.25839 20.3111 5.35605 19.8582 5.51781 19.2225C5.62627 18.7962 5.76269 18.2914 5.92018 17.7087C6.22053 16.5972 6.59748 15.2024 7.00309 13.5286L7.07553 13.2297L6.84141 13.0303C5.52399 11.9079 4.39683 10.9982 3.50024 10.2747C3.03915 9.90254 2.63904 9.57963 2.30539 9.30232C1.80195 8.88388 1.45729 8.57562 1.25116 8.34437C1.22315 8.31293 1.19929 8.28466 1.17903 8.25939C1.20999 8.25084 1.24557 8.24198 1.28628 8.233C1.58841 8.1663 2.048 8.11835 2.701 8.07418C3.1353 8.0448 3.65101 8.01744 4.24568 7.98589C5.39523 7.9249 6.83989 7.84824 8.56208 7.71111L8.86638 7.68688L8.98388 7.40514C9.61646 5.88824 10.1238 4.58366 10.5314 3.53571C10.7656 2.93365 10.9668 2.4163 11.1399 1.99205C11.3854 1.39027 11.5751 0.972355 11.7339 0.708729C11.7601 0.66516 11.7838 0.628777 11.8048 0.598565C11.8256 0.628571 11.849 0.664658 11.8748 0.707817C12.0327 0.971308 12.2212 1.38911 12.465 1.99089C12.6368 2.41509 12.8365 2.93242 13.0689 3.53445C13.4735 4.58244 13.9771 5.88709 14.6053 7.404Z"
|
||||
fill="#F2C94C" stroke="#DCBB58"
|
||||
|
||||
@@ -1,300 +0,0 @@
|
||||
<style scoped>
|
||||
</style>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<notifications group="main" position="bottom right" />
|
||||
|
||||
<!-- Title -->
|
||||
<div>
|
||||
<img src="img/people/gifts.svg" :alt="$t('people.gifts_title')" class="icon-section icon-tasks" />
|
||||
<h3>
|
||||
{{ $t('people.gifts_title') }}
|
||||
<a :href="'people/' + hash + '/gifts/create'" cy-name="add-gift-button" class="btn f6 pt2" :class="[ dirltr ? 'fr' : 'fl' ]">
|
||||
{{ $t('people.gifts_add_gift') }}
|
||||
</a>
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<!-- Listing -->
|
||||
<div>
|
||||
<ul class="mb3">
|
||||
<li class="di">
|
||||
<p :class="[activeTab == 'ideas' ? 'di pointer mr3 b' : 'di pointer mr3 black-50']" @click.prevent="setActiveTab('ideas')">
|
||||
{{ $t('people.gifts_ideas') }} ({{ ideas.length }})
|
||||
</p>
|
||||
</li>
|
||||
<li class="di">
|
||||
<p :class="[activeTab == 'offered' ? 'di pointer mr3 b' : 'di pointer mr3 black-50']" @click.prevent="setActiveTab('offered')">
|
||||
{{ $t('people.gifts_offered') }} ({{ offered.length }})
|
||||
</p>
|
||||
</li>
|
||||
<li class="di">
|
||||
<p :class="[activeTab == 'received' ? 'di pointer mr3 b' : 'di pointer mr3 black-50']" @click.prevent="setActiveTab('received')">
|
||||
{{ $t('people.gifts_received') }} ({{ received.length }})
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div v-if="activeTab == 'ideas'">
|
||||
<div v-for="gift in ideas" :key="gift.id" class="ba b--gray-monica mb3 br2" :cy-name="'gift-idea-item-' + gift.id">
|
||||
<p class="mb1 bb b--gray-monica pa2">
|
||||
<strong>{{ gift.name }}</strong>
|
||||
|
||||
<span v-if="gift.recipient_name">
|
||||
<span class="mr1 black-50">
|
||||
•
|
||||
</span>
|
||||
{{ $t('people.gifts_for') }} {{ gift.recipient_name }}
|
||||
</span>
|
||||
|
||||
<span v-if="gift.url">
|
||||
<span class="mr1 black-50">
|
||||
•
|
||||
</span>
|
||||
<a :href="gift.url" target="_blank">
|
||||
{{ $t('people.gifts_link') }}
|
||||
</a>
|
||||
</span>
|
||||
</p>
|
||||
<div class="f6 ph2 pv1 mb1">
|
||||
<span v-if="gift.does_value_exist">
|
||||
{{ gift.value }}
|
||||
<span class="ml1 mr1 black-50">
|
||||
•
|
||||
</span>
|
||||
</span>
|
||||
<a v-if="gift.comment" class="ml1 mr1 pointer" href="" @click.prevent="toggleComment(gift)">
|
||||
{{ $t('people.gifts_view_comment') }}
|
||||
</a>
|
||||
<a class="pointer mr1" href="" @click.prevent="toggle(gift)">
|
||||
{{ $t('people.gifts_mark_offered') }}
|
||||
</a>
|
||||
<a :href="'people/' + hash + '/gifts/' + gift.id + '/edit'" :cy-name="'edit-gift-button-' + gift.id">
|
||||
{{ $t('app.edit') }}
|
||||
</a>
|
||||
<a class="mr1 pointer" :cy-name="'delete-gift-button-' + gift.id" href="" @click.prevent="showDeleteModal(gift)">
|
||||
{{ $t('app.delete') }}
|
||||
</a>
|
||||
<div v-if="gift.show_comment" class="mb1 mt1">
|
||||
{{ gift.comment }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template v-else-if="activeTab == 'offered'">
|
||||
<div v-for="gift in offered" :key="gift.id" class="ba b--gray-monica mb3 br2">
|
||||
<p class="mb1 bb b--gray-monica pa2">
|
||||
<strong>{{ gift.name }}</strong>
|
||||
|
||||
<span v-if="gift.recipient_name">
|
||||
<span class="mr1 black-50">
|
||||
•
|
||||
</span>
|
||||
{{ $t('people.gifts_for') }} {{ gift.recipient_name }}
|
||||
</span>
|
||||
|
||||
<span v-if="gift.url">
|
||||
<span class="mr1 black-50">
|
||||
•
|
||||
</span>
|
||||
<a :href="gift.url" target="_blank">
|
||||
{{ $t('people.gifts_link') }}
|
||||
</a>
|
||||
</span>
|
||||
</p>
|
||||
<div class="f6 ph2 pv1 mb1">
|
||||
<span v-if="gift.does_value_exist">
|
||||
{{ gift.value }}
|
||||
<span class="ml1 mr1 black-50">
|
||||
•
|
||||
</span>
|
||||
</span>
|
||||
<a v-if="gift.comment" class="ml1 mr1 pointer" href="" @click.prevent="toggleComment(gift)">
|
||||
{{ $t('people.gifts_view_comment') }}
|
||||
</a>
|
||||
<a class="pointer mr1" href="" @click.prevent="toggle(gift)">
|
||||
{{ $t('people.gifts_offered_as_an_idea') }}
|
||||
</a>
|
||||
<a :href="'people/' + hash + '/gifts/' + gift.id + '/edit'" :cy-name="'edit-gift-button-' + gift.id">
|
||||
{{ $t('app.edit') }}
|
||||
</a>
|
||||
<a class="mr1 pointer" :cy-name="'delete-gift-button-' + gift.id" href="" @click.prevent="showDeleteModal(gift)">
|
||||
{{ $t('app.delete') }}
|
||||
</a>
|
||||
<div v-if="gift.show_comment" class="mb1 mt1">
|
||||
{{ gift.comment }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else-if="activeTab == 'received'">
|
||||
<div v-for="gift in received" :key="gift.id" class="ba b--gray-monica mb3 br2">
|
||||
<p class="mb1 bb b--gray-monica pa2">
|
||||
<strong>{{ gift.name }}</strong>
|
||||
|
||||
<span v-if="gift.recipient_name">
|
||||
<span class="mr1 black-50">
|
||||
•
|
||||
</span>
|
||||
{{ $t('people.gifts_for') }} {{ gift.recipient_name }}
|
||||
</span>
|
||||
|
||||
<span v-if="gift.url">
|
||||
<span class="mr1 black-50">
|
||||
•
|
||||
</span>
|
||||
<a :href="gift.url" target="_blank">
|
||||
{{ $t('people.gifts_link') }}
|
||||
</a>
|
||||
</span>
|
||||
</p>
|
||||
<div class="f6 ph2 pv1 mb1">
|
||||
<span v-if="gift.does_value_exist">
|
||||
{{ gift.value }}
|
||||
<span class="ml1 mr1 black-50">
|
||||
•
|
||||
</span>
|
||||
</span>
|
||||
<a v-if="gift.comment" class="ml1 mr1 pointer" href="" @click.prevent="toggleComment(gift)">
|
||||
{{ $t('people.gifts_view_comment') }}
|
||||
</a>
|
||||
<a :href="'people/' + hash + '/gifts/' + gift.id + '/edit'">
|
||||
{{ $t('app.edit') }}
|
||||
</a>
|
||||
<a class="mr1 pointer" href="" @click.prevent="showDeleteModal(gift)">
|
||||
{{ $t('app.delete') }}
|
||||
</a>
|
||||
<div v-if="gift.show_comment" class="mb1 mt1">
|
||||
{{ gift.comment }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<sweet-modal ref="modal" overlay-theme="dark" title="Delete gift">
|
||||
<form>
|
||||
<div class="mb4">
|
||||
{{ $t('people.gifts_delete_confirmation') }}
|
||||
</div>
|
||||
</form>
|
||||
<div slot="button">
|
||||
<a class="btn" href="" @click.prevent="closeDeleteModal()">
|
||||
{{ $t('app.cancel') }}
|
||||
</a>
|
||||
<a class="btn" :cy-name="'modal-delete-gift-button-' + giftToTrash.id" href="" @click.prevent="trash(giftToTrash)">
|
||||
{{ $t('app.delete') }}
|
||||
</a>
|
||||
</div>
|
||||
</sweet-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { SweetModal } from 'sweet-modal-vue';
|
||||
|
||||
export default {
|
||||
|
||||
components: {
|
||||
SweetModal
|
||||
},
|
||||
|
||||
props: {
|
||||
hash: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
giftsActiveTab: {
|
||||
type: String,
|
||||
default: 'ideas',
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
gifts: [],
|
||||
activeTab: '',
|
||||
giftToTrash: '',
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
dirltr() {
|
||||
return this.$root.htmldir == 'ltr';
|
||||
},
|
||||
|
||||
ideas: function () {
|
||||
return this.gifts.filter(function (gift) {
|
||||
return gift.is_an_idea === true;
|
||||
});
|
||||
},
|
||||
|
||||
offered: function () {
|
||||
return this.gifts.filter(function (gift) {
|
||||
return gift.has_been_offered === true;
|
||||
});
|
||||
},
|
||||
|
||||
received: function () {
|
||||
return this.gifts.filter(function (gift) {
|
||||
return gift.has_been_received === true;
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.prepareComponent();
|
||||
},
|
||||
|
||||
methods: {
|
||||
prepareComponent() {
|
||||
this.getGifts();
|
||||
this.setActiveTab(this.giftsActiveTab);
|
||||
},
|
||||
|
||||
toggleComment(gift) {
|
||||
Vue.set(gift, 'show_comment', !gift.show_comment);
|
||||
},
|
||||
|
||||
setActiveTab(view) {
|
||||
this.activeTab = view;
|
||||
},
|
||||
|
||||
getGifts() {
|
||||
axios.get('people/' + this.hash + '/gifts')
|
||||
.then(response => {
|
||||
this.gifts = response.data;
|
||||
});
|
||||
},
|
||||
|
||||
toggle(gift) {
|
||||
axios.post('people/' + this.hash + '/gifts/' + gift.id + '/toggle')
|
||||
.then(response => {
|
||||
Vue.set(gift, 'is_an_idea', response.data.is_an_idea);
|
||||
Vue.set(gift, 'has_been_offered', response.data.has_been_offered);
|
||||
});
|
||||
},
|
||||
|
||||
showDeleteModal(gift) {
|
||||
this.$refs.modal.open();
|
||||
this.giftToTrash = gift;
|
||||
},
|
||||
|
||||
trash(gift) {
|
||||
axios.delete('people/' + this.hash + '/gifts/' + gift.id)
|
||||
.then(response => {
|
||||
this.gifts.splice(this.gifts.indexOf(gift), 1);
|
||||
this.closeDeleteModal();
|
||||
});
|
||||
},
|
||||
|
||||
closeDeleteModal() {
|
||||
this.$refs.modal.close();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -119,8 +119,13 @@
|
||||
|
||||
<script>
|
||||
import moment from 'moment';
|
||||
import CreateActivity from './CreateActivity.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CreateActivity
|
||||
},
|
||||
|
||||
filters: {
|
||||
moment: function (date) {
|
||||
return moment.utc(date).format('LL');
|
||||
|
||||
@@ -125,9 +125,11 @@
|
||||
<script>
|
||||
import moment from 'moment';
|
||||
import Error from '../../partials/Error.vue';
|
||||
import ActivityTypeList from './ActivityTypeList.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ActivityTypeList,
|
||||
Error
|
||||
},
|
||||
|
||||
|
||||
@@ -0,0 +1,387 @@
|
||||
<style scoped>
|
||||
.photo {
|
||||
height: 200px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<!-- Add a gift -->
|
||||
<transition name="fade">
|
||||
<div class="ba br3 mb3 pa3 b--black-40">
|
||||
<div class="pb3 mb3 flex-ns b--gray-monica">
|
||||
<!-- STATUS -->
|
||||
<form-radio
|
||||
:id="'status_idea'"
|
||||
v-model="newGift.status"
|
||||
:name="'status'"
|
||||
:required="true"
|
||||
:value="'idea'"
|
||||
:color="'success'"
|
||||
:full-class="'p-default p-fill p-curve'"
|
||||
>
|
||||
{{ $t('people.gifts_add_gift_idea') }}
|
||||
</form-radio>
|
||||
|
||||
<form-radio
|
||||
:id="'status_offered'"
|
||||
v-model="newGift.status"
|
||||
:name="'status'"
|
||||
:required="true"
|
||||
:value="'offered'"
|
||||
:color="'info'"
|
||||
:full-class="'p-default p-fill p-curve'"
|
||||
>
|
||||
{{ $t('people.gifts_add_gift_already_offered') }}
|
||||
</form-radio>
|
||||
|
||||
<form-radio
|
||||
:id="'status_received'"
|
||||
v-model="newGift.status"
|
||||
:name="'status'"
|
||||
:required="true"
|
||||
:value="'received'"
|
||||
:color="'warning'"
|
||||
:full-class="'p-default p-fill p-curve'"
|
||||
>
|
||||
{{ $t('people.gifts_add_gift_received') }}
|
||||
</form-radio>
|
||||
</div>
|
||||
|
||||
<div class="dt dt--fixed pb3 mb3 mb0-ns bb b--gray-monica">
|
||||
<!-- NAME -->
|
||||
<form-input
|
||||
:id="'name'"
|
||||
v-model="newGift.name"
|
||||
:input-type="'text'"
|
||||
:maxlength="255"
|
||||
:required="true"
|
||||
:class="'dtc pr2'"
|
||||
:title="$t('people.gifts_add_gift_name')"
|
||||
@submit="store"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- ADDITIONAL FIELDS -->
|
||||
<div v-show="displayMenu" class="bb b--gray-monica pv3 mb3">
|
||||
<ul class="list">
|
||||
<li v-show="!displayComment" class="di pointer" :class="dirltr ? 'mr3' : 'ml3'">
|
||||
<a href="" @click.prevent="displayComment = true">{{ $t('people.gifts_add_comment') }}</a>
|
||||
</li>
|
||||
<li v-show="!displayUrl" class="di pointer" :class="dirltr ? 'mr3' : 'ml3'">
|
||||
<a href="" @click.prevent="displayUrl = true">{{ $t('people.gifts_add_link') }}</a>
|
||||
</li>
|
||||
<li v-show="!displayAmount" class="di pointer" :class="dirltr ? 'mr3' : 'ml3'">
|
||||
<a href="" @click.prevent="displayAmount = true; newGift.amount = 0;">{{ $t('people.gifts_add_value') }}</a>
|
||||
</li>
|
||||
<li v-if="familyContacts.length > 0" v-show="!displayRecipient" class="di pointer" :class="dirltr ? 'mr3' : 'ml3'">
|
||||
<a href="" @click.prevent="displayRecipient = true">{{ $t('people.gifts_add_recipient') }}</a>
|
||||
</li>
|
||||
<li v-if="!reachLimit" v-show="!displayUpload" class="di pointer" :class="dirltr ? 'mr3' : 'ml3'">
|
||||
<a href="" @click.prevent="() => { displayUpload = true; $refs.upload.showUploadZone(); }">{{ $t('people.gifts_add_photo') }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div v-if="displayComment" class="dt dt--fixed pb3 mb3 bb b--gray-monica">
|
||||
<!-- COMMENT -->
|
||||
<form-input
|
||||
:id="'comment'"
|
||||
v-model="newGift.comment"
|
||||
:input-type="'text'"
|
||||
:class="'dtc pr2'"
|
||||
:title="$t('people.gifts_add_comment')"
|
||||
@submit="store"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="displayUrl" class="dt dt--fixed pb3 mb3 bb b--gray-monica">
|
||||
<!-- URL -->
|
||||
<form-input
|
||||
:id="'url'"
|
||||
v-model="newGift.url"
|
||||
:input-type="'text'"
|
||||
:class="'dtc pr2'"
|
||||
:title="$t('people.gifts_add_link')"
|
||||
:placeholder="'https://'"
|
||||
@submit="store"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="displayAmount" class="dt dt--fixed pb3 mb3 bb b--gray-monica">
|
||||
<!-- AMOUNT -->
|
||||
<form-input
|
||||
:id="'amount'"
|
||||
v-model="newGift.amount"
|
||||
:input-type="'number'"
|
||||
:class="'dtc pr2'"
|
||||
:title="$t('people.gifts_add_value')"
|
||||
:required="displayAmount"
|
||||
@submit="store"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="displayRecipient" class="dt dt--fixed pb3 mb3 bb b--gray-monica">
|
||||
<!-- RECIPIENT -->
|
||||
<form-checkbox
|
||||
v-model="hasRecipient"
|
||||
:name="'has_recipient'"
|
||||
@change="() => { this.$refs.recipient.focus() }"
|
||||
>
|
||||
{{ $t('people.gifts_add_someone', {name: ''}) }}
|
||||
</form-checkbox>
|
||||
<form-select
|
||||
ref="recipient"
|
||||
v-model="newGift.recipient_id"
|
||||
:options="familyContacts"
|
||||
@input="hasRecipient = true"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-show="displayUpload" class="dt dt--fixed pb3 mb3 bb b--gray-monica">
|
||||
<span class="mb2 b">
|
||||
{{ $t('people.gifts_add_photo_title') }}
|
||||
</span>
|
||||
|
||||
<photo-upload
|
||||
v-show="photos.length == 0"
|
||||
ref="upload"
|
||||
:contact-id="contactId"
|
||||
@upload.stop="handlePhoto($event)"
|
||||
/>
|
||||
|
||||
<!-- LIST OF PHOTO -->
|
||||
<div v-show="photos.length > 0">
|
||||
<div class="flex flex-wrap">
|
||||
<div v-for="photo in photos" :key="photo.id" class="w-third-ns w-100">
|
||||
<div v-if="photo.id > 0" class="pa2 mb3 br2 ba b--gray-monica" :class="dirltr ? 'mr3' : 'ml3'">
|
||||
<div class="cover bg-center photo w-100 h-100 br2 bb b--gray-monica pb2"
|
||||
:style="'background-image: url(' + photo.link + ');'"
|
||||
>
|
||||
</div>
|
||||
<div class="pt2">
|
||||
<ul>
|
||||
<li>
|
||||
<a class="pointer" href="" @click.prevent="deletePhoto(photo)">
|
||||
{{ $t('people.photo_delete') }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="ba br3 photo-upload-zone mb3 pa3">
|
||||
<div class="tc dib w-100 relative">
|
||||
{{ $tc('app.file_selected', photos.length, {count: photos.length}) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<error :errors="errors" />
|
||||
|
||||
<!-- ACTIONS -->
|
||||
<div class="pt3">
|
||||
<div class="flex-ns justify-between">
|
||||
<div class="">
|
||||
<a class="btn btn-secondary tc w-auto-ns w-100 mb2 pb0-ns" @click.prevent="close">
|
||||
{{ $t('app.cancel') }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="">
|
||||
<button class="btn btn-primary w-auto-ns w-100 mb2 pb0-ns" @click.prevent="store">
|
||||
{{ gift ? $t('app.update') : $t('app.add') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import Error from '../../partials/Error.vue';
|
||||
import PhotoUpload from '../photo/PhotoUpload.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Error,
|
||||
PhotoUpload
|
||||
},
|
||||
|
||||
props: {
|
||||
contactId: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
gift: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
familyContacts: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
reachLimit: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
photos: [],
|
||||
displayComment: false,
|
||||
displayUrl: false,
|
||||
displayAmount: false,
|
||||
displayRecipient: false,
|
||||
displayUpload: false,
|
||||
newGift: {
|
||||
name: '',
|
||||
status: 'idea',
|
||||
comment: null,
|
||||
url: null,
|
||||
amount: null,
|
||||
date: null,
|
||||
recipient_id: null,
|
||||
photo_id: null,
|
||||
},
|
||||
hasRecipient: false,
|
||||
errors: [],
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
locale() {
|
||||
return this.$root.locale;
|
||||
},
|
||||
|
||||
dirltr() {
|
||||
return this.$root.htmldir == 'ltr';
|
||||
},
|
||||
|
||||
displayMenu() {
|
||||
return !this.displayComment ||
|
||||
!this.displayUrl ||
|
||||
!this.displayAmount ||
|
||||
!(this.displayRecipient || this.familyContacts.length == 0) ||
|
||||
!(this.displayUpload || this.reachLimit);
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.resetFields();
|
||||
},
|
||||
|
||||
methods: {
|
||||
resetFields() {
|
||||
this.newGift.contact_id = this.contactId;
|
||||
if (this.gift) {
|
||||
this.newGift.contact_id = this.gift.contact.id;
|
||||
this.newGift.name = this.gift.name;
|
||||
this.newGift.comment = this.gift.comment;
|
||||
this.newGift.url = this.gift.url;
|
||||
this.newGift.amount = this.gift.amount;
|
||||
this.newGift.status = this.gift.status;
|
||||
this.newGift.recipient_id = this.gift.recipient ? this.gift.recipient.id : null;
|
||||
this.hasRecipient = this.newGift.recipient_id != null;
|
||||
this.newGift.date = this.gift.date;
|
||||
this.photos = this.gift.photos;
|
||||
} else {
|
||||
this.newGift.name = '';
|
||||
this.newGift.comment = null;
|
||||
this.newGift.url = null;
|
||||
this.newGift.amount = null;
|
||||
this.newGift.status = 'idea';
|
||||
this.newGift.recipient_id = null;
|
||||
this.newGift.date = null;
|
||||
this.hasRecipient = false;
|
||||
}
|
||||
this.displayComment = this.gift ? this.gift.comment : false;
|
||||
this.displayUrl = this.gift ? this.gift.url : false;
|
||||
this.displayAmount = this.gift ? this.gift.amount : false;
|
||||
this.displayRecipient = this.gift ? (this.gift.recipient ? this.gift.recipient.id !== 0 : false) : false;
|
||||
this.displayUpload= this.gift ? this.gift.photos.length > 0 : false;
|
||||
|
||||
this.errors = [];
|
||||
},
|
||||
|
||||
close() {
|
||||
this.resetFields();
|
||||
this.$emit('cancel');
|
||||
},
|
||||
|
||||
store() {
|
||||
if (! this.hasRecipient) {
|
||||
this.newGift.recipient_id = null;
|
||||
}
|
||||
|
||||
let method = this.gift ? 'put' : 'post';
|
||||
let url = this.gift ? 'api/gifts/'+this.gift.id : 'api/gifts';
|
||||
|
||||
let vm = this;
|
||||
axios[method](url, this.newGift)
|
||||
.then(response => {
|
||||
return vm.storePhoto(response);
|
||||
})
|
||||
.then(response => {
|
||||
vm.close();
|
||||
vm.$emit('update', response.data.data);
|
||||
return response;
|
||||
})
|
||||
.then(() => {
|
||||
this.$notify({
|
||||
group: 'main',
|
||||
title: vm.$t('people.gifts_add_success'),
|
||||
text: '',
|
||||
type: 'success'
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
vm._errorHandle(error);
|
||||
});
|
||||
},
|
||||
|
||||
storePhoto(response) {
|
||||
let vm = this;
|
||||
return this.$refs.upload.forceFileUpload()
|
||||
.then(photo => {
|
||||
if (photo !== undefined) {
|
||||
axios.put('api/gifts/'+response.data.data.id+'/photo/'+photo.id);
|
||||
response.data.data.photos.push(photo);
|
||||
}
|
||||
return response;
|
||||
})
|
||||
.catch(error => {
|
||||
vm._errorHandle(error);
|
||||
return response;
|
||||
});
|
||||
},
|
||||
|
||||
_errorHandle(error) {
|
||||
if (error.response && typeof error.response.data === 'object') {
|
||||
this.errors = _.flatten(_.toArray(error.response.data));
|
||||
} else {
|
||||
this.errors = [vm.$t('app.error_try_again'), error.message];
|
||||
}
|
||||
},
|
||||
|
||||
deletePhoto(photo) {
|
||||
axios.delete('api/photos/' + photo.id)
|
||||
.then(response => {
|
||||
this.photos.splice(this.photos.indexOf(photo), 1);
|
||||
if (this.photos.length == 0) {
|
||||
this.$refs.upload.showUploadZone();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
handlePhoto(event) {
|
||||
this.photos.push({ id: -1, link: '' });
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,106 @@
|
||||
<style scoped>
|
||||
.photo {
|
||||
height: 200px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<p class="mb1 bb b--gray-monica pa2">
|
||||
<span>
|
||||
<a v-if="gift.url" :href="gift.url" rel="noopener noreferrer" target="_blank">
|
||||
<strong>{{ gift.name }}</strong>
|
||||
</a>
|
||||
<strong v-else>{{ gift.name }}</strong>
|
||||
</span>
|
||||
|
||||
<span v-if="gift.recipient && gift.recipient.complete_name">
|
||||
<span class="black-50 mr1 ml1">
|
||||
•
|
||||
</span>
|
||||
{{ $t('people.gifts_for', { name: gift.recipient.complete_name }) }}
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<!-- LIST OF PHOTO -->
|
||||
<div v-if="gift.photos !== undefined && gift.photos.length > 0" class="bb b--gray-monica pa1">
|
||||
<div class="flex flex-wrap">
|
||||
<div v-for="photo in gift.photos" :key="photo.id" class="w-third-ns w-100">
|
||||
<div class="pa2 mb1 br2 ba b--gray-monica" :class="dirltr ? 'mr3' : 'ml3'">
|
||||
<div class="cover bg-center photo w-100 h-auto br2 bb b--gray-monica pb2"
|
||||
:style="'background-image: url(' + photo.link + ');'"
|
||||
@click.prevent="modalPhoto(photo)"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="gift.amount || gift.comment" class="f6 pv1 mb1 ph2 pb2 bb b--gray-monica">
|
||||
<span v-if="gift.amount">
|
||||
{{ gift.amount_with_currency }}
|
||||
</span>
|
||||
<span v-if="gift.amount && gift.comment" class="black-50 mr1 ml1">
|
||||
•
|
||||
</span>
|
||||
<a v-if="gift.comment" class="pointer" href="" @click.prevent="comment = !comment">
|
||||
{{ $t('people.gifts_view_comment') }}
|
||||
</a>
|
||||
<div v-if="comment" class="mb1 mt1">
|
||||
{{ gift.comment }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ph2 pb2 cf f7">
|
||||
<slot></slot>
|
||||
</div>
|
||||
|
||||
<!-- MODAL ZOOM PHOTO -->
|
||||
<sweet-modal ref="modalPhoto" tabindex="-1" role="dialog" :enable-mobile-fullscreen="true" width="33%">
|
||||
<img :src="url" :alt="$t('people.photo_title')" class="mw-90 h-auto mb3" />
|
||||
</sweet-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { SweetModal } from 'sweet-modal-vue';
|
||||
|
||||
export default {
|
||||
|
||||
components: {
|
||||
SweetModal,
|
||||
},
|
||||
|
||||
props: {
|
||||
gift: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
comment: false,
|
||||
showModal: false,
|
||||
url: '',
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
dirltr() {
|
||||
return this.$root.htmldir == 'ltr';
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
modalPhoto(photo) {
|
||||
this.url = photo.link;
|
||||
this.$refs.modalPhoto.open();
|
||||
},
|
||||
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,257 @@
|
||||
<style scoped>
|
||||
</style>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<notifications group="main" position="bottom right" />
|
||||
|
||||
<!-- Title -->
|
||||
<div>
|
||||
<img src="img/people/gifts.svg" :alt="$t('people.gifts_title')" class="icon-section icon-tasks" />
|
||||
<h3>
|
||||
{{ $t('people.gifts_title') }}
|
||||
<a href="" cy-name="add-gift-button" class="btn f6 pt2" :class="[ dirltr ? 'fr' : 'fl' ]"
|
||||
@click.prevent="displayCreateGift = true"
|
||||
>
|
||||
{{ $t('people.gifts_add_gift') }}
|
||||
</a>
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<template v-if="displayCreateGift">
|
||||
<create-gift
|
||||
:contact-id="contactId"
|
||||
:family-contacts="familyContacts"
|
||||
:reach-limit="reachLimit"
|
||||
@update="updateList($event)"
|
||||
@cancel="displayCreateGift = false"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- Listing -->
|
||||
<div>
|
||||
<ul class="mb3">
|
||||
<li class="di">
|
||||
<p class="di pointer" :class="[activeTab == 'idea' ? 'b' : 'black-50', dirltr ? 'mr3' : 'ml3']"
|
||||
@click.prevent="setActiveTab('idea')"
|
||||
>
|
||||
{{ $t('people.gifts_ideas') }} ({{ ideas.length }})
|
||||
</p>
|
||||
</li>
|
||||
<li class="di">
|
||||
<p class="di pointer" :class="[activeTab == 'offered' ? 'b' : 'black-50', dirltr ? 'mr3' : 'ml3']"
|
||||
@click.prevent="setActiveTab('offered')"
|
||||
>
|
||||
{{ $t('people.gifts_offered') }} ({{ offered.length }})
|
||||
</p>
|
||||
</li>
|
||||
<li class="di">
|
||||
<p class="di pointer" :class="[activeTab == 'received' ? 'b' : 'black-50', dirltr ? 'mr3' : 'ml3']"
|
||||
@click.prevent="setActiveTab('received')"
|
||||
>
|
||||
{{ $t('people.gifts_received') }} ({{ received.length }})
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div v-for="gift in filteredGifts" :key="gift.id" class="ba b--gray-monica mb3 br2" :cy-name="'gift-item-' + gift.id">
|
||||
<gift v-if="!gift.edit"
|
||||
:gift="gift"
|
||||
@update="($event) => { updateList($event) }"
|
||||
>
|
||||
<div :class="dirltr ? 'fl' : 'fr'">
|
||||
<a v-if="gift.status == 'idea'" class="di" href="" @click.prevent="toggle(gift)">
|
||||
{{ $t('people.gifts_mark_offered') }}
|
||||
</a>
|
||||
<a v-if="gift.status == 'offered'" class="di" href="" @click.prevent="toggle(gift)">
|
||||
{{ $t('people.gifts_offered_as_an_idea') }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div :class="dirltr ? 'fr' : 'fl'">
|
||||
<a :class="dirltr ? 'mr1' : 'ml1'" class="di" href="" :cy-name="'edit-gift-button-' + gift.id"
|
||||
@click.prevent="$set(gift, 'edit', true)"
|
||||
>
|
||||
{{ $t('app.edit') }}
|
||||
</a>
|
||||
<a :class="dirltr ? 'mr1' : 'ml1'" class="di" href="" :cy-name="'delete-gift-button-' + gift.id"
|
||||
@click.prevent="showDeleteModal(gift)"
|
||||
>
|
||||
{{ $t('app.delete') }}
|
||||
</a>
|
||||
</div>
|
||||
</gift>
|
||||
<create-gift
|
||||
v-else
|
||||
:gift="gift"
|
||||
:contact-id="contactId"
|
||||
:family-contacts="familyContacts"
|
||||
:reach-limit="reachLimit"
|
||||
@update="updateGift(gift, $event)"
|
||||
@cancel="$set(gift, 'edit', false)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<sweet-modal ref="modal" overlay-theme="dark" :title="$t('people.gifts_delete_title')">
|
||||
<form>
|
||||
<div class="mb4">
|
||||
{{ $t('people.gifts_delete_confirmation') }}
|
||||
</div>
|
||||
</form>
|
||||
<div slot="button">
|
||||
<a class="btn" href="" @click.prevent="closeDeleteModal()">
|
||||
{{ $t('app.cancel') }}
|
||||
</a>
|
||||
<a class="btn btn-primary" :cy-name="'modal-delete-gift-button-' + giftToTrash.id" href="" @click.prevent="trash(giftToTrash)">
|
||||
{{ $t('app.delete') }}
|
||||
</a>
|
||||
</div>
|
||||
</sweet-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { SweetModal } from 'sweet-modal-vue';
|
||||
import Gift from './Gift.vue';
|
||||
import CreateGift from './CreateGift.vue';
|
||||
import moment from 'moment';
|
||||
|
||||
export default {
|
||||
|
||||
components: {
|
||||
Gift,
|
||||
CreateGift,
|
||||
SweetModal,
|
||||
},
|
||||
|
||||
props: {
|
||||
hash: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
contactId: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
giftsActiveTab: {
|
||||
type: String,
|
||||
default: 'idea',
|
||||
},
|
||||
familyContacts: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
reachLimit: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
gifts: [],
|
||||
activeTab: '',
|
||||
giftToTrash: '',
|
||||
displayCreateGift: false,
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
dirltr() {
|
||||
return this.$root.htmldir == 'ltr';
|
||||
},
|
||||
|
||||
ideas() {
|
||||
return this.gifts.filter(gift => gift.status == 'idea');
|
||||
},
|
||||
|
||||
offered() {
|
||||
return this.gifts.filter(gift => gift.status == 'offered');
|
||||
},
|
||||
|
||||
received() {
|
||||
return this.gifts.filter(gift => gift.status == 'received');
|
||||
},
|
||||
|
||||
filteredGifts() {
|
||||
let vm = this;
|
||||
return this.gifts.filter(gift => gift.status == vm.activeTab);
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.prepareComponent();
|
||||
},
|
||||
|
||||
methods: {
|
||||
prepareComponent() {
|
||||
this.getGifts();
|
||||
this.setActiveTab(this.giftsActiveTab);
|
||||
},
|
||||
|
||||
setActiveTab(view) {
|
||||
this.activeTab = view === 'ideas' ? 'idea' : view;
|
||||
},
|
||||
|
||||
getGifts() {
|
||||
axios.get('api/contacts/' + this.contactId + '/gifts')
|
||||
.then(response => {
|
||||
this.gifts = response.data.data;
|
||||
});
|
||||
},
|
||||
|
||||
toggle(gift) {
|
||||
if (gift.status == 'idea') {
|
||||
gift.status = 'offered';
|
||||
gift.date = moment().format('YYYY-MM-DD');
|
||||
} else {
|
||||
gift.status = 'idea';
|
||||
gift.date = null;
|
||||
}
|
||||
gift.contact_id = this.contactId;
|
||||
axios.put('api/gifts/' + gift.id, gift)
|
||||
.then(response => {
|
||||
Vue.set(gift, 'status', response.data.data.status);
|
||||
Vue.set(gift, 'date', response.data.data.date);
|
||||
});
|
||||
},
|
||||
|
||||
showDeleteModal(gift) {
|
||||
this.$refs.modal.open();
|
||||
this.giftToTrash = gift;
|
||||
},
|
||||
|
||||
trash(gift) {
|
||||
axios.delete('api/gifts/' + gift.id)
|
||||
.then(response => {
|
||||
this.gifts.splice(this.gifts.indexOf(gift), 1);
|
||||
this.closeDeleteModal();
|
||||
});
|
||||
},
|
||||
|
||||
updateList(activity) {
|
||||
this.displayCreateGift = false;
|
||||
this.getGifts();
|
||||
},
|
||||
|
||||
updateGift(gift, response) {
|
||||
this.$set(gift, 'edit', false);
|
||||
this.$set(gift, 'name', response.name);
|
||||
this.$set(gift, 'comment', response.comment);
|
||||
this.$set(gift, 'url', response.url);
|
||||
this.$set(gift, 'amount', response.amount);
|
||||
this.$set(gift, 'status', response.status);
|
||||
this.$set(gift, 'recipient', response.recipient);
|
||||
this.$set(gift, 'date', response.date);
|
||||
this.$set(gift, 'photos', response.photos);
|
||||
this.$emit('update', response);
|
||||
},
|
||||
|
||||
closeDeleteModal() {
|
||||
this.$refs.modal.close();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -247,12 +247,12 @@ export default {
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the date when the user chooses either an empty month
|
||||
* or an empty day of the month.
|
||||
* If the user chooses an empty day, the day is set to 1 and we use
|
||||
* a boolean to indicate that the day is unknown.
|
||||
* Same for the month.
|
||||
*/
|
||||
* Sets the date when the user chooses either an empty month
|
||||
* or an empty day of the month.
|
||||
* If the user chooses an empty day, the day is set to 1 and we use
|
||||
* a boolean to indicate that the day is unknown.
|
||||
* Same for the month.
|
||||
*/
|
||||
updateDate() {
|
||||
if (this.selectedDay == 0) {
|
||||
this.newLifeEvent.happened_at_day_unknown = true;
|
||||
|
||||
@@ -2,31 +2,6 @@
|
||||
.photo {
|
||||
height: 250px;
|
||||
}
|
||||
|
||||
.photo-upload-zone {
|
||||
background: #fff;
|
||||
border: 1px solid #d6d6d6;
|
||||
border-style: dashed;
|
||||
}
|
||||
|
||||
progress {
|
||||
-webkit-appearance: none;
|
||||
border: none;
|
||||
height: 8px;
|
||||
margin-bottom: 3px;
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
progress::-webkit-progress-bar {
|
||||
background: #e2e7ee;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
progress::-webkit-progress-value {
|
||||
border-radius: 3px;
|
||||
box-shadow: inset 0 1px 1px 0 rgba(255, 255, 255, 0.4);
|
||||
background-color: #329FF1;
|
||||
}
|
||||
</style>
|
||||
|
||||
<template>
|
||||
@@ -34,11 +9,15 @@
|
||||
<div class="">
|
||||
<h3>
|
||||
📄 {{ $t('people.photo_list_title') }}
|
||||
<span v-show="reachLimit == 'false'" class="fr relative" style="top: -7px;">
|
||||
<a v-if="displayUploadZone == false && displayUploadError == false && displayUploadProgress == false" class="btn" href="" @click.prevent="displayUploadZone = true">
|
||||
<span v-if="reachLimit == 'false'" class="fr relative" style="top: -7px;">
|
||||
<a v-if="!onUpload" class="btn" href=""
|
||||
@click.prevent="() => { onUpload = true; $refs.upload.showUploadZone(); }"
|
||||
>
|
||||
{{ $t('people.photo_list_cta') }}
|
||||
</a>
|
||||
<a v-if="displayUploadZone || displayUploadError || displayUploadProgress" class="btn" href="" @click.prevent="displayUploadZone = false; displayUploadError = false; displayUploadProgress = false">
|
||||
<a v-else class="btn" href=""
|
||||
@click.prevent="() => { onUpload = false; $refs.upload.cancelUpload(); }"
|
||||
>
|
||||
{{ $t('app.cancel') }}
|
||||
</a>
|
||||
</span>
|
||||
@@ -50,7 +29,7 @@
|
||||
</p>
|
||||
|
||||
<!-- EMPTY STATE -->
|
||||
<div v-if="displayUploadZone == false && displayUploadError == false && displayUploadProgress == false && photos.length == 0" class="ltr w-100 pt2">
|
||||
<div v-if="!onUpload && photos.length == 0" class="ltr w-100 pt2">
|
||||
<div class="section-blank">
|
||||
<h3 class="mb4 mt3">
|
||||
{{ $t('people.photo_list_blank_desc') }}
|
||||
@@ -59,62 +38,21 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- FIRST STEP OF PHOTO UPLOAD -->
|
||||
<transition name="fade">
|
||||
<div v-if="displayUploadZone" class="ba br3 photo-upload-zone mb3 pa3">
|
||||
<div class="tc">
|
||||
</div>
|
||||
<div class="tc dib w-100 relative">
|
||||
<button class="btn">
|
||||
{{ $t('people.photo_upload_zone_cta') }}
|
||||
</button>
|
||||
<input id="file" ref="file" type="file" class="absolute o-0 w-100 h-100 pointer" style="left:0;"
|
||||
@change="handleFileUpload()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<!-- LAST STEP OF PHOTO UPLOAD -->
|
||||
<div v-if="displayUploadProgress" class="ba br3 photo-upload-zone mb3 pa3">
|
||||
<p class="tc mb1">
|
||||
{{ $t('people.document_upload_zone_progress') }}
|
||||
</p>
|
||||
<div class="tc mb1">
|
||||
<progress max="100" :value.prop="uploadPercentage"></progress>
|
||||
</div>
|
||||
<p class="tc f6 mb0">
|
||||
{{ $t('app.percent_uploaded', {percent: uploadPercentage}) }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- ERROR STEP WHEN UPLOADING A PHOTO -->
|
||||
<transition name="fade">
|
||||
<div v-if="displayUploadError" class="ba br3 photo-upload-zone mb3 pa3">
|
||||
<div class="tc">
|
||||
<svg width="120" height="150" viewBox="0 0 120 150" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M27.2012 35.9138V101.562H92.7999V18.4375H44.6762L27.2012 35.9138ZM44.1274 21.6388V35.365H30.4024L44.1274 21.6388ZM46.0024 20.3125H90.9249V99.6875H29.0762V37.24H46.0024V20.3125Z" fill="#868E99" />
|
||||
<path d="M83.6988 72.3676H36.3013V74.2426H83.6988V72.3676Z" fill="#868E99" />
|
||||
<path d="M83.6988 82.245H36.3013V84.12H83.6988V82.245Z" fill="#868E99" />
|
||||
<path d="M83.6988 92.1226H36.3013V93.9976H83.6988V92.1226Z" fill="#868E99" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M78.5862 46.875C79.367 46.875 80 46.2314 80 45.4375C80 44.6436 79.367 44 78.5862 44H68.6897C67.9088 44 67.2759 44.6436 67.2759 45.4375C67.2759 47.8192 69.1748 49.75 71.5172 49.75C73.364 49.75 74.935 48.5499 75.5173 46.875H78.5862ZM40.4138 46.875C39.633 46.875 39 46.2314 39 45.4375C39 44.6436 39.633 44 40.4138 44H50.3103C51.0912 44 51.7241 44.6436 51.7241 45.4375C51.7241 47.8192 49.8252 49.75 47.4828 49.75C45.636 49.75 44.065 48.5499 43.4827 46.875H40.4138ZM55.7524 66.3213C55.888 66.0995 56.2309 65.69 56.7753 65.2677C57.698 64.552 58.8279 64.1248 60.2069 64.1248C61.5859 64.1248 62.7158 64.552 63.6385 65.2677C64.1829 65.69 64.5258 66.0995 64.6614 66.3213C65.0736 66.9955 65.9454 67.2023 66.6085 66.7831C67.2716 66.364 67.475 65.4776 67.0628 64.8034C66.7589 64.3064 66.199 63.6378 65.3535 62.9819C63.9596 61.9008 62.2381 61.2499 60.2069 61.2499C58.1757 61.2499 56.4542 61.9008 55.0603 62.9819C54.2148 63.6378 53.6549 64.3064 53.351 64.8034C52.9388 65.4776 53.1422 66.364 53.8053 66.7831C54.4684 67.2023 55.3402 66.9955 55.7524 66.3213Z" fill="#868E99" />
|
||||
</svg>
|
||||
</div>
|
||||
<p class="tc mb3">
|
||||
{{ $t('people.document_upload_zone_error') }}
|
||||
</p>
|
||||
<p class="tc">
|
||||
<input id="file" ref="file" type="file" @change="handleFileUpload()" />
|
||||
</p>
|
||||
</div>
|
||||
</transition>
|
||||
<photo-upload
|
||||
ref="upload"
|
||||
:contact-id="contactId"
|
||||
@newphoto="handleNewPhoto($event)"
|
||||
/>
|
||||
|
||||
<!-- LIST OF PHOTO -->
|
||||
<div class="db mt3">
|
||||
<div class="flex flex-wrap">
|
||||
<div v-for="photo in photos" :key="photo.id" class="w-third-ns w-100">
|
||||
<div class="pa3 mr3 mb3 br2 ba b--gray-monica">
|
||||
<div class="cover bg-center photo w-100 h-100 br2 bb b--gray-monica pb2" :style="'background-image: url(' + photo.link + ');'" @click.prevent="modalPhoto(photo)">
|
||||
<div class="pa3 mb3 br2 ba b--gray-monica" :class="dirltr ? 'mr3' : 'ml3'">
|
||||
<div class="cover bg-center photo w-100 h-100 br2 bb b--gray-monica pb2"
|
||||
:style="'background-image: url(' + photo.link + ');'"
|
||||
@click.prevent="modalPhoto(photo)"
|
||||
>
|
||||
</div>
|
||||
<div class="pt2">
|
||||
<ul>
|
||||
@@ -164,13 +102,24 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import PhotoUpload from './PhotoUpload.vue';
|
||||
|
||||
export default {
|
||||
|
||||
components: {
|
||||
PhotoUpload,
|
||||
},
|
||||
|
||||
props: {
|
||||
hash: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
contactId: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
reachLimit: {
|
||||
type: String,
|
||||
default: '',
|
||||
@@ -184,14 +133,12 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
photos: [],
|
||||
displayUploadZone: false,
|
||||
displayUploadProgress: false,
|
||||
displayUploadError: false,
|
||||
file: '',
|
||||
uploadPercentage: 0,
|
||||
confirmDestroyPhotoId: 0,
|
||||
showModal: false,
|
||||
url: '',
|
||||
onUpload: false,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -212,45 +159,15 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
showUploadZone() {
|
||||
this.displayUploadZone = true;
|
||||
},
|
||||
handleNewPhoto(photo) {
|
||||
this.$notify({
|
||||
group: 'main',
|
||||
title: this.$t('app.default_save_success'),
|
||||
text: '',
|
||||
type: 'success'
|
||||
});
|
||||
|
||||
handleFileUpload(){
|
||||
this.file = this.$refs.file.files[0];
|
||||
this.submitFile();
|
||||
},
|
||||
|
||||
submitFile(){
|
||||
this.displayUploadZone = false;
|
||||
this.displayUploadProgress = true;
|
||||
let formData = new FormData();
|
||||
formData.append('photo', this.file);
|
||||
axios.post( 'people/' + this.hash + '/photos',
|
||||
formData,
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
},
|
||||
onUploadProgress: function( progressEvent ) {
|
||||
this.uploadPercentage = parseInt( Math.round( ( progressEvent.loaded * 100 ) / progressEvent.total ) );
|
||||
}.bind(this)
|
||||
}
|
||||
).then(response => {
|
||||
this.displayUploadProgress = false;
|
||||
this.photos.push(response.data.data);
|
||||
this.$notify({
|
||||
group: 'main',
|
||||
title: this.$t('app.default_save_success'),
|
||||
text: '',
|
||||
type: 'success'
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
this.displayUploadProgress = false;
|
||||
this.file = null;
|
||||
this.displayUploadError = true;
|
||||
});
|
||||
this.photos.push(photo);
|
||||
},
|
||||
|
||||
deletePhoto(photo) {
|
||||
@@ -278,7 +195,7 @@ export default {
|
||||
modalPhoto(photo) {
|
||||
this.url = photo.link;
|
||||
this.showModal = true;
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
<style scoped lang="scss">
|
||||
.photo-upload-zone {
|
||||
background: #fff;
|
||||
border: 1px solid #d6d6d6;
|
||||
border-style: dashed;
|
||||
}
|
||||
|
||||
progress {
|
||||
-webkit-appearance: none;
|
||||
border: none;
|
||||
height: 8px;
|
||||
margin-bottom: 3px;
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
progress::-webkit-progress-bar {
|
||||
background: #e2e7ee;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
progress::-webkit-progress-value {
|
||||
border-radius: 3px;
|
||||
box-shadow: inset 0 1px 1px 0 rgba(255, 255, 255, 0.4);
|
||||
background-color: #329FF1;
|
||||
}
|
||||
</style>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<!-- FIRST STEP OF PHOTO UPLOAD -->
|
||||
<transition name="fade">
|
||||
<div v-if="displayUploadZone" class="ba br3 photo-upload-zone mb3 pa3">
|
||||
<div class="tc">
|
||||
</div>
|
||||
<div class="tc dib w-100 relative">
|
||||
<button class="btn">
|
||||
{{ $t('people.photo_upload_zone_cta') }}
|
||||
</button>
|
||||
<input id="file" ref="file" type="file" class="absolute o-0 w-100 h-100 pointer" style="left:0;"
|
||||
@change="handleFileUpload($event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<!-- LAST STEP OF PHOTO UPLOAD -->
|
||||
<div v-if="displayUploadProgress" class="ba br3 photo-upload-zone mb3 pa3">
|
||||
<p class="tc mb1">
|
||||
{{ $t('people.document_upload_zone_progress') }}
|
||||
</p>
|
||||
<div class="tc mb1">
|
||||
<progress max="100" :value.prop="uploadPercentage"></progress>
|
||||
</div>
|
||||
<p class="tc f6 mb0">
|
||||
{{ $t('app.percent_uploaded', {percent: uploadPercentage}) }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- ERROR STEP WHEN UPLOADING A PHOTO -->
|
||||
<transition name="fade">
|
||||
<div v-if="displayUploadError" class="ba br3 photo-upload-zone mb3 pa3">
|
||||
<div class="tc">
|
||||
<svg width="120" height="150" viewBox="0 0 120 150" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M27.2012 35.9138V101.562H92.7999V18.4375H44.6762L27.2012 35.9138ZM44.1274 21.6388V35.365H30.4024L44.1274 21.6388ZM46.0024 20.3125H90.9249V99.6875H29.0762V37.24H46.0024V20.3125Z" fill="#868E99" />
|
||||
<path d="M83.6988 72.3676H36.3013V74.2426H83.6988V72.3676Z" fill="#868E99" />
|
||||
<path d="M83.6988 82.245H36.3013V84.12H83.6988V82.245Z" fill="#868E99" />
|
||||
<path d="M83.6988 92.1226H36.3013V93.9976H83.6988V92.1226Z" fill="#868E99" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M78.5862 46.875C79.367 46.875 80 46.2314 80 45.4375C80 44.6436 79.367 44 78.5862 44H68.6897C67.9088 44 67.2759 44.6436 67.2759 45.4375C67.2759 47.8192 69.1748 49.75 71.5172 49.75C73.364 49.75 74.935 48.5499 75.5173 46.875H78.5862ZM40.4138 46.875C39.633 46.875 39 46.2314 39 45.4375C39 44.6436 39.633 44 40.4138 44H50.3103C51.0912 44 51.7241 44.6436 51.7241 45.4375C51.7241 47.8192 49.8252 49.75 47.4828 49.75C45.636 49.75 44.065 48.5499 43.4827 46.875H40.4138ZM55.7524 66.3213C55.888 66.0995 56.2309 65.69 56.7753 65.2677C57.698 64.552 58.8279 64.1248 60.2069 64.1248C61.5859 64.1248 62.7158 64.552 63.6385 65.2677C64.1829 65.69 64.5258 66.0995 64.6614 66.3213C65.0736 66.9955 65.9454 67.2023 66.6085 66.7831C67.2716 66.364 67.475 65.4776 67.0628 64.8034C66.7589 64.3064 66.199 63.6378 65.3535 62.9819C63.9596 61.9008 62.2381 61.2499 60.2069 61.2499C58.1757 61.2499 56.4542 61.9008 55.0603 62.9819C54.2148 63.6378 53.6549 64.3064 53.351 64.8034C52.9388 65.4776 53.1422 66.364 53.8053 66.7831C54.4684 67.2023 55.3402 66.9955 55.7524 66.3213Z" fill="#868E99" />
|
||||
</svg>
|
||||
</div>
|
||||
<p class="tc mb3">
|
||||
{{ $t('people.document_upload_zone_error') }}
|
||||
</p>
|
||||
<p class="tc">
|
||||
<input id="file" ref="file" type="file" @change="handleFileUpload($event)" />
|
||||
</p>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
hash: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
contactId: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
reachLimit: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
currentPhotoIdAsAvatar: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
displayUploadZone: false,
|
||||
displayUploadProgress: false,
|
||||
displayUploadError: false,
|
||||
file: '',
|
||||
uploadPercentage: 0,
|
||||
confirmDestroyPhotoId: 0,
|
||||
url: '',
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
showUploadZone() {
|
||||
this.displayUploadZone = true;
|
||||
},
|
||||
|
||||
inUpload() {
|
||||
return this.displayUploadZone || this.displayUploadError || this.displayUploadProgress;
|
||||
},
|
||||
|
||||
cancelUpload() {
|
||||
this.displayUploadZone = false;
|
||||
this.displayUploadError = false;
|
||||
this.displayUploadProgress = false;
|
||||
},
|
||||
|
||||
handleFileUpload(event){
|
||||
this.$emit('upload', event);
|
||||
if (!event.cancelBubble) {
|
||||
this.forceFileUpload();
|
||||
}
|
||||
},
|
||||
|
||||
forceFileUpload(){
|
||||
let f = this.$refs.file !== undefined ? this.$refs.file.files[0] : undefined;
|
||||
if (f === undefined) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
this.file = f;
|
||||
return this.submitFile();
|
||||
},
|
||||
|
||||
submitFile(){
|
||||
this.displayUploadZone = false;
|
||||
this.displayUploadProgress = true;
|
||||
|
||||
let formData = new FormData();
|
||||
formData.append('contact_id', this.contactId);
|
||||
formData.append('photo', this.file);
|
||||
|
||||
return axios.post('api/photos',
|
||||
formData,
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
},
|
||||
onUploadProgress: function( progressEvent ) {
|
||||
this.uploadPercentage = parseInt( Math.round( ( progressEvent.loaded * 100 ) / progressEvent.total ) );
|
||||
}.bind(this)
|
||||
}
|
||||
).then(response => {
|
||||
this.displayUploadProgress = false;
|
||||
|
||||
this.$emit('newphoto', response.data.data);
|
||||
|
||||
return response.data.data;
|
||||
})
|
||||
.catch(error => {
|
||||
this.displayUploadProgress = false;
|
||||
this.file = null;
|
||||
this.displayUploadError = true;
|
||||
});
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -37,6 +37,7 @@ return [
|
||||
'retry' => 'Retry',
|
||||
'filter' => 'Filter the list',
|
||||
'go_back' => 'Go back',
|
||||
'file_selected' => '1 file selected...|{count} files selected...',
|
||||
|
||||
'application_title' => 'Monica – personal relationship manager',
|
||||
'application_description' => 'Monica is a tool to manage your interactions with your loved ones, friends, and family.',
|
||||
|
||||
@@ -296,17 +296,22 @@ return [
|
||||
'gifts_delete_confirmation' => 'Are you sure you want to delete this gift?',
|
||||
'gifts_add_gift' => 'Add a gift',
|
||||
'gifts_link' => 'Link',
|
||||
'gifts_for' => 'For:',
|
||||
'gifts_for' => 'For: {name}',
|
||||
'gifts_delete_cta' => 'Delete',
|
||||
'gifts_add_title' => 'Gift management for :name',
|
||||
'gifts_add_gift_idea' => 'Gift idea',
|
||||
'gifts_add_gift_already_offered' => 'Gift offered',
|
||||
'gifts_add_gift_received' => 'Gift received',
|
||||
'gifts_add_gift_title' => 'What is this gift?',
|
||||
'gifts_add_gift_name' => 'Gift name',
|
||||
'gifts_add_link' => 'Link to the web page (optional)',
|
||||
'gifts_add_value' => 'Value (optional)',
|
||||
'gifts_add_comment' => 'Comment (optional)',
|
||||
'gifts_add_someone' => 'This gift is for someone in :name’s family in particular',
|
||||
'gifts_add_recipient' => 'Recipient (optional)',
|
||||
'gifts_add_photo' => 'Photo (optional)',
|
||||
'gifts_add_photo_title' => 'Add a photo for this gift',
|
||||
'gifts_add_someone' => 'This gift is for someone in {name}’s family in particular',
|
||||
'gifts_delete_title' => 'Delete a gift',
|
||||
'gifts_ideas' => 'Gift ideas',
|
||||
'gifts_offered' => 'Gifts offered',
|
||||
'gifts_offered_as_an_idea' => 'Mark as an idea',
|
||||
|
||||
@@ -1,45 +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-12">
|
||||
<ul class="horizontal">
|
||||
<li>
|
||||
<a href="{{ route('dashboard.index') }}">{{ trans('app.breadcrumb_dashboard') }}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('people.index') }}">{{ trans('app.breadcrumb_list_contacts') }}</a>
|
||||
</li>
|
||||
<li>
|
||||
{{ $contact->name }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Page header -->
|
||||
@include('people._header')
|
||||
|
||||
<!-- Page content -->
|
||||
<div class="main-content gifts central-form">
|
||||
<div class="{{ Auth::user()->getFluidLayout() }}">
|
||||
<div class="row">
|
||||
<div class="col-12 col-sm-6 offset-sm-3 offset-sm-3-right">
|
||||
@include('people.gifts.form', [
|
||||
'method' => 'POST',
|
||||
'action' => route('people.gifts.store', $contact)
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@endsection
|
||||
@@ -1,45 +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-12">
|
||||
<ul class="horizontal">
|
||||
<li>
|
||||
<a href="{{ route('dashboard.index') }}">{{ trans('app.breadcrumb_dashboard') }}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('people.index') }}">{{ trans('app.breadcrumb_list_contacts') }}</a>
|
||||
</li>
|
||||
<li>
|
||||
{{ $contact->name }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Page header -->
|
||||
@include('people._header')
|
||||
|
||||
<!-- Page content -->
|
||||
<div class="main-content gifts central-form">
|
||||
<div class="{{ Auth::user()->getFluidLayout() }}">
|
||||
<div class="row">
|
||||
<div class="col-12 col-sm-6 offset-sm-3 offset-sm-3-right">
|
||||
@include('people.gifts.form', [
|
||||
'method' => 'PUT',
|
||||
'action' => route('people.gifts.update', [$contact, $gift])
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@endsection
|
||||
@@ -1,78 +0,0 @@
|
||||
<form method="POST" action="{{ $action }}">
|
||||
@method($method)
|
||||
@csrf
|
||||
|
||||
<h2>{{ trans('people.gifts_add_title', ['name' => $contact->first_name]) }}</h2>
|
||||
|
||||
@include('partials.errors')
|
||||
|
||||
{{-- Nature of gift --}}
|
||||
<fieldset class="form-group">
|
||||
<label class="form-check-inline" for="idea">
|
||||
<input type="radio" class="form-check-input" name="offered" id="idea" value="idea" @if(old('idea') !== true || $gift->is_an_idea) checked @endif>
|
||||
{{ trans('people.gifts_add_gift_idea') }}
|
||||
</label>
|
||||
|
||||
<label class="form-check-inline" for="offered">
|
||||
<input type="radio" class="form-check-input" name="offered" id="offered" value="offered" @if(old('offered') === true || $gift->has_been_offered) checked @endif>
|
||||
{{ trans('people.gifts_add_gift_already_offered') }}
|
||||
</label>
|
||||
|
||||
<label class="form-check-inline" for="received">
|
||||
<input type="radio" class="form-check-input" name="offered" id="received" value="received" @if(old('received') === true || $gift->has_been_received) checked @endif>
|
||||
{{ trans('people.gifts_add_gift_received') }}
|
||||
</label>
|
||||
</fieldset>
|
||||
|
||||
{{-- Title --}}
|
||||
<div class="form-group">
|
||||
<label for="name">{{ trans('people.gifts_add_gift_title') }}</label>
|
||||
<input type="text" class="form-control" name="name" id="name" value="{{ old('name') ?? $gift->name }}" required>
|
||||
</div>
|
||||
|
||||
{{-- URL --}}
|
||||
<div class="form-group">
|
||||
<label for="url">{{ trans('people.gifts_add_link') }}</label>
|
||||
<input type="text" class="form-control" name="url" id="url" dir="ltr" value="{{ old('url') ?? $gift->url }}" placeholder="https://">
|
||||
</div>
|
||||
|
||||
{{-- Value --}}
|
||||
<div class="form-group">
|
||||
<label for="value">{{ trans('people.gifts_add_value') }} ({{ Auth::user()->currency->symbol}})</label>
|
||||
<input type="number" class="form-control" name="value" id="value" placeholder="0" value="{{ old('value') ?? $gift->value }}">
|
||||
</div>
|
||||
|
||||
{{-- Comment --}}
|
||||
<div class="form-group">
|
||||
<label for="comment">{{ trans('people.gifts_add_comment') }}</label>
|
||||
<textarea class="form-control" id="comment" name="comment" rows="3">{{ old('comment') ?? $gift->comment }}</textarea>
|
||||
</div>
|
||||
|
||||
@if ($familyRelationships->count() !== 0)
|
||||
<div class="form-group">
|
||||
<form-checkbox
|
||||
:name="'has_recipient'"
|
||||
:value="'1'"
|
||||
:modelValue="{{ \Safe\json_encode($gift->hasParticularRecipient()) }}"
|
||||
:dclass="'form-check form-check-label'"
|
||||
:iclass="'form-check-input'"
|
||||
>
|
||||
{{ trans('people.gifts_add_someone', ['name' => $contact->first_name]) }}
|
||||
</form-checkbox>
|
||||
<select id="recipient" name="recipient" class="form-control">
|
||||
@foreach($familyRelationships as $familyRelationship)
|
||||
<option value="{{ $familyRelationship->ofContact->id }}"
|
||||
@if($gift->is_for === $familyRelationship->ofContact->id)
|
||||
selected
|
||||
@endif
|
||||
>{{ $familyRelationship->ofContact->first_name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="form-group actions">
|
||||
<button type="submit" cy-name="save-gift-button" class="btn btn-primary">{{ trans('app.save') }}</button>
|
||||
<a href="{{ route('people.show', $contact) }}" class="btn btn-secondary">{{ trans('app.cancel') }}</a>
|
||||
</div>
|
||||
</form>
|
||||
@@ -1,3 +1,15 @@
|
||||
<div class="col-12 section-title">
|
||||
<contact-gift hash="{{ $contact->hashID() }}" :gifts-active-tab="'{{ auth()->user()->gifts_active_tab }}'"></contact-gift>
|
||||
<contact-gift
|
||||
hash="{{ $contact->hashID() }}"
|
||||
:contact-id="{{ $contact->id }}"
|
||||
:gifts-active-tab="'{{ auth()->user()->gifts_active_tab }}'"
|
||||
:family-contacts="{{ $familyRelationships->map(function ($familyRelationship) {
|
||||
return [
|
||||
'id' => $familyRelationship->ofContact->id,
|
||||
'name' => $familyRelationship->ofContact->first_name,
|
||||
];
|
||||
}) }}"
|
||||
:reach-limit="{{ \Safe\json_encode(auth()->user()->account->hasReachedAccountStorageLimit()) }}"
|
||||
>
|
||||
</contact-gift>
|
||||
</div>
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
<div class="br2 bg-white mb4">
|
||||
<photo-list
|
||||
hash="{{ $contact->hashID() }}"
|
||||
contact-name="{{ $contact->first_name }}"
|
||||
contact-id="{{ $contact->id }}"
|
||||
contact-name="'{{ $contact->first_name }}''"
|
||||
current-photo-id-as-avatar="{{ $contact->avatar_photo_id }}"
|
||||
reach-limit="{{ \Safe\json_encode($contact->account->hasReachedAccountStorageLimit()) }}">
|
||||
reach-limit="{{ \Safe\json_encode($contact->account->hasReachedAccountStorageLimit()) }}"
|
||||
>
|
||||
</photo-list>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -98,6 +98,7 @@ Route::group(['middleware' => ['auth:api']], function () {
|
||||
// Gifts
|
||||
Route::apiResource('gifts', 'ApiGiftController');
|
||||
Route::get('/contacts/{contact}/gifts', 'ApiGiftController@gifts');
|
||||
Route::put('/gifts/{gift}/photo/{photo}', 'ApiGiftController@associate');
|
||||
|
||||
// Debts
|
||||
Route::apiResource('debts', 'ApiDebtController');
|
||||
|
||||
@@ -147,10 +147,6 @@ Route::middleware(['auth', 'verified', 'mfa'])->group(function () {
|
||||
'index', 'store', 'update', 'destroy',
|
||||
]);
|
||||
|
||||
// Gifts
|
||||
Route::resource('people/{contact}/gifts', 'Contacts\\GiftsController')->except(['show']);
|
||||
Route::post('/people/{contact}/gifts/{gift}/toggle', 'Contacts\\GiftsController@toggle');
|
||||
|
||||
// Debt
|
||||
Route::resource('people/{contact}/debts', 'Contacts\\DebtController')->except(['index', 'show']);
|
||||
|
||||
|
||||
@@ -15,14 +15,13 @@ class ApiGiftsTest extends ApiTestCase
|
||||
protected $jsonGift = [
|
||||
'id',
|
||||
'object',
|
||||
'date_offered',
|
||||
'has_been_offered',
|
||||
'status',
|
||||
'comment',
|
||||
'is_an_idea',
|
||||
'is_for',
|
||||
'name',
|
||||
'url',
|
||||
'value',
|
||||
'amount',
|
||||
'amount_with_currency',
|
||||
'status',
|
||||
'account' => [
|
||||
'id',
|
||||
],
|
||||
@@ -165,6 +164,7 @@ class ApiGiftsTest extends ApiTestCase
|
||||
|
||||
$response = $this->json('POST', '/api/gifts', [
|
||||
'contact_id' => $contact->id,
|
||||
'status' => 'idea',
|
||||
'name' => 'the gift',
|
||||
]);
|
||||
|
||||
@@ -201,7 +201,8 @@ class ApiGiftsTest extends ApiTestCase
|
||||
$response = $this->json('POST', '/api/gifts', [
|
||||
'contact_id' => $contact->id,
|
||||
'name' => 'the gift',
|
||||
'is_for' => $contact2->id,
|
||||
'status' => 'idea',
|
||||
'recipient_id' => $contact2->id,
|
||||
]);
|
||||
|
||||
$response->assertStatus(201);
|
||||
@@ -240,7 +241,8 @@ class ApiGiftsTest extends ApiTestCase
|
||||
$response = $this->json('POST', '/api/gifts', [
|
||||
'contact_id' => $contact->id,
|
||||
'name' => 'the gift',
|
||||
'is_for' => $contact2->id,
|
||||
'status' => 'idea',
|
||||
'recipient_id' => $contact2->id,
|
||||
]);
|
||||
|
||||
$this->expectNotFound($response);
|
||||
@@ -256,6 +258,7 @@ class ApiGiftsTest extends ApiTestCase
|
||||
|
||||
$response = $this->json('POST', '/api/gifts', [
|
||||
'contact_id' => $contact->id,
|
||||
'status' => 'idea',
|
||||
]);
|
||||
|
||||
$this->expectDataError($response, [
|
||||
@@ -276,6 +279,7 @@ class ApiGiftsTest extends ApiTestCase
|
||||
$response = $this->json('POST', '/api/gifts', [
|
||||
'contact_id' => $contact->id,
|
||||
'name' => 'the gift',
|
||||
'status' => 'idea',
|
||||
]);
|
||||
|
||||
$this->expectNotFound($response);
|
||||
@@ -296,6 +300,7 @@ class ApiGiftsTest extends ApiTestCase
|
||||
$response = $this->json('PUT', '/api/gifts/'.$gift->id, [
|
||||
'contact_id' => $contact->id,
|
||||
'name' => 'the gift',
|
||||
'status' => 'idea',
|
||||
'comment' => 'one comment',
|
||||
]);
|
||||
|
||||
@@ -338,8 +343,9 @@ class ApiGiftsTest extends ApiTestCase
|
||||
$response = $this->json('PUT', '/api/gifts/'.$gift->id, [
|
||||
'contact_id' => $contact->id,
|
||||
'name' => 'the gift',
|
||||
'status' => 'idea',
|
||||
'comment' => 'one comment',
|
||||
'is_for' => $contact2->id,
|
||||
'recipient_id' => $contact2->id,
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
@@ -374,6 +380,7 @@ class ApiGiftsTest extends ApiTestCase
|
||||
|
||||
$response = $this->json('PUT', '/api/gifts/'.$gift->id, [
|
||||
'contact_id' => $gift->contact_id,
|
||||
'status' => 'idea',
|
||||
]);
|
||||
|
||||
$this->expectDataError($response, [
|
||||
@@ -398,6 +405,7 @@ class ApiGiftsTest extends ApiTestCase
|
||||
$response = $this->json('PUT', '/api/gifts/'.$gift->id, [
|
||||
'contact_id' => $contact->id,
|
||||
'name' => 'the gift',
|
||||
'status' => 'idea',
|
||||
'comment' => 'one comment',
|
||||
]);
|
||||
|
||||
@@ -424,6 +432,11 @@ class ApiGiftsTest extends ApiTestCase
|
||||
$response = $this->json('DELETE', '/api/gifts/'.$gift->id);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson([
|
||||
'deleted' => true,
|
||||
'id' => $gift->id,
|
||||
]);
|
||||
|
||||
$this->assertDatabaseMissing('gifts', [
|
||||
'account_id' => $user->account->id,
|
||||
'contact_id' => $contact->id,
|
||||
@@ -438,6 +451,17 @@ class ApiGiftsTest extends ApiTestCase
|
||||
|
||||
$response = $this->json('DELETE', '/api/gifts/0');
|
||||
|
||||
$response->assertStatus(422);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function gifts_delete_wrong_account()
|
||||
{
|
||||
$user = $this->signin();
|
||||
$gift = factory(Gift::class)->create();
|
||||
|
||||
$response = $this->json('DELETE', '/api/gifts/'.$gift->id);
|
||||
|
||||
$this->expectNotFound($response);
|
||||
}
|
||||
}
|
||||
|
||||
+21
-181
@@ -4,12 +4,12 @@ namespace Tests\Feature;
|
||||
|
||||
use Tests\FeatureTestCase;
|
||||
use App\Models\Contact\Tag;
|
||||
use Illuminate\Support\Arr;
|
||||
use App\Models\Contact\Gift;
|
||||
use App\Helpers\StringHelper;
|
||||
use App\Models\Contact\Contact;
|
||||
use App\Models\Account\Activity;
|
||||
use App\Models\Contact\Reminder;
|
||||
use App\Models\Settings\Currency;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
|
||||
@@ -325,186 +325,6 @@ class ContactTest extends FeatureTestCase
|
||||
);
|
||||
}
|
||||
|
||||
public function test_user_can_add_a_gift_idea_to_a_contact()
|
||||
{
|
||||
[$user, $contact] = $this->fetchUser();
|
||||
|
||||
$gift = [
|
||||
'offered' => 'idea',
|
||||
'name' => $this->faker->word,
|
||||
'url' => $this->faker->url,
|
||||
'value' => $this->faker->numberBetween(1, 2000),
|
||||
'comment' => $this->faker->sentence(),
|
||||
];
|
||||
|
||||
$this->post(
|
||||
'/people/'.$contact->hashID().'/gifts',
|
||||
$gift
|
||||
);
|
||||
|
||||
array_shift($gift);
|
||||
|
||||
$this->assertDatabaseHas(
|
||||
'gifts',
|
||||
$gift + [
|
||||
'is_an_idea' => true,
|
||||
'has_been_offered' => false,
|
||||
'contact_id' => $contact->id,
|
||||
'account_id' => $user->account_id,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function test_user_can_add_a_gift_idea_with_recipient()
|
||||
{
|
||||
[$user, $contact] = $this->fetchUser();
|
||||
|
||||
$otherContact = factory(Contact::class)->create([
|
||||
'account_id' => $user->account_id,
|
||||
]);
|
||||
|
||||
$gift = [
|
||||
'offered' => 'idea',
|
||||
'name' => $this->faker->word,
|
||||
'url' => $this->faker->url,
|
||||
'value' => $this->faker->numberBetween(1, 2000),
|
||||
'comment' => $this->faker->sentence(),
|
||||
'has_recipient' => true,
|
||||
'recipient' => $otherContact->id,
|
||||
];
|
||||
|
||||
$this->post(
|
||||
'/people/'.$contact->hashID().'/gifts',
|
||||
$gift
|
||||
);
|
||||
|
||||
$gift = Arr::except($gift, ['offered', 'has_recipient', 'recipient']);
|
||||
|
||||
$this->assertDatabaseHas(
|
||||
'gifts',
|
||||
$gift + [
|
||||
'is_an_idea' => true,
|
||||
'has_been_offered' => false,
|
||||
'contact_id' => $contact->id,
|
||||
'account_id' => $user->account_id,
|
||||
'is_for' => $otherContact->id,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function test_user_can_add_a_gift_idea_with_bad_recipient()
|
||||
{
|
||||
[$user, $contact] = $this->fetchUser();
|
||||
|
||||
$gift = [
|
||||
'offered' => 'idea',
|
||||
'name' => $this->faker->word,
|
||||
'url' => $this->faker->url,
|
||||
'value' => $this->faker->numberBetween(1, 2000),
|
||||
'comment' => $this->faker->sentence(),
|
||||
'has_recipient' => true,
|
||||
'recipient' => 0,
|
||||
];
|
||||
|
||||
$this->post(
|
||||
'/people/'.$contact->hashID().'/gifts',
|
||||
$gift
|
||||
);
|
||||
|
||||
$gift = Arr::except($gift, ['offered', 'has_recipient', 'recipient']);
|
||||
|
||||
$this->assertDatabaseHas(
|
||||
'gifts',
|
||||
$gift + [
|
||||
'is_an_idea' => true,
|
||||
'has_been_offered' => false,
|
||||
'contact_id' => $contact->id,
|
||||
'account_id' => $user->account_id,
|
||||
'is_for' => null,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function test_user_can_edit_a_gift_()
|
||||
{
|
||||
[$user, $contact] = $this->fetchUser();
|
||||
|
||||
$oldGift = factory(Gift::class)->create([
|
||||
'contact_id' => $contact->id,
|
||||
'account_id' => $user->account_id,
|
||||
]);
|
||||
|
||||
$gift = [
|
||||
'offered' => 'idea',
|
||||
'name' => $this->faker->word,
|
||||
'url' => $this->faker->url,
|
||||
'value' => $this->faker->numberBetween(1, 2000),
|
||||
'comment' => $this->faker->sentence(),
|
||||
];
|
||||
|
||||
$this->put(
|
||||
'/people/'.$contact->hashID().'/gifts/'.$oldGift->id,
|
||||
$gift
|
||||
);
|
||||
|
||||
array_shift($gift);
|
||||
|
||||
$this->assertDatabaseHas(
|
||||
'gifts',
|
||||
$gift + [
|
||||
'is_an_idea' => true,
|
||||
'has_been_offered' => false,
|
||||
'contact_id' => $contact->id,
|
||||
'account_id' => $user->account_id,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function test_user_can_add_recipient_to_a_gift()
|
||||
{
|
||||
[$user, $contact] = $this->fetchUser();
|
||||
|
||||
$oldGift = factory(Gift::class)->create([
|
||||
'contact_id' => $contact->id,
|
||||
'account_id' => $user->account_id,
|
||||
]);
|
||||
|
||||
$otherContact = factory(Contact::class)->create([
|
||||
'account_id' => $user->account_id,
|
||||
]);
|
||||
|
||||
$gift = [
|
||||
'offered' => 'idea',
|
||||
'name' => $this->faker->word,
|
||||
'url' => $this->faker->url,
|
||||
'value' => $this->faker->numberBetween(1, 2000),
|
||||
'comment' => $this->faker->sentence(),
|
||||
'has_recipient' => true,
|
||||
'recipient' => $otherContact->id,
|
||||
];
|
||||
|
||||
$this->put(
|
||||
'/people/'.$contact->hashID().'/gifts/'.$oldGift->id,
|
||||
$gift
|
||||
);
|
||||
|
||||
$gift = Arr::except($gift, ['offered', 'has_recipient', 'recipient']);
|
||||
|
||||
$this->assertDatabaseHas(
|
||||
'gifts',
|
||||
$gift + [
|
||||
'is_an_idea' => true,
|
||||
'has_been_offered' => false,
|
||||
'contact_id' => $contact->id,
|
||||
'account_id' => $user->account_id,
|
||||
'is_for' => $otherContact->id,
|
||||
]
|
||||
);
|
||||
|
||||
$newGift = Gift::find($oldGift->id);
|
||||
$this->assertEquals($otherContact->first_name, $newGift->recipient_name);
|
||||
}
|
||||
|
||||
public function test_user_can_be_in_debt_to_a_contact()
|
||||
{
|
||||
[$user, $contact] = $this->fetchUser();
|
||||
@@ -811,4 +631,24 @@ class ContactTest extends FeatureTestCase
|
||||
'initial_date' => '2012-06-22',
|
||||
]);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_gets_the_value()
|
||||
{
|
||||
$user = $this->signin();
|
||||
$currency = factory(Currency::class)->create([
|
||||
'iso' => 'USD',
|
||||
'symbol' => '$',
|
||||
]);
|
||||
$user->currency()->associate($currency);
|
||||
$user->save();
|
||||
|
||||
$gift = factory(Gift::class)->make();
|
||||
$gift->value = '100';
|
||||
|
||||
$this->assertEquals(
|
||||
'$100.00',
|
||||
$gift->amount
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,52 +11,6 @@ class GiftTest extends TestCase
|
||||
{
|
||||
use DatabaseTransactions;
|
||||
|
||||
/** @test */
|
||||
public function toggle_a_gift_idea()
|
||||
{
|
||||
$gift = factory(Gift::class)->make();
|
||||
$gift->is_an_idea = true;
|
||||
$gift->toggle();
|
||||
|
||||
$this->assertEquals(
|
||||
false,
|
||||
$gift->is_an_idea
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
true,
|
||||
$gift->has_been_offered
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
false,
|
||||
$gift->has_been_received
|
||||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function toggle_a_gift_offered()
|
||||
{
|
||||
$gift = factory(Gift::class)->make();
|
||||
$gift->has_been_offered = true;
|
||||
$gift->toggle();
|
||||
|
||||
$this->assertEquals(
|
||||
true,
|
||||
$gift->is_an_idea
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
false,
|
||||
$gift->has_been_offered
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
false,
|
||||
$gift->has_been_received
|
||||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function has_particular_recipient_returns_false_if_it_s_for_no_specific_recipient()
|
||||
{
|
||||
@@ -147,11 +101,11 @@ class GiftTest extends TestCase
|
||||
public function it_gets_the_value()
|
||||
{
|
||||
$gift = factory(Gift::class)->make();
|
||||
$gift->value = '100$';
|
||||
$gift->value = '100';
|
||||
|
||||
$this->assertEquals(
|
||||
'100$',
|
||||
$gift->value
|
||||
'100',
|
||||
$gift->amount
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Services\Contact\Conversation;
|
||||
|
||||
use Tests\TestCase;
|
||||
use App\Models\Contact\Gift;
|
||||
use App\Models\Account\Photo;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use App\Services\Contact\Gift\AssociatePhotoToGift;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
|
||||
class AssociateGiftToPhotoTest extends TestCase
|
||||
{
|
||||
use DatabaseTransactions;
|
||||
|
||||
/** @test */
|
||||
public function it_associates_a_photo_to_a_gift()
|
||||
{
|
||||
$gift = factory(Gift::class)->create();
|
||||
$photo = factory(Photo::class)->create([
|
||||
'account_id' => $gift->account_id,
|
||||
]);
|
||||
|
||||
app(AssociatePhotoToGift::class)->execute([
|
||||
'account_id' => $gift->account_id,
|
||||
'gift_id' => $gift->id,
|
||||
'photo_id' => $photo->id,
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('gift_photo', [
|
||||
'gift_id' => $gift->id,
|
||||
'photo_id' => $photo->id,
|
||||
]);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_fails_if_wrong_parameters_are_given()
|
||||
{
|
||||
$this->expectException(ValidationException::class);
|
||||
|
||||
app(AssociatePhotoToGift::class)->execute([
|
||||
'account_id' => -1,
|
||||
'gift_id' => -1,
|
||||
'photo_id' => -1,
|
||||
]);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_fails_if_photo_is_wrong_account()
|
||||
{
|
||||
$gift = factory(Gift::class)->create();
|
||||
$photo = factory(Photo::class)->create();
|
||||
|
||||
$this->expectException(ModelNotFoundException::class);
|
||||
|
||||
app(AssociatePhotoToGift::class)->execute([
|
||||
'account_id' => $gift->account_id,
|
||||
'gift_id' => $gift->id,
|
||||
'photo_id' => $photo->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Services\Contact\Conversation;
|
||||
|
||||
use Tests\TestCase;
|
||||
use App\Models\Contact\Gift;
|
||||
use App\Models\Account\Account;
|
||||
use App\Models\Contact\Contact;
|
||||
use App\Services\Contact\Gift\CreateGift;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
|
||||
class CreateGiftTest extends TestCase
|
||||
{
|
||||
use DatabaseTransactions;
|
||||
|
||||
/** @test */
|
||||
public function it_creates_a_gift()
|
||||
{
|
||||
$contact = factory(Contact::class)->create();
|
||||
|
||||
$gift = app(CreateGift::class)->execute([
|
||||
'account_id' => $contact->account_id,
|
||||
'contact_id' => $contact->id,
|
||||
'name' => 'Book',
|
||||
'status' => 'idea',
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('gifts', [
|
||||
'id' => $gift->id,
|
||||
'account_id' => $contact->account_id,
|
||||
'contact_id' => $contact->id,
|
||||
'name' => 'Book',
|
||||
'status' => 'idea',
|
||||
]);
|
||||
|
||||
$this->assertInstanceOf(
|
||||
Gift::class,
|
||||
$gift
|
||||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_fails_if_wrong_parameters_are_given()
|
||||
{
|
||||
$this->expectException(ValidationException::class);
|
||||
|
||||
app(CreateGift::class)->execute([
|
||||
'account_id' => -1,
|
||||
'contact_id' => -1,
|
||||
'name' => 'Book',
|
||||
'status' => 'idea',
|
||||
]);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_fails_if_contact_is_wrong_account()
|
||||
{
|
||||
$account = factory(Account::class)->create();
|
||||
$contact = factory(Contact::class)->create();
|
||||
|
||||
$this->expectException(ModelNotFoundException::class);
|
||||
|
||||
$gift = app(CreateGift::class)->execute([
|
||||
'account_id' => $account->id,
|
||||
'contact_id' => $contact->id,
|
||||
'name' => 'Book',
|
||||
'status' => 'idea',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Services\Contact\Conversation;
|
||||
|
||||
use Tests\TestCase;
|
||||
use App\Models\Contact\Gift;
|
||||
use App\Models\Account\Account;
|
||||
use App\Services\Contact\Gift\DestroyGift;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
|
||||
class DestroyGiftTest extends TestCase
|
||||
{
|
||||
use DatabaseTransactions;
|
||||
|
||||
/** @test */
|
||||
public function it_destroys_a_gift()
|
||||
{
|
||||
$gift = factory(Gift::class)->create();
|
||||
|
||||
$this->assertDatabaseHas('gifts', [
|
||||
'account_id' => $gift->account_id,
|
||||
'contact_id' => $gift->contact_id,
|
||||
'id' => $gift->id,
|
||||
]);
|
||||
|
||||
app(DestroyGift::class)->execute([
|
||||
'account_id' => $gift->account_id,
|
||||
'gift_id' => $gift->id,
|
||||
]);
|
||||
|
||||
$this->assertDatabaseMissing('gifts', [
|
||||
'id' => $gift->id,
|
||||
]);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_fails_if_wrong_parameters_are_given()
|
||||
{
|
||||
$this->expectException(ValidationException::class);
|
||||
|
||||
app(DestroyGift::class)->execute([
|
||||
'account_id' => -1,
|
||||
]);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_fails_if_gift_is_wrong_account()
|
||||
{
|
||||
$account = factory(Account::class)->create();
|
||||
$gift = factory(Gift::class)->create();
|
||||
|
||||
$this->expectException(ModelNotFoundException::class);
|
||||
|
||||
app(DestroyGift::class)->execute([
|
||||
'account_id' => $account->id,
|
||||
'gift_id' => $gift->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Services\Contact\Conversation;
|
||||
|
||||
use Tests\TestCase;
|
||||
use App\Models\Contact\Gift;
|
||||
use App\Models\Account\Account;
|
||||
use App\Services\Contact\Gift\UpdateGift;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
|
||||
class UpdateGiftTest extends TestCase
|
||||
{
|
||||
use DatabaseTransactions;
|
||||
|
||||
/** @test */
|
||||
public function it_updates_a_gift()
|
||||
{
|
||||
$gift = factory(Gift::class)->create();
|
||||
|
||||
$gift = app(UpdateGift::class)->execute([
|
||||
'account_id' => $gift->account_id,
|
||||
'gift_id' => $gift->id,
|
||||
'contact_id' => $gift->contact_id,
|
||||
'name' => 'Book',
|
||||
'status' => 'offered',
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('gifts', [
|
||||
'id' => $gift->id,
|
||||
'name' => 'Book',
|
||||
'status' => 'offered',
|
||||
]);
|
||||
|
||||
$this->assertInstanceOf(
|
||||
Gift::class,
|
||||
$gift
|
||||
);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_fails_if_wrong_parameters_are_given()
|
||||
{
|
||||
$this->expectException(ValidationException::class);
|
||||
|
||||
app(UpdateGift::class)->execute([
|
||||
'account_id' => -1,
|
||||
'gift_id' => -1,
|
||||
]);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function it_throws_an_exception_if_gift_wrong_account()
|
||||
{
|
||||
$account = factory(Account::class)->create();
|
||||
$gift = factory(Gift::class)->create();
|
||||
|
||||
$this->expectException(ModelNotFoundException::class);
|
||||
|
||||
app(UpdateGift::class)->execute([
|
||||
'account_id' => $account->id,
|
||||
'gift_id' => $gift->id,
|
||||
'contact_id' => $gift->contact_id,
|
||||
'name' => 'Book',
|
||||
'status' => 'offered',
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user