56 Commits

Author SHA1 Message Date
f89e0639dc build: update composer and npm 2021-05-02 11:20:07 -03:00
a5a2990518 chore: Add phpstan and phpinsights 2021-04-28 13:40:38 -03:00
de91c4ef9f chore: add helper scripts
- Deploy (merge into production, push, composer install and npm install)
- start_vagrant (just start vagrant and browser)
- ssh_homestead (a simple ssh into homestead)
2021-04-21 08:09:53 -03:00
56bbef2a04 chore: add script to update Composer and NPM 2021-04-21 07:22:44 -03:00
967db67ce3 chore: Update npm 2021-04-21 07:22:24 -03:00
f3d41e71d3 chore: update composer packages 2021-04-20 12:03:45 -03:00
26eab2646d chore: update npm ssri to 6.0.2 2021-04-20 09:57:04 -03:00
a18d4ba808 Updating NPM packages 2021-02-23 20:06:56 -03:00
1acc2b66f7 Update to Laravel 8.* 2021-02-23 20:00:23 -03:00
bc5f8feaac Update Laravel to 7.x 2021-02-19 20:27:28 -03:00
47c68c8860 Update Laravel to version 6.* 2021-02-17 20:02:27 -03:00
bd8927d408 Fix "@production" message appearing on screen
It was just for the privacy respect statistics script not appearing on
test phase.
2021-02-16 20:13:12 -03:00
d441c82890 Update Laravel to 5.8.* 2021-02-16 20:03:05 -03:00
54abd983aa Update js libraries 2021-02-14 23:20:43 -03:00
5d7aee7778 Showing tracker only in production 2020-08-22 20:01:58 -03:00
b767265200 Update NPM and compile assets 2020-08-22 14:14:46 -03:00
2ed6065901 Including the Tracking number directly 2020-08-22 14:10:59 -03:00
0b3f6d4837 Including my OWA Tracker 2020-08-22 11:44:03 -03:00
6339a23b02 Composer update 2020-08-22 01:26:20 -03:00
dc0c4e679b NPM Update 2020-08-22 01:26:08 -03:00
bb849dba4b npm updated 2020-04-05 12:19:23 -03:00
437e847cdd Updating npm and composer 2019-12-03 00:02:43 -03:00
vagrant
f31228843f Updating libraries 2019-07-18 22:56:17 +00:00
28520edee9 Merge branch 'master' of github.com:brunofontes/shareit 2019-07-18 19:46:11 -03:00
74eb254297 Including home.blade -> hasFocus test 2019-05-31 11:42:18 -03:00
2f16d4dc60 Composer update 2019-05-31 11:42:00 -03:00
130e47b198 Create test.php 2019-02-26 20:18:22 -03:00
6be295e25b Bug Fixing - forgot to add the use App\FlashMessage on AlertController 2018-10-21 13:13:09 -03:00
00c382e1cc Avoiding issues and refactoring code
I made the code more passive, avoiding issued at taking, returning,
storing alerts or removing alerts from an item.

Now they all check if it is with you before returning/deleting
alert etc. I am not sure if all cases are covered, but they are
better than before. I had one only issued on this on that time,
but I prefer to prioritize safety/security.

I took the opportunitie to move some code from Controllers to
the model itself, as they were changing with the DB.
2018-10-21 13:09:06 -03:00
2bc2792a24 Bug Fixed: avoiding item waiting_user across take/return
It happend once: a user asked to be added to the waiting
list at the same time other user was returning the item.

So the user ended up waiting for the user he was already
using.
2018-10-21 11:57:23 -03:00
3d6b0dbeca Fixed bug causing error 500 on production
Some PHP version or configuration were causing this error.

On app.blade.php of local branch, I could use "$usedItems ? :"
even if $usedItems were null, but I had to check an "isset" to the production.

On HomecController, I had to change the "object" parameter of getUsername
to "\Illuminate\Database\Eloquent\Collection" to make it work.

I took the chance to just show the number of itens in use
if it were greater than 0.
2018-10-16 20:28:59 -03:00
52223676dc Trying to fix an 500 error on production 2018-10-16 20:18:10 -03:00
7a8d5ccaff Refreshing and Including number of used items on Title
It was necessary to keep refreshing the page to
check if an item was returned when we did not
want to be alerted. So, now, the page refresh
itself every 2 minutes (while I do not know how
to use Laravel broadcasting) and the title shows
the number of items in use (including when used
by you).
2018-10-16 15:15:05 -03:00
8c4fe0a489 Refactoring
Including some use for classes;
REfactoring the HomeController, to make it cleaner
and avoid repeating code.
2018-10-10 00:46:28 -03:00
69f0722c79 Let me know if anyone subscribed during alpha
As the project is still alpha, I just want to know if someone
subscribed at it using a dummy e-mail address.
2018-10-03 19:42:35 -03:00
5667fd0a86 Composer update
Plugins updated with composer.
2018-10-03 00:32:23 -03:00
954820f46b Added: language to User profile (DB)
The language were set only on session. But now it is
stored with user profile, on DB.

It is important as now I can send alert e-mails to each
user on their own languages and not the activer
user language.

Also, wherever the user logs out and logs in again,
it will see the same site locale.
2018-10-02 23:53:35 -03:00
8157a183c7 Refactoring: just including "use App" to make code a bit cleaner
I was using "\App::setLocale" on code, but it was annoind me.
So I included the "use App" and now I am colling
directly the "App::setLocale".
2018-10-02 23:49:16 -03:00
528d3b4caf Refactoring: TakeController returned item Mail moved to a listener
The TakeController was manually sending the email message
as well as dealing with item return, so I moved the email message
to it's own listener, I created an event for it and moved the
return part to the item model.
2018-09-30 21:37:44 -03:00
4ec2b24885 BugFixing: home page were blank on no items
When we had no items showing on home page, it were just blank.
Turns out that the @empty call that shows the default message
were appearing just when it had no items on a product, instead
of no items at all.

Fixed it moving the paragraph to the right place.
2018-09-29 12:09:18 -03:00
5be29fc3d8 Added: buttons to change language
Now the welcome.blade has two new links to change language.
Other pages has a footer with the language switcher too.
2018-09-29 01:49:02 -03:00
6b20397e9d Help Page translated into PTB
Now the entire website should be translated into PTB.
2018-09-28 19:14:29 -03:00
67d7e34baa Adding: help file translation to PTB
The help file is now translated into PTB
2018-09-28 02:09:00 -03:00
b7002e1d05 Adding translation of product page
The news/product/*.blade.php files are translated into PTB.
2018-09-28 01:54:51 -03:00
f6ab437889 Added PTB Translation of emails and some pages
Now the main page, item page and e-mails are translated
into Brazilian Portuguese
2018-09-27 20:13:14 -03:00
9a66b65f38 Starting to add localization into PTB 2018-09-26 23:06:38 -03:00
b49ec06062 Creating a readme.md file
The first readme.md was just some annotations to myself.
Now I created a first draft of a readme.md.
2018-09-22 16:07:49 -03:00
4764b74415 Removing start_vagrant.sh from git 2018-09-22 15:50:50 -03:00
a3e9abbd4c Improving email contents
On email body, it usually appeared as the button to the website
would take directly an action, so the text was changed to make
sure the user will understand he is only going to access
the website.
2018-09-22 15:47:24 -03:00
0cd807169e Groups HOME itesm by Product 2018-09-21 00:45:06 -03:00
d87d4ba4bf Fixing home alignment when person name is too big 2018-09-21 00:05:57 -03:00
61b54ecdd8 Changing home layout from FLOAT to GRID 2018-09-20 20:37:21 -03:00
b99f6a5b68 Including FlashMessage Model 2018-09-20 00:18:30 -03:00
1f23753f70 Invalid product now redirect to Products.index page 2018-09-19 21:12:48 -03:00
1fd78fe94a Including messages on registering and invalid product 2018-09-19 21:07:40 -03:00
0bcefb43e6 Product/index view is now only allowed to Verified users 2018-09-19 20:41:36 -03:00
84 changed files with 692884 additions and 7372 deletions

6
.gitignore vendored
View File

@@ -5,13 +5,7 @@
/vendor
/.idea
/.vscode
/.vagrant
Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
.env
.phpunit.result.cache
mergeAndDeploy.sh
start_vagrant.sh
ssh_homestead.sh

40
app/Events/ReturnItem.php Normal file
View File

@@ -0,0 +1,40 @@
<?php
namespace App\Events;
use \App\Item;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class ReturnItem
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $item;
/**
* Create a new event instance.
*
* @param Item $item The returned item.
*
* @return void
*/
public function __construct(Item $item)
{
$this->item = $item;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}

View File

@@ -3,6 +3,7 @@
namespace App\Exceptions;
use Exception;
Use Throwable;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
class Handler extends ExceptionHandler
@@ -32,7 +33,7 @@ class Handler extends ExceptionHandler
* @param \Exception $exception
* @return void
*/
public function report(Exception $exception)
public function report(Throwable $exception)
{
parent::report($exception);
}
@@ -44,7 +45,7 @@ class Handler extends ExceptionHandler
* @param \Exception $exception
* @return \Illuminate\Http\Response
*/
public function render($request, Exception $exception)
public function render($request, Throwable $exception)
{
return parent::render($request, $exception);
}

11
app/FlashMessage.php Normal file
View File

@@ -0,0 +1,11 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class FlashMessage extends Model
{
const PRIMARY = 'primary';
const DANGER = 'danger';
}

View File

@@ -2,24 +2,46 @@
namespace App\Http\Controllers;
use Auth;
use Mail;
use \App\User;
use App\FlashMessage;
use \App\Mail\UserWaiting;
use Illuminate\Http\Request;
class AlertController extends Controller
{
/**
* Store the waiting_user_id on db
* so the user can be alerted when
* the item is free
*
* @param Request $request Form data
*
* @return redirect to home
*/
public function store(Request $request)
{
$item = User::loggedIn()->items()->find(request('item'));
$item->waiting_user_id = \Auth::id();
$item->timestamps = false;
$item->save();
if (!$item->used_by) {
session()->flash(
FlashMessage::PRIMARY,
__('Oh! This item has just being returned. Take it before anyone else!')
);
return redirect('home');
}
$loggedUser = \Auth::user()->name;
if ($item->used_by == Auth::id()) {
return redirect('home');
}
$item->storeAlert();
$loggedUser = Auth::user()->name;
$userWithItem = User::find($item->used_by);
\Mail::to($userWithItem)->send(
new UserWaiting($loggedUser, $userWithItem->name, $item)
);
Mail::to($userWithItem)
->locale($userWithItem->language)
->send(new UserWaiting($loggedUser, $userWithItem->name, $item));
return redirect('home');
}
@@ -27,10 +49,12 @@ class AlertController extends Controller
public function delete(Request $request)
{
$item = User::loggedIn()->items()->find(request('item'));
$item->waiting_user_id = null;
$item->timestamps = false;
$item->save();
if ($item->waiting_user_id != Auth::id()) {
return redirect('home');
}
$item->removeAlert();
return redirect('home');
}
}

View File

@@ -8,6 +8,8 @@ use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\Auth\RegistersUsers;
use App\FlashMessage;
use Mail;
class RegisterController extends Controller
{
@@ -70,7 +72,15 @@ class RegisterController extends Controller
'password' => Hash::make($data['password']),
]);
\Mail::to($user)->send(new Welcome($user));
Mail::to($user)->send(new Welcome($user));
$text = "Share it! New user: {$user->name} ($user->email)";
Mail::raw($text, function ($message) use ($text) {
$message->to('brunofontes.shareit@mailnull.com');
$message->subject($text);
});
session()->flash(FlashMessage::PRIMARY, __('Thanks for registering. Please, do not forget to validate your e-mail address.'));
return $user;
}

View File

@@ -25,7 +25,7 @@ class VerificationController extends Controller
*
* @var string
*/
protected $redirectTo = '/home';
protected $redirectTo = '/';
/**
* Create a new controller instance.

View File

@@ -3,10 +3,11 @@
namespace App\Http\Controllers;
use \App\User;
use Illuminate\Http\Request;
class HomeController extends Controller
{
protected $activeUsers = [];
/**
* Create a new controller instance.
*
@@ -17,6 +18,50 @@ class HomeController extends Controller
$this->middleware('auth');
}
/**
* Show the application dashboard.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$items = User::loggedIn()->items()->with('users')->get();
$numberOfUsedItems = 0;
foreach ($items as $item) {
if (isset($item->used_by)) {
$numberOfUsedItems++;
}
$this->getUsername($item->users, $item->used_by);
$this->getUsername($item->users, $item->waiting_user_id);
}
$products = $items
->sortBy('product.name', SORT_NATURAL | SORT_FLAG_CASE)
->groupBy('product.name');
return view(
'home',
['products' => $products, 'users' => $this->activeUsers, 'usedItems' => $numberOfUsedItems]
);
}
/**
* Get the username from an specified user id.
*
* @param object $itemUsers Array with IDs and usernames
* @param int $id The user id to search for
*
* @return void
*/
protected function getUsername(\Illuminate\Database\Eloquent\Collection $itemUsers, ?int $id)
{
if ($id && !isset($this->activeUsers[$id])) {
$this->activeUsers[$id] = $this->findName($itemUsers, $id);
}
}
/**
* Get a name from a specific id in a array
*
@@ -33,26 +78,4 @@ class HomeController extends Controller
}
}
}
/**
* Show the application dashboard.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$users = [];
$items = User::loggedIn()->items()->with('users')->get();
foreach ($items as $item) {
if ($item->used_by && !isset($users[$item->used_by])) {
$users[$item->used_by] = $this->findName($item->users, $item->used_by);
}
if ($item->waiting_user_id && !isset($users[$item->waiting_user_id])) {
$users[$item->waiting_user_id] = $this->findName($item->users, $item->waiting_user_id);
}
}
return view('home', compact('items', 'users'));
}
}

View File

@@ -5,13 +5,21 @@ namespace App\Http\Controllers;
use \App\Item;
use \App\User;
use Illuminate\Http\Request;
use App\FlashMessage as flash;
class ItemController extends Controller
{
public function show($id)
{
$item = Item::find($id);
if (!$item || $item->product->user_id != \Auth::id()) return back();
if (!$item || $item->product->user_id != \Auth::id()) {
session()->flash(flash::DANGER,
\Lang::getFromJson(
"The item doesn't exist."
)
);
return back();
}
$users = $item->users()->get();
$otherItems = Item::where([['product_id', $item->product_id], ['id', '!=', $id]])->get();
@@ -43,7 +51,7 @@ class ItemController extends Controller
$authUser = User::loggedIn();
$authUser->items()->create(['name' => request('item'), 'product_id' => request('product_id')]);
return redirect('product/'.request('product_id'));
return redirect('product/' . request('product_id'));
}
public function patch(Request $request)
@@ -52,7 +60,7 @@ class ItemController extends Controller
$item = User::loggedIn()->items()->find(request('item'));
$item->name = request('name');
$item->save();
return redirect('item/'.request('item'));
return redirect('item/' . request('item'));
}
public function delete(Request $request)

View File

@@ -0,0 +1,91 @@
<?php
namespace App\Http\Controllers;
use App;
use Lang;
use App\User;
use Illuminate\Http\Request;
class LanguageController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* @param int $locale
* @return \Illuminate\Http\Response
*/
public function update($locale)
{
App::setLocale($locale);
User::setLanguage($locale);
session(['lang' => $locale]);
session()->save();
return back();
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
//
}
}

View File

@@ -6,6 +6,7 @@ use \App\Item;
use \App\User;
use \App\Product;
use Illuminate\Http\Request;
use App\FlashMessage as flash;
class ProductController extends Controller
{
@@ -55,7 +56,7 @@ class ProductController extends Controller
$product->name = request('name');
$product->url = request('url');
$product->save();
return redirect('product/'.request('product'));
return redirect('product/' . request('product'));
}
/**
@@ -70,8 +71,13 @@ class ProductController extends Controller
$product = Product::fromAuthUser()->find($id);
if (!$product) {
return back();
session()->flash(
flash::DANGER,
\Lang::getFromJson(
"The product doesn't exist or doesn't belongs to you."
)
);
return redirect('/product');
}
return view('product.show', compact('product'));
}

View File

@@ -2,40 +2,63 @@
namespace App\Http\Controllers;
use \App\Item;
use \App\User;
use App\Mail\ItemAvailable;
use Auth;
use Lang;
use App\Item;
use App\User;
use App\Events\ReturnItem;
use Illuminate\Http\Request;
use PhpParser\Node\Stmt\TryCatch;
/**
* Responsible to Take and Return an Item.
*/
class TakeController extends Controller
{
/**
* The user take an item
*
* @param Request $request The form data
*
* @return home view
*/
public function store(Request $request)
{
$item = User::loggedIn()->items()->find(request('item'));
if ($item->used_by) {
return back()->withErrors("This item is already taken");
}
$item->used_by = \Auth::id();
$item->save();
return redirect('home');
}
public function delete(Request $request)
{
$item = User::loggedIn()->items()->find(request('item'));
$waiting_id = $item->waiting_user_id;
$item->used_by = null;
$item->waiting_user_id = null;
$item->save();
//Send e-mail to waiting user
if ($waiting_id) {
$user = User::find($waiting_id);
\Mail::to($user)->send(
new ItemAvailable($user->name, $item)
try {
$item->takeItem();
} catch (\Exception $e) {
return back()->withErrors(
Lang::getFromJson('This item is already taken')
);
}
return redirect('home');
}
/**
* User return an item
* Trigger an event: ReturnItem
*
* @param Request $request Form data
*
* @return View home
*/
public function delete(Request $request)
{
$item = User::loggedIn()->items()->find(request('item'));
try {
$item->returnItem();
} catch (\Exception $e) {
return back()->withErrors(
Lang::getFromJson("You cannot return an item that is not with you")
);
}
event(new ReturnItem($item));
return redirect('home');
}
}

View File

@@ -2,6 +2,7 @@
namespace App\Http\Controllers;
use \Lang;
use \App\User;
use \App\Item;
use \App\Product;
@@ -42,14 +43,22 @@ class UserController extends Controller
$userArray = User::where('email', request('email'))->get();
if (count($userArray) == 0) {
return back()->withErrors("The e-mail address is not registered yet.");
return back()->withErrors(
Lang::getFromJson("The e-mail address is not registered yet.")
);
}
$item = Item::findOrFail(request('item_id'));
if ($item->product->user_id == \Auth::id()) {
User::findOrFail($userArray[0]->id)->items()->syncWithoutDetaching([request('item_id')]);
User::findOrFail($userArray[0]->id)
->items()
->syncWithoutDetaching([request('item_id')]);
} else {
return back()->withErrors("You cannot add a user to a product that is not yourse.");
return back()->withErrors(
Lang::getFromJson(
"You cannot add a user to a product that is not yourse."
)
);
}
return back();
}
@@ -67,12 +76,20 @@ class UserController extends Controller
$item = Item::findOrFail(request('item_id'));
if ($item->product->user_id == \Auth::id()) {
$returnItem = User::findOrFail(request('user_id'))->items()->findOrFail(request('item_id'));
$returnItem = User::findOrFail(request('user_id'))
->items()
->findOrFail(request('item_id'));
$returnItem->used_by = null;
$returnItem->save();
User::findOrFail(request('user_id'))->items()->detach([request('item_id')]);
User::findOrFail(request('user_id'))
->items()
->detach([request('item_id')]);
} else {
return back()->withErrors("You cannot remove a user from a product that is not yourse.");
return back()->withErrors(
Lang::getFromJson(
"You cannot remove a user from a product that is not yourse."
)
);
}
return back();
}

View File

@@ -35,6 +35,7 @@ class Kernel extends HttpKernel
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\Locale::class,
],
'api' => [

View File

@@ -0,0 +1,22 @@
<?php
namespace App\Http\Middleware;
use App;
use Closure;
class Locale
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
App::setLocale(session('lang'));
return $next($request);
}
}

View File

@@ -2,7 +2,10 @@
namespace App;
use Auth;
use Lang;
use Illuminate\Database\Eloquent\Model;
use Exception;
class Item extends Model
{
@@ -36,6 +39,61 @@ class Item extends Model
*/
public static function fromAuthUser()
{
return (new static)->where('user_id', \Auth::id());
return (new static)->where('user_id', Auth::id());
}
/**
* Take a specified item
*
* @return void
*/
public function takeItem()
{
if (isset($this->used_by)) {
throw new Exception("Trying to take an Item that is in use", 1);
}
$this->used_by = Auth::id();
$this->waiting_user_id = null;
$this->save();
}
/**
* Return a specified item
*
* @return void
*/
public function returnItem()
{
if ($this->used_by != Auth::id()) {
throw new Exception("Trying to return an empty Item or from other user", 1);
}
$this->used_by = null;
$this->save();
}
/**
* Store a waiting user to the item
*
* @return void
*/
public function storeAlert()
{
$this->waiting_user_id = Auth::id();
$this->timestamps = false;
$this->save();
}
/**
* Remove a waiting user to the item
*
* @return void
*/
public function removeAlert()
{
$this->waiting_user_id = null;
$this->timestamps = false;
$this->save();
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace App\Listeners;
use Mail;
use App\User;
use App\Events\ReturnItem;
use App\Mail\ItemAvailable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class AlertReturnedItem
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Send an email to the user that
* is waiting for the item
*
* @param ReturnItem $event The return event that contains an item
*
* @return void
*/
public function handle(ReturnItem $event)
{
if ($event->item->waiting_user_id) {
$user = User::find($event->item->waiting_user_id);
Mail::to($user)
->locale($user->language)
->send(new ItemAvailable($user->name, $event->item));
}
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace App\Listeners;
use App\User;
use IlluminateAuthEventsLogin;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Auth\Events\Login;
class SetLanguage
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param IlluminateAuthEventsLogin $event
* @return void
*/
public function handle(Login $event)
{
session(['lang' => User::loggedIn()->language]);
session()->save();
}
}

View File

@@ -32,7 +32,11 @@ class ItemAvailable extends Mailable
*/
public function build()
{
return $this->subject($this->item->name . ' is available!')
->markdown('emails.itemAvailable');
return $this->subject(
\Lang::getFromJson(
':itemname is available!',
['itemname' => $this->item->name]
)
)->markdown('emails.itemAvailable');
}
}

View File

@@ -2,6 +2,7 @@
namespace App\Mail;
use Lang;
use \App\Item;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
@@ -34,7 +35,14 @@ class UserWaiting extends Mailable
*/
public function build()
{
return $this->subject($this->waitingUser . ' wants to use ' . $this->item->name)
->markdown('emails.userWaiting');
return $this->subject(
Lang::getFromJson(
':waitinguser wants to use :itemname',
[
'waitinguser' => $this->waitingUser,
'itemname' => $this->item->name
]
)
)->markdown('emails.userWaiting');
}
}

View File

@@ -2,6 +2,7 @@
namespace App\Mail;
use \Lang;
use \App\User;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
@@ -30,6 +31,6 @@ class Welcome extends Mailable
*/
public function build()
{
return $this->markdown('emails.welcome');
return $this->subject(Lang::get('Welcome'))->markdown('emails.welcome');
}
}

View File

@@ -2,6 +2,9 @@
namespace App\Providers;
use App\Events\ReturnItem;
use App\Listeners\SetLanguage;
use App\Listeners\AlertReturnedItem;
use Illuminate\Support\Facades\Event;
use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
@@ -18,6 +21,14 @@ class EventServiceProvider extends ServiceProvider
Registered::class => [
SendEmailVerificationNotification::class,
],
ReturnItem::class => [
AlertReturnedItem::class,
],
'Illuminate\Auth\Events\Login' => [
SetLanguage::class,
],
];
/**

View File

@@ -2,6 +2,7 @@
namespace App;
use Auth;
use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
@@ -46,6 +47,23 @@ class User extends Authenticatable implements MustVerifyEmail
*/
public static function loggedIn()
{
return (new static)->findOrFail(\Auth::id());
return (new static)->findOrFail(Auth::id());
}
/**
* Set the default website language
* for the acual user
*
* @param string $language The language code
*
* @return void
*/
public static function setLanguage(string $language)
{
if (Auth::check()) {
$user = self::loggedIn();
$user->language = $language;
$user->save();
}
}
}

2
check.sh Normal file
View File

@@ -0,0 +1,2 @@
./vendor/bin/phpstan analyse --memory-limit=2G
php artisan insights

View File

@@ -5,18 +5,23 @@
"license": "MIT",
"type": "project",
"require": {
"php": "^7.1.3",
"php": "^7.3.0",
"fideloper/proxy": "^4.0",
"laravel/framework": "5.7.*",
"laravel/tinker": "^1.0"
"laravel/framework": "^8.0",
"laravel/helpers": "^1.4",
"laravel/tinker": "^2.0",
"laravel/ui": "^3.0"
},
"require-dev": {
"beyondcode/laravel-dump-server": "^1.0",
"filp/whoops": "^2.0",
"fzaninotto/faker": "^1.4",
"mockery/mockery": "^1.0",
"nunomaduro/collision": "^2.0",
"phpunit/phpunit": "^7.0"
"nunomaduro/collision": "^5.0",
"nunomaduro/larastan": "^0.7.4",
"nunomaduro/phpinsights": "^1.14",
"phpstan/phpstan": "^0.12.85",
"phpunit/phpunit": "^9.0"
},
"autoload": {
"classmap": [

6955
composer.lock generated

File diff suppressed because it is too large Load Diff

113
config/insights.php Normal file
View File

@@ -0,0 +1,113 @@
<?php
declare(strict_types=1);
use NunoMaduro\PhpInsights\Domain\Insights\ForbiddenDefineFunctions;
use NunoMaduro\PhpInsights\Domain\Insights\ForbiddenFinalClasses;
use NunoMaduro\PhpInsights\Domain\Insights\ForbiddenNormalClasses;
use NunoMaduro\PhpInsights\Domain\Insights\ForbiddenPrivateMethods;
use NunoMaduro\PhpInsights\Domain\Insights\ForbiddenTraits;
use NunoMaduro\PhpInsights\Domain\Metrics\Architecture\Classes;
use SlevomatCodingStandard\Sniffs\Commenting\UselessFunctionDocCommentSniff;
use SlevomatCodingStandard\Sniffs\Namespaces\AlphabeticallySortedUsesSniff;
use SlevomatCodingStandard\Sniffs\TypeHints\DeclareStrictTypesSniff;
use SlevomatCodingStandard\Sniffs\TypeHints\DisallowMixedTypeHintSniff;
use SlevomatCodingStandard\Sniffs\TypeHints\ParameterTypeHintSniff;
use SlevomatCodingStandard\Sniffs\TypeHints\PropertyTypeHintSniff;
use SlevomatCodingStandard\Sniffs\TypeHints\ReturnTypeHintSniff;
return [
/*
|--------------------------------------------------------------------------
| Default Preset
|--------------------------------------------------------------------------
|
| This option controls the default preset that will be used by PHP Insights
| to make your code reliable, simple, and clean. However, you can always
| adjust the `Metrics` and `Insights` below in this configuration file.
|
| Supported: "default", "laravel", "symfony", "magento2", "drupal"
|
*/
'preset' => 'laravel',
/*
|--------------------------------------------------------------------------
| IDE
|--------------------------------------------------------------------------
|
| This options allow to add hyperlinks in your terminal to quickly open
| files in your favorite IDE while browsing your PhpInsights report.
|
| Supported: "textmate", "macvim", "emacs", "sublime", "phpstorm",
| "atom", "vscode".
|
| If you have another IDE that is not in this list but which provide an
| url-handler, you could fill this config with a pattern like this:
|
| myide://open?url=file://%f&line=%l
|
*/
'ide' => null,
/*
|--------------------------------------------------------------------------
| Configuration
|--------------------------------------------------------------------------
|
| Here you may adjust all the various `Insights` that will be used by PHP
| Insights. You can either add, remove or configure `Insights`. Keep in
| mind that all added `Insights` must belong to a specific `Metric`.
|
*/
'exclude' => [
// 'path/to/directory-or-file'
],
'add' => [
Classes::class => [
ForbiddenFinalClasses::class,
],
],
'remove' => [
AlphabeticallySortedUsesSniff::class,
DeclareStrictTypesSniff::class,
DisallowMixedTypeHintSniff::class,
ForbiddenDefineFunctions::class,
ForbiddenNormalClasses::class,
ForbiddenTraits::class,
ParameterTypeHintSniff::class,
PropertyTypeHintSniff::class,
ReturnTypeHintSniff::class,
UselessFunctionDocCommentSniff::class,
],
'config' => [
ForbiddenPrivateMethods::class => [
'title' => 'The usage of private methods is not idiomatic in Laravel.',
],
],
/*
|--------------------------------------------------------------------------
| Requirements
|--------------------------------------------------------------------------
|
| Here you may define a level you want to reach per `Insights` category.
| When a score is lower than the minimum level defined, then an error
| code will be returned. This is optional and individually defined.
|
*/
'requirements' => [
// 'min-quality' => 0,
// 'min-complexity' => 0,
// 'min-architecture' => 0,
// 'min-style' => 0,
// 'disable-security-check' => false,
],
];

View File

@@ -164,7 +164,7 @@ return [
|
*/
'secure' => env('SESSION_SECURE_COOKIE', false),
'secure' => env('SESSION_SECURE_COOKIE', null),
/*
|--------------------------------------------------------------------------

View File

@@ -0,0 +1,32 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddLocationToUsers extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('language')->after('email_verified_at')->default('en');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('language');
});
}
}

View File

@@ -1,5 +1,7 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder

2
deploy.sh Normal file
View File

@@ -0,0 +1,2 @@
#!/bin/sh
git checkout production && git merge master && git checkout - && git push origin production && ssh -A contabo -t "cd /var/www/shareit.brunofontes.net; git fetch --all; git checkout --force production; git pull origin production --force; ~/composer.phar install -n --optimize-autoloader --no-dev; npm install"

9534
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -10,13 +10,17 @@
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
},
"devDependencies": {
"axios": "^0.18",
"bootstrap": "^4.0.0",
"cross-env": "^5.1",
"jquery": "^3.2",
"laravel-mix": "^2.0",
"lodash": "^4.17.5",
"popper.js": "^1.12",
"vue": "^2.5.7"
"axios": "^0.21",
"bootstrap": "^4.6.0",
"cross-env": "^5.2.1",
"jquery": "^3.5.1",
"laravel-mix": "^6.0.18",
"lodash": "^4.17.21",
"popper.js": "^1.16.1",
"resolve-url-loader": "^3.1.3",
"sass": "^1.32.12",
"sass-loader": "^8.0.2",
"vue": "^2.6.12",
"vue-template-compiler": "^2.6.12"
}
}

17
phpstan.neon Normal file
View File

@@ -0,0 +1,17 @@
includes:
- ./vendor/nunomaduro/larastan/extension.neon
parameters:
paths:
- app
# The level 8 is the highest level
level: 5
ignoreErrors:
- '#Unsafe usage of new static#'
excludePaths:
- ./*/*/FileToBeExcluded.php
checkMissingIterableValueType: false

14
public/css/app.css vendored

File diff suppressed because one or more lines are too long

3
public/js/app.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,77 @@
/*!
* Bootstrap v4.5.2 (https://getbootstrap.com/)
* Copyright 2011-2020 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
/*!
* Determine if an object is a Buffer
*
* @author Feross Aboukhadijeh <https://feross.org>
* @license MIT
*/
/*!
* Sizzle CSS Selector Engine v2.3.5
* https://sizzlejs.com/
*
* Copyright JS Foundation and other contributors
* Released under the MIT license
* https://js.foundation/
*
* Date: 2020-03-14
*/
/*!
* Vue.js v2.6.12
* (c) 2014-2020 Evan You
* Released under the MIT License.
*/
/*!
* jQuery JavaScript Library v3.5.1
* https://jquery.com/
*
* Includes Sizzle.js
* https://sizzlejs.com/
*
* Copyright JS Foundation and other contributors
* Released under the MIT license
* https://jquery.org/license
*
* Date: 2020-05-04T22:49Z
*/
/**
* @license
* Lodash <https://lodash.com/>
* Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
* Released under MIT license <https://lodash.com/license>
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
*/
/**!
* @fileOverview Kickass library to create and place poppers near their reference elements.
* @version 1.16.1
* @license
* Copyright (c) 2016 Federico Zivolo and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

4
public/mix-manifest.json Normal file
View File

@@ -0,0 +1,4 @@
{
"/js/app.js": "/js/app.js",
"/css/app.css": "/css/app.css"
}

View File

@@ -1,16 +1,7 @@
# Share It
Cada usuário, identificado por e-mail, pode ter outros amigos
Just a simple tool to share items with friends.
## DB
It is my first try with Laravel, so I am just learning it. :)
[x] usuário(nome, email)
[x] product[site/software](nome, admin)
[ ] usersPerItem(productID, userID)
[x] item[licença](nome, productID, used_by, usedSince)
[ ] waiting(userID, itemID, waitingSince)
## VIEWS
- Product (administration)
- Item (view itens, other itens from the same product if this one is occupied)
More information about **Share It** [here](https://shareit.brunofontes.net/help).

View File

@@ -0,0 +1,73 @@
<?php
/**
* Strings from the Help page
* They are separeted by help group
*/
return [
'helpTitle' => 'Help...',
/**
* What is it?
*/
'whatIsIt' => 'What is it?',
'siteHelpOthers' => '<p><strong>Share&nbsp;It!</strong> is a site dedicated to help you sharing items with others.</p>
<p>But before sharing anything, you just need to understand 2 basic ideas:</p>',
'product_item' => '<li><strong>Product</strong> - The category that has some similar items</li>
<li><strong>Item</strong> - The item that will be shared. Every item belongs to a <strong>Product</strong></li>',
'examples' => "<strong><em>Examples:</em></strong> You can create the following Categories/Items</p>
<p>
<ul>
<li>Books -> Don Quixote, The Hitchhiker's Guide to the Galaxy</li>
<li>Matrix Movies -> Matrix 1, Matrix 2, Matrix 3</li>
<li>Website Y -> <em>YourAccount</em>*</li>
</ul>
<p><em>* Please note that many websites does not allow sharing your account. Read the site EULA before include it here.</em></p>",
/**
* Step-by-step
*/
'step_title' => 'Step-by-step',
'step_sharing_subtitle' => 'Sharing a product/item',
'step_sharing_steps' => '<li>Go to the <strong><a href="/product">Products</a></strong> page;</li>
<li>Include the Product and click on it;</li>
<li>Include an Item that belongs to that Product and click on it;</li>
<li>Add other people with their subscribed e-mail address.</li>',
'step_sharedItem_subtitle' => 'Using a shared item',
'step_sharedItem_steps' => '<li>Go to the <strong><a href="/home">Home</a></strong> page (tip: add this page as a bookmark);</li>
<li>Click on <strong>Take</strong> to take the item you want to use;</li>
<li>When you finish using it, click on <strong>Return</strong> button.</li>',
'step_getAlerted_subtitle' => 'Getting alerted when an item is available',
'step_getAlerted_steps' => '<li>Go to the <strong><a href="/home">Home</a></strong> page (tip: add this page as a bookmark);</li>
<li>Click on <strong>Alert me</strong> next the taken item;</li>
<li>The active user will be alerted you want to use it later. When the person return it, you will be notified.</li>',
/**
* Screens
*/
'screens_title' => 'Screens',
/**
* Home
*/
'home_title' => 'Home',
'home_body' => '<p>The <strong>Home</strong> is the main screen where you will going to <strong>Take</strong>
and <strong>Return</strong> items.</p>
<p>It shows all items that were shared with you or that you are sharing, unless you remove yourself from there.</p>
<p>If the item has a website, the items name will become a link, so you can just click on it to open.</p>
<p>When the item you want is already taken, you are going to see who got it and for how long.
So you can identify if the person is still using it or if someone just forgot to return it.</p>
<p>You can also ask to be alerted when the item is available. This will notify the active
user that you want to use that item. So the person can return it as soon as they finish using it.</p>',
/**
* Products
*/
'products_title' => 'Products',
'products_body' => '<p>The <strong>Products</strong> is the screen to include Products and Items.</p>
<p>You can also <strong>add users</strong> to your items from this screen.
To be able to do that you just need to select the item.</p>
<p class="mb-4">When adding a Product, you can specify a webpage (this is optional).</p>',
];

View File

@@ -0,0 +1,12 @@
<?php
// /resources/views/home.blade.php
return [
'no_messages' => 'There are no items shared with you yet.',
'share_item' => 'Share an item!',
'return' => 'Return It',
'cancel_alert' => 'Cancel Alert',
'alert_me' => 'Alert me',
'take' => 'Take It'
];

View File

@@ -0,0 +1,40 @@
<?php
return [
/**
* Strings from Item Delete button menu
*/
'confirmation' => 'Confirmation...',
'confirmDeletion' => 'Would you like to delete the item <strong>:itemname</strong>?',
'notAbleRestore' => 'You will not be able to restore it after deletion.',
'close' => 'Close',
'delete' => 'Delete',
/**
* Strings from Item Edit button menu
*/
'edit' => 'Edit',
'edititem' => 'Edit item',
'newname' => 'New name:',
/**
* Strings from the Users box on Item page
*/
'users' => 'Users:',
'noItems' => 'There are no items yet. Include one with the form above.',
'addUser' => 'Add user',
'email' => 'E-mail: ',
'nameDomain' => 'name@domain.com',
'insert' => 'Insert',
/**
* String from otherItems.blade.php - Other items box from item page
*/
'otherItems' => 'Other items from the same product',
/**
* String from item.blade.php
*/
'back' => 'BACK',
];

View File

@@ -0,0 +1,63 @@
<?php
/**
* Strings from the product pages
* They are separeted by the file that calls them.
* Sometimes, a string is used on another file,
* so it will stay at the common segment.
*/
return [
/**
* COMMON
*/
'insert' => 'Insert',
'close' => 'Close',
'' => '',
'' => '',
/**
* addItemForm.blade.php
*/
'item' => 'Item:',
'100yearsSolitude' => 'One Hundred Years of Solitude',
/**
* addProductForm.blade.php
*/
'name' => 'Name:',
'book' => 'Book',
'url' => 'URL:',
'optionalUrlExample' => '(Optional) http://bookwebsite.com',
/**
* deleteButton.blade.php
*/
'delete' => 'Delete',
'confirmation' => 'Confirmation...',
'wannaDelete' => "Would you like to delete the product <strong>:productname</strong> and it's items?",
'notAbleRestore' => 'You will not be able to restore it after deletion.',
/**
* editButton.blade.php
*/
'edit' => 'Edit',
'editProduct' => 'Edit product',
'newName' => 'New name:',
'newUrl' => 'New url:',
/**
* index.blade.php
*/
'products' => 'Products',
'noProductsYet' => 'There are no products yet. Include one with the form above.',
'addProduct' => 'Add product',
'yourItems' => 'Your items',
/**
* show.blade.php
*/
'items' => 'Items:',
'noItemsYet' => 'There are no items yet. Include one with the form above.',
'addItem' => 'Add item',
'back' => 'BACK',
];

View File

@@ -0,0 +1,15 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used during authentication for various
| messages that we need to display to the user. You are free to modify
| these language lines according to your application's requirements.
|
*/
'failed' => 'These credentials do not match our records.',
'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
];

View File

@@ -0,0 +1,10 @@
<?php
return [
'Home' => 'Home',
'Login' => 'Login',
'Register' => 'Register',
'Products' => 'Products',
'Help' => 'Help',
'copyright' => '© 2018 Bruno Fontes All Rights Reserved',
'byAuthor' => 'By Bruno Fontes',
];

52
resources/lang/pt-br.json Normal file
View File

@@ -0,0 +1,52 @@
{
"Reset Password": "Redefinir senha",
"E-Mail Address" : "E-mail",
"Send Password Reset Link" : "Redefinir senha",
"Password" : "Senha",
"Confirm Password" : "Confirmar senha",
"Login" : "Entrar",
"Remember Me" : "Manter conectado",
"Forgot Your Password?" : "Esqueceu sua senha?",
"Register" : "Cadastrar",
"Name" : "Nome",
"Logout" : "Desconectar",
"Verify Your Email Address" : "Confirme seu e-mail",
"A fresh verification link has been sent to your email address." : "Enviamos outro link de confirmação para o seu e-mail.",
"Before proceeding, please check your email for a verification link." : "Antes de continuar, use o link de confirmação que enviamos para o seu e-mail.",
"If you did not receive the email" : "Se você não recebeu o e-mail",
"click here to request another" : "clique aqui para enviar novamente",
"Thanks for registering. Please, do not forget to validate your e-mail address." : "Obrigado por se cadastrar. Não esqueça de validar seu e-mail.",
"Welcome": "Bem-vindo!",
"Welcome, :username,": "Bem-vindo, :username,",
"Thank's for registering at **Share It!**" : "Obrigado por se registrar no **Share It!**",
"Your account was created, but you still need to activate it. We've sent you another e-mail and we are ready to go!" : "Sua conta foi criada, mas você ainda precisa confirmar o seu e-mail para usá-la. Enviamos um outro e-mail com a confirmação.",
"And you? Are you ready to Share It with your friends?" : "E você? Está pronto para compartilhar?",
":waitinguser wants to use :itemname": ":waitinguser quer usar :itemname",
"Hello!": "Olá!",
"Verify Email Address" : "Confirme o seu e-mail",
"Please click the button below to verify your email address." : "Clique no botão abaixo para confirmar o seu e-mail.",
"If you did not create an account, no further action is required." : "Se você não criou uma conta, basta ignorar este e-mail.",
"Regards": "Obrigado",
":itemname is available!": ":itemname está disponível!",
"Hi, :username,": "Olá, :username,",
"Good news: :itemname is available!": "Boa notícia: :itemname está disponível!",
"The item <em>:itemname (:productname)</em> is now available on **Share&nbsp;It**.": "O item <em>:itemname (:productname)</em> já está disponível no **Share&nbsp;It**.",
"**Take It** before anyone else at the website:": "Entre no nosso site para usar o item antes de todo mundo.",
"Are you still using :itemname?": "Você ainda está usando o item :itemname?",
"We just want to let you know that :waitinguser asked us to be alerted when this item were available.": "A gente apenas queria dizer que :waitinguser nos pediu para avisar quando esse item estivesse disponível.",
"So, if you are not using it anymore, please **Return It** at the website:": "Então, se você não estiver mais usando, por favor, **Devolva** no nosso site:",
"The e-mail address is not registered yet.": "Esse usuário ainda não está cadastrado.",
"You cannot add a user to a product that is not yourse.": "Você não pode adicionar um usuário a um produto que não é seu.",
"You cannot remove a user from a product that is not yourse.": "Você não pode remover um usuário de um produto que não é seu.",
"The item doesn't exist.": "O item não existe.",
"The product doesn't exist or doesn't belongs to you.": "O produto não existe ou não é seu.",
"This item is already taken": "Esse item já está sendo usado.",
"You cannot return an item that is not with you": "Você não pode devolver um item que não está com você.",
"Oh! This item has just being returned. Take it before anyone else!": "Opa! Esse item acabou de ser devolvido. Aproveite!"
}

View File

@@ -0,0 +1,19 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used during authentication for various
| messages that we need to display to the user. You are free to modify
| these language lines according to your application's requirements.
|
*/
'failed' => 'Login ou senha inválidos.',
'throttle' => 'Limite tentativas excedido. Você pode tentar novamente após :seconds segundos.',
];

View File

@@ -0,0 +1,74 @@
<?php
/**
* Strings from the Help page
* They are separeted by help group
*/
return [
'helpTitle' => 'Ajuda...',
/**
* What is it?
*/
'whatIsIt' => 'O que é?',
'siteHelpOthers' => '<p><strong>Share&nbsp;It!</strong> é um site que te ajudar a compartilhar seus items com seus amigos.</p>
<p>Mas antes de qualquer coisa, você precisa entender duas ideias básicas:</p>',
'product_item' => '<li><strong>Produto</strong> - Um grupo que contém itens similares</li>
<li><strong>Item</strong> - O item que será compartilhado. Cada item pertence a um <strong>Produto</strong></li>',
'examples' => "<strong><em>Exemplos:</em></strong> Você pode criar os seguintes Produtos/Itens:</p>
<p>
<ul>
<li>Livros -> Don Quixote, O guia do mochileiro das galáxias</li>
<li>Filmes Matrix -> Matrix 1, Matrix 2, Matrix 3</li>
<li>Site Y -> <em>SuaConta</em>*</li>
</ul>
<p><em>* Observe que muitos sites não permitem compartilhar sua conta. Leia atentamente o contrato do serviço antes de incluí-lo aqui.</em></p>",
/**
* Step-by-step
*/
'step_title' => 'Passo a passo',
'step_sharing_subtitle' => 'Compartilhando um produto/item',
'step_sharing_steps' => '<li>Vá para a página <strong><a href="/product">Produtos</a></strong>;</li>
<li>Inclua o Produto e clique nele;</li>
<li>Inclua um item que pertence ao produto cadastrado e clique nele;</li>
<li>Adicione outras pessoas pelo e-mail que elas se cadastram.</li>',
'step_sharedItem_subtitle' => 'Usando um item compartilhado',
'step_sharedItem_steps' => '<li>Vá até a página <strong><a href="/home">Início</a></strong> (dica: adicione essa página aos seus favoritos);</li>
<li>Clique em <strong>Usar</strong> para usar o item que deseja;</li>
<li>Ao terminar de usar, clique no botão <strong>Devolver</strong>.</li>',
'step_getAlerted_subtitle' => 'Sendo avisado quando um item está disponível',
'step_getAlerted_steps' => '<li>Vá até a página <strong><a href="/home">Início</a></strong> (dica: adicione essa página aos seus favoritos);</li>
<li>Clique no botão <strong>Alertar</strong> próximo ao item que está em uso;</li>
<li>O atual usuário será avisado que você quer usar o item. Quando a pessoa devolver, você será notificado.</li>',
/**
* Screens
*/
'screens_title' => 'Páginas',
/**
* Home
*/
'home_title' => 'Início',
'home_body' => '<p>A tela <strong>Início</strong> é a sua página principal. Lá você vai <strong>Usar</strong>
e <strong>Devolver</strong> itens.</p>
<p>Ela exibe todos os itens que estão sendo compartilhados com você ou que você está compartilhando
a menos que você se remova de lá.</p>
<p>Se um item tiver um site cadastrado, ele aparecerá como um link. Então você pode clicar nele para abrir a página.</p>
<p>Quanto o item que você quiser já estiver em uso, você vai ver quem o está usando e por quanto tempo.
Então você pode identificar se a pessoa ainda está usando ou se aparentemente ela esqueceu de retorná-lo.</p>
<p>Você também pode pedir para ser avisado quando o item estiver disponível. Isso vai avisar o usuário ativo
que você está esperando por ele. Então a pessoa pode devolvê-lo assim que ela terminar de usar.</p>',
/**
* Products
*/
'products_title' => 'Produtos',
'products_body' => '<p>A página <strong>Produtos</strong> é a tela usada para incluir Produtos e Itens.</p>
<p>Você também pode <strong>incluir usuários</strong> aos seus items nessa tela.
Para fazer isso, você só precisa clicar no item que deseja compartilhar e incluir outras pessoas.</p>
<p class="mb-4">Ao adicionar um produto, você pode especificar um site (opcional).</p>',
];

View File

@@ -0,0 +1,12 @@
<?php
// /resources/views/home.blade.php
return [
'no_messages' => 'Ainda não há itens compartilhados com você.',
'share_item' => 'Compartilhe um item!',
'return' => 'Devolver',
'cancel_alert' => 'Cancelar alerta',
'alert_me' => 'Alertar',
'take' => 'Usar'
];

View File

@@ -0,0 +1,39 @@
<?php
return [
/**
* Strings from Item Delete button menu
*/
'confirmation' => 'Confirmação...',
'confirmDeletion' => 'Você gostaria de apagar o item <strong>:itemname</strong>?',
'notAbleRestore' => 'Não será possível restaurá-lo posteriormente.',
'close' => 'Fechar',
'delete' => 'Apagar',
/**
* Strings from Item Edit button menu
*/
'edit' => 'Editar',
'edititem' => 'Editar item',
'newname' => 'Novo nome:',
/**
* Strings from the Users box on Item page
*/
'users' => 'Usuários:',
'noItems' => 'Não há itens cadastrados. Adicione um no formulário acima.',
'addUser' => 'Adicionar usuário',
'email' => 'E-mail: ',
'nameDomain' => 'nome@dominio.com.br',
'insert' => 'Inserir',
/**
* String from otherItems.blade.php - Other items box from item page
*/
'otherItems' => 'Outros items do mesmo produto',
/**
* String from item.blade.php
*/
'back' => 'VOLTAR',
];

View File

@@ -0,0 +1,19 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Pagination Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used by the paginator library to build
| the simple pagination links. You are free to change them to anything
| you want to customize your views to better match your application.
|
*/
'previous' => '« Anterior',
'next' => 'Próxima »',
];

View File

@@ -0,0 +1,22 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Password Reset Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are the default lines which match reasons
| that are given by the password broker for a password update attempt
| has failed, such as for an invalid token or invalid new password.
|
*/
'password' => 'As senhas precisam ter pelo menos 6 caracteres e serem iguais a confirmação.',
'reset' => 'Sua senha foi redefinida!',
'sent' => 'O link para redefinir sua senha foi enviado por e-mail!',
'token' => 'O token de redefinição de senha é inválido.',
'user' => "Não foi possível encontrar um usuário com o e-mail digitado.",
];

View File

@@ -0,0 +1,61 @@
<?php
/**
* Strings from the product pages
* They are separeted by the file that calls them.
* Sometimes, a string is used on another file,
* so it will stay at the common segment.
*/
return [
/**
* COMMON
*/
'insert' => 'Inserir',
'close' => 'Fechar',
/**
* addItemForm.blade.php
*/
'item' => 'Item:',
'100yearsSolitude' => 'Cem anos de solidão',
/**
* addProductForm.blade.php
*/
'name' => 'Nome:',
'book' => 'Livro',
'url' => 'URL:',
'optionalUrlExample' => '(Opcional) http://sitedolivro.com.br',
/**
* deleteButton.blade.php
*/
'delete' => 'Apagar',
'confirmation' => 'Confirmação...',
'wannaDelete' => "Você gostaria de apagar o produto <strong>:productname</strong> e seus items?",
'notAbleRestore' => 'Não será possível recuperá-los depois.',
/**
* editButton.blade.php
*/
'edit' => 'Editar',
'editProduct' => 'Editar produto',
'newName' => 'Novo nome:',
'newUrl' => 'Nova url:',
/**
* index.blade.php
*/
'products' => 'Produtos',
'noProductsYet' => 'Ainda não há produtos cadastrados. Inclua um no formulário acima.',
'addProduct' => 'Incluir produto',
'yourItems' => 'Seus items',
/**
* show.blade.php
*/
'items' => 'Items:',
'noItemsYet' => 'Ainda não há itens cadastrados. Inclua um no formulário acima.',
'addItem' => 'Incluir item',
'back' => 'VOLTAR',
];

View File

@@ -0,0 +1,148 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Validation Language Lines
|--------------------------------------------------------------------------
|
| The following language lines contain the default error messages used by
| the validator class. Some of these rules have multiple versions such
| as the size rules. Feel free to tweak each of these messages here.
|
*/
'accepted' => 'The :attribute must be accepted.',
'active_url' => 'The :attribute is not a valid URL.',
'after' => 'The :attribute must be a date after :date.',
'after_or_equal' => 'The :attribute must be a date after or equal to :date.',
'alpha' => 'The :attribute may only contain letters.',
'alpha_dash' => 'The :attribute may only contain letters, numbers, dashes and underscores.',
'alpha_num' => 'The :attribute may only contain letters and numbers.',
'array' => 'The :attribute must be an array.',
'before' => 'The :attribute must be a date before :date.',
'before_or_equal' => 'The :attribute must be a date before or equal to :date.',
'between' => [
'numeric' => 'The :attribute must be between :min and :max.',
'file' => 'The :attribute must be between :min and :max kilobytes.',
'string' => 'The :attribute must be between :min and :max characters.',
'array' => 'The :attribute must have between :min and :max items.',
],
'boolean' => 'The :attribute field must be true or false.',
'confirmed' => 'Os campos :attribute e confirmar :attribute são diferentes.',
'date' => 'The :attribute is not a valid date.',
'date_format' => 'The :attribute does not match the format :format.',
'different' => 'The :attribute and :other must be different.',
'digits' => 'The :attribute must be :digits digits.',
'digits_between' => 'The :attribute must be between :min and :max digits.',
'dimensions' => 'The :attribute has invalid image dimensions.',
'distinct' => 'The :attribute field has a duplicate value.',
'email' => 'The :attribute must be a valid email address.',
'exists' => 'The selected :attribute is invalid.',
'file' => 'The :attribute must be a file.',
'filled' => 'The :attribute field must have a value.',
'gt' => [
'numeric' => 'The :attribute must be greater than :value.',
'file' => 'The :attribute must be greater than :value kilobytes.',
'string' => 'The :attribute must be greater than :value characters.',
'array' => 'The :attribute must have more than :value items.',
],
'gte' => [
'numeric' => 'The :attribute must be greater than or equal :value.',
'file' => 'The :attribute must be greater than or equal :value kilobytes.',
'string' => 'The :attribute must be greater than or equal :value characters.',
'array' => 'The :attribute must have :value items or more.',
],
'image' => 'The :attribute must be an image.',
'in' => 'The selected :attribute is invalid.',
'in_array' => 'The :attribute field does not exist in :other.',
'integer' => 'The :attribute must be an integer.',
'ip' => 'The :attribute must be a valid IP address.',
'ipv4' => 'The :attribute must be a valid IPv4 address.',
'ipv6' => 'The :attribute must be a valid IPv6 address.',
'json' => 'The :attribute must be a valid JSON string.',
'lt' => [
'numeric' => 'The :attribute must be less than :value.',
'file' => 'The :attribute must be less than :value kilobytes.',
'string' => 'The :attribute must be less than :value characters.',
'array' => 'The :attribute must have less than :value items.',
],
'lte' => [
'numeric' => 'The :attribute must be less than or equal :value.',
'file' => 'The :attribute must be less than or equal :value kilobytes.',
'string' => 'The :attribute must be less than or equal :value characters.',
'array' => 'The :attribute must not have more than :value items.',
],
'max' => [
'numeric' => 'The :attribute may not be greater than :max.',
'file' => 'The :attribute may not be greater than :max kilobytes.',
'string' => 'The :attribute may not be greater than :max characters.',
'array' => 'The :attribute may not have more than :max items.',
],
'mimes' => 'The :attribute must be a file of type: :values.',
'mimetypes' => 'The :attribute must be a file of type: :values.',
'min' => [
'numeric' => 'O campo :attribute precisa ter pelo menos :min.',
'file' => 'The :attribute must be at least :min kilobytes.',
'string' => 'O campo :attribute precisa ter pelo menos :min caracteres.',
'array' => 'The :attribute must have at least :min items.',
],
'not_in' => 'The selected :attribute is invalid.',
'not_regex' => 'The :attribute format is invalid.',
'numeric' => 'The :attribute must be a number.',
'present' => 'The :attribute field must be present.',
'regex' => 'The :attribute format is invalid.',
'required' => 'The :attribute field is required.',
'required_if' => 'The :attribute field is required when :other is :value.',
'required_unless' => 'The :attribute field is required unless :other is in :values.',
'required_with' => 'The :attribute field is required when :values is present.',
'required_with_all' => 'The :attribute field is required when :values is present.',
'required_without' => 'The :attribute field is required when :values is not present.',
'required_without_all' => 'The :attribute field is required when none of :values are present.',
'same' => 'The :attribute and :other must match.',
'size' => [
'numeric' => 'The :attribute must be :size.',
'file' => 'The :attribute must be :size kilobytes.',
'string' => 'The :attribute must be :size characters.',
'array' => 'The :attribute must contain :size items.',
],
'string' => 'The :attribute must be a string.',
'timezone' => 'The :attribute must be a valid zone.',
'unique' => 'The :attribute has already been taken.',
'uploaded' => 'The :attribute failed to upload.',
'url' => 'The :attribute format is invalid.',
/*
|--------------------------------------------------------------------------
| Custom Validation Language Lines
|--------------------------------------------------------------------------
|
| Here you may specify custom validation messages for attributes using the
| convention "attribute.rule" to name the lines. This makes it quick to
| specify a specific custom language line for a given attribute rule.
|
*/
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
/*
|--------------------------------------------------------------------------
| Custom Validation Attributes
|--------------------------------------------------------------------------
|
| The following language lines are used to swap attribute place-holders
| with something more reader friendly such as E-Mail Address instead
| of "email". This simply helps us make messages a little cleaner.
|
*/
'attributes' => [
'password' => 'senha'
],
];

View File

@@ -0,0 +1,10 @@
<?php
return [
'Home' => 'Início',
'Login' => 'Login',
'Register' => 'Cadastrar',
'Products' => 'Produtos',
'Help' => 'Ajuda',
'copyright' => '© 2018 Bruno Fontes Todos os direitos reservados',
'byAuthor' => 'Por Bruno Fontes',
];

View File

@@ -14,8 +14,10 @@
</div>
@endif
<form class="d-inline" method="POST" action="{{ route('verification.resend') }}">
@csrf
{{ __('Before proceeding, please check your email for a verification link.') }}
{{ __('If you did not receive the email') }}, <a href="{{ route('verification.resend') }}">{{ __('click here to request another') }}</a>.
{{ __('If you did not receive the email') }}, <button type="submit" class="btn btn-link p-0 m-0 align-baseline">{{ __('click here to request another') }}</button>.
</div>
</div>
</div>

View File

@@ -1,12 +1,18 @@
@component('mail::message')
Hi, {{$username}},
&nbsp;<br>
&nbsp;<br>
#Good news: {{$item->name}} is available!
&nbsp;<br>
The item <em>{{$item->name}} ({{$item->product->name}})</em> is now available on **Share&nbsp;It**.
&nbsp;<br>
&nbsp;<br>
<a href="https://shareit.brunofontes.net/home">Take it</a> before anyone else.
{!! __('Hi, :username,', ['username' => $username]) !!}
#{!! __('Good news: :itemname is available!', ['itemname' => $item->name]) !!}
{!! __('The item <em>:itemname (:productname)</em> is now available on **Share&nbsp;It**.', ['itemname' => $item->name, 'productname' => $item->product->name]) !!}
{!! __('**Take It** before anyone else at the website:') !!}
@component('mail::button', ['url' => 'https://shareit.brunofontes.net/home'])
{{ config('app.name') }}
@endcomponent
@endcomponent

View File

@@ -1,14 +1,18 @@
@component('mail::message')
Hello, {{$userWithItem}},
{!! __('Hi, :username,', ['username' => $userWithItem]) !!}
Are you still using {{$item->name}}?
We just want to let you know that {{$waitingUser}} asked us to be alerted when this item were available.
{!! __('Are you still using :itemname?', ['itemname' => $item->name]) !!}
{!! __('We just want to let you know that :waitinguser asked us to be alerted when this item were available.', ['waitinguser' => $waitingUser]) !!}
{!! __('So, if you are not using it anymore, please **Return It** at the website:') !!}
So, if you are not using it anymore, please go to our site and...
@component('mail::button', ['url' => 'https://shareit.brunofontes.net/home'])
Return it!
Share It!
@endcomponent
Thanks,<br>

View File

@@ -1,14 +1,17 @@
@component('mail::message')
# Welcome, {{$user->name}},
# @lang('Welcome, :username,', ['username' => $user->name])
Thank's for registering at **Share&nbsp;It!**
Your account was created and it is active. And you? Are you ready to Share It with your friends? :)
@lang("Thank's for registering at **Share It!**")
@lang("Your account was created, but you still need to activate it. We've sent you another e-mail and we are ready to go!")
@lang("And you? Are you ready to Share It with your friends?")
@component('mail::button', ['url' => 'https://shareit.brunofontes.net'])
Share it!
@endcomponent
Thanks,<br>
{{ config('app.name') }}
@endcomponent
@endcomponent

View File

@@ -4,26 +4,16 @@
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="row"><h1>Help...</h1></div>
<div class="row"><h1>{!! __('help.helpTitle') !!}</h1></div>
<div class="card">
<div class="card-header"><strong>What is it?</strong></div>
<div class="card-header"><strong>{!! __('help.whatIsIt') !!}</strong></div>
<div class="card-body">
<p>
<strong>Share&nbsp;It!</strong> is a site dedicated to help you sharing items with others.
</p>
<p>But before sharing anything, you just need to understand 2 basic ideas:</p>
{!! __('help.siteHelpOthers') !!}
<p class="my-4"><ul>
<li><strong>Product</strong> - The category that has some similar items</li>
<li><strong>Item</strong> - The item that will be shared. Every item belongs to a <strong>Product</strong></li>
{!! __('help.product_item') !!}
</ul></p>
<p class="my-4"><strong><em>Examples:</em></strong> You can create the following Categories/Items</p>
<p>
<ul>
<li>Books -> Don Quixote, The Hitchhiker's Guide to the Galaxy</li>
<li>Matrix Movies -> Matrix 1, Matrix 2, Matrix 3</li>
<li>Website Y -> <em>YourAccount</em>*</li>
</ul>
<p><em>* Please note that many websites does not allow sharing your account. Read the site EULA before include it here.</em></p>
<p class="my-4">
{!! __('help.examples') !!}
</p>
</div>
</div>
@@ -32,31 +22,24 @@
<div class="row justify-content-center mt-4">
<div class="col-md-8">
<div class="card">
<div class="card-header"><strong>Step-by-step</strong></div>
<div class="card-header"><strong>{!! __('help.step_title') !!}</strong></div>
<div class="card-body">
<div>
<strong>Sharing a product/item</strong>
<strong>{!! __('help.step_sharing_subtitle') !!}</strong>
<ol>
<li>Go to the <strong><a href="/product">Products</a></strong> page;</li>
<li>Include the Product and click on it;</li>
<li>Include an Item that belongs to that Product and click on it;</li>
<li>Add other people with their subscribed e-mail address.</li>
{!! __('help.step_sharing_steps') !!}
</ol>
</div>
<div>
<strong>Using a shared item</strong>
<strong>{!! __('help.step_sharedItem_subtitle') !!}</strong>
<ol>
<li>Go to the <strong><a href="/home">Home</a></strong> page (tip: add this page as a bookmark);</li>
<li>Click on <strong>Take</strong> to take the item you want to use;</li>
<li>When you finish using it, click on <strong>Return</strong> button.</li>
{!! __('help.step_sharedItem_steps') !!}
</ol>
</div>
<div>
<strong>Getting alerted when an item is available</strong>
<strong>{!! __('help.step_getAlerted_subtitle') !!}</strong>
<ol>
<li>Go to the <strong><a href="/home">Home</a></strong> page (tip: add this page as a bookmark);</li>
<li>Click on <strong>Alert me</strong> next the taken item;</li>
<li>The active user will be alerted you want to use it later. When the person return it, you will be notified.</li>
{!! __('help.step_getAlerted_steps') !!}
</ol>
</div>
</div>
@@ -66,23 +49,16 @@
<div class="row justify-content-center mt-4">
<div class="col-md-8">
<div class="card">
<div class="card-header"><strong>Screens</strong></div>
<div class="card-header"><strong>{!! __('help.screens_title') !!}</strong></div>
</div>
</div>
</div>
<div class="row justify-content-center mt-4">
<div class="col-md-8">
<div class="card">
<div class="card-header"><strong>Home</strong></div>
<div class="card-header"><strong>{!! __('help.home_title') !!}</strong></div>
<div class="card-body">
<p>The <strong>Home</strong> is the main screen where you will going to <strong>Take</strong>
and <strong>Return</strong> items.</p>
<p>It shows all items that were shared with you or that you are sharing, unless you remove yourself from there.</p>
<p>If the item has a website, the items name will become a link, so you can just click on it to open.</p>
<p>When the item you want is already taken, you are going to see who got it and for how long.
So you can identify if the person is still using it or if someone just forgot to return it.</p>
<p>You can also ask to be alerted when the item is available. This will notify the active
user that you want to use that item. So the person can return it as soon as they finish using it.</p>
{!! __('help.home_body') !!}
</div>
</div>
</div>
@@ -90,12 +66,9 @@
<div class="row justify-content-center mt-4">
<div class="col-md-8">
<div class="card">
<div class="card-header"><strong>Products</strong></div>
<div class="card-header"><strong>{!! __('help.products_title') !!}</strong></div>
<div class="card-body">
<p>The <strong>Products</strong> is the screen to include Products and Items.</p>
<p>You can also <strong>add users</strong> to your items from this screen.
To be able to do that you just need to select the item.</p>
<p class="mb-4">When adding a Product, you can specify a webpage (this is optional).</p>
{!! __('help.products_body') !!}
</div>
</div>
</div>

View File

@@ -1,44 +1,55 @@
@extends('layouts.app')
@section('content')
<script type="text/javascript">
setInterval(
function() {
if (!document.hasFocus() ) {
window.location.reload(true);
}
},
2*60000); //NOTE: period is passed in milliseconds
</script>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Your items</div>
@forelse ($products as $items)
<div class="card mb-4">
<div class="card-header">{{$items->first()->product->name}}</div>
<div class="card-body">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@forelse ($items->sortBy('name', SORT_NATURAL | SORT_FLAG_CASE) as $item)
@if (!$loop->first)
<hr class="m-3">
@endif
@forelse ($items as $item)
@if (!$loop->first)
<hr class="m-3">
@endif
<div class="my-4 p-2">
<div class="row align-items-center p-2">
<div class="col col-xs-auto">
@if ($item->product->url)
<a href="{{$item->product->url}}" class="link-unstyled" target="_blank" rel="noopener noreferrer">
@endif
<a href="{{$item->product->url}}" class="link-unstyled" target="_blank" rel="noopener noreferrer">
@endif
<strong>{{$item->name}}</strong> ({{$item->product->name}})
<strong>{{$item->name}}</strong>
@if ($item->product->url)
</a>
@endif
@if ($item->used_by)
@include('home.usedItem')
@else
@include('home.unusedItem')
@if ($item->product->url)
</a>
@endif
</div>
<div class="ml-auto align-self-end text-right">
@if ($item->used_by)
@include('home.usedItem')
@else
@include('home.unusedItem')
@endif
</div>
</div>
@empty
<p>There are no items shared with you yet. <a href="/product">Share an item!</a></p>
@endforelse
</div>
</div>
@empty
<p>@lang('home.no_messages') <a href="/product">@lang('home.share_item')</a></p>
@endforelse
</div>
</div>
</div>

View File

@@ -1,7 +1,5 @@
<span class="float-right">
<form action="/take" method="POST">
{{ csrf_field() }}
<input type="hidden" name="item" value="{{$item->id}}">
<button type="submit" class="btn btn-sm btn-success">Take It</button>
</form>
</span>
<form action="/take" method="POST">
{{ csrf_field() }}
<input type="hidden" name="item" value="{{$item->id}}">
<button type="submit" class="btn btn-sm btn-success">@lang('home.take')</button>
</form>

View File

@@ -1,35 +1,31 @@
@if ($item->used_by == \Auth::id())
<span class="float-right">
<form action="/take" method="POST">
{{ csrf_field() }}
@method('DELETE')
<input type="hidden" name="item" value="{{$item->id}}">
<button type="submit" class="btn btn-sm btn-danger">Return It</button>
</form>
</span>
<span class="float-right mr-3"><em>{{\Carbon\Carbon::parse($item->updated_at)->diffForHumans()}}</em></span>
<form action="/take" method="POST" class="form-inline">
<em class="pr-sm-2 ml-auto">{{\Carbon\Carbon::parse($item->updated_at)->diffForHumans()}}</em>
<div class="w-100 d-xm-block d-sm-none"></div>
{{ csrf_field() }}
@method('DELETE')
<input type="hidden" name="item" value="{{$item->id}}">
<button type="submit" class="btn btn-sm btn-danger ml-auto">@lang('home.return')</button>
</form>
@else
<span class="float-right">
<form action="/alert" method="POST">
{{ csrf_field() }}
<input type="hidden" name="item" value="{{$item->id}}">
@if ($item->waiting_user_id == \Auth::id())
@method('DELETE')
<button type="submit" class="btn btn-sm btn-outline-danger">Cancel Alert</button>
@elseif (!$item->waiting_user_id)
<button type="submit" class="btn btn-sm btn-outline-secondary">Alert me</button>
@endif
</form>
</span>
<span class="float-right mr-3">
<em>
{{str_limit($users[$item->used_by], 15, '...')}}
@if ($item->waiting_user_id && $item->waiting_user_id != \Auth::id())
<strong>> {{str_limit($users[$item->waiting_user_id], 15, '...')}}</strong>
@endif
<small>({{$item->updated_at->diffForHumans()}})</small>
</em>
</span>
<form action="/alert" method="POST" class="form-inline">
<em class="pr-sm-2 ml-auto">
{{str_limit($users[$item->used_by], 15, '...')}}
@if ($item->waiting_user_id && $item->waiting_user_id != \Auth::id())
<strong>> {{str_limit($users[$item->waiting_user_id], 15, '...')}}</strong>
@endif
<small>({{$item->updated_at->diffForHumans()}})</small>
</em>
<div class="w-100 d-xm-block d-sm-none"></div>
{{ csrf_field() }}
<input type="hidden" name="item" value="{{$item->id}}">
@if ($item->waiting_user_id == \Auth::id())
@method('DELETE')
<button type="submit" class="btn btn-sm btn-outline-danger ml-auto">@lang('home.cancel_alert')</button>
@elseif (!$item->waiting_user_id)
<button type="submit" class="btn btn-sm btn-outline-secondary ml-auto">@lang('home.alert_me')</button>
@endif
</form>
@endif

View File

@@ -29,7 +29,7 @@
@include ('item.otherItems')
<div class="float-right mt-2"><a class="btn btn-secondary" href="/product/{{ $item->product->id }}">BACK</a></div>
<div class="float-right mt-2"><a class="btn btn-secondary" href="/product/{{ $item->product->id }}">{{ __('item.back') }}</a></div>
</div>
</div>
</div>

View File

@@ -5,22 +5,22 @@
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="deleteModalLabel">Confirmation...</h5>
<h5 class="modal-title" id="deleteModalLabel">{{ __('item.confirmation') }}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>Would you like to delete the item <strong>{{$item->name}}</strong>?</p>
<p>You will not be able to restore it after deletion.</p>
<p>{!! __('item.confirmDeletion', ['itemname' => $item->name]) !!}</p>
<p>{!! __('item.notAbleRestore') !!}</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ __('item.close') }}</button>
<form action="/item/" method="POST">
@method('DELETE')
{{ csrf_field() }}
<input type="hidden" name="item" value="{{$item->id}}">
<button type="submit" class="btn btn-danger">Delete</button>
<button type="submit" class="btn btn-danger">{{ __('item.delete') }}</button>
</form>
</div>
</div>

View File

@@ -1,11 +1,11 @@
<button type="button" class="btn btn-sm btn-secondary mr-1" data-toggle="modal" data-target="#editModal">Edit</button>
<button type="button" class="btn btn-sm btn-secondary mr-1" data-toggle="modal" data-target="#editModal">{{ __('item.edit') }}</button>
<!-- MODAL - CHANGE WINDOW -->
<div class="modal fade" id="editModal" tabindex="-1" role="dialog" aria-labelledby="editModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="editModalLabel">Edit item</h5>
<h5 class="modal-title" id="editModalLabel">{{ __('item.edititem') }}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
@@ -14,7 +14,7 @@
<form action="/item" method="POST">
<div class="modal-body">
<div class="form-group">
<label for="name" class="col-form-label">New name: </label>
<label for="name" class="col-form-label">{{ __('item.newname') }} </label>
<input type="text" name="name" id="name" class="form-control" value="{{$item->name}}">
</div>
</div>
@@ -22,8 +22,8 @@
@method('PATCH')
{{ csrf_field() }}
<input type="hidden" name="item" value="{{$item->id}}">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-danger">Edit</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ __('item.close') }}</button>
<button type="submit" class="btn btn-danger">{{ __('item.edit') }}</button>
</div>
</form>

View File

@@ -1,6 +1,6 @@
<div class="card mt-4">
<div class="card-header">
Other items from the same product
{{ __('item.otherItems') }}
</div>
<div class="card-body">
@@ -10,7 +10,7 @@
<li><a href="/item/{{ $otherItem->id }}">{{ $otherItem->name }}</a></li>
@endif
@empty
<p>There are no items yet. Include one with the form above.</p>
<p>{{ __('item.noItems') }}</p>
@endforelse
</ul>
</div>

View File

@@ -1,4 +1,4 @@
<p><strong>Users:</strong></p>
<p><strong>{{ __('item.users') }}</strong></p>
@forelse ($users as $user)
@if (!$loop->first)
<hr>
@@ -14,27 +14,27 @@
@method('DELETE')
<input type="hidden" name="item_id" id="item_id" value="{{ $item['id'] }}">
<input type="hidden" class="form-control" name="user_id" id="user_id" value="{{$user->id}}">
<button type="submit" class="btn btn-sm btn-danger">Delete</button>
<button type="submit" class="btn btn-sm btn-danger">{{ __('item.delete') }}</button>
</div>
</div>
</div>
</form>
@empty
<p>There are no items yet. Include one with the form above.</p>
<p>{{ __('item.noItems') }}</p>
@endforelse
<!-- ADD USERS -->
<hr class="mt-5">
<p><strong>Add user</strong></p>
<p><strong>{{ __('item.addUser') }}</strong></p>
<form method="POST" action="/user">
<div class="container-fluid">
<div class="form-group row mt-2">
{{ csrf_field() }}
<div class="col-sm-2 col-lg-auto"><label for="name">E-mail: </label></div>
<div class="col-xs-12 col-sm mb-3"><input type="email" class="form-control" name="email" id="email" placeholder="name@domain.com" required></div>
<div class="col-sm-2 col-lg-auto"><label for="name">{{ __('item.email') }}</label></div>
<div class="col-xs-12 col-sm mb-3"><input type="email" class="form-control" name="email" id="email" placeholder="{{ __('item.nameDomain') }}" required></div>
<input type="hidden" name="item_id" id="item_id" value="{{ $item['id'] }}" required>
<div class="col-sm-2 col-lg-1 mr-4"><button type="submit" class="btn btn-primary">Insert</button></div>
<div class="col-sm-2 col-lg-1 mr-4"><button type="submit" class="btn btn-primary">{{ __('item.insert') }}</button></div>
</div>
</div>
</form>

View File

@@ -1,5 +1,6 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
@@ -7,7 +8,7 @@
<!-- CSRF Token -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel') }}</title>
<title>{{ config('app.name', 'Laravel') }} {{ isset($usedItems) && $usedItems > 0 ? "(${usedItems})" : '' }}</title>
<!-- Scripts -->
<script src="{{ asset('js/app.js') }}" defer></script>
@@ -19,6 +20,7 @@
<!-- Styles -->
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body>
<div id="app">
<nav class="navbar navbar-expand-md navbar-light navbar-laravel">
@@ -26,7 +28,8 @@
<a class="navbar-brand" href="{{ url('/') }}">
{{ config('app.name', 'Laravel') }}
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
<span class="navbar-toggler-icon"></span>
</button>
@@ -40,39 +43,49 @@
<ul class="navbar-nav ml-auto">
<!-- Authentication Links -->
@guest
<li class="nav-item">
<a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
</li>
@else
<li class="nav-item dropdown">
<a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
{{ Auth::user()->name }} <span class="caret"></span>
<li class="nav-item dropdown">
<a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false" v-pre>
{{ Auth::user()->name }} <span class="caret"></span>
</a>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="{{ route('logout') }}" onclick="event.preventDefault();
document.getElementById('logout-form').submit();">
{{ __('Logout') }}
</a>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="{{ route('logout') }}"
onclick="event.preventDefault();
document.getElementById('logout-form').submit();">
{{ __('Logout') }}
</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
@csrf
</form>
</div>
</li>
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
@csrf
</form>
</div>
</li>
@endguest
</ul>
</div>
</div>
</nav>
<main class="py-4">
@if ($flashMsg = session('primary'))
<div class="alert alert-primary text-center" role="alert">{{ $flashMsg }}</div>
@endif
@if ($flashMsg = session('danger'))
<div class="alert alert-danger text-center" role="alert">{{ $flashMsg }}</div>
@endif
<main class="py-4 mb-5">
@yield('content')
</main>
@include('layouts.footer')
</div>
</body>
</html>

View File

@@ -0,0 +1,20 @@
<div class="my-4"><br></div>
<!-- Footer -->
<footer class="fixed-bottom page-footer font-small mt-5">
<div class="row footer-copyright text-left bg-secondary text-white mt-5 py-3">
<!-- Copyright -->
<div class="col ml-4">
© 2018<?='2018' != date('Y')?'-' . date('Y'):'';?> Copyright&nbsp; <a href="https://brunofontes.net" class="link text-white-50" target="_blank">Bruno Fontes</a>
</div>
<div class="col mr-4 text-right">
<a href="{{ url('/lang/pt-br') }}" class="link text-white">Português</a>
<span class="d-none d-sm-inline"> | </span>
<span class="d-xs-block d-sm-none"> <br> </span>
<a href="{{ url('/lang/en') }}" class="link text-white">English</a>
</div>
<!-- Copyright -->
</div>
</footer>
@include('layouts.tracker')

View File

@@ -0,0 +1,3 @@
<script type="text/javascript">
var owa_baseUrl='https://brunofontes.net/owa/';var owa_cmds=owa_cmds||[];owa_cmds.push(['setSiteId','15a38975230dfe7528d647a1419be7f7']);owa_cmds.push(['trackPageView']);owa_cmds.push(['trackClicks']);owa_cmds.push(['trackDomStream']);(function(){var _owa=document.createElement('script');_owa.type='text/javascript';_owa.async=true;owa_baseUrl=('https:'==document.location.protocol?window.owa_baseSecUrl||owa_baseUrl.replace(/http:/,'https:'):owa_baseUrl);_owa.src=owa_baseUrl+'modules/base/js/owa.tracker-combined-min.js';var _owa_s=document.getElementsByTagName('script')[0];_owa_s.parentNode.insertBefore(_owa,_owa_s)}());
</script>

View File

@@ -3,10 +3,10 @@
<div class="container-fluid">
<div class="form-group row mt-2">
<div class="col-sm-2 col-lg-1"><label for="product">Item:</label></div>
<div class="col-xs-12 col-sm mb-3"><input type="text" class="form-control" name="item" id="item" placeholder="One Hundred Years of Solitude" required></div>
<div class="col-sm-2 col-lg-1"><label for="product">{{ __('product.item') }}</label></div>
<div class="col-xs-12 col-sm mb-3"><input type="text" class="form-control" name="item" id="item" placeholder="{{ __('product.100yearsSolitude') }}" required></div>
<input type="hidden" name="product_id" id="product_id" value="{{ $product['id'] }}" required>
<div class="col-sm-2 col-lg-1 mr-4"><button type="submit" class="btn btn-primary">Insert</button></div>
<div class="col-sm-2 col-lg-1 mr-4"><button type="submit" class="btn btn-primary">{{ __('product.insert') }}</button></div>
</div>
</div>

View File

@@ -2,16 +2,16 @@
{{ csrf_field() }}
<div class="container-fluid">
<div class="form-group row mt-2">
<div class="col-sm-2 col-lg-1"><label for="product">Name:</label></div>
<div class="col-xs-12 col-sm"><input type="text" class="form-control" name="product" id="product" placeholder="Book" required></div>
<div class="col-sm-2 col-lg-1"><label for="product">{{ __('product.name') }}</label></div>
<div class="col-xs-12 col-sm"><input type="text" class="form-control" name="product" id="product" placeholder="{{ __('product.book') }}" required></div>
</div>
<div class="form-group row mt-2">
<div class="col-sm-2 col-lg-1"><label for="product">URL:</label></div>
<div class="col-xs-12 col-sm"><input type="text" class="form-control" name="url" id="url" placeholder="(Optional) http://bookwebsite.com"></div>
<div class="col-sm-2 col-lg-1"><label for="product">{{ __('product.url') }}</label></div>
<div class="col-xs-12 col-sm"><input type="text" class="form-control" name="url" id="url" placeholder="{{ __('product.optionalUrlExample') }}"></div>
</div>
<div class="form-group row mt-2">
<div class="col-xs-0 col-sm-1 col-md-1 col-lg-1 col-xg-1"></div>
<div class="col col-xs-12"><button type="submit" class="btn btn-primary">Insert</button></div>
<div class="col col-xs-12"><button type="submit" class="btn btn-primary">{{ __('product.insert') }}</button></div>
</div>
</div>
@include ('layouts.errors')

View File

@@ -1,26 +1,26 @@
<button type="button" class="btn btn-sm btn-danger" data-toggle="modal" data-target="#deleteModal">Delete</button>
<button type="button" class="btn btn-sm btn-danger" data-toggle="modal" data-target="#deleteModal">{{ __('product.delete') }}</button>
<!-- MODAL - DELETE CONFIRMATION -->
<div class="modal fade" id="deleteModal" tabindex="-1" role="dialog" aria-labelledby="deleteModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="deleteModalLabel">Confirmation...</h5>
<h5 class="modal-title" id="deleteModalLabel">{{ __('product.confirmation') }}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
Would you like to delete the product <strong>{{$product->name}}</strong> and it's items?
<p>You will not be able to restore it after deletion.</p>
{!! __('product.wannaDelete', ['productname' => $product->name]) !!}
<p>{{ __('product.notAbleRestore') }}</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ __('product.close') }}</button>
<form action="/product" method="POST">
@method('DELETE')
{{ csrf_field() }}
<input type="hidden" name="product" value="{{$product->id}}">
<button type="submit" class="btn btn-danger">Delete</button>
<button type="submit" class="btn btn-danger">{{ __('product.delete') }}</button>
</form>
</div>
</div>

View File

@@ -1,11 +1,11 @@
<button type="button" class="btn btn-sm btn-secondary mr-1" data-toggle="modal" data-target="#editModal">Edit</button>
<button type="button" class="btn btn-sm btn-secondary mr-1" data-toggle="modal" data-target="#editModal">{{ __('product.edit') }}</button>
<!-- MODAL - CHANGE WINDOW -->
<div class="modal fade" id="editModal" tabindex="-1" role="dialog" aria-labelledby="editModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="editModalLabel">Edit product</h5>
<h5 class="modal-title" id="editModalLabel">{{ __('product.editProduct') }}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
@@ -14,11 +14,11 @@
<form action="/product" method="POST">
<div class="modal-body">
<div class="form-group">
<label for="name" class="col-form-label">New name: </label>
<label for="name" class="col-form-label">{{ __('product.newName') }} </label>
<input type="text" name="name" id="name" class="form-control" value="{{$product->name}}">
</div>
<div class="form-group">
<label for="url" class="col-form-label">New url: </label>
<label for="url" class="col-form-label">{{ __('product.newUrl') }} </label>
<input type="text" name="url" id="url" class="form-control" value="{{$product->url}}">
</div>
</div>
@@ -26,8 +26,8 @@
@method('PATCH')
{{ csrf_field() }}
<input type="hidden" name="product" value="{{$product->id}}">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-danger">Edit</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ __('product.close') }}</button>
<button type="submit" class="btn btn-danger">{{ __('product.edit') }}</button>
</div>
</form>

View File

@@ -5,7 +5,7 @@
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card mt-4">
<div class="card-header"><strong>Products</strong></div>
<div class="card-header"><strong>{{ __('product.products') }}</strong></div>
<div class="card-body">
@if (session('status'))
@@ -18,17 +18,17 @@
@forelse ($products as $product)
<li><a href="/product/{{$product['id']}}">{{$product['name']}}</a></li>
@empty
<p>There are no products yet. Include one with the form above.</p>
<p>{{ __('product.noProductsYet') }}</p>
@endforelse
</ul>
</div>
</div>
<div class="card mt-4">
<div class="card-header">Add product</div>
<div class="card-header">{{ __('product.addProduct') }}</div>
<div class="card-body">@include('product.addProductForm')</div>
</div>
<div class="float-right mt-2"><a class="btn btn-secondary" href="/home">Your items</a></div>
<div class="float-right mt-2"><a class="btn btn-secondary" href="/home">{{ __('product.yourItems') }}</a></div>
</div>
</div>
</div>

View File

@@ -25,23 +25,23 @@
</div>
@endif
<strong>Items:</strong>
<strong>{{ __('product.items') }}</strong>
<ul>
@forelse ($product->items as $item)
<li><a href="/item/{{ $item->id }}">{{ $item->name }}</a></li>
@empty
<p>There are no items yet. Include one with the form above.</p>
<p>{{ __('product.noItemsYet') }}</p>
@endforelse
</ul>
</div>
</div>
<div class="card mt-4">
<div class="card-header">Add item</div>
<div class="card-header">{{ __('product.addItem') }}</div>
<div class="card-body">@include('product.addItemForm')</div>
</div>
<div class="float-right mt-2"><a class="btn btn-secondary" href="/product">BACK</a></div>
<div class="float-right mt-2"><a class="btn btn-secondary" href="/product">{{ __('product.back') }}</a></div>
</div>
</div>
</div>

View File

@@ -79,10 +79,10 @@
@if (Route::has('login'))
<div class="top-right links">
@auth
<a href="{{ url('/home') }}">Home</a>
<a href="{{ url('/home') }}">@lang('welcome.Home')</a>
@else
<a href="{{ route('login') }}">Login</a>
<a href="{{ route('register') }}">Register</a>
<a href="{{ route('login') }}">@lang('welcome.Login')</a>
<a href="{{ route('register') }}">@lang('welcome.Register')</a>
@endauth
</div>
@endif
@@ -94,22 +94,26 @@
<div class="links">
@auth
<a href="/home">Home</a>
<a href="/product">Products</a>
<a href="/home">@lang('welcome.Home')</a>
<a href="/product">@lang('welcome.Products')</a>
@else
<a href="/register">Register</a>
<a href="/login">Login</a>
<a href="/login">@lang('welcome.Login')</a>
<a href="/register">@lang('welcome.Register')</a>
@endauth
<a href="/help">Help</a>
<a href="/help">@lang('welcome.Help')</a>
</div>
<div>
<br>
<div class="mt-5 mb-5"><br></div>
<div class="links">
<a href="{{ url('/lang/pt-br') }}">Português</a>
<a href="{{ url('/lang/en') }}">English</a>
</div>
<div class="mt-5 mb-5"><br></div>
<div class="links footer">
<a href="https://brunofontes.net">By Bruno Fontes</a>
<p>© 2018 Bruno Fontes All Rights Reserved</p>
<a href="https://brunofontes.net">@lang('welcome.byAuthor')</a>
<p>@lang('welcome.copyright')</p>
</div>
</div>
</div>
@include('layouts.tracker')
</body>
</html>

View File

@@ -15,7 +15,9 @@ Route::get('/', function () {
return view('welcome');
});
Route::get('/product', 'ProductController@index')->middleware('auth');
Route::get('/lang/{locale}', 'LanguageController@update')->name('language');
Route::get('/product', 'ProductController@index')->middleware('verified');
Route::get('/product/{product}', 'ProductController@show')->middleware('verified');
Route::post('/product', 'ProductController@store')->middleware('verified');
Route::patch('/product', 'ProductController@patch')->middleware('verified');

1
ssh_homestead.sh Normal file
View File

@@ -0,0 +1 @@
ssh homestead -t "cd code/Bruno\ Fontes/shareit; bash --login"

View File

@@ -1,3 +1,4 @@
#source ~/.zshrc
vagrant up
./ssh_homestead.sh
#!/bin/zsh
builtin cd ~/development/laravelHomestead && /usr/bin/vagrant up && builtin cd -
~/Apps/firefox/firefox-bin http://shareit.test
~/Apps/firefox/firefox-bin https://mailtrap.io/inboxes/455614/messages

675749
tags Normal file

File diff suppressed because it is too large Load Diff

1
updateCompserNPM.sh Normal file
View File

@@ -0,0 +1 @@
ssh homestead -t 'cd code/Bruno\ Fontes/shareit; echo; echo -n "Press enter to update Composer..."; read; composer update; echo; echo -n "Press enter to update NPM..."; read; npm update; echo; echo -n "Press enter to exit..."; read'

5923
yarn.lock

File diff suppressed because it is too large Load Diff