Initial commit
This commit is contained in:
parent
d0531481eb
commit
14192d0e46
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"plugins": ["@babel/plugin-syntax-dynamic-import"]
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
module.exports = {
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:vue/recommended',
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
"indent": ['error', 2],
|
||||||
|
'quotes': ['warn', 'single'],
|
||||||
|
'semi': ['warn', 'never'],
|
||||||
|
'comma-dangle': ['warn', 'always-multiline'],
|
||||||
|
'vue/max-attributes-per-line': false,
|
||||||
|
'vue/require-default-prop': false,
|
||||||
|
'vue/singleline-html-element-content-newline': false,
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,14 @@
|
||||||
/node_modules
|
/node_modules
|
||||||
|
/public/css
|
||||||
/public/hot
|
/public/hot
|
||||||
|
/public/js
|
||||||
|
/public/mix-manifest.json
|
||||||
/public/storage
|
/public/storage
|
||||||
/storage/*.key
|
/storage/*.key
|
||||||
/vendor
|
/vendor
|
||||||
|
.DS_Store
|
||||||
.env
|
.env
|
||||||
|
.php_cs.dist
|
||||||
.phpunit.result.cache
|
.phpunit.result.cache
|
||||||
Homestead.json
|
Homestead.json
|
||||||
Homestead.yaml
|
Homestead.yaml
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
|
class Account extends Model
|
||||||
|
{
|
||||||
|
public function users()
|
||||||
|
{
|
||||||
|
return $this->hasMany(User::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function organizations()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Organization::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function contacts()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Contact::class);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
|
class Contact extends Model
|
||||||
|
{
|
||||||
|
use SoftDeletes;
|
||||||
|
|
||||||
|
public function organization()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Organization::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getNameAttribute()
|
||||||
|
{
|
||||||
|
return $this->first_name.' '.$this->last_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function scopeOrderByName($query)
|
||||||
|
{
|
||||||
|
$query->orderBy('last_name')->orderBy('first_name');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function scopeFilter($query, array $filters)
|
||||||
|
{
|
||||||
|
$query->when($filters['search'] ?? null, function ($query, $search) {
|
||||||
|
$query->where(function ($query) use ($search) {
|
||||||
|
$query->where('first_name', 'ilike', '%'.$search.'%')
|
||||||
|
->orWhere('last_name', 'ilike', '%'.$search.'%')
|
||||||
|
->orWhere('email', 'ilike', '%'.$search.'%')
|
||||||
|
->orWhereHas('organization', function ($query) use ($search) {
|
||||||
|
$query->where('name', 'ilike', '%'.$search.'%');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// })->when($filters['role'] ?? null, function ($query, $role) {
|
||||||
|
// $query->whereRole($role);
|
||||||
|
})->when($filters['trashed'] ?? null, function ($query, $trashed) {
|
||||||
|
if ($trashed === 'with') {
|
||||||
|
$query->withTrashed();
|
||||||
|
} elseif ($trashed === 'only') {
|
||||||
|
$query->onlyTrashed();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,13 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers\Auth;
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use Inertia\Inertia;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\URL;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Support\Facades\Session;
|
||||||
|
use Illuminate\Support\Facades\Redirect;
|
||||||
|
use Illuminate\Support\Facades\Response;
|
||||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||||
|
|
||||||
class LoginController extends Controller
|
class LoginController extends Controller
|
||||||
|
@ -25,15 +31,22 @@ class LoginController extends Controller
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $redirectTo = '/home';
|
protected $redirectTo = '/';
|
||||||
|
|
||||||
/**
|
public function showLoginForm()
|
||||||
* Create a new controller instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
{
|
||||||
$this->middleware('guest')->except('logout');
|
return Inertia::render('Auth/Login', [
|
||||||
|
'intendedUrl' => Session::pull('url.intended', URL::route('dashboard')),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function authenticated(Request $request, $user)
|
||||||
|
{
|
||||||
|
return Response::json(['success' => true]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function loggedOut(Request $request)
|
||||||
|
{
|
||||||
|
return Redirect::route('login');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Contact;
|
||||||
|
use Inertia\Inertia;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Request;
|
||||||
|
|
||||||
|
class ContactsController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
return Inertia::render('Contacts/Index', [
|
||||||
|
'filters' => Request::all('search', 'trashed'),
|
||||||
|
'contacts' => Auth::user()->account->contacts()
|
||||||
|
->orderByName()
|
||||||
|
->filter(Request::only('search', 'trashed'))
|
||||||
|
->paginate()
|
||||||
|
->transform(function ($contact) {
|
||||||
|
return [
|
||||||
|
'id' => $contact->id,
|
||||||
|
'name' => $contact->name,
|
||||||
|
'phone' => $contact->phone,
|
||||||
|
'city' => $contact->city,
|
||||||
|
'deleted_at' => $contact->deleted_at,
|
||||||
|
'organization' => $contact->organization ? $contact->organization->only('name') : null,
|
||||||
|
];
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create()
|
||||||
|
{
|
||||||
|
return Inertia::render('Contacts/Create', [
|
||||||
|
'organizations' => Auth::user()->account
|
||||||
|
->organizations()
|
||||||
|
->orderBy('name')
|
||||||
|
->get()
|
||||||
|
->map
|
||||||
|
->only('id', 'name'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store()
|
||||||
|
{
|
||||||
|
return Auth::user()->account->contacts()->create(
|
||||||
|
Request::validate([
|
||||||
|
'first_name' => ['required', 'max:50'],
|
||||||
|
'last_name' => ['required', 'max:50'],
|
||||||
|
'organization_id' => ['nullable', Rule::exists('organizations', 'id')->where(function ($query) {
|
||||||
|
$query->where('account_id', Auth::user()->account_id);
|
||||||
|
})],
|
||||||
|
'email' => ['nullable', 'max:50', 'email'],
|
||||||
|
'phone' => ['nullable', 'max:50'],
|
||||||
|
'address' => ['nullable', 'max:150'],
|
||||||
|
'city' => ['nullable', 'max:50'],
|
||||||
|
'region' => ['nullable', 'max:50'],
|
||||||
|
'country' => ['nullable', 'max:2'],
|
||||||
|
'postal_code' => ['nullable', 'max:25'],
|
||||||
|
])
|
||||||
|
)->only('id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit(Contact $contact)
|
||||||
|
{
|
||||||
|
return Inertia::render('Contacts/Edit', [
|
||||||
|
'contact' => [
|
||||||
|
'id' => $contact->id,
|
||||||
|
'first_name' => $contact->first_name,
|
||||||
|
'last_name' => $contact->last_name,
|
||||||
|
'organization_id' => $contact->organization_id,
|
||||||
|
'email' => $contact->email,
|
||||||
|
'phone' => $contact->phone,
|
||||||
|
'address' => $contact->address,
|
||||||
|
'city' => $contact->city,
|
||||||
|
'region' => $contact->region,
|
||||||
|
'country' => $contact->country,
|
||||||
|
'postal_code' => $contact->postal_code,
|
||||||
|
'deleted_at' => $contact->deleted_at,
|
||||||
|
],
|
||||||
|
'organizations' => Auth::user()->account->organizations()
|
||||||
|
->orderBy('name')
|
||||||
|
->get()
|
||||||
|
->map
|
||||||
|
->only('id', 'name'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(Contact $contact)
|
||||||
|
{
|
||||||
|
$contact->update(
|
||||||
|
Request::validate([
|
||||||
|
'first_name' => ['required', 'max:50'],
|
||||||
|
'last_name' => ['required', 'max:50'],
|
||||||
|
'organization_id' => ['nullable', Rule::exists('organizations', 'id')->where(function ($query) {
|
||||||
|
$query->where('account_id', Auth::user()->account_id);
|
||||||
|
})],
|
||||||
|
'email' => ['nullable', 'max:50', 'email'],
|
||||||
|
'phone' => ['nullable', 'max:50'],
|
||||||
|
'address' => ['nullable', 'max:150'],
|
||||||
|
'city' => ['nullable', 'max:50'],
|
||||||
|
'region' => ['nullable', 'max:50'],
|
||||||
|
'country' => ['nullable', 'max:2'],
|
||||||
|
'postal_code' => ['nullable', 'max:25'],
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy(Contact $contact)
|
||||||
|
{
|
||||||
|
$contact->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function restore(Contact $contact)
|
||||||
|
{
|
||||||
|
$contact->restore();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Inertia\Inertia;
|
||||||
|
|
||||||
|
class DashboardController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
return Inertia::render('Dashboard/Index');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Inertia\Inertia;
|
||||||
|
use App\Organization;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Request;
|
||||||
|
|
||||||
|
class OrganizationsController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
return Inertia::render('Organizations/Index', [
|
||||||
|
'filters' => Request::all('search', 'role', 'trashed'),
|
||||||
|
'organizations' => Auth::user()->account->organizations()
|
||||||
|
->orderBy('name')
|
||||||
|
->filter(Request::only('search', 'role', 'trashed'))
|
||||||
|
->paginate()
|
||||||
|
->only('id', 'name', 'phone', 'city', 'deleted_at'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create()
|
||||||
|
{
|
||||||
|
return Inertia::render('Organizations/Create');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store()
|
||||||
|
{
|
||||||
|
return Auth::user()->account->organizations()->create(
|
||||||
|
Request::validate([
|
||||||
|
'name' => ['required', 'max:100'],
|
||||||
|
'email' => ['nullable', 'max:50', 'email'],
|
||||||
|
'phone' => ['nullable', 'max:50'],
|
||||||
|
'address' => ['nullable', 'max:150'],
|
||||||
|
'city' => ['nullable', 'max:50'],
|
||||||
|
'region' => ['nullable', 'max:50'],
|
||||||
|
'country' => ['nullable', 'max:2'],
|
||||||
|
'postal_code' => ['nullable', 'max:25'],
|
||||||
|
])
|
||||||
|
)->only('id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit(Organization $organization)
|
||||||
|
{
|
||||||
|
return Inertia::render('Organizations/Edit', [
|
||||||
|
'organization' => [
|
||||||
|
'id' => $organization->id,
|
||||||
|
'name' => $organization->name,
|
||||||
|
'email' => $organization->email,
|
||||||
|
'phone' => $organization->phone,
|
||||||
|
'address' => $organization->address,
|
||||||
|
'city' => $organization->city,
|
||||||
|
'region' => $organization->region,
|
||||||
|
'country' => $organization->country,
|
||||||
|
'postal_code' => $organization->postal_code,
|
||||||
|
'deleted_at' => $organization->deleted_at,
|
||||||
|
'contacts' => $organization->contacts()->orderByName()->get()->map->only('id', 'name', 'city', 'phone'),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(Organization $organization)
|
||||||
|
{
|
||||||
|
$organization->update(
|
||||||
|
Request::validate([
|
||||||
|
'name' => ['required', 'max:100'],
|
||||||
|
'email' => ['nullable', 'max:50', 'email'],
|
||||||
|
'phone' => ['nullable', 'max:50'],
|
||||||
|
'address' => ['nullable', 'max:150'],
|
||||||
|
'city' => ['nullable', 'max:50'],
|
||||||
|
'region' => ['nullable', 'max:50'],
|
||||||
|
'country' => ['nullable', 'max:2'],
|
||||||
|
'postal_code' => ['nullable', 'max:25'],
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy(Organization $organization)
|
||||||
|
{
|
||||||
|
$organization->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function restore(Organization $organization)
|
||||||
|
{
|
||||||
|
$organization->restore();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Inertia\Inertia;
|
||||||
|
|
||||||
|
class ReportsController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
return Inertia::render('Reports/Index');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\User;
|
||||||
|
use Inertia\Inertia;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Request;
|
||||||
|
|
||||||
|
class UsersController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
return Inertia::render('Users/Index', [
|
||||||
|
'filters' => Request::all('search', 'role', 'trashed'),
|
||||||
|
'users' => Auth::user()->account->users()
|
||||||
|
->orderByName()
|
||||||
|
->filter(Request::only('search', 'role', 'trashed'))
|
||||||
|
->get()
|
||||||
|
->transform(function ($user) {
|
||||||
|
return [
|
||||||
|
'id' => $user->id,
|
||||||
|
'name' => $user->name,
|
||||||
|
'email' => $user->email,
|
||||||
|
'owner' => $user->owner,
|
||||||
|
'deleted_at' => $user->deleted_at,
|
||||||
|
];
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create()
|
||||||
|
{
|
||||||
|
return Inertia::render('Users/Create');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store()
|
||||||
|
{
|
||||||
|
return Auth::user()->account->users()->create(
|
||||||
|
Request::validate([
|
||||||
|
'first_name' => ['required', 'max:50'],
|
||||||
|
'last_name' => ['required', 'max:50'],
|
||||||
|
'email' => ['required', 'max:50', 'email', Rule::unique('users')],
|
||||||
|
'password' => ['nullable'],
|
||||||
|
'owner' => ['required', 'boolean'],
|
||||||
|
])
|
||||||
|
)->only('id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit(User $user)
|
||||||
|
{
|
||||||
|
return Inertia::render('Users/Edit', [
|
||||||
|
'user' => [
|
||||||
|
'id' => $user->id,
|
||||||
|
'first_name' => $user->first_name,
|
||||||
|
'last_name' => $user->last_name,
|
||||||
|
'email' => $user->email,
|
||||||
|
'owner' => $user->owner,
|
||||||
|
'deleted_at' => $user->deleted_at,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(User $user)
|
||||||
|
{
|
||||||
|
Request::validate([
|
||||||
|
'first_name' => ['required', 'max:50'],
|
||||||
|
'last_name' => ['required', 'max:50'],
|
||||||
|
'email' => ['required', 'max:50', 'email', Rule::unique('users')->ignore($user->id)],
|
||||||
|
'password' => ['nullable'],
|
||||||
|
'owner' => ['required', 'boolean'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$user->update(Request::only('first_name', 'last_name', 'email', 'owner'));
|
||||||
|
|
||||||
|
if (Request::get('password')) {
|
||||||
|
$user->update(['password' => Request::get('password')]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy(User $user)
|
||||||
|
{
|
||||||
|
$user->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function restore(User $user)
|
||||||
|
{
|
||||||
|
$user->restore();
|
||||||
|
}
|
||||||
|
}
|
|
@ -59,6 +59,7 @@ class Kernel extends HttpKernel
|
||||||
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
||||||
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
|
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
|
||||||
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||||
|
'remember' => \App\Http\Middleware\RememberQueryStrings::class,
|
||||||
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
|
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
|
||||||
|
class RememberQueryStrings
|
||||||
|
{
|
||||||
|
public function handle($request, Closure $next)
|
||||||
|
{
|
||||||
|
if ($request->wantsJson()) {
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($request->all())) {
|
||||||
|
return $this->remembered($next, $request);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->get('remember') === 'no') {
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->get('remember') === 'forget') {
|
||||||
|
return $this->forget($next, $request);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->remember($next, $request);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function remembered($next, $request)
|
||||||
|
{
|
||||||
|
$remembered = array_filter($request->session()->get('remember_query_strings.'.$request->route()->getName()) ?? []);
|
||||||
|
|
||||||
|
if ($remembered) {
|
||||||
|
$request->session()->reflash();
|
||||||
|
|
||||||
|
return redirect(url($request->path()).'?'.http_build_query($remembered));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function remember($next, $request)
|
||||||
|
{
|
||||||
|
$request->session()->put('remember_query_strings.'.$request->route()->getName(), array_filter($request->all()));
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function forget($next, $request)
|
||||||
|
{
|
||||||
|
$request->session()->remove('remember_query_strings.'.$request->route()->getName());
|
||||||
|
|
||||||
|
return redirect(url($request->path()));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\App;
|
||||||
|
use Illuminate\Database\Eloquent\Model as Eloquent;
|
||||||
|
|
||||||
|
abstract class Model extends Eloquent
|
||||||
|
{
|
||||||
|
protected $guarded = [];
|
||||||
|
|
||||||
|
public function getPerPage()
|
||||||
|
{
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function resolveRouteBinding($value)
|
||||||
|
{
|
||||||
|
return $this->where('id', $value)->withTrashed()->first() ?? App::abort(404);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
|
class Organization extends Model
|
||||||
|
{
|
||||||
|
use SoftDeletes;
|
||||||
|
|
||||||
|
public function contacts()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Contact::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function scopeFilter($query, array $filters)
|
||||||
|
{
|
||||||
|
$query->when($filters['search'] ?? null, function ($query, $search) {
|
||||||
|
$query->where('name', 'ilike', '%'.$search.'%');
|
||||||
|
})->when($filters['trashed'] ?? null, function ($query, $trashed) {
|
||||||
|
if ($trashed === 'with') {
|
||||||
|
$query->withTrashed();
|
||||||
|
} elseif ($trashed === 'only') {
|
||||||
|
$query->onlyTrashed();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,27 +2,129 @@
|
||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use Debugbar;
|
||||||
|
use Inertia\Inertia;
|
||||||
|
use OpenPsa\Ranger\Ranger;
|
||||||
|
use Carbon\CarbonImmutable;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Pagination\UrlWindow;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Date;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
use Illuminate\Support\Facades\Request;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
use Illuminate\Pagination\LengthAwarePaginator;
|
||||||
|
|
||||||
class AppServiceProvider extends ServiceProvider
|
class AppServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Register any application services.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function register()
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bootstrap any application services.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function boot()
|
public function boot()
|
||||||
{
|
{
|
||||||
//
|
Debugbar::disable();
|
||||||
|
Date::use(CarbonImmutable::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function register()
|
||||||
|
{
|
||||||
|
Inertia::share('app.name', Config::get('app.name'));
|
||||||
|
Inertia::share('auth.user', function () {
|
||||||
|
if (Auth::user()) {
|
||||||
|
return [
|
||||||
|
'id' => Auth::user()->id,
|
||||||
|
'first_name' => Auth::user()->first_name,
|
||||||
|
'last_name' => Auth::user()->last_name,
|
||||||
|
'email' => Auth::user()->email,
|
||||||
|
'role' => Auth::user()->role,
|
||||||
|
'account' => [
|
||||||
|
'id' => Auth::user()->account->id,
|
||||||
|
'name' => Auth::user()->account->name,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->registerLengthAwarePaginator();
|
||||||
|
$this->registerCarbonMarcos();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function registerLengthAwarePaginator()
|
||||||
|
{
|
||||||
|
$this->app->bind(LengthAwarePaginator::class, function ($app, $values) {
|
||||||
|
return new class(...array_values($values)) extends LengthAwarePaginator {
|
||||||
|
public function only(...$attributes)
|
||||||
|
{
|
||||||
|
return $this->transform(function ($item) use ($attributes) {
|
||||||
|
return $item->only($attributes);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function transform($callback)
|
||||||
|
{
|
||||||
|
$this->items->transform($callback);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toArray()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'data' => $this->items->toArray(),
|
||||||
|
'links' => $this->links(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function links($view = null, $data = [])
|
||||||
|
{
|
||||||
|
$this->appends(Request::all());
|
||||||
|
|
||||||
|
$window = UrlWindow::make($this);
|
||||||
|
|
||||||
|
$elements = array_filter([
|
||||||
|
$window['first'],
|
||||||
|
is_array($window['slider']) ? '...' : null,
|
||||||
|
$window['slider'],
|
||||||
|
is_array($window['last']) ? '...' : null,
|
||||||
|
$window['last'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
return Collection::make($elements)->flatMap(function ($item) {
|
||||||
|
if (is_array($item)) {
|
||||||
|
return Collection::make($item)->map(function ($url, $page) {
|
||||||
|
return [
|
||||||
|
'url' => $url,
|
||||||
|
'label' => $page,
|
||||||
|
'active' => $this->currentPage() === $page,
|
||||||
|
];
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'url' => null,
|
||||||
|
'label' => '...',
|
||||||
|
'active' => false,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
})->prepend([
|
||||||
|
'url' => $this->previousPageUrl(),
|
||||||
|
'label' => 'Previous',
|
||||||
|
'active' => false,
|
||||||
|
])->push([
|
||||||
|
'url' => $this->nextPageUrl(),
|
||||||
|
'label' => 'Next',
|
||||||
|
'active' => false,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function registerCarbonMarcos()
|
||||||
|
{
|
||||||
|
CarbonImmutable::macro('range', function ($to) {
|
||||||
|
return (new Ranger('en'))->format(
|
||||||
|
$this->toDateString(),
|
||||||
|
$to->toDateString()
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
81
app/User.php
81
app/User.php
|
@ -2,38 +2,61 @@
|
||||||
|
|
||||||
namespace App;
|
namespace App;
|
||||||
|
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Auth\Authenticatable;
|
||||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
use Illuminate\Support\Facades\Hash;
|
||||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use Illuminate\Foundation\Auth\Access\Authorizable;
|
||||||
|
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
|
||||||
|
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
|
||||||
|
|
||||||
class User extends Authenticatable
|
class User extends Model implements AuthenticatableContract, AuthorizableContract
|
||||||
{
|
{
|
||||||
use Notifiable;
|
use SoftDeletes, Authenticatable, Authorizable;
|
||||||
|
|
||||||
/**
|
public function account()
|
||||||
* The attributes that are mass assignable.
|
{
|
||||||
*
|
return $this->belongsTo(Account::class);
|
||||||
* @var array
|
}
|
||||||
*/
|
|
||||||
protected $fillable = [
|
|
||||||
'name', 'email', 'password',
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
public function getNameAttribute()
|
||||||
* The attributes that should be hidden for arrays.
|
{
|
||||||
*
|
return $this->first_name.' '.$this->last_name;
|
||||||
* @var array
|
}
|
||||||
*/
|
|
||||||
protected $hidden = [
|
|
||||||
'password', 'remember_token',
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
public function setPasswordAttribute($password)
|
||||||
* The attributes that should be cast to native types.
|
{
|
||||||
*
|
$this->attributes['password'] = Hash::make($password);
|
||||||
* @var array
|
}
|
||||||
*/
|
|
||||||
protected $casts = [
|
public function scopeOrderByName($query)
|
||||||
'email_verified_at' => 'datetime',
|
{
|
||||||
];
|
$query->orderBy('last_name')->orderBy('first_name');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function scopeWhereRole($query, $role)
|
||||||
|
{
|
||||||
|
switch ($role) {
|
||||||
|
case 'user': return $query->where('owner', false);
|
||||||
|
case 'owner': return $query->where('owner', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function scopeFilter($query, array $filters)
|
||||||
|
{
|
||||||
|
$query->when($filters['search'] ?? null, function ($query, $search) {
|
||||||
|
$query->where(function ($query) use ($search) {
|
||||||
|
$query->where('first_name', 'ilike', '%'.$search.'%')
|
||||||
|
->orWhere('last_name', 'ilike', '%'.$search.'%')
|
||||||
|
->orWhere('email', 'ilike', '%'.$search.'%');
|
||||||
|
});
|
||||||
|
})->when($filters['role'] ?? null, function ($query, $role) {
|
||||||
|
$query->whereRole($role);
|
||||||
|
})->when($filters['trashed'] ?? null, function ($query, $trashed) {
|
||||||
|
if ($trashed === 'with') {
|
||||||
|
$query->withTrashed();
|
||||||
|
} elseif ($trashed === 'only') {
|
||||||
|
$query->onlyTrashed();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,62 +1,81 @@
|
||||||
{
|
{
|
||||||
"name": "laravel/laravel",
|
"name": "laravel/laravel",
|
||||||
"type": "project",
|
|
||||||
"description": "The Laravel Framework.",
|
"description": "The Laravel Framework.",
|
||||||
"keywords": [
|
"keywords": ["framework", "laravel"],
|
||||||
"framework",
|
|
||||||
"laravel"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"type": "project",
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "path",
|
||||||
|
"url": "../inertia-laravel"
|
||||||
|
}
|
||||||
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^7.1.3",
|
"php": "~7.2.0",
|
||||||
|
"ext-redis": "*",
|
||||||
"fideloper/proxy": "^4.0",
|
"fideloper/proxy": "^4.0",
|
||||||
|
"fzaninotto/faker": "^1.4",
|
||||||
"laravel/framework": "5.8.*",
|
"laravel/framework": "5.8.*",
|
||||||
"laravel/tinker": "^1.0"
|
"laravel/tinker": "^1.0",
|
||||||
|
"openpsa/ranger": "^0.4.0",
|
||||||
|
"reinink/advanced-eloquent": "^0.2.0",
|
||||||
|
"reinink/inertia-laravel": "@dev",
|
||||||
|
"tightenco/ziggy": "^0.6.9"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
|
"barryvdh/laravel-debugbar": "^3.2",
|
||||||
"beyondcode/laravel-dump-server": "^1.0",
|
"beyondcode/laravel-dump-server": "^1.0",
|
||||||
"filp/whoops": "^2.0",
|
"filp/whoops": "^2.0",
|
||||||
"fzaninotto/faker": "^1.4",
|
|
||||||
"mockery/mockery": "^1.0",
|
"mockery/mockery": "^1.0",
|
||||||
"nunomaduro/collision": "^2.0",
|
"nunomaduro/collision": "^2.0",
|
||||||
"phpunit/phpunit": "^7.5"
|
"phpunit/phpunit": "^7.0"
|
||||||
},
|
|
||||||
"config": {
|
|
||||||
"optimize-autoloader": true,
|
|
||||||
"preferred-install": "dist",
|
|
||||||
"sort-packages": true
|
|
||||||
},
|
|
||||||
"extra": {
|
|
||||||
"laravel": {
|
|
||||||
"dont-discover": []
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
|
||||||
"App\\": "app/"
|
|
||||||
},
|
|
||||||
"classmap": [
|
"classmap": [
|
||||||
"database/seeds",
|
"database/seeds",
|
||||||
"database/factories"
|
"database/factories"
|
||||||
]
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"App\\": "app/"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"autoload-dev": {
|
"autoload-dev": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"Tests\\": "tests/"
|
"Tests\\": "tests/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"minimum-stability": "dev",
|
"extra": {
|
||||||
"prefer-stable": true,
|
"laravel": {
|
||||||
|
"dont-discover": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"post-autoload-dump": [
|
"compile": [
|
||||||
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
|
"npm run prod",
|
||||||
"@php artisan package:discover --ansi"
|
"@php artisan vendor:publish --provider=\"Laravel\\Horizon\\HorizonServiceProvider\"",
|
||||||
|
"@php artisan migrate --force"
|
||||||
|
],
|
||||||
|
"reseed": [
|
||||||
|
"@php artisan migrate:fresh",
|
||||||
|
"@php artisan db:seed"
|
||||||
],
|
],
|
||||||
"post-root-package-install": [
|
"post-root-package-install": [
|
||||||
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
|
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
|
||||||
],
|
],
|
||||||
"post-create-project-cmd": [
|
"post-create-project-cmd": [
|
||||||
"@php artisan key:generate --ansi"
|
"@php artisan key:generate"
|
||||||
|
],
|
||||||
|
"post-autoload-dump": [
|
||||||
|
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
|
||||||
|
"@php artisan package:discover"
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
"config": {
|
||||||
|
"preferred-install": "dist",
|
||||||
|
"sort-packages": true,
|
||||||
|
"optimize-autoloader": true
|
||||||
|
},
|
||||||
|
"minimum-stability": "dev",
|
||||||
|
"prefer-stable": true
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "4731429c7f25ab92eaa813447a972209",
|
"content-hash": "1a9f92a6ff3cce7a387436a576d6b8a9",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "dnoegel/php-xdg-base-dir",
|
"name": "dnoegel/php-xdg-base-dir",
|
||||||
|
@ -366,6 +366,56 @@
|
||||||
],
|
],
|
||||||
"time": "2019-01-10T14:06:47+00:00"
|
"time": "2019-01-10T14:06:47+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "fzaninotto/faker",
|
||||||
|
"version": "v1.8.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/fzaninotto/Faker.git",
|
||||||
|
"reference": "f72816b43e74063c8b10357394b6bba8cb1c10de"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/fzaninotto/Faker/zipball/f72816b43e74063c8b10357394b6bba8cb1c10de",
|
||||||
|
"reference": "f72816b43e74063c8b10357394b6bba8cb1c10de",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^5.3.3 || ^7.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"ext-intl": "*",
|
||||||
|
"phpunit/phpunit": "^4.8.35 || ^5.7",
|
||||||
|
"squizlabs/php_codesniffer": "^1.5"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.8-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Faker\\": "src/Faker/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "François Zaninotto"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Faker is a PHP library that generates fake data for you.",
|
||||||
|
"keywords": [
|
||||||
|
"data",
|
||||||
|
"faker",
|
||||||
|
"fixtures"
|
||||||
|
],
|
||||||
|
"time": "2018-07-12T10:23:15+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "jakub-onderka/php-console-color",
|
"name": "jakub-onderka/php-console-color",
|
||||||
"version": "v0.2",
|
"version": "v0.2",
|
||||||
|
@ -937,6 +987,48 @@
|
||||||
],
|
],
|
||||||
"time": "2019-02-16T20:54:15+00:00"
|
"time": "2019-02-16T20:54:15+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "openpsa/ranger",
|
||||||
|
"version": "v0.4.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/flack/ranger.git",
|
||||||
|
"reference": "c0af25836e5923ee69f57ce4a8ff740b19ecfad6"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/flack/ranger/zipball/c0af25836e5923ee69f57ce4a8ff740b19ecfad6",
|
||||||
|
"reference": "c0af25836e5923ee69f57ce4a8ff740b19ecfad6",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.4.0",
|
||||||
|
"symfony/intl": "~2.6|~3.4|~4.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"OpenPsa\\Ranger\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Andreas Flack",
|
||||||
|
"email": "flack@contentcontrol-berlin.de",
|
||||||
|
"homepage": "http://www.contentcontrol-berlin.de/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Formatter for date and time ranges with i18n support",
|
||||||
|
"keywords": [
|
||||||
|
"date range",
|
||||||
|
"time range"
|
||||||
|
],
|
||||||
|
"time": "2018-07-23T12:30:50+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "opis/closure",
|
"name": "opis/closure",
|
||||||
"version": "3.1.6",
|
"version": "3.1.6",
|
||||||
|
@ -1393,6 +1485,93 @@
|
||||||
],
|
],
|
||||||
"time": "2018-07-19T23:38:55+00:00"
|
"time": "2018-07-19T23:38:55+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "reinink/advanced-eloquent",
|
||||||
|
"version": "v0.2.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/reinink/advanced-eloquent.git",
|
||||||
|
"reference": "2d92b29ba085fd537c198868e8e5c15a0bf205b8"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/reinink/advanced-eloquent/zipball/2d92b29ba085fd537c198868e8e5c15a0bf205b8",
|
||||||
|
"reference": "2d92b29ba085fd537c198868e8e5c15a0bf205b8",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"laravel": {
|
||||||
|
"providers": [
|
||||||
|
"AdvancedEloquent\\ServiceProvider"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"AdvancedEloquent\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Jonathan Reinink",
|
||||||
|
"email": "jonathan@reinink.ca",
|
||||||
|
"homepage": "https://reinink.ca"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A set of advanced Eloquent macros for Laravel",
|
||||||
|
"homepage": "https://github.com/reinink/advanced-eloquent",
|
||||||
|
"keywords": [
|
||||||
|
"advanced",
|
||||||
|
"eloquent",
|
||||||
|
"laravel",
|
||||||
|
"macro"
|
||||||
|
],
|
||||||
|
"time": "2018-11-23T20:30:53+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "reinink/inertia-laravel",
|
||||||
|
"version": "dev-master",
|
||||||
|
"dist": {
|
||||||
|
"type": "path",
|
||||||
|
"url": "../inertia-laravel",
|
||||||
|
"reference": "3a9577862dcd68e63a46a5b67068de007d6cea29",
|
||||||
|
"shasum": null
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"orchestra/testbench": "~3.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Inertia\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"Tests\\": "tests/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Jonathan Reinink",
|
||||||
|
"email": "jonathan@reinink.ca",
|
||||||
|
"homepage": "https://reinink.ca"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "The Laravel adapter for Inertia.",
|
||||||
|
"keywords": [
|
||||||
|
"inertia",
|
||||||
|
"laravel"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "swiftmailer/swiftmailer",
|
"name": "swiftmailer/swiftmailer",
|
||||||
"version": "v6.1.3",
|
"version": "v6.1.3",
|
||||||
|
@ -1957,6 +2136,81 @@
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"time": "2019-03-03T19:38:09+00:00"
|
"time": "2019-03-03T19:38:09+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/intl",
|
||||||
|
"version": "v4.2.4",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/intl.git",
|
||||||
|
"reference": "b2af5ce379781fd4811f79746512fc1934333fbb"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/intl/zipball/b2af5ce379781fd4811f79746512fc1934333fbb",
|
||||||
|
"reference": "b2af5ce379781fd4811f79746512fc1934333fbb",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.1.3",
|
||||||
|
"symfony/polyfill-intl-icu": "~1.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"symfony/filesystem": "~3.4|~4.0"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-intl": "to use the component with locales other than \"en\""
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "4.2-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Symfony\\Component\\Intl\\": ""
|
||||||
|
},
|
||||||
|
"classmap": [
|
||||||
|
"Resources/stubs"
|
||||||
|
],
|
||||||
|
"exclude-from-classmap": [
|
||||||
|
"/Tests/"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Bernhard Schussek",
|
||||||
|
"email": "bschussek@gmail.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Eriksen Costa",
|
||||||
|
"email": "eriksen.costa@infranology.com.br"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Igor Wiedler",
|
||||||
|
"email": "igor@wiedler.ch"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A PHP replacement layer for the C intl extension that includes additional data from the ICU library.",
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"keywords": [
|
||||||
|
"i18n",
|
||||||
|
"icu",
|
||||||
|
"internationalization",
|
||||||
|
"intl",
|
||||||
|
"l10n",
|
||||||
|
"localization"
|
||||||
|
],
|
||||||
|
"time": "2019-02-23T15:17:42+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-ctype",
|
"name": "symfony/polyfill-ctype",
|
||||||
"version": "v1.10.0",
|
"version": "v1.10.0",
|
||||||
|
@ -2015,6 +2269,64 @@
|
||||||
],
|
],
|
||||||
"time": "2018-08-06T14:22:27+00:00"
|
"time": "2018-08-06T14:22:27+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/polyfill-intl-icu",
|
||||||
|
"version": "v1.10.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/polyfill-intl-icu.git",
|
||||||
|
"reference": "f22a90256d577c7ef7efad8df1f0201663d57644"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/f22a90256d577c7ef7efad8df1f0201663d57644",
|
||||||
|
"reference": "f22a90256d577c7ef7efad8df1f0201663d57644",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.3.3",
|
||||||
|
"symfony/intl": "~2.3|~3.0|~4.0"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-intl": "For best performance"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.9-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"bootstrap.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Nicolas Grekas",
|
||||||
|
"email": "p@tchwork.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Symfony polyfill for intl's ICU-related data and classes",
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"keywords": [
|
||||||
|
"compatibility",
|
||||||
|
"icu",
|
||||||
|
"intl",
|
||||||
|
"polyfill",
|
||||||
|
"portable",
|
||||||
|
"shim"
|
||||||
|
],
|
||||||
|
"time": "2018-08-06T14:22:27+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-mbstring",
|
"name": "symfony/polyfill-mbstring",
|
||||||
"version": "v1.10.0",
|
"version": "v1.10.0",
|
||||||
|
@ -2404,6 +2716,57 @@
|
||||||
],
|
],
|
||||||
"time": "2019-02-23T15:17:42+00:00"
|
"time": "2019-02-23T15:17:42+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "tightenco/ziggy",
|
||||||
|
"version": "v0.6.9",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/tightenco/ziggy.git",
|
||||||
|
"reference": "0f59b703236f6b19002bc28d1d3eafcbf479bd43"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/tightenco/ziggy/zipball/0f59b703236f6b19002bc28d1d3eafcbf479bd43",
|
||||||
|
"reference": "0f59b703236f6b19002bc28d1d3eafcbf479bd43",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"laravel/framework": "~5.4"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"mikey179/vfsstream": "^1.6",
|
||||||
|
"orchestra/testbench": "~3.6"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"laravel": {
|
||||||
|
"providers": [
|
||||||
|
"Tightenco\\Ziggy\\ZiggyServiceProvider"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Tightenco\\Ziggy\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Matt Stauffer",
|
||||||
|
"email": "matt@tighten.co"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Daniel Coulbourne",
|
||||||
|
"email": "daniel@tighten.co"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Generates a Blade directive exporting all of your named Laravel routes. Also provides a nice route() helper function in JavaScript.",
|
||||||
|
"time": "2018-10-29T06:06:46+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "tijsverkoyen/css-to-inline-styles",
|
"name": "tijsverkoyen/css-to-inline-styles",
|
||||||
"version": "2.2.1",
|
"version": "2.2.1",
|
||||||
|
@ -2453,16 +2816,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "vlucas/phpdotenv",
|
"name": "vlucas/phpdotenv",
|
||||||
"version": "v3.3.2",
|
"version": "v3.3.3",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/vlucas/phpdotenv.git",
|
"url": "https://github.com/vlucas/phpdotenv.git",
|
||||||
"reference": "1ee9369cfbf26cfcf1f2515d98f15fab54e9647a"
|
"reference": "dbcc609971dd9b55f48b8008b553d79fd372ddde"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/1ee9369cfbf26cfcf1f2515d98f15fab54e9647a",
|
"url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/dbcc609971dd9b55f48b8008b553d79fd372ddde",
|
||||||
"reference": "1ee9369cfbf26cfcf1f2515d98f15fab54e9647a",
|
"reference": "dbcc609971dd9b55f48b8008b553d79fd372ddde",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -2501,10 +2864,78 @@
|
||||||
"env",
|
"env",
|
||||||
"environment"
|
"environment"
|
||||||
],
|
],
|
||||||
"time": "2019-01-30T10:43:17+00:00"
|
"time": "2019-03-06T09:39:45+00:00"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"packages-dev": [
|
"packages-dev": [
|
||||||
|
{
|
||||||
|
"name": "barryvdh/laravel-debugbar",
|
||||||
|
"version": "v3.2.3",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/barryvdh/laravel-debugbar.git",
|
||||||
|
"reference": "5fcba4cc8e92a230b13b99c1083fc22ba8a5c479"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/5fcba4cc8e92a230b13b99c1083fc22ba8a5c479",
|
||||||
|
"reference": "5fcba4cc8e92a230b13b99c1083fc22ba8a5c479",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"illuminate/routing": "5.5.x|5.6.x|5.7.x|5.8.x",
|
||||||
|
"illuminate/session": "5.5.x|5.6.x|5.7.x|5.8.x",
|
||||||
|
"illuminate/support": "5.5.x|5.6.x|5.7.x|5.8.x",
|
||||||
|
"maximebf/debugbar": "~1.15.0",
|
||||||
|
"php": ">=7.0",
|
||||||
|
"symfony/debug": "^3|^4",
|
||||||
|
"symfony/finder": "^3|^4"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"laravel/framework": "5.5.x"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "3.2-dev"
|
||||||
|
},
|
||||||
|
"laravel": {
|
||||||
|
"providers": [
|
||||||
|
"Barryvdh\\Debugbar\\ServiceProvider"
|
||||||
|
],
|
||||||
|
"aliases": {
|
||||||
|
"Debugbar": "Barryvdh\\Debugbar\\Facade"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Barryvdh\\Debugbar\\": "src/"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"src/helpers.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Barry vd. Heuvel",
|
||||||
|
"email": "barryvdh@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "PHP Debugbar integration for Laravel",
|
||||||
|
"keywords": [
|
||||||
|
"debug",
|
||||||
|
"debugbar",
|
||||||
|
"laravel",
|
||||||
|
"profiler",
|
||||||
|
"webprofiler"
|
||||||
|
],
|
||||||
|
"time": "2019-02-26T18:01:54+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "beyondcode/laravel-dump-server",
|
"name": "beyondcode/laravel-dump-server",
|
||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
|
@ -2681,56 +3112,6 @@
|
||||||
],
|
],
|
||||||
"time": "2018-10-23T09:00:00+00:00"
|
"time": "2018-10-23T09:00:00+00:00"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "fzaninotto/faker",
|
|
||||||
"version": "v1.8.0",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/fzaninotto/Faker.git",
|
|
||||||
"reference": "f72816b43e74063c8b10357394b6bba8cb1c10de"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/fzaninotto/Faker/zipball/f72816b43e74063c8b10357394b6bba8cb1c10de",
|
|
||||||
"reference": "f72816b43e74063c8b10357394b6bba8cb1c10de",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": "^5.3.3 || ^7.0"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"ext-intl": "*",
|
|
||||||
"phpunit/phpunit": "^4.8.35 || ^5.7",
|
|
||||||
"squizlabs/php_codesniffer": "^1.5"
|
|
||||||
},
|
|
||||||
"type": "library",
|
|
||||||
"extra": {
|
|
||||||
"branch-alias": {
|
|
||||||
"dev-master": "1.8-dev"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"Faker\\": "src/Faker/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "François Zaninotto"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Faker is a PHP library that generates fake data for you.",
|
|
||||||
"keywords": [
|
|
||||||
"data",
|
|
||||||
"faker",
|
|
||||||
"fixtures"
|
|
||||||
],
|
|
||||||
"time": "2018-07-12T10:23:15+00:00"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "hamcrest/hamcrest-php",
|
"name": "hamcrest/hamcrest-php",
|
||||||
"version": "v2.0.0",
|
"version": "v2.0.0",
|
||||||
|
@ -2779,6 +3160,67 @@
|
||||||
],
|
],
|
||||||
"time": "2016-01-20T08:20:44+00:00"
|
"time": "2016-01-20T08:20:44+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "maximebf/debugbar",
|
||||||
|
"version": "v1.15.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/maximebf/php-debugbar.git",
|
||||||
|
"reference": "30e7d60937ee5f1320975ca9bc7bcdd44d500f07"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/30e7d60937ee5f1320975ca9bc7bcdd44d500f07",
|
||||||
|
"reference": "30e7d60937ee5f1320975ca9bc7bcdd44d500f07",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.3.0",
|
||||||
|
"psr/log": "^1.0",
|
||||||
|
"symfony/var-dumper": "^2.6|^3.0|^4.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^4.0|^5.0"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"kriswallsmith/assetic": "The best way to manage assets",
|
||||||
|
"monolog/monolog": "Log using Monolog",
|
||||||
|
"predis/predis": "Redis storage"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.14-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"DebugBar\\": "src/DebugBar/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Maxime Bouroumeau-Fuseau",
|
||||||
|
"email": "maxime.bouroumeau@gmail.com",
|
||||||
|
"homepage": "http://maximebf.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Barry vd. Heuvel",
|
||||||
|
"email": "barryvdh@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Debug bar in the browser for php application",
|
||||||
|
"homepage": "https://github.com/maximebf/php-debugbar",
|
||||||
|
"keywords": [
|
||||||
|
"debug",
|
||||||
|
"debugbar"
|
||||||
|
],
|
||||||
|
"time": "2017-12-15T11:13:46+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "mockery/mockery",
|
"name": "mockery/mockery",
|
||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
|
@ -4269,11 +4711,14 @@
|
||||||
],
|
],
|
||||||
"aliases": [],
|
"aliases": [],
|
||||||
"minimum-stability": "dev",
|
"minimum-stability": "dev",
|
||||||
"stability-flags": [],
|
"stability-flags": {
|
||||||
|
"reinink/inertia-laravel": 20
|
||||||
|
},
|
||||||
"prefer-stable": true,
|
"prefer-stable": true,
|
||||||
"prefer-lowest": false,
|
"prefer-lowest": false,
|
||||||
"platform": {
|
"platform": {
|
||||||
"php": "^7.1.3"
|
"php": "~7.2.0",
|
||||||
|
"ext-redis": "*"
|
||||||
},
|
},
|
||||||
"platform-dev": []
|
"platform-dev": []
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Faker\Generator as Faker;
|
||||||
|
|
||||||
|
$factory->define(App\Contact::class, function (Faker $faker) {
|
||||||
|
return [
|
||||||
|
'first_name' => $faker->firstName,
|
||||||
|
'last_name' => $faker->lastName,
|
||||||
|
'email' => $faker->unique()->safeEmail,
|
||||||
|
'phone' => $faker->tollFreePhoneNumber,
|
||||||
|
'address' => $faker->streetAddress,
|
||||||
|
'city' => $faker->city,
|
||||||
|
'region' => $faker->state,
|
||||||
|
'country' => 'US',
|
||||||
|
'postal_code' => $faker->postcode,
|
||||||
|
];
|
||||||
|
});
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Faker\Generator as Faker;
|
||||||
|
|
||||||
|
$factory->define(App\Organization::class, function (Faker $faker) {
|
||||||
|
return [
|
||||||
|
'name' => $faker->company,
|
||||||
|
'email' => $faker->companyEmail,
|
||||||
|
'phone' => $faker->tollFreePhoneNumber,
|
||||||
|
'address' => $faker->streetAddress,
|
||||||
|
'city' => $faker->city,
|
||||||
|
'region' => $faker->state,
|
||||||
|
'country' => 'US',
|
||||||
|
'postal_code' => $faker->postcode,
|
||||||
|
];
|
||||||
|
});
|
|
@ -1,7 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\User;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use Faker\Generator as Faker;
|
use Faker\Generator as Faker;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -15,12 +13,13 @@ use Faker\Generator as Faker;
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$factory->define(User::class, function (Faker $faker) {
|
$factory->define(App\User::class, function (Faker $faker) {
|
||||||
return [
|
return [
|
||||||
'name' => $faker->name,
|
'first_name' => $faker->firstName,
|
||||||
|
'last_name' => $faker->lastName,
|
||||||
'email' => $faker->unique()->safeEmail,
|
'email' => $faker->unique()->safeEmail,
|
||||||
'email_verified_at' => now(),
|
'password' => 'secret',
|
||||||
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
|
'remember_token' => str_random(10),
|
||||||
'remember_token' => Str::random(10),
|
'owner' => false,
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Schema;
|
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
|
||||||
use Illuminate\Database\Migrations\Migration;
|
|
||||||
|
|
||||||
class CreateUsersTable extends Migration
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Run the migrations.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function up()
|
|
||||||
{
|
|
||||||
Schema::create('users', function (Blueprint $table) {
|
|
||||||
$table->bigIncrements('id');
|
|
||||||
$table->string('name');
|
|
||||||
$table->string('email')->unique();
|
|
||||||
$table->timestamp('email_verified_at')->nullable();
|
|
||||||
$table->string('password');
|
|
||||||
$table->rememberToken();
|
|
||||||
$table->timestamps();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reverse the migrations.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function down()
|
|
||||||
{
|
|
||||||
Schema::dropIfExists('users');
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
|
||||||
|
class CreateAccountsTable extends Migration
|
||||||
|
{
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('accounts', function (Blueprint $table) {
|
||||||
|
$table->increments('id');
|
||||||
|
$table->string('name', 50);
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
|
||||||
|
class CreateContactsTable extends Migration
|
||||||
|
{
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('contacts', function (Blueprint $table) {
|
||||||
|
$table->increments('id');
|
||||||
|
$table->integer('account_id')->index();
|
||||||
|
$table->integer('organization_id')->nullable()->index();
|
||||||
|
$table->string('first_name', 25);
|
||||||
|
$table->string('last_name', 25);
|
||||||
|
$table->string('email', 50)->nullable();
|
||||||
|
$table->string('phone', 50)->nullable();
|
||||||
|
$table->string('address', 150)->nullable();
|
||||||
|
$table->string('city', 50)->nullable();
|
||||||
|
$table->string('region', 50)->nullable();
|
||||||
|
$table->string('country', 2)->nullable();
|
||||||
|
$table->string('postal_code', 25)->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
$table->softDeletes();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
|
||||||
|
class CreateOrganizationsTable extends Migration
|
||||||
|
{
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('organizations', function (Blueprint $table) {
|
||||||
|
$table->increments('id');
|
||||||
|
$table->integer('account_id')->index();
|
||||||
|
$table->string('name', 100);
|
||||||
|
$table->string('email', 50)->nullable();
|
||||||
|
$table->string('phone', 50)->nullable();
|
||||||
|
$table->string('address', 150)->nullable();
|
||||||
|
$table->string('city', 50)->nullable();
|
||||||
|
$table->string('region', 50)->nullable();
|
||||||
|
$table->string('country', 2)->nullable();
|
||||||
|
$table->string('postal_code', 25)->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
$table->softDeletes();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,11 +6,6 @@ use Illuminate\Database\Migrations\Migration;
|
||||||
|
|
||||||
class CreatePasswordResetsTable extends Migration
|
class CreatePasswordResetsTable extends Migration
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Run the migrations.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function up()
|
public function up()
|
||||||
{
|
{
|
||||||
Schema::create('password_resets', function (Blueprint $table) {
|
Schema::create('password_resets', function (Blueprint $table) {
|
||||||
|
@ -19,14 +14,4 @@ class CreatePasswordResetsTable extends Migration
|
||||||
$table->timestamp('created_at')->nullable();
|
$table->timestamp('created_at')->nullable();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Reverse the migrations.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function down()
|
|
||||||
{
|
|
||||||
Schema::dropIfExists('password_resets');
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
|
||||||
|
class CreateUsersTable extends Migration
|
||||||
|
{
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('users', function (Blueprint $table) {
|
||||||
|
$table->increments('id');
|
||||||
|
$table->integer('account_id')->index();
|
||||||
|
$table->string('first_name', 25);
|
||||||
|
$table->string('last_name', 25);
|
||||||
|
$table->string('email', 50)->unique();
|
||||||
|
$table->string('password')->nullable();
|
||||||
|
$table->boolean('owner')->default(false);
|
||||||
|
$table->rememberToken();
|
||||||
|
$table->timestamps();
|
||||||
|
$table->softDeletes();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +1,34 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\User;
|
||||||
|
use App\Account;
|
||||||
|
use App\Contact;
|
||||||
|
use App\Organization;
|
||||||
use Illuminate\Database\Seeder;
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
class DatabaseSeeder extends Seeder
|
class DatabaseSeeder extends Seeder
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Seed the application's database.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function run()
|
public function run()
|
||||||
{
|
{
|
||||||
// $this->call(UsersTableSeeder::class);
|
$account = Account::create(['name' => 'Acme Corporation']);
|
||||||
|
|
||||||
|
factory(User::class)->create([
|
||||||
|
'account_id' => $account->id,
|
||||||
|
'first_name' => 'Jonathan',
|
||||||
|
'last_name' => 'Reinink',
|
||||||
|
'email' => 'jonathan@reinink.ca',
|
||||||
|
'owner' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
factory(User::class, 5)->create(['account_id' => $account->id]);
|
||||||
|
|
||||||
|
$organizations = factory(Organization::class, 100)
|
||||||
|
->create(['account_id' => $account->id]);
|
||||||
|
|
||||||
|
factory(Contact::class, 100)
|
||||||
|
->create(['account_id' => $account->id])
|
||||||
|
->each(function ($contact) use ($organizations) {
|
||||||
|
$contact->update(['organization_id' => $organizations->random()->id]);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
17
package.json
17
package.json
|
@ -10,16 +10,23 @@
|
||||||
"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"
|
"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": {
|
"devDependencies": {
|
||||||
|
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
||||||
|
"autosize": "^4.0.2",
|
||||||
"axios": "^0.18",
|
"axios": "^0.18",
|
||||||
"bootstrap": "^4.0.0",
|
|
||||||
"cross-env": "^5.1",
|
"cross-env": "^5.1",
|
||||||
"jquery": "^3.2",
|
"eslint": "^5.14.1",
|
||||||
|
"eslint-plugin-vue": "^5.2.2",
|
||||||
|
"fuse.js": "^3.4.2",
|
||||||
|
"inertia-vue": "file:../inertia-vue",
|
||||||
"laravel-mix": "^4.0.7",
|
"laravel-mix": "^4.0.7",
|
||||||
"lodash": "^4.17.5",
|
"lodash": "^4.17.5",
|
||||||
"popper.js": "^1.12",
|
"popper.js": "^1.12",
|
||||||
|
"portal-vue": "^1.5.1",
|
||||||
|
"postcss-import": "^12.0.1",
|
||||||
|
"postcss-nesting": "^7.0.0",
|
||||||
"resolve-url-loader": "^2.3.1",
|
"resolve-url-loader": "^2.3.1",
|
||||||
"sass": "^1.15.2",
|
"tailwindcss": "^0.7.4",
|
||||||
"sass-loader": "^7.1.0",
|
"vue": "^2.6.6",
|
||||||
"vue": "^2.5.17"
|
"vue-template-compiler": "^2.6.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,12 @@
|
||||||
|
/* Reset */
|
||||||
|
@import "tailwindcss/preflight";
|
||||||
|
@import "reset";
|
||||||
|
|
||||||
|
/* Components */
|
||||||
|
@import "buttons";
|
||||||
|
@import "form";
|
||||||
|
@import "nprogress";
|
||||||
|
@import "spinner";
|
||||||
|
|
||||||
|
/* Utilities */
|
||||||
|
@import "tailwindcss/utilities";
|
|
@ -0,0 +1,5 @@
|
||||||
|
.btn-indigo {
|
||||||
|
@apply px-6 py-3 rounded bg-indigo-dark text-white text-sm font-bold whitespace-no-wrap;
|
||||||
|
|
||||||
|
&:hover, &:focus { @apply bg-orange }
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
.form-label {
|
||||||
|
@apply .mb-2 .block .text-grey-darkest .select-none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input,
|
||||||
|
.form-textarea,
|
||||||
|
.form-select {
|
||||||
|
@apply .p-2 .leading-normal .block .w-full .border .text-grey-darkest .bg-white .font-sans .rounded .text-left .appearance-none .relative;
|
||||||
|
|
||||||
|
&:focus,
|
||||||
|
&.focus {
|
||||||
|
@apply .border-indigo;
|
||||||
|
box-shadow: 0 0 0 1px config('colors.indigo');
|
||||||
|
}
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
@apply .text-grey-dark .opacity-100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-select {
|
||||||
|
@apply .pr-6;
|
||||||
|
|
||||||
|
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAQCAYAAAAMJL+VAAAABGdBTUEAALGPC/xhBQAAAQtJREFUOBG1lEEOgjAQRalbGj2OG9caOACn4ALGtfEuHACiazceR1PWOH/CNA3aMiTaBDpt/7zPdBKy7M/DCL9pGkvxxVp7KsvyJftL5rZt1865M+Ucq6pyyF3hNcI7Cuu+728QYn/JQA5yKaempxuZmQngOwEaYx55nu+1lQh8GIatMGi+01NwBcEmhxBqK4nAPZJ78K0KKFAJmR3oPp8+Iwgob0Oa6+TLoeCvRx+mTUYf/FVBGTPRwDkfLxnaSrRwcH0FWhNOmrkWYbE2XEicqgSa1J0LQ+aPCuQgZiLnwewbGuz5MGoAhcIkCQcjaTBjMgtXGURMVHC1wcQEy0J+Zlj8bKAnY1/UzDe2dbAVqfXn6wAAAABJRU5ErkJggg==');
|
||||||
|
background-size: 0.7rem;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: right 0.7rem center;
|
||||||
|
|
||||||
|
&::-ms-expand {
|
||||||
|
@apply .opacity-0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-error {
|
||||||
|
@apply .text-red .mt-2 .text-sm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input.error,
|
||||||
|
.form-textarea.error,
|
||||||
|
.form-select.error {
|
||||||
|
@apply .border-red-light;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
box-shadow: 0 0 0 1px config('colors.red');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
/* Make clicks pass-through */
|
||||||
|
#nprogress {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nprogress .bar {
|
||||||
|
background: #29d;
|
||||||
|
|
||||||
|
position: fixed;
|
||||||
|
z-index: 1031;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fancy blur effect */
|
||||||
|
#nprogress .peg {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
right: 0px;
|
||||||
|
width: 100px;
|
||||||
|
height: 100%;
|
||||||
|
box-shadow: 0 0 10px #29d, 0 0 5px #29d;
|
||||||
|
opacity: 1.0;
|
||||||
|
|
||||||
|
-webkit-transform: rotate(3deg) translate(0px, -4px);
|
||||||
|
-ms-transform: rotate(3deg) translate(0px, -4px);
|
||||||
|
transform: rotate(3deg) translate(0px, -4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove these to get rid of the spinner */
|
||||||
|
#nprogress .spinner {
|
||||||
|
display: block;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 1031;
|
||||||
|
top: 15px;
|
||||||
|
right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nprogress .spinner-icon {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
border: solid 2px transparent;
|
||||||
|
border-top-color: #29d;
|
||||||
|
border-left-color: #29d;
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
-webkit-animation: nprogress-spinner 400ms linear infinite;
|
||||||
|
animation: nprogress-spinner 400ms linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nprogress-custom-parent {
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nprogress-custom-parent #nprogress .spinner,
|
||||||
|
.nprogress-custom-parent #nprogress .bar {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes nprogress-spinner {
|
||||||
|
0% { -webkit-transform: rotate(0deg); }
|
||||||
|
100% { -webkit-transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
@keyframes nprogress-spinner {
|
||||||
|
0% { transform: rotate(0deg); }
|
||||||
|
100% { transform: rotate(360deg); }
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input, select, textarea, button, div, a {
|
||||||
|
&:focus, &:active {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
.spinner,
|
||||||
|
.spinner:after {
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 1.5em;
|
||||||
|
height: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
font-size: 10px;
|
||||||
|
position: relative;
|
||||||
|
text-indent: -9999em;
|
||||||
|
border-top: .2em solid white;
|
||||||
|
border-right: .2em solid white;
|
||||||
|
border-bottom: .2em solid white;
|
||||||
|
border-left: .2em solid transparent;
|
||||||
|
-webkit-transform: translateZ(0);
|
||||||
|
-ms-transform: translateZ(0);
|
||||||
|
transform: translateZ(0);
|
||||||
|
-webkit-animation: loading 1s infinite linear;
|
||||||
|
animation: loading 1s infinite linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes loading {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: rotate(0deg);
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
-webkit-transform: rotate(360deg);
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
<template>
|
||||||
|
<div class="p-6 bg-indigo-darker min-h-screen flex justify-center items-center">
|
||||||
|
<div class="w-full max-w-sm">
|
||||||
|
<logo class="block mx-auto w-full max-w-xs fill-white" height="50" />
|
||||||
|
<form class="mt-8 bg-white rounded-lg shadow-lg overflow-hidden" @submit.prevent="submit">
|
||||||
|
<div class="px-10 py-12">
|
||||||
|
<h1 class="text-center font-bold text-3xl">Welcome Back!</h1>
|
||||||
|
<div class="mx-auto mt-6 w-24 border-b-2" />
|
||||||
|
<text-input v-model="form.fields.email" class="mt-10" label="Email" :error="form.errors.first('email')" type="email" autofocus autocapitalize="off" />
|
||||||
|
<text-input v-model="form.fields.password" class="mt-6" label="Password" :error="form.errors.first('password')" type="password" />
|
||||||
|
<label class="mt-6 select-none flex items-center" for="remember">
|
||||||
|
<input id="remember" v-model="form.fields.remember" class="mr-1" type="checkbox">
|
||||||
|
<span class="text-sm">Remember Me</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="px-10 py-4 bg-grey-lightest border-t border-grey-lighter flex justify-between items-center">
|
||||||
|
<a class="hover:underline" tabindex="-1" href="#reset-password">Forget password?</a>
|
||||||
|
<loading-button :loading="form.sending" class="btn-indigo" type="submit">Login</loading-button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { Inertia } from 'inertia-vue'
|
||||||
|
import Form from '@/Utils/Form'
|
||||||
|
import LoadingButton from '@/Shared/LoadingButton'
|
||||||
|
import Logo from '@/Shared/Logo'
|
||||||
|
import TextInput from '@/Shared/TextInput'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
LoadingButton,
|
||||||
|
Logo,
|
||||||
|
TextInput,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
intendedUrl: String,
|
||||||
|
},
|
||||||
|
inject: ['page'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
form: new Form({
|
||||||
|
email: null,
|
||||||
|
password: null,
|
||||||
|
remember: null,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
document.title = `Login | ${this.page.props.app.name}`
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
submit() {
|
||||||
|
this.form.post({
|
||||||
|
url: this.route('login.attempt').url(),
|
||||||
|
then: () => Inertia.visit(this.intendedUrl),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,80 @@
|
||||||
|
<template>
|
||||||
|
<layout title="Create Contact">
|
||||||
|
<h1 class="mb-8 font-bold text-3xl">
|
||||||
|
<inertia-link class="text-indigo-light hover:text-indigo-dark" :href="route('contacts')">Contacts</inertia-link>
|
||||||
|
<span class="text-indigo-light font-medium">/</span> Create
|
||||||
|
</h1>
|
||||||
|
<div class="bg-white rounded shadow overflow-hidden max-w-lg">
|
||||||
|
<form @submit.prevent="submit">
|
||||||
|
<div class="p-8 -mr-6 -mb-8 flex flex-wrap">
|
||||||
|
<text-input v-model="form.fields.first_name" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('first_name')" label="First name" />
|
||||||
|
<text-input v-model="form.fields.last_name" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('last_name')" label="Last name" />
|
||||||
|
<select-input v-model="form.fields.organization_id" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('organization_id')" label="Organization">
|
||||||
|
<option :value="null" />
|
||||||
|
<option v-for="organization in organizations" :key="organization.id" :value="organization.id">{{ organization.name }}</option>
|
||||||
|
</select-input>
|
||||||
|
<text-input v-model="form.fields.email" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('email')" label="Email" />
|
||||||
|
<text-input v-model="form.fields.phone" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('phone')" label="Phone" />
|
||||||
|
<text-input v-model="form.fields.address" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('address')" label="Address" />
|
||||||
|
<text-input v-model="form.fields.city" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('city')" label="City" />
|
||||||
|
<text-input v-model="form.fields.region" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('region')" label="Province/State" />
|
||||||
|
<select-input v-model="form.fields.country" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('country')" label="Country">
|
||||||
|
<option :value="null" />
|
||||||
|
<option value="CA">Canada</option>
|
||||||
|
<option value="US">United States</option>
|
||||||
|
</select-input>
|
||||||
|
<text-input v-model="form.fields.postal_code" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('postal_code')" label="Postal code" />
|
||||||
|
</div>
|
||||||
|
<div class="px-8 py-4 bg-grey-lightest border-t border-grey-lighter flex justify-end items-center">
|
||||||
|
<loading-button :loading="form.sending" class="btn-indigo" type="submit">Create Contact</loading-button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { Inertia, InertiaLink } from 'inertia-vue'
|
||||||
|
import Form from '@/Utils/Form'
|
||||||
|
import Layout from '@/Shared/Layout'
|
||||||
|
import LoadingButton from '@/Shared/LoadingButton'
|
||||||
|
import SelectInput from '@/Shared/SelectInput'
|
||||||
|
import TextInput from '@/Shared/TextInput'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
InertiaLink,
|
||||||
|
Layout,
|
||||||
|
LoadingButton,
|
||||||
|
SelectInput,
|
||||||
|
TextInput,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
organizations: Array,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
form: new Form({
|
||||||
|
first_name: null,
|
||||||
|
last_name: null,
|
||||||
|
organization_id: null,
|
||||||
|
email: null,
|
||||||
|
phone: null,
|
||||||
|
address: null,
|
||||||
|
city: null,
|
||||||
|
region: null,
|
||||||
|
country: null,
|
||||||
|
postal_code: null,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
submit() {
|
||||||
|
this.form.post({
|
||||||
|
url: this.route('contacts.store').url(),
|
||||||
|
then: data => Inertia.visit(this.route('contacts.edit', data.id)),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,104 @@
|
||||||
|
<template>
|
||||||
|
<layout :title="`${form.fields.first_name} ${form.fields.last_name}`">
|
||||||
|
<h1 class="mb-8 font-bold text-3xl">
|
||||||
|
<inertia-link class="text-indigo-light hover:text-indigo-dark" :href="route('contacts')">Contacts</inertia-link>
|
||||||
|
<span class="text-indigo-light font-medium">/</span>
|
||||||
|
{{ form.fields.first_name }} {{ form.fields.last_name }}
|
||||||
|
</h1>
|
||||||
|
<trashed-message v-if="contact.deleted_at" class="mb-6" @restore="restore">
|
||||||
|
This contact has been deleted.
|
||||||
|
</trashed-message>
|
||||||
|
<div class="bg-white rounded shadow overflow-hidden max-w-lg">
|
||||||
|
<form @submit.prevent="submit">
|
||||||
|
<div class="p-8 -mr-6 -mb-8 flex flex-wrap">
|
||||||
|
<text-input v-model="form.fields.first_name" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('first_name')" label="First name" />
|
||||||
|
<text-input v-model="form.fields.last_name" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('last_name')" label="Last name" />
|
||||||
|
<select-input v-model="form.fields.organization_id" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('organization_id')" label="Organization">
|
||||||
|
<option :value="null" />
|
||||||
|
<option v-for="organization in organizations" :key="organization.id" :value="organization.id">{{ organization.name }}</option>
|
||||||
|
</select-input>
|
||||||
|
<text-input v-model="form.fields.email" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('email')" label="Email" />
|
||||||
|
<text-input v-model="form.fields.phone" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('phone')" label="Phone" />
|
||||||
|
<text-input v-model="form.fields.address" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('address')" label="Address" />
|
||||||
|
<text-input v-model="form.fields.city" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('city')" label="City" />
|
||||||
|
<text-input v-model="form.fields.region" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('region')" label="Province/State" />
|
||||||
|
<select-input v-model="form.fields.country" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('country')" label="Country">
|
||||||
|
<option :value="null" />
|
||||||
|
<option value="CA">Canada</option>
|
||||||
|
<option value="US">United States</option>
|
||||||
|
</select-input>
|
||||||
|
<text-input v-model="form.fields.postal_code" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('postal_code')" label="Postal code" />
|
||||||
|
</div>
|
||||||
|
<div class="px-8 py-4 bg-grey-lightest border-t border-grey-lighter flex items-center">
|
||||||
|
<button v-if="!contact.deleted_at" class="text-red hover:underline" tabindex="-1" type="button" @click="destroy">Delete Contact</button>
|
||||||
|
<loading-button :loading="form.sending" class="btn-indigo ml-auto" type="submit">Update Contact</loading-button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { Inertia, InertiaLink } from 'inertia-vue'
|
||||||
|
import Form from '@/Utils/Form'
|
||||||
|
import Layout from '@/Shared/Layout'
|
||||||
|
import LoadingButton from '@/Shared/LoadingButton'
|
||||||
|
import SelectInput from '@/Shared/SelectInput'
|
||||||
|
import TextInput from '@/Shared/TextInput'
|
||||||
|
import TrashedMessage from '@/Shared/TrashedMessage'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
InertiaLink,
|
||||||
|
Layout,
|
||||||
|
LoadingButton,
|
||||||
|
SelectInput,
|
||||||
|
TextInput,
|
||||||
|
TrashedMessage,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
contact: Object,
|
||||||
|
organizations: Array,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
form: new Form({
|
||||||
|
first_name: this.contact.first_name,
|
||||||
|
last_name: this.contact.last_name,
|
||||||
|
organization_id: this.contact.organization_id,
|
||||||
|
email: this.contact.email,
|
||||||
|
phone: this.contact.phone,
|
||||||
|
address: this.contact.address,
|
||||||
|
city: this.contact.city,
|
||||||
|
region: this.contact.region,
|
||||||
|
country: this.contact.country,
|
||||||
|
postal_code: this.contact.postal_code,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
submit() {
|
||||||
|
this.form.put({
|
||||||
|
url: this.route('contacts.update', this.contact.id).url(),
|
||||||
|
then: () => Inertia.visit(this.route('contacts')),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
destroy() {
|
||||||
|
if (confirm('Are you sure you want to delete this contact?')) {
|
||||||
|
this.form.delete({
|
||||||
|
url: this.route('contacts.destroy', this.contact.id).url(),
|
||||||
|
then: () => Inertia.replace(this.route('contacts.edit', this.contact.id).url()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
restore() {
|
||||||
|
if (confirm('Are you sure you want to restore this contact?')) {
|
||||||
|
this.form.put({
|
||||||
|
url: this.route('contacts.restore', this.contact.id).url(),
|
||||||
|
then: () => Inertia.replace(this.route('contacts.edit', this.contact.id).url()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,108 @@
|
||||||
|
<template>
|
||||||
|
<layout title="Contacts">
|
||||||
|
<h1 class="mb-8 font-bold text-3xl">Contacts</h1>
|
||||||
|
<div class="mb-6 flex justify-between items-center">
|
||||||
|
<search-filter v-model="form.search" class="w-full max-w-sm mr-4" @reset="reset">
|
||||||
|
<label class="block text-grey-darkest">Trashed:</label>
|
||||||
|
<select v-model="form.trashed" class="mt-1 w-full form-select">
|
||||||
|
<option :value="null" />
|
||||||
|
<option value="with">With Trashed</option>
|
||||||
|
<option value="only">Only Trashed</option>
|
||||||
|
</select>
|
||||||
|
</search-filter>
|
||||||
|
<inertia-link class="btn-indigo" :href="route('contacts.create')">
|
||||||
|
<span>Create</span>
|
||||||
|
<span class="hidden md:inline">Contact</span>
|
||||||
|
</inertia-link>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white rounded shadow overflow-x-auto">
|
||||||
|
<table class="w-full whitespace-no-wrap">
|
||||||
|
<tr class="text-left font-bold">
|
||||||
|
<th class="px-6 pt-6 pb-4">Name</th>
|
||||||
|
<th class="px-6 pt-6 pb-4">Organization</th>
|
||||||
|
<th class="px-6 pt-6 pb-4">City</th>
|
||||||
|
<th class="px-6 pt-6 pb-4" colspan="2">Phone</th>
|
||||||
|
</tr>
|
||||||
|
<tr v-for="contact in contacts.data" :key="contact.id" class="hover:bg-grey-lightest focus-within:bg-grey-lightest">
|
||||||
|
<td class="border-t">
|
||||||
|
<inertia-link class="px-6 py-4 flex items-center focus:text-indigo" :href="route('contacts.edit', contact.id)">
|
||||||
|
{{ contact.name }}
|
||||||
|
<icon v-if="contact.deleted_at" name="trash" class="flex-no-shrink w-3 h-3 fill-grey ml-2" />
|
||||||
|
</inertia-link>
|
||||||
|
</td>
|
||||||
|
<td class="border-t">
|
||||||
|
<inertia-link class="px-6 py-4 flex items-center" :href="route('contacts.edit', contact.id)" tabindex="-1">
|
||||||
|
<div v-if="contact.organization">
|
||||||
|
{{ contact.organization.name }}
|
||||||
|
</div>
|
||||||
|
</inertia-link>
|
||||||
|
</td>
|
||||||
|
<td class="border-t">
|
||||||
|
<inertia-link class="px-6 py-4 flex items-center" :href="route('contacts.edit', contact.id)" tabindex="-1">
|
||||||
|
{{ contact.city }}
|
||||||
|
</inertia-link>
|
||||||
|
</td>
|
||||||
|
<td class="border-t">
|
||||||
|
<inertia-link class="px-6 py-4 flex items-center" :href="route('contacts.edit', contact.id)" tabindex="-1">
|
||||||
|
{{ contact.phone }}
|
||||||
|
</inertia-link>
|
||||||
|
</td>
|
||||||
|
<td class="border-t w-px">
|
||||||
|
<inertia-link class="px-4 flex items-center" :href="route('contacts.edit', contact.id)" tabindex="-1">
|
||||||
|
<icon name="cheveron-right" class="block w-6 h-6 fill-grey" />
|
||||||
|
</inertia-link>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="contacts.data.length === 0">
|
||||||
|
<td class="border-t px-6 py-4" colspan="4">No contacts found.</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<pagination :links="contacts.links" />
|
||||||
|
</layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import _ from 'lodash'
|
||||||
|
import { Inertia, InertiaLink } from 'inertia-vue'
|
||||||
|
import Icon from '@/Shared/Icon'
|
||||||
|
import Layout from '@/Shared/Layout'
|
||||||
|
import Pagination from '@/Shared/Pagination'
|
||||||
|
import SearchFilter from '@/Shared/SearchFilter'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
InertiaLink,
|
||||||
|
Icon,
|
||||||
|
Layout,
|
||||||
|
Pagination,
|
||||||
|
SearchFilter,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
contacts: Object,
|
||||||
|
filters: Object,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
form: {
|
||||||
|
search: this.filters.search,
|
||||||
|
trashed: this.filters.trashed,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
form: {
|
||||||
|
handler: _.throttle(function() {
|
||||||
|
let query = _.pickBy(this.form)
|
||||||
|
Inertia.replace(this.route('contacts', Object.keys(query).length ? query : { remember: 'forget' }).url())
|
||||||
|
}, 150),
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
reset() {
|
||||||
|
this.form = _.mapValues(this.form, () => null)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,21 @@
|
||||||
|
<template>
|
||||||
|
<layout title="Dashboard">
|
||||||
|
<h1 class="mb-8 font-bold text-3xl">Dashboard</h1>
|
||||||
|
<div class="mt-16">
|
||||||
|
<inertia-link class="btn-indigo" href="/500">500 error</inertia-link>
|
||||||
|
<inertia-link class="btn-indigo" href="/404">404 error</inertia-link>
|
||||||
|
</div>
|
||||||
|
</layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { InertiaLink } from 'inertia-vue'
|
||||||
|
import Layout from '@/Shared/Layout'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Layout,
|
||||||
|
InertiaLink,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,70 @@
|
||||||
|
<template>
|
||||||
|
<layout title="Create Organization">
|
||||||
|
<h1 class="mb-8 font-bold text-3xl">
|
||||||
|
<inertia-link class="text-indigo-light hover:text-indigo-dark" :href="route('organizations')">Organizations</inertia-link>
|
||||||
|
<span class="text-indigo-light font-medium">/</span> Create
|
||||||
|
</h1>
|
||||||
|
<div class="bg-white rounded shadow overflow-hidden max-w-lg">
|
||||||
|
<form @submit.prevent="submit">
|
||||||
|
<div class="p-8 -mr-6 -mb-8 flex flex-wrap">
|
||||||
|
<text-input v-model="form.fields.name" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('name')" label="Name" />
|
||||||
|
<text-input v-model="form.fields.email" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('email')" label="Email" />
|
||||||
|
<text-input v-model="form.fields.phone" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('phone')" label="Phone" />
|
||||||
|
<text-input v-model="form.fields.address" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('address')" label="Address" />
|
||||||
|
<text-input v-model="form.fields.city" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('city')" label="City" />
|
||||||
|
<text-input v-model="form.fields.region" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('region')" label="Province/State" />
|
||||||
|
<select-input v-model="form.fields.country" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('country')" label="Country">
|
||||||
|
<option :value="null" />
|
||||||
|
<option value="CA">Canada</option>
|
||||||
|
<option value="US">United States</option>
|
||||||
|
</select-input>
|
||||||
|
<text-input v-model="form.fields.postal_code" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('postal_code')" label="Postal code" />
|
||||||
|
</div>
|
||||||
|
<div class="px-8 py-4 bg-grey-lightest border-t border-grey-lighter flex justify-end items-center">
|
||||||
|
<loading-button :loading="form.sending" class="btn-indigo" type="submit">Create Organization</loading-button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { Inertia, InertiaLink } from 'inertia-vue'
|
||||||
|
import Form from '@/Utils/Form'
|
||||||
|
import Layout from '@/Shared/Layout'
|
||||||
|
import LoadingButton from '@/Shared/LoadingButton'
|
||||||
|
import SelectInput from '@/Shared/SelectInput'
|
||||||
|
import TextInput from '@/Shared/TextInput'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
InertiaLink,
|
||||||
|
Layout,
|
||||||
|
LoadingButton,
|
||||||
|
SelectInput,
|
||||||
|
TextInput,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
form: new Form({
|
||||||
|
name: null,
|
||||||
|
email: null,
|
||||||
|
phone: null,
|
||||||
|
address: null,
|
||||||
|
city: null,
|
||||||
|
region: null,
|
||||||
|
country: null,
|
||||||
|
postal_code: null,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
submit() {
|
||||||
|
this.form.post({
|
||||||
|
url: this.route('organizations.store').url(),
|
||||||
|
then: data => Inertia.visit(this.route('organizations.edit', data.id)),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,134 @@
|
||||||
|
<template>
|
||||||
|
<layout :title="form.fields.name">
|
||||||
|
<h1 class="mb-8 font-bold text-3xl">
|
||||||
|
<inertia-link class="text-indigo-light hover:text-indigo-dark" :href="route('organizations')">Organizations</inertia-link>
|
||||||
|
<span class="text-indigo-light font-medium">/</span>
|
||||||
|
{{ form.fields.name }}
|
||||||
|
</h1>
|
||||||
|
<trashed-message v-if="organization.deleted_at" class="mb-6" @restore="restore">
|
||||||
|
This organization has been deleted.
|
||||||
|
</trashed-message>
|
||||||
|
<div class="bg-white rounded shadow overflow-hidden max-w-lg">
|
||||||
|
<form @submit.prevent="submit">
|
||||||
|
<div class="p-8 -mr-6 -mb-8 flex flex-wrap">
|
||||||
|
<text-input v-model="form.fields.name" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('name')" label="Name" />
|
||||||
|
<text-input v-model="form.fields.email" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('email')" label="Email" />
|
||||||
|
<text-input v-model="form.fields.phone" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('phone')" label="Phone" />
|
||||||
|
<text-input v-model="form.fields.address" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('address')" label="Address" />
|
||||||
|
<text-input v-model="form.fields.city" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('city')" label="City" />
|
||||||
|
<text-input v-model="form.fields.region" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('region')" label="Province/State" />
|
||||||
|
<select-input v-model="form.fields.country" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('country')" label="Country">
|
||||||
|
<option :value="null" />
|
||||||
|
<option value="CA">Canada</option>
|
||||||
|
<option value="US">United States</option>
|
||||||
|
</select-input>
|
||||||
|
<text-input v-model="form.fields.postal_code" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('postal_code')" label="Postal code" />
|
||||||
|
</div>
|
||||||
|
<div class="px-8 py-4 bg-grey-lightest border-t border-grey-lighter flex items-center">
|
||||||
|
<button v-if="!organization.deleted_at" class="text-red hover:underline" tabindex="-1" type="button" @click="destroy">Delete Organization</button>
|
||||||
|
<loading-button :loading="form.sending" class="btn-indigo ml-auto" type="submit">Update Organization</loading-button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<h2 class="mt-12 font-bold text-2xl">Contacts</h2>
|
||||||
|
<div class="mt-6 bg-white rounded shadow overflow-x-auto">
|
||||||
|
<table class="w-full whitespace-no-wrap">
|
||||||
|
<tr class="text-left font-bold">
|
||||||
|
<th class="px-6 pt-6 pb-4">Name</th>
|
||||||
|
<th class="px-6 pt-6 pb-4">City</th>
|
||||||
|
<th class="px-6 pt-6 pb-4" colspan="2">Phone</th>
|
||||||
|
</tr>
|
||||||
|
<tr v-for="contact in organization.contacts" :key="contact.id" class="hover:bg-grey-lightest focus-within:bg-grey-lightest">
|
||||||
|
<td class="border-t">
|
||||||
|
<inertia-link class="px-6 py-4 flex items-center focus:text-indigo" :href="route('contacts.edit', contact.id)">
|
||||||
|
{{ contact.name }}
|
||||||
|
<icon v-if="contact.deleted_at" name="trash" class="flex-no-shrink w-3 h-3 fill-grey ml-2" />
|
||||||
|
</inertia-link>
|
||||||
|
</td>
|
||||||
|
<td class="border-t">
|
||||||
|
<inertia-link class="px-6 py-4 flex items-center" :href="route('contacts.edit', contact.id)" tabindex="-1">
|
||||||
|
{{ contact.city }}
|
||||||
|
</inertia-link>
|
||||||
|
</td>
|
||||||
|
<td class="border-t">
|
||||||
|
<inertia-link class="px-6 py-4 flex items-center" :href="route('contacts.edit', contact.id)" tabindex="-1">
|
||||||
|
{{ contact.phone }}
|
||||||
|
</inertia-link>
|
||||||
|
</td>
|
||||||
|
<td class="border-t w-px">
|
||||||
|
<inertia-link class="px-4 flex items-center" :href="route('contacts.edit', contact.id)" tabindex="-1">
|
||||||
|
<icon name="cheveron-right" class="block w-6 h-6 fill-grey" />
|
||||||
|
</inertia-link>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="organization.contacts.length === 0">
|
||||||
|
<td class="border-t px-6 py-4" colspan="4">No contacts found.</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { Inertia, InertiaLink } from 'inertia-vue'
|
||||||
|
import Form from '@/Utils/Form'
|
||||||
|
import Icon from '@/Shared/Icon'
|
||||||
|
import Layout from '@/Shared/Layout'
|
||||||
|
import LoadingButton from '@/Shared/LoadingButton'
|
||||||
|
import SelectInput from '@/Shared/SelectInput'
|
||||||
|
import TextInput from '@/Shared/TextInput'
|
||||||
|
import TrashedMessage from '@/Shared/TrashedMessage'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
InertiaLink,
|
||||||
|
Icon,
|
||||||
|
Layout,
|
||||||
|
LoadingButton,
|
||||||
|
SelectInput,
|
||||||
|
TextInput,
|
||||||
|
TrashedMessage,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
organization: Object,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
form: new Form({
|
||||||
|
name: this.organization.name,
|
||||||
|
email: this.organization.email,
|
||||||
|
phone: this.organization.phone,
|
||||||
|
address: this.organization.address,
|
||||||
|
city: this.organization.city,
|
||||||
|
region: this.organization.region,
|
||||||
|
country: this.organization.country,
|
||||||
|
postal_code: this.organization.postal_code,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
submit() {
|
||||||
|
this.form.put({
|
||||||
|
url: this.route('organizations.update', this.organization.id).url(),
|
||||||
|
then: () => Inertia.visit(this.route('organizations')),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
destroy() {
|
||||||
|
if (confirm('Are you sure you want to delete this organization?')) {
|
||||||
|
this.form.delete({
|
||||||
|
url: this.route('organizations.destroy', this.organization.id).url(),
|
||||||
|
then: () => Inertia.replace(this.route('organizations.edit', this.organization.id).url()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
restore() {
|
||||||
|
if (confirm('Are you sure you want to restore this organization?')) {
|
||||||
|
this.form.put({
|
||||||
|
url: this.route('organizations.restore', this.organization.id).url(),
|
||||||
|
then: () => Inertia.replace(this.route('organizations.edit', this.organization.id).url()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,100 @@
|
||||||
|
<template>
|
||||||
|
<layout title="Organizations">
|
||||||
|
<h1 class="mb-8 font-bold text-3xl">Organizations</h1>
|
||||||
|
<div class="mb-6 flex justify-between items-center">
|
||||||
|
<search-filter v-model="form.search" class="w-full max-w-sm mr-4" @reset="reset">
|
||||||
|
<label class="block text-grey-darkest">Trashed:</label>
|
||||||
|
<select v-model="form.trashed" class="mt-1 w-full form-select">
|
||||||
|
<option :value="null" />
|
||||||
|
<option value="with">With Trashed</option>
|
||||||
|
<option value="only">Only Trashed</option>
|
||||||
|
</select>
|
||||||
|
</search-filter>
|
||||||
|
<inertia-link class="btn-indigo" :href="route('organizations.create')">
|
||||||
|
<span>Create</span>
|
||||||
|
<span class="hidden md:inline">Organization</span>
|
||||||
|
</inertia-link>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white rounded shadow overflow-x-auto">
|
||||||
|
<table class="w-full whitespace-no-wrap">
|
||||||
|
<tr class="text-left font-bold">
|
||||||
|
<th class="px-6 pt-6 pb-4">Name</th>
|
||||||
|
<th class="px-6 pt-6 pb-4">City</th>
|
||||||
|
<th class="px-6 pt-6 pb-4" colspan="2">Phone</th>
|
||||||
|
</tr>
|
||||||
|
<tr v-for="organization in organizations.data" :key="organization.id" class="hover:bg-grey-lightest focus-within:bg-grey-lightest">
|
||||||
|
<td class="border-t">
|
||||||
|
<inertia-link class="px-6 py-4 flex items-center focus:text-indigo" :href="route('organizations.edit', organization.id)">
|
||||||
|
{{ organization.name }}
|
||||||
|
<icon v-if="organization.deleted_at" name="trash" class="flex-no-shrink w-3 h-3 fill-grey ml-2" />
|
||||||
|
</inertia-link>
|
||||||
|
</td>
|
||||||
|
<td class="border-t">
|
||||||
|
<inertia-link class="px-6 py-4 flex items-center" :href="route('organizations.edit', organization.id)" tabindex="-1">
|
||||||
|
{{ organization.city }}
|
||||||
|
</inertia-link>
|
||||||
|
</td>
|
||||||
|
<td class="border-t">
|
||||||
|
<inertia-link class="px-6 py-4 flex items-center" :href="route('organizations.edit', organization.id)" tabindex="-1">
|
||||||
|
{{ organization.phone }}
|
||||||
|
</inertia-link>
|
||||||
|
</td>
|
||||||
|
<td class="border-t w-px">
|
||||||
|
<inertia-link class="px-4 flex items-center" :href="route('organizations.edit', organization.id)" tabindex="-1">
|
||||||
|
<icon name="cheveron-right" class="block w-6 h-6 fill-grey" />
|
||||||
|
</inertia-link>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="organizations.data.length === 0">
|
||||||
|
<td class="border-t px-6 py-4" colspan="4">No organizations found.</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<pagination :links="organizations.links" />
|
||||||
|
</layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import _ from 'lodash'
|
||||||
|
import { Inertia, InertiaLink } from 'inertia-vue'
|
||||||
|
import Icon from '@/Shared/Icon'
|
||||||
|
import Layout from '@/Shared/Layout'
|
||||||
|
import Pagination from '@/Shared/Pagination'
|
||||||
|
import SearchFilter from '@/Shared/SearchFilter'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
InertiaLink,
|
||||||
|
Icon,
|
||||||
|
Layout,
|
||||||
|
Pagination,
|
||||||
|
SearchFilter,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
organizations: Object,
|
||||||
|
filters: Object,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
form: {
|
||||||
|
search: this.filters.search,
|
||||||
|
trashed: this.filters.trashed,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
form: {
|
||||||
|
handler: _.throttle(function() {
|
||||||
|
let query = _.pickBy(this.form)
|
||||||
|
Inertia.replace(this.route('organizations', Object.keys(query).length ? query : { remember: 'forget' }).url())
|
||||||
|
}, 150),
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
reset() {
|
||||||
|
this.form = _.mapValues(this.form, () => null)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,15 @@
|
||||||
|
<template>
|
||||||
|
<layout title="Reports">
|
||||||
|
<h1 class="mb-8 font-bold text-3xl">Reports</h1>
|
||||||
|
</layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Layout from '@/Shared/Layout'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Layout,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,66 @@
|
||||||
|
<template>
|
||||||
|
<layout title="Create User">
|
||||||
|
<h1 class="mb-8 font-bold text-3xl">
|
||||||
|
<inertia-link class="text-indigo-light hover:text-indigo-dark" :href="route('users')">Users</inertia-link>
|
||||||
|
<span class="text-indigo-light font-medium">/</span> Create
|
||||||
|
</h1>
|
||||||
|
<div class="bg-white rounded shadow overflow-hidden max-w-lg">
|
||||||
|
<form @submit.prevent="submit">
|
||||||
|
<div class="p-8 -mr-6 -mb-8 flex flex-wrap">
|
||||||
|
<text-input v-model="form.fields.first_name" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('first_name')" label="First name" />
|
||||||
|
<text-input v-model="form.fields.last_name" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('last_name')" label="Last name" />
|
||||||
|
<text-input v-model="form.fields.email" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('email')" label="Email" />
|
||||||
|
<text-input v-model="form.fields.password" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('password')" type="password" autocomplete="new-password" label="Password" />
|
||||||
|
<select-input v-model="form.fields.owner" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('owner')" label="Owner">
|
||||||
|
<option :value="true">Yes</option>
|
||||||
|
<option :value="false">No</option>
|
||||||
|
</select-input>
|
||||||
|
</div>
|
||||||
|
<div class="px-8 py-4 bg-grey-lightest border-t border-grey-lighter flex justify-end items-center">
|
||||||
|
<loading-button :loading="form.sending" class="btn-indigo" type="submit">Create User</loading-button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { Inertia, InertiaLink } from 'inertia-vue'
|
||||||
|
import Form from '@/Utils/Form'
|
||||||
|
import Layout from '@/Shared/Layout'
|
||||||
|
import LoadingButton from '@/Shared/LoadingButton'
|
||||||
|
import SelectInput from '@/Shared/SelectInput'
|
||||||
|
import TextInput from '@/Shared/TextInput'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
InertiaLink,
|
||||||
|
Layout,
|
||||||
|
LoadingButton,
|
||||||
|
SelectInput,
|
||||||
|
TextInput,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
organizations: Array,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
form: new Form({
|
||||||
|
first_name: null,
|
||||||
|
last_name: null,
|
||||||
|
email: null,
|
||||||
|
password: null,
|
||||||
|
owner: null,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
submit() {
|
||||||
|
this.form.post({
|
||||||
|
url: this.route('users.store').url(),
|
||||||
|
then: data => Inertia.visit(this.route('users.edit', data.id)),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,90 @@
|
||||||
|
<template>
|
||||||
|
<layout :title="`${form.fields.first_name} ${form.fields.last_name}`">
|
||||||
|
<h1 class="mb-8 font-bold text-3xl">
|
||||||
|
<inertia-link class="text-indigo-light hover:text-indigo-dark" :href="route('users')">Users</inertia-link>
|
||||||
|
<span class="text-indigo-light font-medium">/</span>
|
||||||
|
{{ form.fields.first_name }} {{ form.fields.last_name }}
|
||||||
|
</h1>
|
||||||
|
<trashed-message v-if="user.deleted_at" class="mb-6" @restore="restore">
|
||||||
|
This user has been deleted.
|
||||||
|
</trashed-message>
|
||||||
|
<div class="bg-white rounded shadow overflow-hidden max-w-lg">
|
||||||
|
<form @submit.prevent="submit">
|
||||||
|
<div class="p-8 -mr-6 -mb-8 flex flex-wrap">
|
||||||
|
<text-input v-model="form.fields.first_name" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('first_name')" label="First name" />
|
||||||
|
<text-input v-model="form.fields.last_name" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('last_name')" label="Last name" />
|
||||||
|
<text-input v-model="form.fields.email" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('email')" label="Email" />
|
||||||
|
<text-input v-model="form.fields.password" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('password')" type="password" autocomplete="new-password" label="Password" />
|
||||||
|
<select-input v-model="form.fields.owner" class="pr-6 pb-8 w-full lg:w-1/2" :error="form.errors.first('owner')" label="Owner">
|
||||||
|
<option :value="true">Yes</option>
|
||||||
|
<option :value="false">No</option>
|
||||||
|
</select-input>
|
||||||
|
</div>
|
||||||
|
<div class="px-8 py-4 bg-grey-lightest border-t border-grey-lighter flex items-center">
|
||||||
|
<button v-if="!user.deleted_at" class="text-red hover:underline" tabindex="-1" type="button" @click="destroy">Delete User</button>
|
||||||
|
<loading-button :loading="form.sending" class="btn-indigo ml-auto" type="submit">Update User</loading-button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { Inertia, InertiaLink } from 'inertia-vue'
|
||||||
|
import Form from '@/Utils/Form'
|
||||||
|
import Layout from '@/Shared/Layout'
|
||||||
|
import LoadingButton from '@/Shared/LoadingButton'
|
||||||
|
import SelectInput from '@/Shared/SelectInput'
|
||||||
|
import TextInput from '@/Shared/TextInput'
|
||||||
|
import TrashedMessage from '@/Shared/TrashedMessage'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
InertiaLink,
|
||||||
|
Layout,
|
||||||
|
LoadingButton,
|
||||||
|
SelectInput,
|
||||||
|
TextInput,
|
||||||
|
TrashedMessage,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
user: Object,
|
||||||
|
organizations: Array,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
form: new Form({
|
||||||
|
first_name: this.user.first_name,
|
||||||
|
last_name: this.user.last_name,
|
||||||
|
email: this.user.email,
|
||||||
|
password: this.user.password,
|
||||||
|
owner: this.user.owner,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
submit() {
|
||||||
|
this.form.put({
|
||||||
|
url: this.route('users.update', this.user.id).url(),
|
||||||
|
then: () => Inertia.visit(this.route('users')),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
destroy() {
|
||||||
|
if (confirm('Are you sure you want to delete this user?')) {
|
||||||
|
this.form.delete({
|
||||||
|
url: this.route('users.destroy', this.user.id).url(),
|
||||||
|
then: () => Inertia.replace(this.route('users.edit', this.user.id).url()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
restore() {
|
||||||
|
if (confirm('Are you sure you want to restore this user?')) {
|
||||||
|
this.form.put({
|
||||||
|
url: this.route('users.restore', this.user.id).url(),
|
||||||
|
then: () => Inertia.replace(this.route('users.edit', this.user.id).url()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,104 @@
|
||||||
|
<template>
|
||||||
|
<layout title="Users">
|
||||||
|
<h1 class="mb-8 font-bold text-3xl">Users</h1>
|
||||||
|
<div class="mb-6 flex justify-between items-center">
|
||||||
|
<search-filter v-model="form.search" class="w-full max-w-sm mr-4" @reset="reset">
|
||||||
|
<label class="block text-grey-darkest">Role:</label>
|
||||||
|
<select v-model="form.role" class="mt-1 w-full form-select">
|
||||||
|
<option :value="null" />
|
||||||
|
<option value="user">User</option>
|
||||||
|
<option value="owner">Owner</option>
|
||||||
|
</select>
|
||||||
|
<label class="mt-4 block text-grey-darkest">Trashed:</label>
|
||||||
|
<select v-model="form.trashed" class="mt-1 w-full form-select">
|
||||||
|
<option :value="null" />
|
||||||
|
<option value="with">With Trashed</option>
|
||||||
|
<option value="only">Only Trashed</option>
|
||||||
|
</select>
|
||||||
|
</search-filter>
|
||||||
|
<inertia-link class="btn-indigo" :href="route('users.create')">
|
||||||
|
<span>Create</span>
|
||||||
|
<span class="hidden md:inline">User</span>
|
||||||
|
</inertia-link>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white rounded shadow overflow-x-auto">
|
||||||
|
<table class="w-full whitespace-no-wrap">
|
||||||
|
<tr class="text-left font-bold">
|
||||||
|
<th class="px-6 pt-6 pb-4">Name</th>
|
||||||
|
<th class="px-6 pt-6 pb-4">Email</th>
|
||||||
|
<th class="px-6 pt-6 pb-4" colspan="2">Role</th>
|
||||||
|
</tr>
|
||||||
|
<tr v-for="user in users" :key="user.id" class="hover:bg-grey-lightest focus-within:bg-grey-lightest">
|
||||||
|
<td class="border-t">
|
||||||
|
<inertia-link class="px-6 py-4 flex items-center focus:text-indigo" :href="route('users.edit', user.id)">
|
||||||
|
{{ user.name }}
|
||||||
|
<icon v-if="user.deleted_at" name="trash" class="flex-no-shrink w-3 h-3 fill-grey ml-2" />
|
||||||
|
</inertia-link>
|
||||||
|
</td>
|
||||||
|
<td class="border-t">
|
||||||
|
<inertia-link class="px-6 py-4 flex items-center" :href="route('users.edit', user.id)" tabindex="-1">
|
||||||
|
{{ user.email }}
|
||||||
|
</inertia-link>
|
||||||
|
</td>
|
||||||
|
<td class="border-t">
|
||||||
|
<inertia-link class="px-6 py-4 flex items-center" :href="route('users.edit', user.id)" tabindex="-1">
|
||||||
|
{{ user.owner ? 'Owner' : 'User' }}
|
||||||
|
</inertia-link>
|
||||||
|
</td>
|
||||||
|
<td class="border-t w-px">
|
||||||
|
<inertia-link class="px-4 flex items-center" :href="route('users.edit', user.id)" tabindex="-1">
|
||||||
|
<icon name="cheveron-right" class="block w-6 h-6 fill-grey" />
|
||||||
|
</inertia-link>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="users.length === 0">
|
||||||
|
<td class="border-t px-6 py-4" colspan="4">No users found.</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import _ from 'lodash'
|
||||||
|
import { Inertia, InertiaLink } from 'inertia-vue'
|
||||||
|
import Icon from '@/Shared/Icon'
|
||||||
|
import Layout from '@/Shared/Layout'
|
||||||
|
import SearchFilter from '@/Shared/SearchFilter'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
InertiaLink,
|
||||||
|
Icon,
|
||||||
|
Layout,
|
||||||
|
SearchFilter,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
users: Array,
|
||||||
|
filters: Object,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
form: {
|
||||||
|
search: this.filters.search,
|
||||||
|
role: this.filters.role,
|
||||||
|
trashed: this.filters.trashed,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
form: {
|
||||||
|
handler: _.throttle(function() {
|
||||||
|
let query = _.pickBy(this.form)
|
||||||
|
Inertia.replace(this.route('users', Object.keys(query).length ? query : { remember: 'forget' }).url())
|
||||||
|
}, 150),
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
reset() {
|
||||||
|
this.form = _.mapValues(this.form, () => null)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,66 @@
|
||||||
|
<template>
|
||||||
|
<button type="button" @click="toggle">
|
||||||
|
<slot />
|
||||||
|
<portal v-if="show" to="dropdown">
|
||||||
|
<div>
|
||||||
|
<div style="position: fixed; top: 0; right: 0; left: 0; bottom: 0; z-index: 99998; background: black; opacity: .2" @click="toggle" />
|
||||||
|
<div ref="dropdown" style="position: absolute; z-index: 99999;" @click.stop>
|
||||||
|
<slot name="dropdown" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</portal>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Popper from 'popper.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
placement: {
|
||||||
|
type: String,
|
||||||
|
default: 'bottom-end',
|
||||||
|
},
|
||||||
|
boundary: {
|
||||||
|
type: String,
|
||||||
|
default: 'scrollParent',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
show: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
show(show) {
|
||||||
|
if (show) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.popper = new Popper(this.$el, this.$refs.dropdown, {
|
||||||
|
placement: this.placement,
|
||||||
|
modifiers: {
|
||||||
|
preventOverflow: { boundariesElement: this.boundary },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else if (this.popper) {
|
||||||
|
setTimeout(() => this.popper.destroy(), 100)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
if (e.keyCode === 27) {
|
||||||
|
this.close()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
close() {
|
||||||
|
this.show = false
|
||||||
|
},
|
||||||
|
toggle() {
|
||||||
|
this.show = !this.show
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,22 @@
|
||||||
|
<template>
|
||||||
|
<svg v-if="name === 'apple'" xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><g fill-rule="nonzero"><path d="M46.173 19.967C49.927-1.838 19.797-.233 14.538.21c-.429.035-.648.4-.483.8 2.004 4.825 14.168 31.66 32.118 18.957zm13.18 1.636c1.269-.891 1.35-1.614.047-2.453l-2.657-1.71c-.94-.607-1.685-.606-2.532.129-5.094 4.42-7.336 9.18-8.211 15.24 1.597.682 3.55.79 5.265.328 1.298-4.283 3.64-8.412 8.088-11.534z" /><path d="M88.588 67.75c9.65-27.532-13.697-45.537-35.453-32.322-1.84 1.118-4.601 1.118-6.441 0-21.757-13.215-45.105 4.79-35.454 32.321 5.302 15.123 17.06 39.95 37.295 29.995.772-.38 1.986-.38 2.758 0 20.235 9.955 31.991-14.872 37.295-29.995z" /></g></svg>
|
||||||
|
<svg v-else-if="name === 'book'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M6 4H5a1 1 0 1 1 0-2h11V1a1 1 0 0 0-1-1H4a2 2 0 0 0-2 2v16c0 1.1.9 2 2 2h12a2 2 0 0 0 2-2V5a1 1 0 0 0-1-1h-7v8l-2-2-2 2V4z" /></svg>
|
||||||
|
<svg v-else-if="name === 'cheveron-down'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z" /></svg>
|
||||||
|
<svg v-else-if="name === 'cheveron-right'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><polygon points="12.95 10.707 13.657 10 8 4.343 6.586 5.757 10.828 10 6.586 14.243 8 15.657 12.95 10.707" /></svg>
|
||||||
|
<svg v-else-if="name === 'dashboard'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm-5.6-4.29a9.95 9.95 0 0 1 11.2 0 8 8 0 1 0-11.2 0zm6.12-7.64l3.02-3.02 1.41 1.41-3.02 3.02a2 2 0 1 1-1.41-1.41z" /></svg>
|
||||||
|
<svg v-else-if="name === 'location'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M10 20S3 10.87 3 7a7 7 0 1 1 14 0c0 3.87-7 13-7 13zm0-11a2 2 0 1 0 0-4 2 2 0 0 0 0 4z" /></svg>
|
||||||
|
<svg v-else-if="name === 'office'" xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path fill-rule="evenodd" d="M7 0h86v100H57.108V88.418H42.892V100H7V0zm9 64h11v15H16V64zm57 0h11v15H73V64zm-19 0h11v15H54V64zm-19 0h11v15H35V64zM16 37h11v15H16V37zm57 0h11v15H73V37zm-19 0h11v15H54V37zm-19 0h11v15H35V37zM16 11h11v15H16V11zm57 0h11v15H73V11zm-19 0h11v15H54V11zm-19 0h11v15H35V11z" /></svg>
|
||||||
|
<svg v-else-if="name === 'printer'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M4 16H0V6h20v10h-4v4H4v-4zm2-4v6h8v-6H6zM4 0h12v5H4V0zM2 8v2h2V8H2zm4 0v2h2V8H6z" /></svg>
|
||||||
|
<svg v-else-if="name === 'shopping-cart'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M4 2h16l-3 9H4a1 1 0 1 0 0 2h13v2H4a3 3 0 0 1 0-6h.33L3 5 2 2H0V0h3a1 1 0 0 1 1 1v1zm1 18a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm10 0a2 2 0 1 1 0-4 2 2 0 0 1 0 4z" /></svg>
|
||||||
|
<svg v-else-if="name === 'store-front'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M18 9.87V20H2V9.87a4.25 4.25 0 0 0 3-.38V14h10V9.5a4.26 4.26 0 0 0 3 .37zM3 0h4l-.67 6.03A3.43 3.43 0 0 1 3 9C1.34 9 .42 7.73.95 6.15L3 0zm5 0h4l.7 6.3c.17 1.5-.91 2.7-2.42 2.7h-.56A2.38 2.38 0 0 1 7.3 6.3L8 0zm5 0h4l2.05 6.15C19.58 7.73 18.65 9 17 9a3.42 3.42 0 0 1-3.33-2.97L13 0z" /></svg>
|
||||||
|
<svg v-else-if="name === 'trash'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M6 2l2-2h4l2 2h4v2H2V2h4zM3 6h14l-1 14H4L3 6zm5 2v10h1V8H8zm3 0v10h1V8h-1z" /></svg>
|
||||||
|
<svg v-else-if="name === 'users'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M7 8a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm0 1c2.15 0 4.2.4 6.1 1.09L12 16h-1.25L10 20H4l-.75-4H2L.9 10.09A17.93 17.93 0 0 1 7 9zm8.31.17c1.32.18 2.59.48 3.8.92L18 16h-1.25L16 20h-3.96l.37-2h1.25l1.65-8.83zM13 0a4 4 0 1 1-1.33 7.76 5.96 5.96 0 0 0 0-7.52C12.1.1 12.53 0 13 0z" /></svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,91 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<portal-target name="dropdown" slim />
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<div class="min-h-screen flex flex-col" @click="hideDropdownMenus">
|
||||||
|
<div class="md:flex">
|
||||||
|
<div class="bg-indigo-darkest md:flex-no-shrink md:w-56 px-6 py-4 flex items-center justify-between md:justify-center">
|
||||||
|
<inertia-link class="mt-1" href="/">
|
||||||
|
<logo class="fill-white" width="120" height="28" />
|
||||||
|
</inertia-link>
|
||||||
|
<dropdown class="md:hidden" placement="bottom-end">
|
||||||
|
<svg class="fill-white w-6 h-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z" /></svg>
|
||||||
|
<div slot="dropdown" class="mt-2 px-8 py-4 shadow-lg bg-indigo-darker rounded">
|
||||||
|
<main-menu />
|
||||||
|
</div>
|
||||||
|
</dropdown>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white border-b w-full p-4 md:py-0 md:px-12 text-sm md:text-base flex justify-between items-center">
|
||||||
|
<div class="mt-1 mr-4">{{ page.props.auth.user.account.name }}</div>
|
||||||
|
<dropdown class="mt-1" placement="bottom-end">
|
||||||
|
<div class="flex items-center cursor-pointer select-none group">
|
||||||
|
<div class="text-grey-darkest group-hover:text-indigo-dark focus:text-indigo-dark mr-1 whitespace-no-wrap">
|
||||||
|
<span>{{ page.props.auth.user.first_name }}</span>
|
||||||
|
<span class="hidden md:inline">{{ page.props.auth.user.last_name }}</span>
|
||||||
|
</div>
|
||||||
|
<icon class="w-5 h-5 group-hover:fill-indigo-dark fill-grey-darkest focus:fill-indigo-dark" name="cheveron-down" />
|
||||||
|
</div>
|
||||||
|
<div slot="dropdown" class="mt-2 py-2 shadow-lg bg-white rounded text-sm">
|
||||||
|
<inertia-link class="block px-6 py-2 hover:bg-indigo hover:text-white" :href="route('users.edit', page.props.auth.user.id)">My Profile</inertia-link>
|
||||||
|
<inertia-link class="block px-6 py-2 hover:bg-indigo hover:text-white" :href="route('users')">Manage Users</inertia-link>
|
||||||
|
<inertia-link class="block px-6 py-2 hover:bg-indigo hover:text-white" :href="route('logout')">Logout</inertia-link>
|
||||||
|
</div>
|
||||||
|
</dropdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-grow">
|
||||||
|
<div class="bg-indigo-darker flex-no-shrink w-56 p-12 hidden md:block">
|
||||||
|
<main-menu />
|
||||||
|
</div>
|
||||||
|
<div class="w-full overflow-hidden px-4 py-8 md:p-12">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { InertiaLink } from 'inertia-vue'
|
||||||
|
import Dropdown from '@/Shared/Dropdown'
|
||||||
|
import Icon from '@/Shared/Icon'
|
||||||
|
import Logo from '@/Shared/Logo'
|
||||||
|
import MainMenu from '@/Shared/MainMenu'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
InertiaLink,
|
||||||
|
Dropdown,
|
||||||
|
Icon,
|
||||||
|
Logo,
|
||||||
|
MainMenu,
|
||||||
|
},
|
||||||
|
inject: ['page'],
|
||||||
|
props: {
|
||||||
|
title: String,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showUserMenu: false,
|
||||||
|
accounts: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
title(title) {
|
||||||
|
this.updatePageTitle(title)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.updatePageTitle(this.title)
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updatePageTitle(title) {
|
||||||
|
document.title = title ? `${title} | ${this.page.props.app.name}` : this.page.props.app.name
|
||||||
|
},
|
||||||
|
hideDropdownMenus() {
|
||||||
|
this.showUserMenu = false
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,14 @@
|
||||||
|
<template>
|
||||||
|
<button :disabled="loading" class="flex items-center">
|
||||||
|
<div v-if="loading" class="spinner mr-2" />
|
||||||
|
<slot />
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
loading: Boolean,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<template>
|
||||||
|
<svg viewBox="0 0 1185 266" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M77.463 265c-19.497 0-35.318-15.405-35.318-34.39v-22.054C17.987 202.676 0 181.326 0 155.948V55.206C0 25.291 24.946 1 55.668 1h154.664C241.054 1 266 25.29 266 55.206v100.806c0 29.916-24.946 54.206-55.668 54.206H145.67c-2.823 0-5.383 1.407-6.827 3.58-10.7 17.067-24.158 31.897-39.98 43.915-6.236 4.794-13.654 7.287-21.4 7.287zM55.701 27.336c-15.771 0-28.65 12.465-28.65 27.87v100.806c0 15.342 12.813 27.87 28.65 27.87 7.49 0 13.536 5.881 13.536 13.168v33.624c0 4.922 4.272 7.99 8.214 7.99 1.709 0 3.286-.575 4.732-1.662 13.273-10.1 24.576-22.565 33.578-36.947 6.309-10.036 17.743-16.237 29.965-16.237h64.727c15.77 0 28.65-12.464 28.65-27.87V55.206c0-15.341-12.814-27.87-28.65-27.87H55.7z" />
|
||||||
|
<path d="M395.752 2.4c37.152 0 65.088 27.936 65.088 64.8 0 36.576-27.936 64.8-65.088 64.8h-46.368v72H322.6V2.4h73.152zm0 104.544c22.176 0 38.592-16.992 38.592-39.744 0-23.04-16.416-39.744-38.592-39.744h-46.368v79.488h46.368zM502.6 33.792c-9.504 0-16.992-7.488-16.992-16.704 0-9.216 7.488-16.992 16.992-16.992 9.216 0 16.704 7.776 16.704 16.992 0 9.216-7.488 16.704-16.704 16.704zM489.928 204V60h25.056v144h-25.056zM625 56.256c33.696 0 55.872 22.464 55.872 59.328V204h-25.056v-86.976c0-23.616-13.536-36.864-35.712-36.864-23.04 0-41.76 13.536-41.76 47.52V204h-25.056V60h25.056v20.736C589 63.744 604.84 56.256 625 56.256zM835.24 60h24.768v137.952c0 44.928-36 67.392-73.44 67.392-32.256 0-56.448-12.384-68.256-35.136l21.888-12.384c6.624 13.536 18.72 24.192 46.944 24.192 29.952 0 48.096-16.992 48.096-44.064v-20.448c-11.52 17.568-29.952 28.8-54.144 28.8-40.896 0-73.44-33.12-73.44-75.168 0-41.76 32.544-74.88 73.44-74.88 24.192 0 42.624 10.944 54.144 28.512V60zm-51.264 122.4c29.088 0 51.264-22.176 51.264-51.264 0-28.8-22.176-50.976-51.264-50.976-29.088 0-51.264 22.176-51.264 50.976 0 29.088 22.176 51.264 51.264 51.264zM946.8 205.08c-28.21 0-45.63-20.8-41.08-48.88 4.42-27.17 26.91-46.28 53.56-46.28 19.37 0 31.59 9.36 38.35 22.36l-23.79 12.61c-3.25-5.85-9.1-9.49-16.9-9.49-12.35 0-23.14 9.23-25.35 22.1-2.08 11.83 4.29 22.1 17.16 22.1 8.06 0 13.91-4.03 18.59-10.14l21.58 13.65c-9.36 13.78-24.44 21.97-42.12 21.97zm126.36-59.93c-1.95 11.18-8.58 19.5-18.2 24.44l11.7 33.28h-26l-9.36-28.6h-8.32l-5.07 28.6h-26l16.12-91h36.4c18.33 0 32.24 13.65 28.73 33.28zm-43.42-9.36l-2.99 16.9h10.66c5.07.13 8.84-2.99 9.75-8.32.91-5.33-1.82-8.58-7.02-8.58h-10.4zM1184.05 112l-15.99 91h-26l7.67-43.81-25.48 33.54h-2.34l-14.82-35.23-7.93 45.5h-26l15.99-91h26l13.65 37.31 27.95-37.31h27.3z" />
|
||||||
|
</svg>
|
||||||
|
</template>
|
|
@ -0,0 +1,49 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<inertia-link class="flex items-center group py-3" :href="route('dashboard')">
|
||||||
|
<icon name="dashboard" class="w-4 h-4 mr-2" :class="isUrl('') ? 'fill-white' : 'fill-indigo-light group-hover:fill-white'" />
|
||||||
|
<div :class="isUrl('') ? 'text-white' : 'text-indigo-lighter group-hover:text-white'">Dashboard</div>
|
||||||
|
</inertia-link>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<inertia-link class="flex items-center group py-3" :href="route('organizations')">
|
||||||
|
<icon name="office" class="w-4 h-4 mr-2" :class="isUrl('organizations') ? 'fill-white' : 'fill-indigo-light group-hover:fill-white'" />
|
||||||
|
<div :class="isUrl('organizations') ? 'text-white' : 'text-indigo-lighter group-hover:text-white'">Organizations</div>
|
||||||
|
</inertia-link>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<inertia-link class="flex items-center group py-3" :href="route('contacts')">
|
||||||
|
<icon name="users" class="w-4 h-4 mr-2" :class="isUrl('contacts') ? 'fill-white' : 'fill-indigo-light group-hover:fill-white'" />
|
||||||
|
<div :class="isUrl('contacts') ? 'text-white' : 'text-indigo-lighter group-hover:text-white'">Contacts</div>
|
||||||
|
</inertia-link>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<inertia-link class="flex items-center group py-3" :href="route('reports')">
|
||||||
|
<icon name="printer" class="w-4 h-4 mr-2" :class="isUrl('reports') ? 'fill-white' : 'fill-indigo-light group-hover:fill-white'" />
|
||||||
|
<div :class="isUrl('reports') ? 'text-white' : 'text-indigo-lighter group-hover:text-white'">Reports</div>
|
||||||
|
</inertia-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { InertiaLink } from 'inertia-vue'
|
||||||
|
import Icon from '@/Shared/Icon'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
InertiaLink,
|
||||||
|
Icon,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isUrl(...urls) {
|
||||||
|
if (urls[0] === '') {
|
||||||
|
return location.pathname.substr(1) === ''
|
||||||
|
}
|
||||||
|
|
||||||
|
return urls.filter(url => location.pathname.substr(1).startsWith(url)).length
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,21 @@
|
||||||
|
<template>
|
||||||
|
<div class="mt-6 -mb-1 flex flex-wrap">
|
||||||
|
<template v-for="(link, key) in links">
|
||||||
|
<div v-if="link.url === null" :key="key" class="mr-1 mb-1 px-4 py-3 text-sm border rounded text-grey" :class="{ 'ml-auto': link.label === 'Next' }">{{ link.label }}</div>
|
||||||
|
<inertia-link v-else :key="key" replace class="mr-1 mb-1 px-4 py-3 text-sm border rounded hover:bg-white focus:border-indigo focus:text-indigo" :class="{ 'bg-white': link.active, 'ml-auto': link.label === 'Next' }" :href="link.url">{{ link.label }}</inertia-link>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { InertiaLink } from 'inertia-vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
InertiaLink,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
links: Array,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,34 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="flex w-full bg-white shadow rounded">
|
||||||
|
<dropdown class="px-4 md:px-6 rounded-l border-r flex items-baseline hover:bg-grey-lightest focus:border-white focus:shadow-outline focus:z-10" placement="bottom-start">
|
||||||
|
<span class="text-grey-darkest hidden md:block">Filter</span>
|
||||||
|
<svg class="w-2 h-2 fill-grey-darker md:ml-2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 961.243 599.998">
|
||||||
|
<path d="M239.998 239.999L0 0h961.243L721.246 240c-131.999 132-240.28 240-240.624 239.999-.345-.001-108.625-108.001-240.624-240z" />
|
||||||
|
</svg>
|
||||||
|
<div slot="dropdown" class="mt-2 px-4 py-6 w-screen shadow-lg bg-white rounded" :style="{ maxWidth: `${maxWidth}px` }">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</dropdown>
|
||||||
|
<input class="relative w-full px-6 py-3 rounded-r focus:shadow-outline" autocomplete="off" type="text" name="search" placeholder="Search…" :value="value" @input="$emit('input', $event.target.value)">
|
||||||
|
</div>
|
||||||
|
<button class="ml-3 text-sm text-grey-dark hover:text-grey-darker focus:text-indigo" type="button" @click="$emit('reset')">Reset</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Dropdown from '@/Shared/Dropdown'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Dropdown,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
value: String,
|
||||||
|
maxWidth: {
|
||||||
|
type: Number,
|
||||||
|
default: 300,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,44 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<label v-if="label" class="form-label" :for="id">{{ label }}:</label>
|
||||||
|
<select :id="id" ref="input" v-model="selected" v-bind="$attrs" class="form-select" :class="{ error: error }">
|
||||||
|
<slot />
|
||||||
|
</select>
|
||||||
|
<div v-if="error" class="form-error">{{ error }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
inheritAttrs: false,
|
||||||
|
props: {
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
default() {
|
||||||
|
return `select-input-${this._uid}`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
value: [String, Number],
|
||||||
|
label: String,
|
||||||
|
error: String,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
selected: this.value,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
selected(selected) {
|
||||||
|
this.$emit('input', selected)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
focus() {
|
||||||
|
this.$refs.input.focus()
|
||||||
|
},
|
||||||
|
select() {
|
||||||
|
this.$refs.input.select()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,39 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<label v-if="label" class="form-label" :for="id">{{ label }}:</label>
|
||||||
|
<input :id="id" ref="input" v-bind="$attrs" class="form-input" :class="{ error: error }" :type="type" :value="value" @input="$emit('input', $event.target.value)">
|
||||||
|
<div v-if="error" class="form-error">{{ error }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
inheritAttrs: false,
|
||||||
|
props: {
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
default() {
|
||||||
|
return `text-input-${this._uid}`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: 'text',
|
||||||
|
},
|
||||||
|
value: String,
|
||||||
|
label: String,
|
||||||
|
error: String,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
focus() {
|
||||||
|
this.$refs.input.focus()
|
||||||
|
},
|
||||||
|
select() {
|
||||||
|
this.$refs.input.select()
|
||||||
|
},
|
||||||
|
setSelectionRange(start, end) {
|
||||||
|
this.$refs.input.setSelectionRange(start, end)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,40 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<label v-if="label" class="form-label" :for="id">{{ label }}:</label>
|
||||||
|
<textarea :id="id" ref="input" v-bind="$attrs" class="form-textarea" :class="{ error: error }" :value="value" @input="$emit('input', $event.target.value)" />
|
||||||
|
<div v-if="error" class="form-error">{{ error }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import autosize from 'autosize'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
inheritAttrs: false,
|
||||||
|
props: {
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
default() {
|
||||||
|
return `textarea-input-${this._uid}`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
value: String,
|
||||||
|
label: String,
|
||||||
|
error: String,
|
||||||
|
autosize: Boolean,
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
if (this.autosize) {
|
||||||
|
autosize(this.$refs.input)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
focus() {
|
||||||
|
this.$refs.input.focus()
|
||||||
|
},
|
||||||
|
select() {
|
||||||
|
this.$refs.input.select()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,21 @@
|
||||||
|
<template>
|
||||||
|
<div class="p-4 bg-yellow-light rounded border border-yellow-dark flex items-center justify-between">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<icon name="trash" class="flex-no-shrink w-4 h-4 fill-yellow-darker mr-2" />
|
||||||
|
<div class="text-yellow-darker">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="text-yellow-darker hover:underline" tabindex="-1" type="button" @click="$emit('restore')">Restore</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Icon from '@/Shared/Icon'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Icon,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,31 @@
|
||||||
|
class Errors {
|
||||||
|
constructor(errors = {}) {
|
||||||
|
this.record(errors)
|
||||||
|
}
|
||||||
|
|
||||||
|
record(errors = {}) {
|
||||||
|
this.errors = errors
|
||||||
|
}
|
||||||
|
|
||||||
|
all() {
|
||||||
|
return this.errors
|
||||||
|
}
|
||||||
|
|
||||||
|
any() {
|
||||||
|
return Object.keys(this.errors).length > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
has(key) {
|
||||||
|
return key in this.errors
|
||||||
|
}
|
||||||
|
|
||||||
|
first(field) {
|
||||||
|
return this.get(field)[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
get(field) {
|
||||||
|
return this.errors[field] || []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Errors
|
|
@ -0,0 +1,44 @@
|
||||||
|
import axios from 'axios'
|
||||||
|
import Errors from '@/Utils/Errors'
|
||||||
|
|
||||||
|
class Form {
|
||||||
|
constructor(fields = {}) {
|
||||||
|
this.fields = fields
|
||||||
|
this.sending = false
|
||||||
|
this.errors = new Errors()
|
||||||
|
this.http = axios.create({
|
||||||
|
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
delete({ url, then }) {
|
||||||
|
this.request(this.http.delete(url), then)
|
||||||
|
}
|
||||||
|
|
||||||
|
post({ url, data = this.fields, then }) {
|
||||||
|
this.request(this.http.post(url, data), then)
|
||||||
|
}
|
||||||
|
|
||||||
|
put({ url, data = this.fields, then }) {
|
||||||
|
this.request(this.http.put(url, data), then)
|
||||||
|
}
|
||||||
|
|
||||||
|
request(request, then) {
|
||||||
|
this.sending = true
|
||||||
|
|
||||||
|
request.then(response => {
|
||||||
|
this.sending = false
|
||||||
|
then(response.data)
|
||||||
|
}).catch(error => {
|
||||||
|
this.sending = false
|
||||||
|
|
||||||
|
if (error.response && error.response.status === 422) {
|
||||||
|
this.errors.record(error.response.data.errors)
|
||||||
|
} else {
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Form
|
|
@ -1,33 +1,21 @@
|
||||||
|
import Inertia from 'inertia-vue'
|
||||||
|
import PortalVue from 'portal-vue'
|
||||||
|
import Vue from 'vue'
|
||||||
|
|
||||||
/**
|
Vue.config.productionTip = false
|
||||||
* First we will load all of this project's JavaScript dependencies which
|
Vue.mixin({ methods: { route: window.route } })
|
||||||
* includes Vue and other libraries. It is a great starting point when
|
Vue.use(PortalVue)
|
||||||
* building robust, powerful web applications using Vue and Laravel.
|
|
||||||
*/
|
|
||||||
|
|
||||||
require('./bootstrap');
|
let app = document.getElementById('app')
|
||||||
|
|
||||||
window.Vue = require('vue');
|
new Vue({
|
||||||
|
render: h => h(Inertia, {
|
||||||
/**
|
props: {
|
||||||
* The following block of code may be used to automatically register your
|
component: app.dataset.component,
|
||||||
* Vue components. It will recursively scan this directory for the Vue
|
props: JSON.parse(app.dataset.props),
|
||||||
* components and automatically register them with their "basename".
|
resolveComponent: (component) => {
|
||||||
*
|
return import(`@/Pages/${component}`).then(module => module.default)
|
||||||
* Eg. ./components/ExampleComponent.vue -> <example-component></example-component>
|
},
|
||||||
*/
|
},
|
||||||
|
}),
|
||||||
// const files = require.context('./', true, /\.vue$/i);
|
}).$mount(app)
|
||||||
// files.keys().map(key => Vue.component(key.split('/').pop().split('.')[0], files(key).default));
|
|
||||||
|
|
||||||
Vue.component('example-component', require('./components/ExampleComponent.vue').default);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Next, we will create a fresh Vue application instance and attach it to
|
|
||||||
* the page. Then, you may begin adding components to this application
|
|
||||||
* or customize the JavaScript scaffolding to fit your unique needs.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const app = new Vue({
|
|
||||||
el: '#app'
|
|
||||||
});
|
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
|
|
||||||
window._ = require('lodash');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We'll load jQuery and the Bootstrap jQuery plugin which provides support
|
|
||||||
* for JavaScript based Bootstrap features such as modals and tabs. This
|
|
||||||
* code may be modified to fit the specific needs of your application.
|
|
||||||
*/
|
|
||||||
|
|
||||||
try {
|
|
||||||
window.Popper = require('popper.js').default;
|
|
||||||
window.$ = window.jQuery = require('jquery');
|
|
||||||
|
|
||||||
require('bootstrap');
|
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We'll load the axios HTTP library which allows us to easily issue requests
|
|
||||||
* to our Laravel back-end. This library automatically handles sending the
|
|
||||||
* CSRF token as a header based on the value of the "XSRF" token cookie.
|
|
||||||
*/
|
|
||||||
|
|
||||||
window.axios = require('axios');
|
|
||||||
|
|
||||||
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Next we will register the CSRF Token as a common header with Axios so that
|
|
||||||
* all outgoing HTTP requests automatically have it attached. This is just
|
|
||||||
* a simple convenience so we don't have to attach every token manually.
|
|
||||||
*/
|
|
||||||
|
|
||||||
let token = document.head.querySelector('meta[name="csrf-token"]');
|
|
||||||
|
|
||||||
if (token) {
|
|
||||||
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
|
|
||||||
} else {
|
|
||||||
console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Echo exposes an expressive API for subscribing to channels and listening
|
|
||||||
* for events that are broadcast by Laravel. Echo and event broadcasting
|
|
||||||
* allows your team to easily build robust real-time web applications.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// import Echo from 'laravel-echo'
|
|
||||||
|
|
||||||
// window.Pusher = require('pusher-js');
|
|
||||||
|
|
||||||
// window.Echo = new Echo({
|
|
||||||
// broadcaster: 'pusher',
|
|
||||||
// key: process.env.MIX_PUSHER_APP_KEY,
|
|
||||||
// cluster: process.env.MIX_PUSHER_APP_CLUSTER,
|
|
||||||
// encrypted: true
|
|
||||||
// });
|
|
|
@ -1,23 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="container">
|
|
||||||
<div class="row justify-content-center">
|
|
||||||
<div class="col-md-8">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">Example Component</div>
|
|
||||||
|
|
||||||
<div class="card-body">
|
|
||||||
I'm an example component.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
mounted() {
|
|
||||||
console.log('Component mounted.')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,20 +0,0 @@
|
||||||
|
|
||||||
// Body
|
|
||||||
$body-bg: #f8fafc;
|
|
||||||
|
|
||||||
// Typography
|
|
||||||
$font-family-sans-serif: "Nunito", sans-serif;
|
|
||||||
$font-size-base: 0.9rem;
|
|
||||||
$line-height-base: 1.6;
|
|
||||||
|
|
||||||
// Colors
|
|
||||||
$blue: #3490dc;
|
|
||||||
$indigo: #6574cd;
|
|
||||||
$purple: #9561e2;
|
|
||||||
$pink: #f66D9b;
|
|
||||||
$red: #e3342f;
|
|
||||||
$orange: #f6993f;
|
|
||||||
$yellow: #ffed4a;
|
|
||||||
$green: #38c172;
|
|
||||||
$teal: #4dc0b5;
|
|
||||||
$cyan: #6cb2eb;
|
|
|
@ -1,14 +0,0 @@
|
||||||
|
|
||||||
// Fonts
|
|
||||||
@import url('https://fonts.googleapis.com/css?family=Nunito');
|
|
||||||
|
|
||||||
// Variables
|
|
||||||
@import 'variables';
|
|
||||||
|
|
||||||
// Bootstrap
|
|
||||||
@import '~bootstrap/scss/bootstrap';
|
|
||||||
|
|
||||||
.navbar-laravel {
|
|
||||||
background-color: #fff;
|
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04);
|
|
||||||
}
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html class="h-full bg-grey-lighter">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
|
||||||
|
<link href="{{ mix('/css/app.css') }}" rel="stylesheet">
|
||||||
|
<script src="{{ mix('/js/app.js') }}" defer></script>
|
||||||
|
@routes
|
||||||
|
</head>
|
||||||
|
<body class="font-sans leading-none text-grey-darkest antialiased">
|
||||||
|
|
||||||
|
<div id="app" data-component="{{ $component }}" data-props="{{ json_encode($props) }}" />
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -1,99 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
|
|
||||||
<title>Laravel</title>
|
|
||||||
|
|
||||||
<!-- Fonts -->
|
|
||||||
<link href="https://fonts.googleapis.com/css?family=Nunito:200,600" rel="stylesheet">
|
|
||||||
|
|
||||||
<!-- Styles -->
|
|
||||||
<style>
|
|
||||||
html, body {
|
|
||||||
background-color: #fff;
|
|
||||||
color: #636b6f;
|
|
||||||
font-family: 'Nunito', sans-serif;
|
|
||||||
font-weight: 200;
|
|
||||||
height: 100vh;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.full-height {
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-center {
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.position-ref {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-right {
|
|
||||||
position: absolute;
|
|
||||||
right: 10px;
|
|
||||||
top: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: 84px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.links > a {
|
|
||||||
color: #636b6f;
|
|
||||||
padding: 0 25px;
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 600;
|
|
||||||
letter-spacing: .1rem;
|
|
||||||
text-decoration: none;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
|
|
||||||
.m-b-md {
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="flex-center position-ref full-height">
|
|
||||||
@if (Route::has('login'))
|
|
||||||
<div class="top-right links">
|
|
||||||
@auth
|
|
||||||
<a href="{{ url('/home') }}">Home</a>
|
|
||||||
@else
|
|
||||||
<a href="{{ route('login') }}">Login</a>
|
|
||||||
|
|
||||||
@if (Route::has('register'))
|
|
||||||
<a href="{{ route('register') }}">Register</a>
|
|
||||||
@endif
|
|
||||||
@endauth
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
|
|
||||||
<div class="content">
|
|
||||||
<div class="title m-b-md">
|
|
||||||
Laravel
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="links">
|
|
||||||
<a href="https://laravel.com/docs">Docs</a>
|
|
||||||
<a href="https://laracasts.com">Laracasts</a>
|
|
||||||
<a href="https://laravel-news.com">News</a>
|
|
||||||
<a href="https://blog.laravel.com">Blog</a>
|
|
||||||
<a href="https://nova.laravel.com">Nova</a>
|
|
||||||
<a href="https://forge.laravel.com">Forge</a>
|
|
||||||
<a href="https://github.com/laravel/laravel">GitHub</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -11,6 +11,54 @@
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Route::get('/', function () {
|
// Auth
|
||||||
return view('welcome');
|
Route::get('login')->name('login')->uses('Auth\LoginController@showLoginForm');
|
||||||
|
Route::post('login')->name('login.attempt')->uses('Auth\LoginController@login');
|
||||||
|
Route::get('logout')->name('logout')->uses('Auth\LoginController@logout');
|
||||||
|
|
||||||
|
// Dashboard
|
||||||
|
Route::get('/')->name('dashboard')->uses('DashboardController@index')->middleware('remember', 'auth');
|
||||||
|
|
||||||
|
// Accounts
|
||||||
|
Route::get('accounts')->name('accounts')->uses('AccountsController@index')->middleware('remember', 'auth');
|
||||||
|
Route::get('accounts/create')->name('accounts.create')->uses('AccountsController@create')->middleware('auth');
|
||||||
|
Route::post('accounts')->name('accounts.store')->uses('AccountsController@store')->middleware('auth');
|
||||||
|
Route::get('accounts/{account}/edit')->name('accounts.edit')->uses('AccountsController@edit')->middleware('auth');
|
||||||
|
Route::put('accounts/{account}')->name('accounts.update')->uses('AccountsController@update')->middleware('auth');
|
||||||
|
Route::delete('accounts/{account}')->name('accounts.destroy')->uses('AccountsController@destroy')->middleware('auth');
|
||||||
|
Route::put('accounts/{account}/restore')->name('accounts.restore')->uses('AccountsController@restore')->middleware('auth');
|
||||||
|
|
||||||
|
// Users
|
||||||
|
Route::get('users')->name('users')->uses('UsersController@index')->middleware('remember', 'auth');
|
||||||
|
Route::get('users/create')->name('users.create')->uses('UsersController@create')->middleware('auth');
|
||||||
|
Route::post('users')->name('users.store')->uses('UsersController@store')->middleware('auth');
|
||||||
|
Route::get('users/{user}/edit')->name('users.edit')->uses('UsersController@edit')->middleware('auth');
|
||||||
|
Route::put('users/{user}')->name('users.update')->uses('UsersController@update')->middleware('auth');
|
||||||
|
Route::delete('users/{user}')->name('users.destroy')->uses('UsersController@destroy')->middleware('auth');
|
||||||
|
Route::put('users/{user}/restore')->name('users.restore')->uses('UsersController@restore')->middleware('auth');
|
||||||
|
|
||||||
|
// Organizations
|
||||||
|
Route::get('organizations')->name('organizations')->uses('OrganizationsController@index')->middleware('remember', 'auth');
|
||||||
|
Route::get('organizations/create')->name('organizations.create')->uses('OrganizationsController@create')->middleware('auth');
|
||||||
|
Route::post('organizations')->name('organizations.store')->uses('OrganizationsController@store')->middleware('auth');
|
||||||
|
Route::get('organizations/{organization}/edit')->name('organizations.edit')->uses('OrganizationsController@edit')->middleware('auth');
|
||||||
|
Route::put('organizations/{organization}')->name('organizations.update')->uses('OrganizationsController@update')->middleware('auth');
|
||||||
|
Route::delete('organizations/{organization}')->name('organizations.destroy')->uses('OrganizationsController@destroy')->middleware('auth');
|
||||||
|
Route::put('organizations/{organization}/restore')->name('organizations.restore')->uses('OrganizationsController@restore')->middleware('auth');
|
||||||
|
|
||||||
|
// Contacts
|
||||||
|
Route::get('contacts')->name('contacts')->uses('ContactsController@index')->middleware('remember', 'auth');
|
||||||
|
Route::get('contacts/create')->name('contacts.create')->uses('ContactsController@create')->middleware('auth');
|
||||||
|
Route::post('contacts')->name('contacts.store')->uses('ContactsController@store')->middleware('auth');
|
||||||
|
Route::get('contacts/{contact}/edit')->name('contacts.edit')->uses('ContactsController@edit')->middleware('auth');
|
||||||
|
Route::put('contacts/{contact}')->name('contacts.update')->uses('ContactsController@update')->middleware('auth');
|
||||||
|
Route::delete('contacts/{contact}')->name('contacts.destroy')->uses('ContactsController@destroy')->middleware('auth');
|
||||||
|
Route::put('contacts/{contact}/restore')->name('contacts.restore')->uses('ContactsController@restore')->middleware('auth');
|
||||||
|
|
||||||
|
// Reports
|
||||||
|
Route::get('reports')->name('reports')->uses('ReportsController@index')->middleware('auth');
|
||||||
|
|
||||||
|
// 500 error
|
||||||
|
Route::get('500', function () {
|
||||||
|
echo $fail;
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
*
|
||||||
|
!.gitignore
|
|
@ -0,0 +1,924 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
Tailwind - The Utility-First CSS Framework
|
||||||
|
|
||||||
|
A project by Adam Wathan (@adamwathan), Jonathan Reinink (@reinink),
|
||||||
|
David Hemphill (@davidhemphill) and Steve Schoger (@steveschoger).
|
||||||
|
|
||||||
|
Welcome to the Tailwind config file. This is where you can customize
|
||||||
|
Tailwind specifically for your project. Don't be intimidated by the
|
||||||
|
length of this file. It's really just a big JavaScript object and
|
||||||
|
we've done our very best to explain each section.
|
||||||
|
|
||||||
|
View the full documentation at https://tailwindcss.com.
|
||||||
|
|
||||||
|
|
||||||
|
|-------------------------------------------------------------------------------
|
||||||
|
| The default config
|
||||||
|
|-------------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This variable contains the default Tailwind config. You don't have
|
||||||
|
| to use it, but it can sometimes be helpful to have available. For
|
||||||
|
| example, you may choose to merge your custom configuration
|
||||||
|
| values with some of the Tailwind defaults.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
let defaultConfig = require('tailwindcss/defaultConfig')()
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-------------------------------------------------------------------------------
|
||||||
|
| Colors https://tailwindcss.com/docs/colors
|
||||||
|
|-------------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here you can specify the colors used in your project. To get you started,
|
||||||
|
| we've provided a generous palette of great looking colors that are perfect
|
||||||
|
| for prototyping, but don't hesitate to change them for your project. You
|
||||||
|
| own these colors, nothing will break if you change everything about them.
|
||||||
|
|
|
||||||
|
| We've used literal color names ("red", "blue", etc.) for the default
|
||||||
|
| palette, but if you'd rather use functional names like "primary" and
|
||||||
|
| "secondary", or even a numeric scale like "100" and "200", go for it.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
let colors = {
|
||||||
|
'transparent': 'transparent',
|
||||||
|
|
||||||
|
'black': '#22292f',
|
||||||
|
'grey-darkest': '#3d4852',
|
||||||
|
'grey-darker': '#606f7b',
|
||||||
|
'grey-dark': '#8795a1',
|
||||||
|
'grey': '#b8c2cc',
|
||||||
|
'grey-light': '#dae1e7',
|
||||||
|
'grey-lighter': '#f1f5f8',
|
||||||
|
'grey-lightest': '#f8fafc',
|
||||||
|
'white': '#ffffff',
|
||||||
|
|
||||||
|
'red-darkest': '#3b0d0c',
|
||||||
|
'red-darker': '#621b18',
|
||||||
|
'red-dark': '#cc1f1a',
|
||||||
|
'red': '#e3342f',
|
||||||
|
'red-light': '#ef5753',
|
||||||
|
'red-lighter': '#f9acaa',
|
||||||
|
'red-lightest': '#fcebea',
|
||||||
|
|
||||||
|
'orange-darkest': '#462a16',
|
||||||
|
'orange-darker': '#613b1f',
|
||||||
|
'orange-dark': '#de751f',
|
||||||
|
'orange': '#f6993f',
|
||||||
|
'orange-light': '#faad63',
|
||||||
|
'orange-lighter': '#fcd9b6',
|
||||||
|
'orange-lightest': '#fff5eb',
|
||||||
|
|
||||||
|
'yellow-darkest': '#453411',
|
||||||
|
'yellow-darker': '#684f1d',
|
||||||
|
'yellow-dark': '#f2d024',
|
||||||
|
'yellow': '#ffed4a',
|
||||||
|
'yellow-light': '#fff382',
|
||||||
|
'yellow-lighter': '#fff9c2',
|
||||||
|
'yellow-lightest': '#fcfbeb',
|
||||||
|
|
||||||
|
'green-darkest': '#0f2f21',
|
||||||
|
'green-darker': '#1a4731',
|
||||||
|
'green-dark': '#1f9d55',
|
||||||
|
'green': '#38c172',
|
||||||
|
'green-light': '#51d88a',
|
||||||
|
'green-lighter': '#a2f5bf',
|
||||||
|
'green-lightest': '#e3fcec',
|
||||||
|
|
||||||
|
'teal-darkest': '#0d3331',
|
||||||
|
'teal-darker': '#20504f',
|
||||||
|
'teal-dark': '#38a89d',
|
||||||
|
'teal': '#4dc0b5',
|
||||||
|
'teal-light': '#64d5ca',
|
||||||
|
'teal-lighter': '#a0f0ed',
|
||||||
|
'teal-lightest': '#e8fffe',
|
||||||
|
|
||||||
|
'blue-darkest': '#12283a',
|
||||||
|
'blue-darker': '#1c3d5a',
|
||||||
|
'blue-dark': '#2779bd',
|
||||||
|
'blue': '#3490dc',
|
||||||
|
'blue-light': '#6cb2eb',
|
||||||
|
'blue-lighter': '#bcdefa',
|
||||||
|
'blue-lightest': '#eff8ff',
|
||||||
|
|
||||||
|
'indigo-darkest': '#191e38',
|
||||||
|
'indigo-darker': '#2f365f',
|
||||||
|
'indigo-dark': '#5661b3',
|
||||||
|
'indigo': '#6574cd',
|
||||||
|
'indigo-light': '#7886d7',
|
||||||
|
'indigo-lighter': '#b2b7ff',
|
||||||
|
'indigo-lightest': '#e6e8ff',
|
||||||
|
|
||||||
|
'purple-darkest': '#21183c',
|
||||||
|
'purple-darker': '#382b5f',
|
||||||
|
'purple-dark': '#794acf',
|
||||||
|
'purple': '#9561e2',
|
||||||
|
'purple-light': '#a779e9',
|
||||||
|
'purple-lighter': '#d6bbfc',
|
||||||
|
'purple-lightest': '#f3ebff',
|
||||||
|
|
||||||
|
'pink-darkest': '#451225',
|
||||||
|
'pink-darker': '#6f213f',
|
||||||
|
'pink-dark': '#eb5286',
|
||||||
|
'pink': '#f66d9b',
|
||||||
|
'pink-light': '#fa7ea8',
|
||||||
|
'pink-lighter': '#ffbbca',
|
||||||
|
'pink-lightest': '#ffebef',
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
| Colors https://tailwindcss.com/docs/colors
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The color palette defined above is also assigned to the "colors" key of
|
||||||
|
| your Tailwind config. This makes it easy to access them in your CSS
|
||||||
|
| using Tailwind's config helper. For example:
|
||||||
|
|
|
||||||
|
| .error { color: config('colors.red') }
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
colors: colors,
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
| Screens https://tailwindcss.com/docs/responsive-design
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Screens in Tailwind are translated to CSS media queries. They define the
|
||||||
|
| responsive breakpoints for your project. By default Tailwind takes a
|
||||||
|
| "mobile first" approach, where each screen size represents a minimum
|
||||||
|
| viewport width. Feel free to have as few or as many screens as you
|
||||||
|
| want, naming them in whatever way you'd prefer for your project.
|
||||||
|
|
|
||||||
|
| Tailwind also allows for more complex screen definitions, which can be
|
||||||
|
| useful in certain situations. Be sure to see the full responsive
|
||||||
|
| documentation for a complete list of options.
|
||||||
|
|
|
||||||
|
| Class name: .{screen}:{utility}
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
screens: {
|
||||||
|
'sm': '576px',
|
||||||
|
'md': '768px',
|
||||||
|
'lg': '992px',
|
||||||
|
'xl': '1200px',
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
| Fonts https://tailwindcss.com/docs/fonts
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here is where you define your project's font stack, or font families.
|
||||||
|
| Keep in mind that Tailwind doesn't actually load any fonts for you.
|
||||||
|
| If you're using custom fonts you'll need to import them prior to
|
||||||
|
| defining them here.
|
||||||
|
|
|
||||||
|
| By default we provide a native font stack that works remarkably well on
|
||||||
|
| any device or OS you're using, since it just uses the default fonts
|
||||||
|
| provided by the platform.
|
||||||
|
|
|
||||||
|
| Class name: .font-{name}
|
||||||
|
| CSS property: font-family
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
fonts: {
|
||||||
|
'sans': [
|
||||||
|
'Cerebri Sans',
|
||||||
|
'system-ui',
|
||||||
|
'BlinkMacSystemFont',
|
||||||
|
'-apple-system',
|
||||||
|
'Segoe UI',
|
||||||
|
'Roboto',
|
||||||
|
'Oxygen',
|
||||||
|
'Ubuntu',
|
||||||
|
'Cantarell',
|
||||||
|
'Fira Sans',
|
||||||
|
'Droid Sans',
|
||||||
|
'Helvetica Neue',
|
||||||
|
'sans-serif',
|
||||||
|
],
|
||||||
|
'serif': [
|
||||||
|
'Constantia',
|
||||||
|
'Lucida Bright',
|
||||||
|
'Lucidabright',
|
||||||
|
'Lucida Serif',
|
||||||
|
'Lucida',
|
||||||
|
'DejaVu Serif',
|
||||||
|
'Bitstream Vera Serif',
|
||||||
|
'Liberation Serif',
|
||||||
|
'Georgia',
|
||||||
|
'serif',
|
||||||
|
],
|
||||||
|
'mono': [
|
||||||
|
'Menlo',
|
||||||
|
'Monaco',
|
||||||
|
'Consolas',
|
||||||
|
'Liberation Mono',
|
||||||
|
'Courier New',
|
||||||
|
'monospace',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
| Text sizes https://tailwindcss.com/docs/text-sizing
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here is where you define your text sizes. Name these in whatever way
|
||||||
|
| makes the most sense to you. We use size names by default, but
|
||||||
|
| you're welcome to use a numeric scale or even something else
|
||||||
|
| entirely.
|
||||||
|
|
|
||||||
|
| By default Tailwind uses the "rem" unit type for most measurements.
|
||||||
|
| This allows you to set a root font size which all other sizes are
|
||||||
|
| then based on. That said, you are free to use whatever units you
|
||||||
|
| prefer, be it rems, ems, pixels or other.
|
||||||
|
|
|
||||||
|
| Class name: .text-{size}
|
||||||
|
| CSS property: font-size
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
textSizes: {
|
||||||
|
'xs': '.75rem', // 12px
|
||||||
|
'sm': '.875rem', // 14px
|
||||||
|
'md': '1rem', // 16px
|
||||||
|
'lg': '1.125rem', // 18px
|
||||||
|
'xl': '1.25rem', // 20px
|
||||||
|
'2xl': '1.5rem', // 24px
|
||||||
|
'3xl': '1.875rem', // 30px
|
||||||
|
'4xl': '2.25rem', // 36px
|
||||||
|
'5xl': '3rem', // 48px
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
| Font weights https://tailwindcss.com/docs/font-weight
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here is where you define your font weights. We've provided a list of
|
||||||
|
| common font weight names with their respective numeric scale values
|
||||||
|
| to get you started. It's unlikely that your project will require
|
||||||
|
| all of these, so we recommend removing those you don't need.
|
||||||
|
|
|
||||||
|
| Class name: .font-{weight}
|
||||||
|
| CSS property: font-weight
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
fontWeights: {
|
||||||
|
'hairline': 100,
|
||||||
|
'thin': 200,
|
||||||
|
'light': 300,
|
||||||
|
'normal': 400,
|
||||||
|
'medium': 500,
|
||||||
|
'semibold': 600,
|
||||||
|
'bold': 700,
|
||||||
|
'extrabold': 800,
|
||||||
|
'black': 900,
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
| Leading (line height) https://tailwindcss.com/docs/line-height
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here is where you define your line height values, or as we call
|
||||||
|
| them in Tailwind, leadings.
|
||||||
|
|
|
||||||
|
| Class name: .leading-{size}
|
||||||
|
| CSS property: line-height
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
leading: {
|
||||||
|
'none': 1,
|
||||||
|
'tight': 1.25,
|
||||||
|
'normal': 1.5,
|
||||||
|
'loose': 2,
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
| Tracking (letter spacing) https://tailwindcss.com/docs/letter-spacing
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here is where you define your letter spacing values, or as we call
|
||||||
|
| them in Tailwind, tracking.
|
||||||
|
|
|
||||||
|
| Class name: .tracking-{size}
|
||||||
|
| CSS property: letter-spacing
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
tracking: {
|
||||||
|
'tight': '-0.05em',
|
||||||
|
'normal': '0',
|
||||||
|
'wide': '0.05em',
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
| Text colors https://tailwindcss.com/docs/text-color
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here is where you define your text colors. By default these use the
|
||||||
|
| color palette we defined above, however you're welcome to set these
|
||||||
|
| independently if that makes sense for your project.
|
||||||
|
|
|
||||||
|
| Class name: .text-{color}
|
||||||
|
| CSS property: color
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
textColors: colors,
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
| Background colors https://tailwindcss.com/docs/background-color
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here is where you define your background colors. By default these use
|
||||||
|
| the color palette we defined above, however you're welcome to set
|
||||||
|
| these independently if that makes sense for your project.
|
||||||
|
|
|
||||||
|
| Class name: .bg-{color}
|
||||||
|
| CSS property: background-color
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
backgroundColors: colors,
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
| Background sizes https://tailwindcss.com/docs/background-size
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here is where you define your background sizes. We provide some common
|
||||||
|
| values that are useful in most projects, but feel free to add other sizes
|
||||||
|
| that are specific to your project here as well.
|
||||||
|
|
|
||||||
|
| Class name: .bg-{size}
|
||||||
|
| CSS property: background-size
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
backgroundSize: {
|
||||||
|
'auto': 'auto',
|
||||||
|
'cover': 'cover',
|
||||||
|
'contain': 'contain',
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
| Border widths https://tailwindcss.com/docs/border-width
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here is where you define your border widths. Take note that border
|
||||||
|
| widths require a special "default" value set as well. This is the
|
||||||
|
| width that will be used when you do not specify a border width.
|
||||||
|
|
|
||||||
|
| Class name: .border{-side?}{-width?}
|
||||||
|
| CSS property: border-width
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
borderWidths: {
|
||||||
|
default: '1px',
|
||||||
|
'0': '0',
|
||||||
|
'2': '2px',
|
||||||
|
'4': '4px',
|
||||||
|
'8': '8px',
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
| Border colors https://tailwindcss.com/docs/border-color
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here is where you define your border colors. By default these use the
|
||||||
|
| color palette we defined above, however you're welcome to set these
|
||||||
|
| independently if that makes sense for your project.
|
||||||
|
|
|
||||||
|
| Take note that border colors require a special "default" value set
|
||||||
|
| as well. This is the color that will be used when you do not
|
||||||
|
| specify a border color.
|
||||||
|
|
|
||||||
|
| Class name: .border-{color}
|
||||||
|
| CSS property: border-color
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
borderColors: global.Object.assign({ default: colors['grey-light'] }, colors),
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
| Border radius https://tailwindcss.com/docs/border-radius
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here is where you define your border radius values. If a `default` radius
|
||||||
|
| is provided, it will be made available as the non-suffixed `.rounded`
|
||||||
|
| utility.
|
||||||
|
|
|
||||||
|
| If your scale includes a `0` value to reset already rounded corners, it's
|
||||||
|
| a good idea to put it first so other values are able to override it.
|
||||||
|
|
|
||||||
|
| Class name: .rounded{-side?}{-size?}
|
||||||
|
| CSS property: border-radius
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
borderRadius: {
|
||||||
|
'none': '0',
|
||||||
|
'sm': '.125rem',
|
||||||
|
default: '.25rem',
|
||||||
|
'lg': '.5rem',
|
||||||
|
'full': '9999px',
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
| Width https://tailwindcss.com/docs/width
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here is where you define your width utility sizes. These can be
|
||||||
|
| percentage based, pixels, rems, or any other units. By default
|
||||||
|
| we provide a sensible rem based numeric scale, a percentage
|
||||||
|
| based fraction scale, plus some other common use-cases. You
|
||||||
|
| can, of course, modify these values as needed.
|
||||||
|
|
|
||||||
|
|
|
||||||
|
| It's also worth mentioning that Tailwind automatically escapes
|
||||||
|
| invalid CSS class name characters, which allows you to have
|
||||||
|
| awesome classes like .w-2/3.
|
||||||
|
|
|
||||||
|
| Class name: .w-{size}
|
||||||
|
| CSS property: width
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
width: {
|
||||||
|
'auto': 'auto',
|
||||||
|
'px': '1px',
|
||||||
|
'1': '0.25rem',
|
||||||
|
'2': '0.5rem',
|
||||||
|
'3': '0.75rem',
|
||||||
|
'4': '1rem',
|
||||||
|
'5': '1.25rem',
|
||||||
|
'6': '1.5rem',
|
||||||
|
'8': '2rem',
|
||||||
|
'10': '2.5rem',
|
||||||
|
'12': '3rem',
|
||||||
|
'16': '4rem',
|
||||||
|
'24': '6rem',
|
||||||
|
'32': '8rem',
|
||||||
|
'48': '12rem',
|
||||||
|
'56': '14rem',
|
||||||
|
'64': '16rem',
|
||||||
|
'1/2': '50%',
|
||||||
|
'1/3': '33.33333%',
|
||||||
|
'2/3': '66.66667%',
|
||||||
|
'1/4': '25%',
|
||||||
|
'3/4': '75%',
|
||||||
|
'1/5': '20%',
|
||||||
|
'2/5': '40%',
|
||||||
|
'3/5': '60%',
|
||||||
|
'4/5': '80%',
|
||||||
|
'1/6': '16.66667%',
|
||||||
|
'5/6': '83.33333%',
|
||||||
|
'full': '100%',
|
||||||
|
'screen': '100vw',
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
| Height https://tailwindcss.com/docs/height
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here is where you define your height utility sizes. These can be
|
||||||
|
| percentage based, pixels, rems, or any other units. By default
|
||||||
|
| we provide a sensible rem based numeric scale plus some other
|
||||||
|
| common use-cases. You can, of course, modify these values as
|
||||||
|
| needed.
|
||||||
|
|
|
||||||
|
| Class name: .h-{size}
|
||||||
|
| CSS property: height
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
height: {
|
||||||
|
'auto': 'auto',
|
||||||
|
'px': '1px',
|
||||||
|
'1': '0.25rem',
|
||||||
|
'2': '0.5rem',
|
||||||
|
'3': '0.75rem',
|
||||||
|
'4': '1rem',
|
||||||
|
'5': '1.25rem',
|
||||||
|
'6': '1.5rem',
|
||||||
|
'8': '2rem',
|
||||||
|
'10': '2.5rem',
|
||||||
|
'12': '3rem',
|
||||||
|
'16': '4rem',
|
||||||
|
'24': '6rem',
|
||||||
|
'32': '8rem',
|
||||||
|
'48': '12rem',
|
||||||
|
'64': '16rem',
|
||||||
|
'full': '100%',
|
||||||
|
'screen': '100vh',
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
| Minimum width https://tailwindcss.com/docs/min-width
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here is where you define your minimum width utility sizes. These can
|
||||||
|
| be percentage based, pixels, rems, or any other units. We provide a
|
||||||
|
| couple common use-cases by default. You can, of course, modify
|
||||||
|
| these values as needed.
|
||||||
|
|
|
||||||
|
| Class name: .min-w-{size}
|
||||||
|
| CSS property: min-width
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
minWidth: {
|
||||||
|
'0': '0',
|
||||||
|
'full': '100%',
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
| Minimum height https://tailwindcss.com/docs/min-height
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here is where you define your minimum height utility sizes. These can
|
||||||
|
| be percentage based, pixels, rems, or any other units. We provide a
|
||||||
|
| few common use-cases by default. You can, of course, modify these
|
||||||
|
| values as needed.
|
||||||
|
|
|
||||||
|
| Class name: .min-h-{size}
|
||||||
|
| CSS property: min-height
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
minHeight: {
|
||||||
|
'0': '0',
|
||||||
|
'full': '100%',
|
||||||
|
'screen': '100vh',
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
| Maximum width https://tailwindcss.com/docs/max-width
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here is where you define your maximum width utility sizes. These can
|
||||||
|
| be percentage based, pixels, rems, or any other units. By default
|
||||||
|
| we provide a sensible rem based scale and a "full width" size,
|
||||||
|
| which is basically a reset utility. You can, of course,
|
||||||
|
| modify these values as needed.
|
||||||
|
|
|
||||||
|
| Class name: .max-w-{size}
|
||||||
|
| CSS property: max-width
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
maxWidth: {
|
||||||
|
'xs': '20rem',
|
||||||
|
'sm': '30rem',
|
||||||
|
'md': '40rem',
|
||||||
|
'lg': '50rem',
|
||||||
|
'xl': '60rem',
|
||||||
|
'2xl': '70rem',
|
||||||
|
'3xl': '80rem',
|
||||||
|
'4xl': '90rem',
|
||||||
|
'5xl': '100rem',
|
||||||
|
'full': '100%',
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
| Maximum height https://tailwindcss.com/docs/max-height
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here is where you define your maximum height utility sizes. These can
|
||||||
|
| be percentage based, pixels, rems, or any other units. We provide a
|
||||||
|
| couple common use-cases by default. You can, of course, modify
|
||||||
|
| these values as needed.
|
||||||
|
|
|
||||||
|
| Class name: .max-h-{size}
|
||||||
|
| CSS property: max-height
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
maxHeight: {
|
||||||
|
'full': '100%',
|
||||||
|
'screen': '100vh',
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
| Padding https://tailwindcss.com/docs/padding
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here is where you define your padding utility sizes. These can be
|
||||||
|
| percentage based, pixels, rems, or any other units. By default we
|
||||||
|
| provide a sensible rem based numeric scale plus a couple other
|
||||||
|
| common use-cases like "1px". You can, of course, modify these
|
||||||
|
| values as needed.
|
||||||
|
|
|
||||||
|
| Class name: .p{side?}-{size}
|
||||||
|
| CSS property: padding
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
padding: {
|
||||||
|
'px': '1px',
|
||||||
|
'0': '0',
|
||||||
|
'1': '0.25rem',
|
||||||
|
'2': '0.5rem',
|
||||||
|
'3': '0.75rem',
|
||||||
|
'4': '1rem',
|
||||||
|
'5': '1.25rem',
|
||||||
|
'6': '1.5rem',
|
||||||
|
'8': '2rem',
|
||||||
|
'10': '2.5rem',
|
||||||
|
'12': '3rem',
|
||||||
|
'16': '4rem',
|
||||||
|
'20': '5rem',
|
||||||
|
'24': '6rem',
|
||||||
|
'32': '8rem',
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
| Margin https://tailwindcss.com/docs/margin
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here is where you define your margin utility sizes. These can be
|
||||||
|
| percentage based, pixels, rems, or any other units. By default we
|
||||||
|
| provide a sensible rem based numeric scale plus a couple other
|
||||||
|
| common use-cases like "1px". You can, of course, modify these
|
||||||
|
| values as needed.
|
||||||
|
|
|
||||||
|
| Class name: .m{side?}-{size}
|
||||||
|
| CSS property: margin
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
margin: {
|
||||||
|
'auto': 'auto',
|
||||||
|
'px': '1px',
|
||||||
|
'0': '0',
|
||||||
|
'1': '0.25rem',
|
||||||
|
'2': '0.5rem',
|
||||||
|
'3': '0.75rem',
|
||||||
|
'4': '1rem',
|
||||||
|
'5': '1.25rem',
|
||||||
|
'6': '1.5rem',
|
||||||
|
'8': '2rem',
|
||||||
|
'10': '2.5rem',
|
||||||
|
'12': '3rem',
|
||||||
|
'16': '4rem',
|
||||||
|
'20': '5rem',
|
||||||
|
'24': '6rem',
|
||||||
|
'32': '8rem',
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
| Negative margin https://tailwindcss.com/docs/negative-margin
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here is where you define your negative margin utility sizes. These can
|
||||||
|
| be percentage based, pixels, rems, or any other units. By default we
|
||||||
|
| provide matching values to the padding scale since these utilities
|
||||||
|
| generally get used together. You can, of course, modify these
|
||||||
|
| values as needed.
|
||||||
|
|
|
||||||
|
| Class name: .-m{side?}-{size}
|
||||||
|
| CSS property: margin
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
negativeMargin: {
|
||||||
|
'px': '1px',
|
||||||
|
'0': '0',
|
||||||
|
'1': '0.25rem',
|
||||||
|
'2': '0.5rem',
|
||||||
|
'3': '0.75rem',
|
||||||
|
'4': '1rem',
|
||||||
|
'5': '1.25rem',
|
||||||
|
'6': '1.5rem',
|
||||||
|
'8': '2rem',
|
||||||
|
'10': '2.5rem',
|
||||||
|
'12': '3rem',
|
||||||
|
'16': '4rem',
|
||||||
|
'20': '5rem',
|
||||||
|
'24': '6rem',
|
||||||
|
'32': '8rem',
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
| Shadows https://tailwindcss.com/docs/shadows
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here is where you define your shadow utilities. As you can see from
|
||||||
|
| the defaults we provide, it's possible to apply multiple shadows
|
||||||
|
| per utility using comma separation.
|
||||||
|
|
|
||||||
|
| If a `default` shadow is provided, it will be made available as the non-
|
||||||
|
| suffixed `.shadow` utility.
|
||||||
|
|
|
||||||
|
| Class name: .shadow-{size?}
|
||||||
|
| CSS property: box-shadow
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
shadows: {
|
||||||
|
default: '0 2px 4px 0 rgba(0,0,0,0.10)',
|
||||||
|
'md': '0 4px 8px 0 rgba(0,0,0,0.12), 0 2px 4px 0 rgba(0,0,0,0.08)',
|
||||||
|
'lg': '0 15px 30px 0 rgba(0,0,0,0.11), 0 5px 15px 0 rgba(0,0,0,0.08)',
|
||||||
|
'inner': 'inset 0 2px 4px 0 rgba(0,0,0,0.06)',
|
||||||
|
'outline': '0 0 0 2px #6574cd',
|
||||||
|
'none': 'none',
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
| Z-index https://tailwindcss.com/docs/z-index
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here is where you define your z-index utility values. By default we
|
||||||
|
| provide a sensible numeric scale. You can, of course, modify these
|
||||||
|
| values as needed.
|
||||||
|
|
|
||||||
|
| Class name: .z-{index}
|
||||||
|
| CSS property: z-index
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
zIndex: {
|
||||||
|
'auto': 'auto',
|
||||||
|
'0': 0,
|
||||||
|
'10': 10,
|
||||||
|
'20': 20,
|
||||||
|
'30': 30,
|
||||||
|
'40': 40,
|
||||||
|
'50': 50,
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
| Opacity https://tailwindcss.com/docs/opacity
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here is where you define your opacity utility values. By default we
|
||||||
|
| provide a sensible numeric scale. You can, of course, modify these
|
||||||
|
| values as needed.
|
||||||
|
|
|
||||||
|
| Class name: .opacity-{name}
|
||||||
|
| CSS property: opacity
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
opacity: {
|
||||||
|
'0': '0',
|
||||||
|
'25': '.25',
|
||||||
|
'50': '.5',
|
||||||
|
'75': '.75',
|
||||||
|
'100': '1',
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
| SVG fill https://tailwindcss.com/docs/svg
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here is where you define your SVG fill colors. By default we just provide
|
||||||
|
| `fill-current` which sets the fill to the current text color. This lets you
|
||||||
|
| specify a fill color using existing text color utilities and helps keep the
|
||||||
|
| generated CSS file size down.
|
||||||
|
|
|
||||||
|
| Class name: .fill-{name}
|
||||||
|
| CSS property: fill
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
svgFill: global.Object.assign({ 'current': 'currentColor' }, colors),
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
| SVG stroke https://tailwindcss.com/docs/svg
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here is where you define your SVG stroke colors. By default we just provide
|
||||||
|
| `stroke-current` which sets the stroke to the current text color. This lets
|
||||||
|
| you specify a stroke color using existing text color utilities and helps
|
||||||
|
| keep the generated CSS file size down.
|
||||||
|
|
|
||||||
|
| Class name: .stroke-{name}
|
||||||
|
| CSS property: stroke
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
svgStroke: {
|
||||||
|
'current': 'currentColor',
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
| Modules https://tailwindcss.com/docs/configuration#modules
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here is where you control which modules are generated and what variants are
|
||||||
|
| generated for each of those modules.
|
||||||
|
|
|
||||||
|
| Currently supported variants:
|
||||||
|
| - responsive
|
||||||
|
| - hover
|
||||||
|
| - focus
|
||||||
|
| - focus-within
|
||||||
|
| - active
|
||||||
|
| - group-hover
|
||||||
|
|
|
||||||
|
| To disable a module completely, use `false` instead of an array.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
modules: 'all',
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
| Plugins https://tailwindcss.com/docs/plugins
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here is where you can register any plugins you'd like to use in your
|
||||||
|
| project. Tailwind's built-in `container` plugin is enabled by default to
|
||||||
|
| give you a Bootstrap-style responsive container component out of the box.
|
||||||
|
|
|
||||||
|
| Be sure to view the complete plugin documentation to learn more about how
|
||||||
|
| the plugin system works.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
plugins: [],
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
| Advanced Options https://tailwindcss.com/docs/configuration#options
|
||||||
|
|-----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here is where you can tweak advanced configuration options. We recommend
|
||||||
|
| leaving these options alone unless you absolutely need to change them.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
options: {
|
||||||
|
prefix: '',
|
||||||
|
important: false,
|
||||||
|
separator: ':',
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,8 @@
|
||||||
const mix = require('laravel-mix');
|
const cssImport = require('postcss-import')
|
||||||
|
const cssNesting = require('postcss-nesting')
|
||||||
|
const mix = require('laravel-mix')
|
||||||
|
const path = require('path')
|
||||||
|
const tailwindcss = require('tailwindcss')
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
@ -12,4 +16,18 @@ const mix = require('laravel-mix');
|
||||||
*/
|
*/
|
||||||
|
|
||||||
mix.js('resources/js/app.js', 'public/js')
|
mix.js('resources/js/app.js', 'public/js')
|
||||||
.sass('resources/sass/app.scss', 'public/css');
|
.postCss('resources/css/app.css', 'public/css', [
|
||||||
|
cssImport(),
|
||||||
|
cssNesting(),
|
||||||
|
tailwindcss('tailwind.js'),
|
||||||
|
])
|
||||||
|
.webpackConfig({
|
||||||
|
output: { chunkFilename: 'js/[name].[contenthash].js' },
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'vue$': 'vue/dist/vue.runtime.js',
|
||||||
|
'@': path.resolve('resources/js'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.version()
|
||||||
|
|
Loading…
Reference in New Issue