commit
cd7696645d
|
@ -11,5 +11,8 @@ trim_trailing_whitespace = true
|
|||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.{yml,yaml}]
|
||||
[*.{yml,yaml,vue}]
|
||||
indent_size = 2
|
||||
|
||||
[docker-compose.yml]
|
||||
indent_size = 4
|
||||
|
|
15
.env.example
15
.env.example
|
@ -5,22 +5,32 @@ APP_DEBUG=true
|
|||
APP_URL=http://localhost
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
LOG_DEPRECATIONS_CHANNEL=null
|
||||
LOG_LEVEL=debug
|
||||
|
||||
DB_CONNECTION=sqlite
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=pingcrm
|
||||
DB_USERNAME=root
|
||||
DB_PASSWORD=
|
||||
|
||||
BROADCAST_DRIVER=log
|
||||
CACHE_DRIVER=file
|
||||
FILESYSTEM_DRIVER=local
|
||||
QUEUE_CONNECTION=sync
|
||||
SESSION_DRIVER=file
|
||||
SESSION_LIFETIME=120
|
||||
|
||||
MEMCACHED_HOST=127.0.0.1
|
||||
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
MAIL_MAILER=smtp
|
||||
MAIL_HOST=smtp.mailtrap.io
|
||||
MAIL_PORT=2525
|
||||
MAIL_HOST=mailhog
|
||||
MAIL_PORT=1025
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_ENCRYPTION=null
|
||||
|
@ -31,6 +41,7 @@ AWS_ACCESS_KEY_ID=
|
|||
AWS_SECRET_ACCESS_KEY=
|
||||
AWS_DEFAULT_REGION=us-east-1
|
||||
AWS_BUCKET=
|
||||
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||
|
||||
PUSHER_APP_ID=
|
||||
PUSHER_APP_KEY=
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
module.exports = {
|
||||
extends: ['eslint:recommended', 'plugin:vue/recommended'],
|
||||
extends: ['eslint:recommended', 'plugin:vue/vue3-recommended'],
|
||||
parserOptions: {
|
||||
ecmaVersion: 2020,
|
||||
sourceType: 'module',
|
||||
|
@ -15,7 +15,9 @@ module.exports = {
|
|||
semi: ['warn', 'never'],
|
||||
'no-unused-vars': ['error', { vars: 'all', args: 'after-used', ignoreRestSiblings: true }],
|
||||
'comma-dangle': ['warn', 'always-multiline'],
|
||||
'vue/multi-word-component-names': 'off',
|
||||
'vue/max-attributes-per-line': 'off',
|
||||
'vue/no-v-html': 'off',
|
||||
'vue/require-default-prop': 'off',
|
||||
'vue/singleline-html-element-content-newline': 'off',
|
||||
'vue/html-self-closing': [
|
||||
|
|
|
@ -10,7 +10,12 @@
|
|||
.env
|
||||
.env.backup
|
||||
.phpunit.result.cache
|
||||
.php-cs-fixer.php
|
||||
.php-cs-fixer.cache
|
||||
docker-compose.override.yml
|
||||
Homestead.json
|
||||
Homestead.yaml
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
/.idea
|
||||
/.vscode
|
||||
|
|
|
@ -0,0 +1,190 @@
|
|||
<?php
|
||||
|
||||
$finder = Symfony\Component\Finder\Finder::create()
|
||||
->notPath('bootstrap')
|
||||
->notPath('node_modules')
|
||||
->notPath('storage')
|
||||
->notPath('vendor')
|
||||
->in(__DIR__)
|
||||
->name('*.php')
|
||||
->notName('*.blade.php');
|
||||
|
||||
return (new PhpCsFixer\Config())
|
||||
->setRiskyAllowed(true)
|
||||
->setRules([
|
||||
'@PSR2' => true,
|
||||
'align_multiline_comment' => [
|
||||
'comment_type' => 'phpdocs_like',
|
||||
],
|
||||
'array_indentation' => true,
|
||||
'array_syntax' => [
|
||||
'syntax' => 'short',
|
||||
],
|
||||
'binary_operator_spaces' => [
|
||||
'operators' => [
|
||||
'=>' => null,
|
||||
'=' => 'single_space',
|
||||
],
|
||||
],
|
||||
'blank_line_after_namespace' => true,
|
||||
'blank_line_after_opening_tag' => true,
|
||||
'blank_line_before_statement' => [
|
||||
'statements' => [
|
||||
'return',
|
||||
],
|
||||
],
|
||||
'braces' => false,
|
||||
'cast_spaces' => true,
|
||||
'class_attributes_separation' => [
|
||||
'elements' => [
|
||||
'method' => 'one',
|
||||
],
|
||||
],
|
||||
'class_definition' => false,
|
||||
'clean_namespace' => true,
|
||||
'compact_nullable_typehint' => true,
|
||||
'concat_space' => [
|
||||
'spacing' => 'none',
|
||||
],
|
||||
'constant_case' => [
|
||||
'case' => 'lower',
|
||||
],
|
||||
'declare_equal_normalize' => true,
|
||||
'elseif' => true,
|
||||
'encoding' => true,
|
||||
'full_opening_tag' => true,
|
||||
'function_declaration' => true,
|
||||
'function_typehint_space' => true,
|
||||
'heredoc_to_nowdoc' => true,
|
||||
'include' => true,
|
||||
'increment_style' => [
|
||||
'style' => 'post',
|
||||
],
|
||||
'indentation_type' => true,
|
||||
'integer_literal_case' => true,
|
||||
'lambda_not_used_import' => true,
|
||||
'line_ending' => true,
|
||||
'list_syntax' => [
|
||||
'syntax' => 'short',
|
||||
],
|
||||
'lowercase_cast' => true,
|
||||
'lowercase_keywords' => true,
|
||||
'lowercase_static_reference' => true,
|
||||
'magic_constant_casing' => true,
|
||||
'magic_method_casing' => true,
|
||||
'method_argument_space' => [
|
||||
'on_multiline' => 'ignore',
|
||||
],
|
||||
'multiline_whitespace_before_semicolons' => true,
|
||||
'native_function_casing' => true,
|
||||
'native_function_type_declaration_casing' => true,
|
||||
'no_alias_language_construct_call' => true,
|
||||
'no_alternative_syntax' => true,
|
||||
'no_binary_string' => true,
|
||||
'no_blank_lines_after_class_opening' => true,
|
||||
'no_blank_lines_after_phpdoc' => true,
|
||||
'no_closing_tag' => true,
|
||||
'no_empty_phpdoc' => true,
|
||||
'no_empty_statement' => true,
|
||||
'no_extra_blank_lines' => [
|
||||
'tokens' => [
|
||||
'extra',
|
||||
'throw',
|
||||
'use',
|
||||
],
|
||||
],
|
||||
'no_space_around_double_colon' => true,
|
||||
'no_leading_import_slash' => true,
|
||||
'no_leading_namespace_whitespace' => true,
|
||||
'no_mixed_echo_print' => [
|
||||
'use' => 'echo',
|
||||
],
|
||||
'no_multiline_whitespace_around_double_arrow' => true,
|
||||
'no_short_bool_cast' => true,
|
||||
'no_singleline_whitespace_before_semicolons' => true,
|
||||
'no_spaces_after_function_name' => true,
|
||||
'no_spaces_around_offset' => [
|
||||
'positions' => [
|
||||
'inside',
|
||||
],
|
||||
],
|
||||
'no_spaces_inside_parenthesis' => true,
|
||||
'no_trailing_comma_in_list_call' => true,
|
||||
'no_trailing_comma_in_singleline_array' => true,
|
||||
'no_trailing_whitespace' => true,
|
||||
'no_trailing_whitespace_in_comment' => true,
|
||||
'no_unneeded_control_parentheses' => true,
|
||||
'no_unneeded_curly_braces' => true,
|
||||
'no_unset_cast' => true,
|
||||
'no_unused_imports' => true,
|
||||
'no_useless_return' => true,
|
||||
'no_whitespace_before_comma_in_array' => true,
|
||||
'no_whitespace_in_blank_line' => true,
|
||||
'normalize_index_brace' => true,
|
||||
'not_operator_with_successor_space' => true,
|
||||
'object_operator_without_whitespace' => true,
|
||||
'ordered_imports' => [
|
||||
'sort_algorithm' => 'alpha',
|
||||
],
|
||||
'phpdoc_indent' => true,
|
||||
'phpdoc_inline_tag_normalizer' => true,
|
||||
'phpdoc_no_alias_tag' => [
|
||||
'replacements' => [
|
||||
'type' => 'var',
|
||||
],
|
||||
],
|
||||
'phpdoc_no_access' => true,
|
||||
'phpdoc_no_package' => true,
|
||||
'phpdoc_no_useless_inheritdoc' => true,
|
||||
'phpdoc_return_self_reference' => true,
|
||||
'phpdoc_scalar' => true,
|
||||
'phpdoc_single_line_var_spacing' => true,
|
||||
'phpdoc_summary' => true,
|
||||
'phpdoc_trim' => true,
|
||||
'phpdoc_types' => true,
|
||||
'phpdoc_var_without_name' => true,
|
||||
'return_type_declaration' => [
|
||||
'space_before' => 'none',
|
||||
],
|
||||
'short_scalar_cast' => true,
|
||||
'single_blank_line_at_eof' => true,
|
||||
'single_blank_line_before_namespace' => true,
|
||||
'single_class_element_per_statement' => true,
|
||||
'single_import_per_statement' => true,
|
||||
'single_line_after_imports' => true,
|
||||
'single_line_comment_style' => [
|
||||
'comment_types' => [
|
||||
'hash',
|
||||
],
|
||||
],
|
||||
'single_quote' => true,
|
||||
'space_after_semicolon' => true,
|
||||
'standardize_not_equals' => true,
|
||||
'switch_case_semicolon_to_colon' => true,
|
||||
'switch_case_space' => true,
|
||||
'switch_continue_to_break' => true,
|
||||
'ternary_operator_spaces' => true,
|
||||
'trailing_comma_in_multiline' => [
|
||||
'elements' => [
|
||||
'arrays',
|
||||
],
|
||||
],
|
||||
'trim_array_spaces' => true,
|
||||
'types_spaces' => [
|
||||
'space' => 'none',
|
||||
],
|
||||
'unary_operator_spaces' => true,
|
||||
'visibility_required' => [
|
||||
'elements' => [
|
||||
'method',
|
||||
'property',
|
||||
],
|
||||
],
|
||||
'whitespace_after_comma_in_array' => true,
|
||||
// Risky
|
||||
'no_alias_functions' => true,
|
||||
'no_unreachable_default_argument_value' => true,
|
||||
'self_accessor' => true,
|
||||
'psr_autoloading' => true,
|
||||
])
|
||||
->setFinder($finder);
|
23
.php_cs.dist
23
.php_cs.dist
|
@ -1,23 +0,0 @@
|
|||
<?php
|
||||
|
||||
// Reference: http://cs.sensiolabs.org/
|
||||
|
||||
return PhpCsFixer\Config::create()
|
||||
->setUsingCache(false)
|
||||
->setRiskyAllowed(true)
|
||||
->setRules([
|
||||
'@PHP70Migration' => true,
|
||||
'@PHP71Migration' => true,
|
||||
'@PSR2' => true,
|
||||
'@Symfony' => true,
|
||||
'array_syntax' => ['syntax' => 'short'],
|
||||
'increment_style' => ['style' => 'post'],
|
||||
'no_multiline_whitespace_before_semicolons' => true,
|
||||
'not_operator_with_successor_space' => true,
|
||||
'ordered_imports' => ['sortAlgorithm' => 'alpha'],
|
||||
'php_unit_method_casing' => ['case' => 'snake_case'],
|
||||
'semicolon_after_instruction' => false,
|
||||
'single_line_throw' => false,
|
||||
'strict_comparison' => true,
|
||||
'yoda_style' => false,
|
||||
]);
|
|
@ -1,5 +1,6 @@
|
|||
php:
|
||||
preset: laravel
|
||||
version: 8
|
||||
disabled:
|
||||
- no_unused_imports
|
||||
finder:
|
||||
|
|
|
@ -7,15 +7,6 @@ use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
|||
|
||||
class Kernel extends ConsoleKernel
|
||||
{
|
||||
/**
|
||||
* The Artisan commands provided by your application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $commands = [
|
||||
//
|
||||
];
|
||||
|
||||
/**
|
||||
* Define the application's command schedule.
|
||||
*
|
||||
|
|
|
@ -10,7 +10,7 @@ class Handler extends ExceptionHandler
|
|||
/**
|
||||
* A list of the exception types that are not reported.
|
||||
*
|
||||
* @var array
|
||||
* @var array<int, class-string<Throwable>>
|
||||
*/
|
||||
protected $dontReport = [
|
||||
//
|
||||
|
@ -19,7 +19,7 @@ class Handler extends ExceptionHandler
|
|||
/**
|
||||
* A list of the inputs that are never flashed for validation exceptions.
|
||||
*
|
||||
* @var array
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $dontFlash = [
|
||||
'current_password',
|
||||
|
|
|
@ -9,7 +9,5 @@ use Illuminate\Routing\Controller as BaseController;
|
|||
|
||||
class Controller extends BaseController
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
use DispatchesJobs;
|
||||
use ValidatesRequests;
|
||||
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
|
||||
}
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use League\Glide\ServerFactory;
|
||||
use Illuminate\Contracts\Filesystem\Filesystem;
|
||||
use Illuminate\Http\Request;
|
||||
use League\Glide\Responses\LaravelResponseFactory;
|
||||
use League\Glide\ServerFactory;
|
||||
|
||||
class ImagesController extends Controller
|
||||
{
|
||||
|
|
|
@ -11,7 +11,7 @@ class Kernel extends HttpKernel
|
|||
*
|
||||
* These middleware are run during every request to your application.
|
||||
*
|
||||
* @var array
|
||||
* @var array<int, class-string|string>
|
||||
*/
|
||||
protected $middleware = [
|
||||
// \App\Http\Middleware\TrustHosts::class,
|
||||
|
@ -26,7 +26,7 @@ class Kernel extends HttpKernel
|
|||
/**
|
||||
* The application's route middleware groups.
|
||||
*
|
||||
* @var array
|
||||
* @var array<string, array<int, class-string|string>>
|
||||
*/
|
||||
protected $middlewareGroups = [
|
||||
'web' => [
|
||||
|
@ -37,10 +37,11 @@ class Kernel extends HttpKernel
|
|||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
\App\Http\Middleware\HandleInertiaRequests::class
|
||||
\App\Http\Middleware\HandleInertiaRequests::class,
|
||||
],
|
||||
|
||||
'api' => [
|
||||
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
|
||||
'throttle:api',
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
],
|
||||
|
@ -51,7 +52,7 @@ class Kernel extends HttpKernel
|
|||
*
|
||||
* These middleware may be assigned to groups or used individually.
|
||||
*
|
||||
* @var array
|
||||
* @var array<string, class-string|string>
|
||||
*/
|
||||
protected $routeMiddleware = [
|
||||
'auth' => \App\Http\Middleware\Authenticate::class,
|
||||
|
|
|
@ -9,7 +9,7 @@ class EncryptCookies extends Middleware
|
|||
/**
|
||||
* The names of the cookies that should not be encrypted.
|
||||
*
|
||||
* @var array
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
|
|
|
@ -9,7 +9,7 @@ class PreventRequestsDuringMaintenance extends Middleware
|
|||
/**
|
||||
* The URIs that should be reachable while maintenance mode is enabled.
|
||||
*
|
||||
* @var array
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
|
|
|
@ -13,9 +13,9 @@ class RedirectIfAuthenticated
|
|||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
|
||||
* @param string|null ...$guards
|
||||
* @return mixed
|
||||
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function handle(Request $request, Closure $next, ...$guards)
|
||||
{
|
||||
|
|
|
@ -9,7 +9,7 @@ class TrimStrings extends Middleware
|
|||
/**
|
||||
* The names of the attributes that should not be trimmed.
|
||||
*
|
||||
* @var array
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $except = [
|
||||
'current_password',
|
||||
|
|
|
@ -9,7 +9,7 @@ class TrustHosts extends Middleware
|
|||
/**
|
||||
* Get the host patterns that should be trusted.
|
||||
*
|
||||
* @return array
|
||||
* @return array<int, string|null>
|
||||
*/
|
||||
public function hosts()
|
||||
{
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Fideloper\Proxy\TrustProxies as Middleware;
|
||||
use Illuminate\Http\Middleware\TrustProxies as Middleware;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class TrustProxies extends Middleware
|
||||
|
@ -10,14 +10,19 @@ class TrustProxies extends Middleware
|
|||
/**
|
||||
* The trusted proxies for this application.
|
||||
*
|
||||
* @var array|string|null
|
||||
* @var array<int, string>|string|null
|
||||
*/
|
||||
protected $proxies = '**';
|
||||
protected $proxies;
|
||||
|
||||
/**
|
||||
* The headers that should be used to detect proxies.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $headers = Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO | Request::HEADER_X_FORWARDED_AWS_ELB;
|
||||
protected $headers =
|
||||
Request::HEADER_X_FORWARDED_FOR |
|
||||
Request::HEADER_X_FORWARDED_HOST |
|
||||
Request::HEADER_X_FORWARDED_PORT |
|
||||
Request::HEADER_X_FORWARDED_PROTO |
|
||||
Request::HEADER_X_FORWARDED_AWS_ELB;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ class VerifyCsrfToken extends Middleware
|
|||
/**
|
||||
* The URIs that should be excluded from CSRF verification.
|
||||
*
|
||||
* @var array
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
|
|
|
@ -7,13 +7,38 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
|||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Laravel\Sanctum\HasApiTokens;
|
||||
|
||||
class User extends Authenticatable
|
||||
{
|
||||
use HasFactory;
|
||||
use Notifiable;
|
||||
use SoftDeletes;
|
||||
use HasApiTokens, HasFactory, Notifiable, SoftDeletes;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'email',
|
||||
'password',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be hidden for serialization.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $hidden = [
|
||||
'password',
|
||||
'remember_token',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'owner' => 'boolean',
|
||||
'email_verified_at' => 'datetime',
|
||||
|
|
|
@ -3,14 +3,13 @@
|
|||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
class AuthServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The policy mappings for the application.
|
||||
*
|
||||
* @var array
|
||||
* @var array<class-string, class-string>
|
||||
*/
|
||||
protected $policies = [
|
||||
// 'App\Models\Model' => 'App\Policies\ModelPolicy',
|
||||
|
|
|
@ -12,7 +12,7 @@ class EventServiceProvider extends ServiceProvider
|
|||
/**
|
||||
* The event listener mappings for the application.
|
||||
*
|
||||
* @var array
|
||||
* @var array<class-string, array<int, class-string>>
|
||||
*/
|
||||
protected $listen = [
|
||||
Registered::class => [
|
||||
|
|
|
@ -11,7 +11,7 @@ define('LARAVEL_START', microtime(true));
|
|||
| Composer provides a convenient, automatically generated class loader
|
||||
| for our application. We just need to utilize it! We'll require it
|
||||
| into the script here so that we do not have to worry about the
|
||||
| loading of any our classes "manually". Feels great to relax.
|
||||
| loading of any of our classes manually. It's great to relax.
|
||||
|
|
||||
*/
|
||||
|
||||
|
|
|
@ -8,22 +8,22 @@
|
|||
"php": "^7.4|^8.0",
|
||||
"ext-exif": "*",
|
||||
"ext-gd": "*",
|
||||
"fakerphp/faker": "^1.9.1",
|
||||
"fideloper/proxy": "^4.4",
|
||||
"fruitcake/laravel-cors": "^2.0",
|
||||
"guzzlehttp/guzzle": "^7.0.1",
|
||||
"inertiajs/inertia-laravel": "^0.4",
|
||||
"laravel/framework": "^8.40",
|
||||
"inertiajs/inertia-laravel": "^0.4.5",
|
||||
"laravel/framework": "^8.65",
|
||||
"laravel/sanctum": "^2.11",
|
||||
"laravel/tinker": "^2.5",
|
||||
"league/glide-laravel": "^1.0",
|
||||
"tightenco/ziggy": "^0.8.0"
|
||||
"league/glide-laravel": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"roave/security-advisories": "dev-latest",
|
||||
"facade/ignition": "^2.5",
|
||||
"fakerphp/faker": "^1.9.1",
|
||||
"laravel/sail": "^1.0.1",
|
||||
"mockery/mockery": "^1.4.2",
|
||||
"nunomaduro/collision": "^5.0",
|
||||
"phpunit/phpunit": "^9.3.3"
|
||||
"mockery/mockery": "^1.4.4",
|
||||
"nunomaduro/collision": "^5.10",
|
||||
"phpunit/phpunit": "^9.5.10"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
@ -42,6 +42,9 @@
|
|||
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
|
||||
"@php artisan package:discover --ansi"
|
||||
],
|
||||
"post-update-cmd": [
|
||||
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
|
||||
],
|
||||
"post-root-package-install": [
|
||||
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
|
||||
],
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -209,12 +209,14 @@ return [
|
|||
'Gate' => Illuminate\Support\Facades\Gate::class,
|
||||
'Hash' => Illuminate\Support\Facades\Hash::class,
|
||||
'Http' => Illuminate\Support\Facades\Http::class,
|
||||
'Js' => Illuminate\Support\Js::class,
|
||||
'Lang' => Illuminate\Support\Facades\Lang::class,
|
||||
'Log' => Illuminate\Support\Facades\Log::class,
|
||||
'Mail' => Illuminate\Support\Facades\Mail::class,
|
||||
'Notification' => Illuminate\Support\Facades\Notification::class,
|
||||
'Password' => Illuminate\Support\Facades\Password::class,
|
||||
'Queue' => Illuminate\Support\Facades\Queue::class,
|
||||
'RateLimiter' => Illuminate\Support\Facades\RateLimiter::class,
|
||||
'Redirect' => Illuminate\Support\Facades\Redirect::class,
|
||||
// 'Redis' => Illuminate\Support\Facades\Redis::class,
|
||||
'Request' => Illuminate\Support\Facades\Request::class,
|
||||
|
|
|
@ -31,7 +31,7 @@ return [
|
|||
| users are actually retrieved out of your database or other storage
|
||||
| mechanisms used by this application to persist your user's data.
|
||||
|
|
||||
| Supported: "session", "token"
|
||||
| Supported: "session"
|
||||
|
|
||||
*/
|
||||
|
||||
|
@ -40,12 +40,6 @@ return [
|
|||
'driver' => 'session',
|
||||
'provider' => 'users',
|
||||
],
|
||||
|
||||
'api' => [
|
||||
'driver' => 'token',
|
||||
'provider' => 'users',
|
||||
'hash' => false,
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|
|
|
@ -27,7 +27,7 @@ return [
|
|||
| same cache driver to group types of items stored in your caches.
|
||||
|
|
||||
| Supported drivers: "apc", "array", "database", "file",
|
||||
| "memcached", "redis", "dynamodb", "null"
|
||||
| "memcached", "redis", "dynamodb", "octane", "null"
|
||||
|
|
||||
*/
|
||||
|
||||
|
@ -88,6 +88,10 @@ return [
|
|||
'endpoint' => env('DYNAMODB_ENDPOINT'),
|
||||
],
|
||||
|
||||
'octane' => [
|
||||
'driver' => 'octane',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|
|
|
@ -50,6 +50,7 @@ return [
|
|||
'bucket' => env('AWS_BUCKET'),
|
||||
'url' => env('AWS_URL'),
|
||||
'endpoint' => env('AWS_ENDPOINT'),
|
||||
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
|
||||
],
|
||||
|
||||
],
|
||||
|
|
|
@ -19,6 +19,19 @@ return [
|
|||
|
||||
'default' => env('LOG_CHANNEL', 'stack'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Deprecations Log Channel
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the log channel that should be used to log warnings
|
||||
| regarding deprecated PHP and library features. This allows you to get
|
||||
| your application ready for upcoming major versions of dependencies.
|
||||
|
|
||||
*/
|
||||
|
||||
'deprecations' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Log Channels
|
||||
|
|
|
@ -29,7 +29,7 @@ return [
|
|||
| mailers below. You are free to add additional mailers as required.
|
||||
|
|
||||
| Supported: "smtp", "sendmail", "mailgun", "ses",
|
||||
| "postmark", "log", "array"
|
||||
| "postmark", "log", "array", "failover"
|
||||
|
|
||||
*/
|
||||
|
||||
|
@ -59,7 +59,7 @@ return [
|
|||
|
||||
'sendmail' => [
|
||||
'transport' => 'sendmail',
|
||||
'path' => '/usr/sbin/sendmail -bs',
|
||||
'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -t -i'),
|
||||
],
|
||||
|
||||
'log' => [
|
||||
|
@ -70,6 +70,14 @@ return [
|
|||
'array' => [
|
||||
'transport' => 'array',
|
||||
],
|
||||
|
||||
'failover' => [
|
||||
'transport' => 'failover',
|
||||
'mailers' => [
|
||||
'smtp',
|
||||
'log',
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Stateful Domains
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Requests from the following domains / hosts will receive stateful API
|
||||
| authentication cookies. Typically, these should include your local
|
||||
| and production domains which access your API via a frontend SPA.
|
||||
|
|
||||
*/
|
||||
|
||||
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
|
||||
'%s%s',
|
||||
'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
|
||||
env('APP_URL') ? ','.parse_url(env('APP_URL'), PHP_URL_HOST) : ''
|
||||
))),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Sanctum Guards
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This array contains the authentication guards that will be checked when
|
||||
| Sanctum is trying to authenticate a request. If none of these guards
|
||||
| are able to authenticate the request, Sanctum will use the bearer
|
||||
| token that's present on an incoming request for authentication.
|
||||
|
|
||||
*/
|
||||
|
||||
'guard' => ['web'],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Expiration Minutes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value controls the number of minutes until an issued token will be
|
||||
| considered expired. If this value is null, personal access tokens do
|
||||
| not expire. This won't tweak the lifetime of first-party sessions.
|
||||
|
|
||||
*/
|
||||
|
||||
'expiration' => null,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Sanctum Middleware
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When authenticating your first-party SPA with Sanctum you may need to
|
||||
| customize some of the middleware Sanctum uses while processing the
|
||||
| request. You may change the middleware listed below as required.
|
||||
|
|
||||
*/
|
||||
|
||||
'middleware' => [
|
||||
'verify_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class,
|
||||
'encrypt_cookies' => App\Http\Middleware\EncryptCookies::class,
|
||||
],
|
||||
|
||||
];
|
|
@ -164,7 +164,7 @@ return [
|
|||
|
|
||||
| By setting this option to true, session cookies will only be sent back
|
||||
| to the server if the browser has a HTTPS connection. This will keep
|
||||
| the cookie from being sent to you if it can not be done securely.
|
||||
| the cookie from being sent to you when it can't be done securely.
|
||||
|
|
||||
*/
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
*.sqlite
|
||||
*.sqlite*
|
||||
|
|
|
@ -2,18 +2,10 @@
|
|||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Contact;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
class ContactFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model = Contact::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
|
|
|
@ -2,18 +2,10 @@
|
|||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Organization;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
class OrganizationFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model = Organization::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
|
|
|
@ -2,19 +2,11 @@
|
|||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class UserFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model = User::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
|
@ -25,9 +17,9 @@ class UserFactory extends Factory
|
|||
return [
|
||||
'first_name' => $this->faker->firstName,
|
||||
'last_name' => $this->faker->lastName,
|
||||
'email' => $this->faker->unique()->safeEmail,
|
||||
'email' => $this->faker->unique()->safeEmail(),
|
||||
'email_verified_at' => now(),
|
||||
'password' => 'secret',
|
||||
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
|
||||
'remember_token' => Str::random(10),
|
||||
'owner' => false,
|
||||
];
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreatePersonalAccessTokensTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('personal_access_tokens', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->morphs('tokenable');
|
||||
$table->string('name');
|
||||
$table->string('token', 64)->unique();
|
||||
$table->text('abilities')->nullable();
|
||||
$table->timestamp('last_used_at')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('personal_access_tokens');
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ class DatabaseSeeder extends Seeder
|
|||
'first_name' => 'John',
|
||||
'last_name' => 'Doe',
|
||||
'email' => 'johndoe@example.com',
|
||||
'password' => 'secret',
|
||||
'owner' => true,
|
||||
]);
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
31
package.json
31
package.json
|
@ -3,6 +3,9 @@
|
|||
"scripts": {
|
||||
"dev": "npm run development",
|
||||
"development": "mix",
|
||||
"fix:eslint": "eslint --ext .js,.vue resources/js/ --fix",
|
||||
"fix:prettier": "prettier --write --loglevel warn 'resources/js/**/*.vue'",
|
||||
"fix-code-style": "npm run fix:prettier && npm run fix:eslint",
|
||||
"watch": "mix watch",
|
||||
"watch-poll": "mix watch -- --watch-options-poll=1000",
|
||||
"hot": "mix watch --hot",
|
||||
|
@ -11,23 +14,23 @@
|
|||
"heroku-postbuild": "npm run prod"
|
||||
},
|
||||
"dependencies": {
|
||||
"@inertiajs/inertia": "^0.9.2",
|
||||
"@inertiajs/inertia-vue": "^0.6.2",
|
||||
"@inertiajs/progress": "^0.2.4",
|
||||
"autoprefixer": "^10.2.4",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-plugin-vue": "^7.5.0",
|
||||
"laravel-mix": "^6.0.6",
|
||||
"@inertiajs/inertia": "^0.10.1",
|
||||
"@inertiajs/inertia-vue3": "^0.5.2",
|
||||
"@inertiajs/progress": "^0.2.6",
|
||||
"@popperjs/core": "^2.11.0",
|
||||
"autoprefixer": "^10.4.0",
|
||||
"eslint": "^8.4.1",
|
||||
"eslint-plugin-vue": "^8.2.0",
|
||||
"laravel-mix": "^6.0.39",
|
||||
"lodash": "^4.17.21",
|
||||
"popper.js": "^1.16.0",
|
||||
"portal-vue": "^1.5.1",
|
||||
"postcss": "^8.2.6",
|
||||
"postcss": "^8.4.4",
|
||||
"postcss-import": "^12.0.1",
|
||||
"postcss-nesting": "^7.0.1",
|
||||
"prettier": "^2.5.1",
|
||||
"prettier-plugin-tailwind": "^2.2.12",
|
||||
"tailwindcss": "^2.0.3",
|
||||
"vue": "^2.6.11",
|
||||
"vue-loader": "^15.9.6",
|
||||
"vue-meta": "^2.3.1",
|
||||
"vue-template-compiler": "^2.6.12"
|
||||
"uuid": "^8.3.2",
|
||||
"vue": "^3.2.24",
|
||||
"vue-loader": "^16.2.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
<server name="APP_ENV" value="testing"/>
|
||||
<server name="BCRYPT_ROUNDS" value="4"/>
|
||||
<server name="CACHE_DRIVER" value="array"/>
|
||||
<server name="DB_CONNECTION" value="sqlite"/>
|
||||
<server name="DB_DATABASE" value=":memory:"/>
|
||||
<!-- <server name="DB_CONNECTION" value="sqlite"/> -->
|
||||
<!-- <server name="DB_DATABASE" value=":memory:"/> -->
|
||||
<server name="MAIL_MAILER" value="array"/>
|
||||
<server name="QUEUE_CONNECTION" value="sync"/>
|
||||
<server name="SESSION_DRIVER" value="array"/>
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
RewriteCond %{REQUEST_URI} (.+)/$
|
||||
RewriteRule ^ %1 [L,R=301]
|
||||
|
||||
# Handle Front Controller...
|
||||
# Send Requests To Front Controller...
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^ index.php [L]
|
||||
|
|
|
@ -48,8 +48,8 @@ $app = require_once __DIR__.'/../bootstrap/app.php';
|
|||
|
||||
$kernel = $app->make(Kernel::class);
|
||||
|
||||
$response = tap($kernel->handle(
|
||||
$response = $kernel->handle(
|
||||
$request = Request::capture()
|
||||
))->send();
|
||||
)->send();
|
||||
|
||||
$kernel->terminate($request, $response);
|
||||
|
|
|
@ -1,20 +1,21 @@
|
|||
<template>
|
||||
<div class="p-6 bg-indigo-800 min-h-screen flex justify-center items-center">
|
||||
<Head title="Login" />
|
||||
<div class="flex items-center justify-center p-6 min-h-screen bg-indigo-800">
|
||||
<div class="w-full max-w-md">
|
||||
<logo class="block mx-auto w-full max-w-xs fill-white" height="50" />
|
||||
<form class="mt-8 bg-white rounded-lg shadow-xl overflow-hidden" @submit.prevent="login">
|
||||
<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" />
|
||||
<h1 class="text-center text-3xl font-bold">Welcome Back!</h1>
|
||||
<div class="mt-6 mx-auto w-24 border-b-2" />
|
||||
<text-input v-model="form.email" :error="form.errors.email" class="mt-10" label="Email" type="email" autofocus autocapitalize="off" />
|
||||
<text-input v-model="form.password" :error="form.errors.password" class="mt-6" label="Password" type="password" />
|
||||
<label class="mt-6 select-none flex items-center" for="remember">
|
||||
<label class="flex items-center mt-6 select-none" for="remember">
|
||||
<input id="remember" v-model="form.remember" class="mr-1" type="checkbox" />
|
||||
<span class="text-sm">Remember Me</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="px-10 py-4 bg-gray-100 border-t border-gray-100 flex">
|
||||
<loading-button :loading="form.processing" class="ml-auto btn-indigo" type="submit">Login</loading-button>
|
||||
<div class="flex px-10 py-4 bg-gray-100 border-t border-gray-100">
|
||||
<loading-button :loading="form.processing" class="btn-indigo ml-auto" type="submit">Login</loading-button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -22,13 +23,14 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { Head } from '@inertiajs/inertia-vue3'
|
||||
import Logo from '@/Shared/Logo'
|
||||
import TextInput from '@/Shared/TextInput'
|
||||
import LoadingButton from '@/Shared/LoadingButton'
|
||||
|
||||
export default {
|
||||
metaInfo: { title: 'Login' },
|
||||
components: {
|
||||
Head,
|
||||
LoadingButton,
|
||||
Logo,
|
||||
TextInput,
|
||||
|
@ -44,7 +46,7 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
login() {
|
||||
this.form.post(this.route('login.store'))
|
||||
this.form.post('/login')
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,31 +1,32 @@
|
|||
<template>
|
||||
<div>
|
||||
<h1 class="mb-8 font-bold text-3xl">
|
||||
<inertia-link class="text-indigo-400 hover:text-indigo-600" :href="route('contacts')">Contacts</inertia-link>
|
||||
<Head title="Create Contact" />
|
||||
<h1 class="mb-8 text-3xl font-bold">
|
||||
<Link class="text-indigo-400 hover:text-indigo-600" href="/contacts">Contacts</Link>
|
||||
<span class="text-indigo-400 font-medium">/</span> Create
|
||||
</h1>
|
||||
<div class="bg-white rounded-md shadow overflow-hidden max-w-3xl">
|
||||
<div class="max-w-3xl bg-white rounded-md shadow overflow-hidden">
|
||||
<form @submit.prevent="store">
|
||||
<div class="p-8 -mr-6 -mb-8 flex flex-wrap">
|
||||
<text-input v-model="form.first_name" :error="form.errors.first_name" class="pr-6 pb-8 w-full lg:w-1/2" label="First name" />
|
||||
<text-input v-model="form.last_name" :error="form.errors.last_name" class="pr-6 pb-8 w-full lg:w-1/2" label="Last name" />
|
||||
<select-input v-model="form.organization_id" :error="form.errors.organization_id" class="pr-6 pb-8 w-full lg:w-1/2" label="Organization">
|
||||
<div class="flex flex-wrap -mb-8 -mr-6 p-8">
|
||||
<text-input v-model="form.first_name" :error="form.errors.first_name" class="pb-8 pr-6 w-full lg:w-1/2" label="First name" />
|
||||
<text-input v-model="form.last_name" :error="form.errors.last_name" class="pb-8 pr-6 w-full lg:w-1/2" label="Last name" />
|
||||
<select-input v-model="form.organization_id" :error="form.errors.organization_id" class="pb-8 pr-6 w-full lg:w-1/2" 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.email" :error="form.errors.email" class="pr-6 pb-8 w-full lg:w-1/2" label="Email" />
|
||||
<text-input v-model="form.phone" :error="form.errors.phone" class="pr-6 pb-8 w-full lg:w-1/2" label="Phone" />
|
||||
<text-input v-model="form.address" :error="form.errors.address" class="pr-6 pb-8 w-full lg:w-1/2" label="Address" />
|
||||
<text-input v-model="form.city" :error="form.errors.city" class="pr-6 pb-8 w-full lg:w-1/2" label="City" />
|
||||
<text-input v-model="form.region" :error="form.errors.region" class="pr-6 pb-8 w-full lg:w-1/2" label="Province/State" />
|
||||
<select-input v-model="form.country" :error="form.errors.country" class="pr-6 pb-8 w-full lg:w-1/2" label="Country">
|
||||
<text-input v-model="form.email" :error="form.errors.email" class="pb-8 pr-6 w-full lg:w-1/2" label="Email" />
|
||||
<text-input v-model="form.phone" :error="form.errors.phone" class="pb-8 pr-6 w-full lg:w-1/2" label="Phone" />
|
||||
<text-input v-model="form.address" :error="form.errors.address" class="pb-8 pr-6 w-full lg:w-1/2" label="Address" />
|
||||
<text-input v-model="form.city" :error="form.errors.city" class="pb-8 pr-6 w-full lg:w-1/2" label="City" />
|
||||
<text-input v-model="form.region" :error="form.errors.region" class="pb-8 pr-6 w-full lg:w-1/2" label="Province/State" />
|
||||
<select-input v-model="form.country" :error="form.errors.country" class="pb-8 pr-6 w-full lg:w-1/2" label="Country">
|
||||
<option :value="null" />
|
||||
<option value="CA">Canada</option>
|
||||
<option value="US">United States</option>
|
||||
</select-input>
|
||||
<text-input v-model="form.postal_code" :error="form.errors.postal_code" class="pr-6 pb-8 w-full lg:w-1/2" label="Postal code" />
|
||||
<text-input v-model="form.postal_code" :error="form.errors.postal_code" class="pb-8 pr-6 w-full lg:w-1/2" label="Postal code" />
|
||||
</div>
|
||||
<div class="px-8 py-4 bg-gray-50 border-t border-gray-100 flex justify-end items-center">
|
||||
<div class="flex items-center justify-end px-8 py-4 bg-gray-50 border-t border-gray-100">
|
||||
<loading-button :loading="form.processing" class="btn-indigo" type="submit">Create Contact</loading-button>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -34,14 +35,16 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { Head, Link } from '@inertiajs/inertia-vue3'
|
||||
import Layout from '@/Shared/Layout'
|
||||
import TextInput from '@/Shared/TextInput'
|
||||
import SelectInput from '@/Shared/SelectInput'
|
||||
import LoadingButton from '@/Shared/LoadingButton'
|
||||
|
||||
export default {
|
||||
metaInfo: { title: 'Create Contact' },
|
||||
components: {
|
||||
Head,
|
||||
Link,
|
||||
LoadingButton,
|
||||
SelectInput,
|
||||
TextInput,
|
||||
|
@ -54,22 +57,22 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
form: this.$inertia.form({
|
||||
first_name: null,
|
||||
last_name: null,
|
||||
first_name: '',
|
||||
last_name: '',
|
||||
organization_id: null,
|
||||
email: null,
|
||||
phone: null,
|
||||
address: null,
|
||||
city: null,
|
||||
region: null,
|
||||
country: null,
|
||||
postal_code: null,
|
||||
email: '',
|
||||
phone: '',
|
||||
address: '',
|
||||
city: '',
|
||||
region: '',
|
||||
country: '',
|
||||
postal_code: '',
|
||||
}),
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
store() {
|
||||
this.form.post(this.route('contacts.store'))
|
||||
this.form.post('/contacts')
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,35 +1,34 @@
|
|||
<template>
|
||||
<div>
|
||||
<h1 class="mb-8 font-bold text-3xl">
|
||||
<inertia-link class="text-indigo-400 hover:text-indigo-600" :href="route('contacts')">Contacts</inertia-link>
|
||||
<Head :title="`${form.first_name} ${form.last_name}`" />
|
||||
<h1 class="mb-8 text-3xl font-bold">
|
||||
<Link class="text-indigo-400 hover:text-indigo-600" href="/contacts">Contacts</Link>
|
||||
<span class="text-indigo-400 font-medium">/</span>
|
||||
{{ form.first_name }} {{ form.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-md shadow overflow-hidden max-w-3xl">
|
||||
<trashed-message v-if="contact.deleted_at" class="mb-6" @restore="restore"> This contact has been deleted. </trashed-message>
|
||||
<div class="max-w-3xl bg-white rounded-md shadow overflow-hidden">
|
||||
<form @submit.prevent="update">
|
||||
<div class="p-8 -mr-6 -mb-8 flex flex-wrap">
|
||||
<text-input v-model="form.first_name" :error="form.errors.first_name" class="pr-6 pb-8 w-full lg:w-1/2" label="First name" />
|
||||
<text-input v-model="form.last_name" :error="form.errors.last_name" class="pr-6 pb-8 w-full lg:w-1/2" label="Last name" />
|
||||
<select-input v-model="form.organization_id" :error="form.errors.organization_id" class="pr-6 pb-8 w-full lg:w-1/2" label="Organization">
|
||||
<div class="flex flex-wrap -mb-8 -mr-6 p-8">
|
||||
<text-input v-model="form.first_name" :error="form.errors.first_name" class="pb-8 pr-6 w-full lg:w-1/2" label="First name" />
|
||||
<text-input v-model="form.last_name" :error="form.errors.last_name" class="pb-8 pr-6 w-full lg:w-1/2" label="Last name" />
|
||||
<select-input v-model="form.organization_id" :error="form.errors.organization_id" class="pb-8 pr-6 w-full lg:w-1/2" 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.email" :error="form.errors.email" class="pr-6 pb-8 w-full lg:w-1/2" label="Email" />
|
||||
<text-input v-model="form.phone" :error="form.errors.phone" class="pr-6 pb-8 w-full lg:w-1/2" label="Phone" />
|
||||
<text-input v-model="form.address" :error="form.errors.address" class="pr-6 pb-8 w-full lg:w-1/2" label="Address" />
|
||||
<text-input v-model="form.city" :error="form.errors.city" class="pr-6 pb-8 w-full lg:w-1/2" label="City" />
|
||||
<text-input v-model="form.region" :error="form.errors.region" class="pr-6 pb-8 w-full lg:w-1/2" label="Province/State" />
|
||||
<select-input v-model="form.country" :error="form.errors.country" class="pr-6 pb-8 w-full lg:w-1/2" label="Country">
|
||||
<text-input v-model="form.email" :error="form.errors.email" class="pb-8 pr-6 w-full lg:w-1/2" label="Email" />
|
||||
<text-input v-model="form.phone" :error="form.errors.phone" class="pb-8 pr-6 w-full lg:w-1/2" label="Phone" />
|
||||
<text-input v-model="form.address" :error="form.errors.address" class="pb-8 pr-6 w-full lg:w-1/2" label="Address" />
|
||||
<text-input v-model="form.city" :error="form.errors.city" class="pb-8 pr-6 w-full lg:w-1/2" label="City" />
|
||||
<text-input v-model="form.region" :error="form.errors.region" class="pb-8 pr-6 w-full lg:w-1/2" label="Province/State" />
|
||||
<select-input v-model="form.country" :error="form.errors.country" class="pb-8 pr-6 w-full lg:w-1/2" label="Country">
|
||||
<option :value="null" />
|
||||
<option value="CA">Canada</option>
|
||||
<option value="US">United States</option>
|
||||
</select-input>
|
||||
<text-input v-model="form.postal_code" :error="form.errors.postal_code" class="pr-6 pb-8 w-full lg:w-1/2" label="Postal code" />
|
||||
<text-input v-model="form.postal_code" :error="form.errors.postal_code" class="pb-8 pr-6 w-full lg:w-1/2" label="Postal code" />
|
||||
</div>
|
||||
<div class="px-8 py-4 bg-gray-50 border-t border-gray-100 flex items-center">
|
||||
<div class="flex items-center px-8 py-4 bg-gray-50 border-t border-gray-100">
|
||||
<button v-if="!contact.deleted_at" class="text-red-600 hover:underline" tabindex="-1" type="button" @click="destroy">Delete Contact</button>
|
||||
<loading-button :loading="form.processing" class="btn-indigo ml-auto" type="submit">Update Contact</loading-button>
|
||||
</div>
|
||||
|
@ -39,6 +38,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { Head, Link } from '@inertiajs/inertia-vue3'
|
||||
import Layout from '@/Shared/Layout'
|
||||
import TextInput from '@/Shared/TextInput'
|
||||
import SelectInput from '@/Shared/SelectInput'
|
||||
|
@ -46,12 +46,9 @@ import LoadingButton from '@/Shared/LoadingButton'
|
|||
import TrashedMessage from '@/Shared/TrashedMessage'
|
||||
|
||||
export default {
|
||||
metaInfo() {
|
||||
return {
|
||||
title: `${this.form.first_name} ${this.form.last_name}`,
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Head,
|
||||
Link,
|
||||
LoadingButton,
|
||||
SelectInput,
|
||||
TextInput,
|
||||
|
@ -81,16 +78,16 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
update() {
|
||||
this.form.put(this.route('contacts.update', this.contact.id))
|
||||
this.form.put(`/contacts/${this.contact.id}`)
|
||||
},
|
||||
destroy() {
|
||||
if (confirm('Are you sure you want to delete this contact?')) {
|
||||
this.$inertia.delete(this.route('contacts.destroy', this.contact.id))
|
||||
this.$inertia.delete(`/contacts/${this.contact.id}`)
|
||||
}
|
||||
},
|
||||
restore() {
|
||||
if (confirm('Are you sure you want to restore this contact?')) {
|
||||
this.$inertia.put(this.route('contacts.restore', this.contact.id))
|
||||
this.$inertia.put(`/contacts/${this.contact.id}/restore`)
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,60 +1,61 @@
|
|||
<template>
|
||||
<div>
|
||||
<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-md mr-4" @reset="reset">
|
||||
<Head title="Contacts" />
|
||||
<h1 class="mb-8 text-3xl font-bold">Contacts</h1>
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<search-filter v-model="form.search" class="mr-4 w-full max-w-md" @reset="reset">
|
||||
<label class="block text-gray-700">Trashed:</label>
|
||||
<select v-model="form.trashed" class="mt-1 w-full form-select">
|
||||
<select v-model="form.trashed" class="form-select mt-1 w-full">
|
||||
<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')">
|
||||
<Link class="btn-indigo" href="/contacts/create">
|
||||
<span>Create</span>
|
||||
<span class="hidden md:inline">Contact</span>
|
||||
</inertia-link>
|
||||
<span class="hidden md:inline"> Contact</span>
|
||||
</Link>
|
||||
</div>
|
||||
<div class="bg-white rounded-md shadow overflow-x-auto">
|
||||
<table class="w-full whitespace-nowrap">
|
||||
<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>
|
||||
<th class="pb-4 pt-6 px-6">Name</th>
|
||||
<th class="pb-4 pt-6 px-6">Organization</th>
|
||||
<th class="pb-4 pt-6 px-6">City</th>
|
||||
<th class="pb-4 pt-6 px-6" colspan="2">Phone</th>
|
||||
</tr>
|
||||
<tr v-for="contact in contacts.data" :key="contact.id" class="hover:bg-gray-100 focus-within:bg-gray-100">
|
||||
<td class="border-t">
|
||||
<inertia-link class="px-6 py-4 flex items-center focus:text-indigo-500" :href="route('contacts.edit', contact.id)">
|
||||
<Link class="flex items-center px-6 py-4 focus:text-indigo-500" :href="`/contacts/${contact.id}/edit`">
|
||||
{{ contact.name }}
|
||||
<icon v-if="contact.deleted_at" name="trash" class="flex-shrink-0 w-3 h-3 fill-gray-400 ml-2" />
|
||||
</inertia-link>
|
||||
<icon v-if="contact.deleted_at" name="trash" class="flex-shrink-0 ml-2 w-3 h-3 fill-gray-400" />
|
||||
</Link>
|
||||
</td>
|
||||
<td class="border-t">
|
||||
<inertia-link class="px-6 py-4 flex items-center" :href="route('contacts.edit', contact.id)" tabindex="-1">
|
||||
<Link class="flex items-center px-6 py-4" :href="`/contacts/${contact.id}/edit`" tabindex="-1">
|
||||
<div v-if="contact.organization">
|
||||
{{ contact.organization.name }}
|
||||
</div>
|
||||
</inertia-link>
|
||||
</Link>
|
||||
</td>
|
||||
<td class="border-t">
|
||||
<inertia-link class="px-6 py-4 flex items-center" :href="route('contacts.edit', contact.id)" tabindex="-1">
|
||||
<Link class="flex items-center px-6 py-4" :href="`/contacts/${contact.id}/edit`" tabindex="-1">
|
||||
{{ contact.city }}
|
||||
</inertia-link>
|
||||
</Link>
|
||||
</td>
|
||||
<td class="border-t">
|
||||
<inertia-link class="px-6 py-4 flex items-center" :href="route('contacts.edit', contact.id)" tabindex="-1">
|
||||
<Link class="flex items-center px-6 py-4" :href="`/contacts/${contact.id}/edit`" tabindex="-1">
|
||||
{{ contact.phone }}
|
||||
</inertia-link>
|
||||
</Link>
|
||||
</td>
|
||||
<td class="border-t w-px">
|
||||
<inertia-link class="px-4 flex items-center" :href="route('contacts.edit', contact.id)" tabindex="-1">
|
||||
<td class="w-px border-t">
|
||||
<Link class="flex items-center px-4" :href="`/contacts/${contact.id}/edit`" tabindex="-1">
|
||||
<icon name="cheveron-right" class="block w-6 h-6 fill-gray-400" />
|
||||
</inertia-link>
|
||||
</Link>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="contacts.data.length === 0">
|
||||
<td class="border-t px-6 py-4" colspan="4">No contacts found.</td>
|
||||
<td class="px-6 py-4 border-t" colspan="4">No contacts found.</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
@ -63,6 +64,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { Head, Link } from '@inertiajs/inertia-vue3'
|
||||
import Icon from '@/Shared/Icon'
|
||||
import pickBy from 'lodash/pickBy'
|
||||
import Layout from '@/Shared/Layout'
|
||||
|
@ -72,9 +74,10 @@ import Pagination from '@/Shared/Pagination'
|
|||
import SearchFilter from '@/Shared/SearchFilter'
|
||||
|
||||
export default {
|
||||
metaInfo: { title: 'Contacts' },
|
||||
components: {
|
||||
Head,
|
||||
Icon,
|
||||
Link,
|
||||
Pagination,
|
||||
SearchFilter,
|
||||
},
|
||||
|
@ -94,8 +97,8 @@ export default {
|
|||
watch: {
|
||||
form: {
|
||||
deep: true,
|
||||
handler: throttle(function() {
|
||||
this.$inertia.get(this.route('contacts'), pickBy(this.form), { preserveState: true })
|
||||
handler: throttle(function () {
|
||||
this.$inertia.get('/contacts', pickBy(this.form), { preserveState: true })
|
||||
}, 150),
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
<template>
|
||||
<div>
|
||||
<h1 class="mb-8 font-bold text-3xl">Dashboard</h1>
|
||||
<p class="mb-8 leading-normal">Hey there! Welcome to Ping CRM, a demo app designed to help illustrate how <a class="text-indigo-500 underline hover:text-orange-600" href="https://inertiajs.com">Inertia.js</a> works.</p>
|
||||
<Head title="Dashboard" />
|
||||
<h1 class="mb-8 text-3xl font-bold">Dashboard</h1>
|
||||
<p class="mb-8 leading-normal">Hey there! Welcome to Ping CRM, a demo app designed to help illustrate how <a class="text-indigo-500 hover:text-orange-600 underline" href="https://inertiajs.com">Inertia.js</a> works.</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Head } from '@inertiajs/inertia-vue3'
|
||||
import Layout from '@/Shared/Layout'
|
||||
|
||||
export default {
|
||||
metaInfo: { title: 'Dashboard' },
|
||||
components: {
|
||||
Head,
|
||||
},
|
||||
layout: Layout,
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,26 +1,27 @@
|
|||
<template>
|
||||
<div>
|
||||
<h1 class="mb-8 font-bold text-3xl">
|
||||
<inertia-link class="text-indigo-400 hover:text-indigo-600" :href="route('organizations')">Organizations</inertia-link>
|
||||
<Head title="Create Organization" />
|
||||
<h1 class="mb-8 text-3xl font-bold">
|
||||
<Link class="text-indigo-400 hover:text-indigo-600" href="/organizations">Organizations</Link>
|
||||
<span class="text-indigo-400 font-medium">/</span> Create
|
||||
</h1>
|
||||
<div class="bg-white rounded-md shadow overflow-hidden max-w-3xl">
|
||||
<div class="max-w-3xl bg-white rounded-md shadow overflow-hidden">
|
||||
<form @submit.prevent="store">
|
||||
<div class="p-8 -mr-6 -mb-8 flex flex-wrap">
|
||||
<text-input v-model="form.name" :error="form.errors.name" class="pr-6 pb-8 w-full lg:w-1/2" label="Name" />
|
||||
<text-input v-model="form.email" :error="form.errors.email" class="pr-6 pb-8 w-full lg:w-1/2" label="Email" />
|
||||
<text-input v-model="form.phone" :error="form.errors.phone" class="pr-6 pb-8 w-full lg:w-1/2" label="Phone" />
|
||||
<text-input v-model="form.address" :error="form.errors.address" class="pr-6 pb-8 w-full lg:w-1/2" label="Address" />
|
||||
<text-input v-model="form.city" :error="form.errors.city" class="pr-6 pb-8 w-full lg:w-1/2" label="City" />
|
||||
<text-input v-model="form.region" :error="form.errors.region" class="pr-6 pb-8 w-full lg:w-1/2" label="Province/State" />
|
||||
<select-input v-model="form.country" :error="form.errors.country" class="pr-6 pb-8 w-full lg:w-1/2" label="Country">
|
||||
<div class="flex flex-wrap -mb-8 -mr-6 p-8">
|
||||
<text-input v-model="form.name" :error="form.errors.name" class="pb-8 pr-6 w-full lg:w-1/2" label="Name" />
|
||||
<text-input v-model="form.email" :error="form.errors.email" class="pb-8 pr-6 w-full lg:w-1/2" label="Email" />
|
||||
<text-input v-model="form.phone" :error="form.errors.phone" class="pb-8 pr-6 w-full lg:w-1/2" label="Phone" />
|
||||
<text-input v-model="form.address" :error="form.errors.address" class="pb-8 pr-6 w-full lg:w-1/2" label="Address" />
|
||||
<text-input v-model="form.city" :error="form.errors.city" class="pb-8 pr-6 w-full lg:w-1/2" label="City" />
|
||||
<text-input v-model="form.region" :error="form.errors.region" class="pb-8 pr-6 w-full lg:w-1/2" label="Province/State" />
|
||||
<select-input v-model="form.country" :error="form.errors.country" class="pb-8 pr-6 w-full lg:w-1/2" label="Country">
|
||||
<option :value="null" />
|
||||
<option value="CA">Canada</option>
|
||||
<option value="US">United States</option>
|
||||
</select-input>
|
||||
<text-input v-model="form.postal_code" :error="form.errors.postal_code" class="pr-6 pb-8 w-full lg:w-1/2" label="Postal code" />
|
||||
<text-input v-model="form.postal_code" :error="form.errors.postal_code" class="pb-8 pr-6 w-full lg:w-1/2" label="Postal code" />
|
||||
</div>
|
||||
<div class="px-8 py-4 bg-gray-50 border-t border-gray-100 flex justify-end items-center">
|
||||
<div class="flex items-center justify-end px-8 py-4 bg-gray-50 border-t border-gray-100">
|
||||
<loading-button :loading="form.processing" class="btn-indigo" type="submit">Create Organization</loading-button>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -29,14 +30,16 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { Head, Link } from '@inertiajs/inertia-vue3'
|
||||
import Layout from '@/Shared/Layout'
|
||||
import TextInput from '@/Shared/TextInput'
|
||||
import SelectInput from '@/Shared/SelectInput'
|
||||
import LoadingButton from '@/Shared/LoadingButton'
|
||||
|
||||
export default {
|
||||
metaInfo: { title: 'Create Organization' },
|
||||
components: {
|
||||
Head,
|
||||
Link,
|
||||
LoadingButton,
|
||||
SelectInput,
|
||||
TextInput,
|
||||
|
@ -59,7 +62,7 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
store() {
|
||||
this.form.post(this.route('organizations.store'))
|
||||
this.form.post('/organizations')
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,68 +1,67 @@
|
|||
<template>
|
||||
<div>
|
||||
<h1 class="mb-8 font-bold text-3xl">
|
||||
<inertia-link class="text-indigo-400 hover:text-indigo-600" :href="route('organizations')">Organizations</inertia-link>
|
||||
<Head :title="form.name" />
|
||||
<h1 class="mb-8 text-3xl font-bold">
|
||||
<Link class="text-indigo-400 hover:text-indigo-600" href="/organizations">Organizations</Link>
|
||||
<span class="text-indigo-400 font-medium">/</span>
|
||||
{{ form.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-md shadow overflow-hidden max-w-3xl">
|
||||
<trashed-message v-if="organization.deleted_at" class="mb-6" @restore="restore"> This organization has been deleted. </trashed-message>
|
||||
<div class="max-w-3xl bg-white rounded-md shadow overflow-hidden">
|
||||
<form @submit.prevent="update">
|
||||
<div class="p-8 -mr-6 -mb-8 flex flex-wrap">
|
||||
<text-input v-model="form.name" :error="form.errors.name" class="pr-6 pb-8 w-full lg:w-1/2" label="Name" />
|
||||
<text-input v-model="form.email" :error="form.errors.email" class="pr-6 pb-8 w-full lg:w-1/2" label="Email" />
|
||||
<text-input v-model="form.phone" :error="form.errors.phone" class="pr-6 pb-8 w-full lg:w-1/2" label="Phone" />
|
||||
<text-input v-model="form.address" :error="form.errors.address" class="pr-6 pb-8 w-full lg:w-1/2" label="Address" />
|
||||
<text-input v-model="form.city" :error="form.errors.city" class="pr-6 pb-8 w-full lg:w-1/2" label="City" />
|
||||
<text-input v-model="form.region" :error="form.errors.region" class="pr-6 pb-8 w-full lg:w-1/2" label="Province/State" />
|
||||
<select-input v-model="form.country" :error="form.errors.country" class="pr-6 pb-8 w-full lg:w-1/2" label="Country">
|
||||
<div class="flex flex-wrap -mb-8 -mr-6 p-8">
|
||||
<text-input v-model="form.name" :error="form.errors.name" class="pb-8 pr-6 w-full lg:w-1/2" label="Name" />
|
||||
<text-input v-model="form.email" :error="form.errors.email" class="pb-8 pr-6 w-full lg:w-1/2" label="Email" />
|
||||
<text-input v-model="form.phone" :error="form.errors.phone" class="pb-8 pr-6 w-full lg:w-1/2" label="Phone" />
|
||||
<text-input v-model="form.address" :error="form.errors.address" class="pb-8 pr-6 w-full lg:w-1/2" label="Address" />
|
||||
<text-input v-model="form.city" :error="form.errors.city" class="pb-8 pr-6 w-full lg:w-1/2" label="City" />
|
||||
<text-input v-model="form.region" :error="form.errors.region" class="pb-8 pr-6 w-full lg:w-1/2" label="Province/State" />
|
||||
<select-input v-model="form.country" :error="form.errors.country" class="pb-8 pr-6 w-full lg:w-1/2" label="Country">
|
||||
<option :value="null" />
|
||||
<option value="CA">Canada</option>
|
||||
<option value="US">United States</option>
|
||||
</select-input>
|
||||
<text-input v-model="form.postal_code" :error="form.errors.postal_code" class="pr-6 pb-8 w-full lg:w-1/2" label="Postal code" />
|
||||
<text-input v-model="form.postal_code" :error="form.errors.postal_code" class="pb-8 pr-6 w-full lg:w-1/2" label="Postal code" />
|
||||
</div>
|
||||
<div class="px-8 py-4 bg-gray-50 border-t border-gray-100 flex items-center">
|
||||
<div class="flex items-center px-8 py-4 bg-gray-50 border-t border-gray-100">
|
||||
<button v-if="!organization.deleted_at" class="text-red-600 hover:underline" tabindex="-1" type="button" @click="destroy">Delete Organization</button>
|
||||
<loading-button :loading="form.processing" class="btn-indigo ml-auto" type="submit">Update Organization</loading-button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<h2 class="mt-12 font-bold text-2xl">Contacts</h2>
|
||||
<h2 class="mt-12 text-2xl font-bold">Contacts</h2>
|
||||
<div class="mt-6 bg-white rounded shadow overflow-x-auto">
|
||||
<table class="w-full whitespace-nowrap">
|
||||
<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>
|
||||
<th class="pb-4 pt-6 px-6">Name</th>
|
||||
<th class="pb-4 pt-6 px-6">City</th>
|
||||
<th class="pb-4 pt-6 px-6" colspan="2">Phone</th>
|
||||
</tr>
|
||||
<tr v-for="contact in organization.contacts" :key="contact.id" class="hover:bg-gray-100 focus-within:bg-gray-100">
|
||||
<td class="border-t">
|
||||
<inertia-link class="px-6 py-4 flex items-center focus:text-indigo-500" :href="route('contacts.edit', contact.id)">
|
||||
<Link class="flex items-center px-6 py-4 focus:text-indigo-500" :href="`/contacts/${contact.id}/edit`">
|
||||
{{ contact.name }}
|
||||
<icon v-if="contact.deleted_at" name="trash" class="flex-shrink-0 w-3 h-3 fill-gray-400 ml-2" />
|
||||
</inertia-link>
|
||||
<icon v-if="contact.deleted_at" name="trash" class="flex-shrink-0 ml-2 w-3 h-3 fill-gray-400" />
|
||||
</Link>
|
||||
</td>
|
||||
<td class="border-t">
|
||||
<inertia-link class="px-6 py-4 flex items-center" :href="route('contacts.edit', contact.id)" tabindex="-1">
|
||||
<Link class="flex items-center px-6 py-4" :href="`/contacts/${contact.id}/edit`" tabindex="-1">
|
||||
{{ contact.city }}
|
||||
</inertia-link>
|
||||
</Link>
|
||||
</td>
|
||||
<td class="border-t">
|
||||
<inertia-link class="px-6 py-4 flex items-center" :href="route('contacts.edit', contact.id)" tabindex="-1">
|
||||
<Link class="flex items-center px-6 py-4" :href="`/contacts/${contact.id}/edit`" tabindex="-1">
|
||||
{{ contact.phone }}
|
||||
</inertia-link>
|
||||
</Link>
|
||||
</td>
|
||||
<td class="border-t w-px">
|
||||
<inertia-link class="px-4 flex items-center" :href="route('contacts.edit', contact.id)" tabindex="-1">
|
||||
<td class="w-px border-t">
|
||||
<Link class="flex items-center px-4" :href="`/contacts/${contact.id}/edit`" tabindex="-1">
|
||||
<icon name="cheveron-right" class="block w-6 h-6 fill-gray-400" />
|
||||
</inertia-link>
|
||||
</Link>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="organization.contacts.length === 0">
|
||||
<td class="border-t px-6 py-4" colspan="4">No contacts found.</td>
|
||||
<td class="px-6 py-4 border-t" colspan="4">No contacts found.</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
@ -70,6 +69,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { Head, Link } from '@inertiajs/inertia-vue3'
|
||||
import Icon from '@/Shared/Icon'
|
||||
import Layout from '@/Shared/Layout'
|
||||
import TextInput from '@/Shared/TextInput'
|
||||
|
@ -78,11 +78,10 @@ import LoadingButton from '@/Shared/LoadingButton'
|
|||
import TrashedMessage from '@/Shared/TrashedMessage'
|
||||
|
||||
export default {
|
||||
metaInfo() {
|
||||
return { title: this.form.name }
|
||||
},
|
||||
components: {
|
||||
Head,
|
||||
Icon,
|
||||
Link,
|
||||
LoadingButton,
|
||||
SelectInput,
|
||||
TextInput,
|
||||
|
@ -109,16 +108,16 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
update() {
|
||||
this.form.put(this.route('organizations.update', this.organization.id))
|
||||
this.form.put(`/organizations/${this.organization.id}`)
|
||||
},
|
||||
destroy() {
|
||||
if (confirm('Are you sure you want to delete this organization?')) {
|
||||
this.$inertia.delete(this.route('organizations.destroy', this.organization.id))
|
||||
this.$inertia.delete(`/organizations/${this.organization.id}`)
|
||||
}
|
||||
},
|
||||
restore() {
|
||||
if (confirm('Are you sure you want to restore this organization?')) {
|
||||
this.$inertia.put(this.route('organizations.restore', this.organization.id))
|
||||
this.$inertia.put(`/organizations/${this.organization.id}/restore`)
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,53 +1,58 @@
|
|||
<template>
|
||||
<div>
|
||||
<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-md mr-4" @reset="reset">
|
||||
<Head title="Organizations" />
|
||||
<h1 class="mb-8 text-3xl font-bold">Organizations</h1>
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<search-filter v-model="form.search" class="mr-4 w-full max-w-md" @reset="reset">
|
||||
<label class="block text-gray-700">Trashed:</label>
|
||||
<select v-model="form.trashed" class="mt-1 w-full form-select">
|
||||
<select v-model="form.trashed" class="form-select mt-1 w-full">
|
||||
<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')">
|
||||
<Link class="btn-indigo" href="/organizations/create">
|
||||
<span>Create</span>
|
||||
<span class="hidden md:inline">Organization</span>
|
||||
</inertia-link>
|
||||
<span class="hidden md:inline"> Organization</span>
|
||||
</Link>
|
||||
</div>
|
||||
<div class="bg-white rounded-md shadow overflow-x-auto">
|
||||
<table class="w-full whitespace-nowrap">
|
||||
<thead>
|
||||
<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>
|
||||
<th class="pb-4 pt-6 px-6">Name</th>
|
||||
<th class="pb-4 pt-6 px-6">City</th>
|
||||
<th class="pb-4 pt-6 px-6" colspan="2">Phone</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="organization in organizations.data" :key="organization.id" class="hover:bg-gray-100 focus-within:bg-gray-100">
|
||||
<td class="border-t">
|
||||
<inertia-link class="px-6 py-4 flex items-center focus:text-indigo-500" :href="route('organizations.edit', organization.id)">
|
||||
<Link class="flex items-center px-6 py-4 focus:text-indigo-500" :href="`/organizations/${organization.id}/edit`">
|
||||
{{ organization.name }}
|
||||
<icon v-if="organization.deleted_at" name="trash" class="flex-shrink-0 w-3 h-3 fill-gray-400 ml-2" />
|
||||
</inertia-link>
|
||||
<icon v-if="organization.deleted_at" name="trash" class="flex-shrink-0 ml-2 w-3 h-3 fill-gray-400" />
|
||||
</Link>
|
||||
</td>
|
||||
<td class="border-t">
|
||||
<inertia-link class="px-6 py-4 flex items-center" :href="route('organizations.edit', organization.id)" tabindex="-1">
|
||||
<Link class="flex items-center px-6 py-4" :href="`/organizations/${organization.id}/edit`" tabindex="-1">
|
||||
{{ organization.city }}
|
||||
</inertia-link>
|
||||
</Link>
|
||||
</td>
|
||||
<td class="border-t">
|
||||
<inertia-link class="px-6 py-4 flex items-center" :href="route('organizations.edit', organization.id)" tabindex="-1">
|
||||
<Link class="flex items-center px-6 py-4" :href="`/organizations/${organization.id}/edit`" tabindex="-1">
|
||||
{{ organization.phone }}
|
||||
</inertia-link>
|
||||
</Link>
|
||||
</td>
|
||||
<td class="border-t w-px">
|
||||
<inertia-link class="px-4 flex items-center" :href="route('organizations.edit', organization.id)" tabindex="-1">
|
||||
<td class="w-px border-t">
|
||||
<Link class="flex items-center px-4" :href="`/organizations/${organization.id}/edit`" tabindex="-1">
|
||||
<icon name="cheveron-right" class="block w-6 h-6 fill-gray-400" />
|
||||
</inertia-link>
|
||||
</Link>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="organizations.data.length === 0">
|
||||
<td class="border-t px-6 py-4" colspan="4">No organizations found.</td>
|
||||
<td class="px-6 py-4 border-t" colspan="4">No organizations found.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<pagination class="mt-6" :links="organizations.links" />
|
||||
|
@ -55,6 +60,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { Head, Link } from '@inertiajs/inertia-vue3'
|
||||
import Icon from '@/Shared/Icon'
|
||||
import pickBy from 'lodash/pickBy'
|
||||
import Layout from '@/Shared/Layout'
|
||||
|
@ -64,9 +70,10 @@ import Pagination from '@/Shared/Pagination'
|
|||
import SearchFilter from '@/Shared/SearchFilter'
|
||||
|
||||
export default {
|
||||
metaInfo: { title: 'Organizations' },
|
||||
components: {
|
||||
Head,
|
||||
Icon,
|
||||
Link,
|
||||
Pagination,
|
||||
SearchFilter,
|
||||
},
|
||||
|
@ -86,8 +93,8 @@ export default {
|
|||
watch: {
|
||||
form: {
|
||||
deep: true,
|
||||
handler: throttle(function() {
|
||||
this.$inertia.get(this.route('organizations'), pickBy(this.form), { preserveState: true })
|
||||
handler: throttle(function () {
|
||||
this.$inertia.get('/organizations', pickBy(this.form), { preserveState: true })
|
||||
}, 150),
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
<template>
|
||||
<div>
|
||||
<h1 class="mb-8 font-bold text-3xl">Reports</h1>
|
||||
<Head title="Reports" />
|
||||
<h1 class="mb-8 text-3xl font-bold">Reports</h1>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Head } from '@inertiajs/inertia-vue3'
|
||||
import Layout from '@/Shared/Layout'
|
||||
|
||||
export default {
|
||||
metaInfo: { title: 'Reports' },
|
||||
components: {
|
||||
Head,
|
||||
},
|
||||
layout: Layout,
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,23 +1,24 @@
|
|||
<template>
|
||||
<div>
|
||||
<h1 class="mb-8 font-bold text-3xl">
|
||||
<inertia-link class="text-indigo-400 hover:text-indigo-600" :href="route('users')">Users</inertia-link>
|
||||
<Head title="Create User" />
|
||||
<h1 class="mb-8 text-3xl font-bold">
|
||||
<Link class="text-indigo-400 hover:text-indigo-600" href="/users">Users</Link>
|
||||
<span class="text-indigo-400 font-medium">/</span> Create
|
||||
</h1>
|
||||
<div class="bg-white rounded-md shadow overflow-hidden max-w-3xl">
|
||||
<div class="max-w-3xl bg-white rounded-md shadow overflow-hidden">
|
||||
<form @submit.prevent="store">
|
||||
<div class="p-8 -mr-6 -mb-8 flex flex-wrap">
|
||||
<text-input v-model="form.first_name" :error="form.errors.first_name" class="pr-6 pb-8 w-full lg:w-1/2" label="First name" />
|
||||
<text-input v-model="form.last_name" :error="form.errors.last_name" class="pr-6 pb-8 w-full lg:w-1/2" label="Last name" />
|
||||
<text-input v-model="form.email" :error="form.errors.email" class="pr-6 pb-8 w-full lg:w-1/2" label="Email" />
|
||||
<text-input v-model="form.password" :error="form.errors.password" class="pr-6 pb-8 w-full lg:w-1/2" type="password" autocomplete="new-password" label="Password" />
|
||||
<select-input v-model="form.owner" :error="form.errors.owner" class="pr-6 pb-8 w-full lg:w-1/2" label="Owner">
|
||||
<div class="flex flex-wrap -mb-8 -mr-6 p-8">
|
||||
<text-input v-model="form.first_name" :error="form.errors.first_name" class="pb-8 pr-6 w-full lg:w-1/2" label="First name" />
|
||||
<text-input v-model="form.last_name" :error="form.errors.last_name" class="pb-8 pr-6 w-full lg:w-1/2" label="Last name" />
|
||||
<text-input v-model="form.email" :error="form.errors.email" class="pb-8 pr-6 w-full lg:w-1/2" label="Email" />
|
||||
<text-input v-model="form.password" :error="form.errors.password" class="pb-8 pr-6 w-full lg:w-1/2" type="password" autocomplete="new-password" label="Password" />
|
||||
<select-input v-model="form.owner" :error="form.errors.owner" class="pb-8 pr-6 w-full lg:w-1/2" label="Owner">
|
||||
<option :value="true">Yes</option>
|
||||
<option :value="false">No</option>
|
||||
</select-input>
|
||||
<file-input v-model="form.photo" :error="form.errors.photo" class="pr-6 pb-8 w-full lg:w-1/2" type="file" accept="image/*" label="Photo" />
|
||||
<file-input v-model="form.photo" :error="form.errors.photo" class="pb-8 pr-6 w-full lg:w-1/2" type="file" accept="image/*" label="Photo" />
|
||||
</div>
|
||||
<div class="px-8 py-4 bg-gray-50 border-t border-gray-100 flex justify-end items-center">
|
||||
<div class="flex items-center justify-end px-8 py-4 bg-gray-50 border-t border-gray-100">
|
||||
<loading-button :loading="form.processing" class="btn-indigo" type="submit">Create User</loading-button>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -26,6 +27,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { Head, Link } from '@inertiajs/inertia-vue3'
|
||||
import Layout from '@/Shared/Layout'
|
||||
import FileInput from '@/Shared/FileInput'
|
||||
import TextInput from '@/Shared/TextInput'
|
||||
|
@ -33,9 +35,10 @@ import SelectInput from '@/Shared/SelectInput'
|
|||
import LoadingButton from '@/Shared/LoadingButton'
|
||||
|
||||
export default {
|
||||
metaInfo: { title: 'Create User' },
|
||||
components: {
|
||||
FileInput,
|
||||
Head,
|
||||
Link,
|
||||
LoadingButton,
|
||||
SelectInput,
|
||||
TextInput,
|
||||
|
@ -45,10 +48,10 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
form: this.$inertia.form({
|
||||
first_name: null,
|
||||
last_name: null,
|
||||
email: null,
|
||||
password: null,
|
||||
first_name: '',
|
||||
last_name: '',
|
||||
email: '',
|
||||
password: '',
|
||||
owner: false,
|
||||
photo: null,
|
||||
}),
|
||||
|
@ -56,7 +59,7 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
store() {
|
||||
this.form.post(this.route('users.store').url())
|
||||
this.form.post('/users')
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,30 +1,29 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="mb-8 flex justify-start max-w-3xl">
|
||||
<h1 class="font-bold text-3xl">
|
||||
<inertia-link class="text-indigo-400 hover:text-indigo-600" :href="route('users')">Users</inertia-link>
|
||||
<Head :title="`${form.first_name} ${form.last_name}`" />
|
||||
<div class="flex justify-start mb-8 max-w-3xl">
|
||||
<h1 class="text-3xl font-bold">
|
||||
<Link class="text-indigo-400 hover:text-indigo-600" href="/users">Users</Link>
|
||||
<span class="text-indigo-400 font-medium">/</span>
|
||||
{{ form.first_name }} {{ form.last_name }}
|
||||
</h1>
|
||||
<img v-if="user.photo" class="block w-8 h-8 rounded-full ml-4" :src="user.photo" />
|
||||
<img v-if="user.photo" class="block ml-4 w-8 h-8 rounded-full" :src="user.photo" />
|
||||
</div>
|
||||
<trashed-message v-if="user.deleted_at" class="mb-6" @restore="restore">
|
||||
This user has been deleted.
|
||||
</trashed-message>
|
||||
<div class="bg-white rounded-md shadow overflow-hidden max-w-3xl">
|
||||
<trashed-message v-if="user.deleted_at" class="mb-6" @restore="restore"> This user has been deleted. </trashed-message>
|
||||
<div class="max-w-3xl bg-white rounded-md shadow overflow-hidden">
|
||||
<form @submit.prevent="update">
|
||||
<div class="p-8 -mr-6 -mb-8 flex flex-wrap">
|
||||
<text-input v-model="form.first_name" :error="form.errors.first_name" class="pr-6 pb-8 w-full lg:w-1/2" label="First name" />
|
||||
<text-input v-model="form.last_name" :error="form.errors.last_name" class="pr-6 pb-8 w-full lg:w-1/2" label="Last name" />
|
||||
<text-input v-model="form.email" :error="form.errors.email" class="pr-6 pb-8 w-full lg:w-1/2" label="Email" />
|
||||
<text-input v-model="form.password" :error="form.errors.password" class="pr-6 pb-8 w-full lg:w-1/2" type="password" autocomplete="new-password" label="Password" />
|
||||
<select-input v-model="form.owner" :error="form.errors.owner" class="pr-6 pb-8 w-full lg:w-1/2" label="Owner">
|
||||
<div class="flex flex-wrap -mb-8 -mr-6 p-8">
|
||||
<text-input v-model="form.first_name" :error="form.errors.first_name" class="pb-8 pr-6 w-full lg:w-1/2" label="First name" />
|
||||
<text-input v-model="form.last_name" :error="form.errors.last_name" class="pb-8 pr-6 w-full lg:w-1/2" label="Last name" />
|
||||
<text-input v-model="form.email" :error="form.errors.email" class="pb-8 pr-6 w-full lg:w-1/2" label="Email" />
|
||||
<text-input v-model="form.password" :error="form.errors.password" class="pb-8 pr-6 w-full lg:w-1/2" type="password" autocomplete="new-password" label="Password" />
|
||||
<select-input v-model="form.owner" :error="form.errors.owner" class="pb-8 pr-6 w-full lg:w-1/2" label="Owner">
|
||||
<option :value="true">Yes</option>
|
||||
<option :value="false">No</option>
|
||||
</select-input>
|
||||
<file-input v-model="form.photo" :error="form.errors.photo" class="pr-6 pb-8 w-full lg:w-1/2" type="file" accept="image/*" label="Photo" />
|
||||
<file-input v-model="form.photo" :error="form.errors.photo" class="pb-8 pr-6 w-full lg:w-1/2" type="file" accept="image/*" label="Photo" />
|
||||
</div>
|
||||
<div class="px-8 py-4 bg-gray-50 border-t border-gray-100 flex items-center">
|
||||
<div class="flex items-center px-8 py-4 bg-gray-50 border-t border-gray-100">
|
||||
<button v-if="!user.deleted_at" class="text-red-600 hover:underline" tabindex="-1" type="button" @click="destroy">Delete User</button>
|
||||
<loading-button :loading="form.processing" class="btn-indigo ml-auto" type="submit">Update User</loading-button>
|
||||
</div>
|
||||
|
@ -34,6 +33,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { Head, Link } from '@inertiajs/inertia-vue3'
|
||||
import Layout from '@/Shared/Layout'
|
||||
import TextInput from '@/Shared/TextInput'
|
||||
import FileInput from '@/Shared/FileInput'
|
||||
|
@ -42,13 +42,10 @@ import LoadingButton from '@/Shared/LoadingButton'
|
|||
import TrashedMessage from '@/Shared/TrashedMessage'
|
||||
|
||||
export default {
|
||||
metaInfo() {
|
||||
return {
|
||||
title: `${this.form.first_name} ${this.form.last_name}`,
|
||||
}
|
||||
},
|
||||
components: {
|
||||
FileInput,
|
||||
Head,
|
||||
Link,
|
||||
LoadingButton,
|
||||
SelectInput,
|
||||
TextInput,
|
||||
|
@ -66,7 +63,7 @@ export default {
|
|||
first_name: this.user.first_name,
|
||||
last_name: this.user.last_name,
|
||||
email: this.user.email,
|
||||
password: null,
|
||||
password: '',
|
||||
owner: this.user.owner,
|
||||
photo: null,
|
||||
}),
|
||||
|
@ -74,18 +71,18 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
update() {
|
||||
this.form.post(this.route('users.update', this.user.id).url(), {
|
||||
this.form.post(`/users/${this.user.id}`, {
|
||||
onSuccess: () => this.form.reset('password', 'photo'),
|
||||
})
|
||||
},
|
||||
destroy() {
|
||||
if (confirm('Are you sure you want to delete this user?')) {
|
||||
this.$inertia.delete(this.route('users.destroy', this.user.id))
|
||||
this.$inertia.delete(`/users/${this.user.id}`)
|
||||
}
|
||||
},
|
||||
restore() {
|
||||
if (confirm('Are you sure you want to restore this user?')) {
|
||||
this.$inertia.put(this.route('users.restore', this.user.id))
|
||||
this.$inertia.put(`/users/${this.user.id}/restore`)
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,59 +1,60 @@
|
|||
<template>
|
||||
<div>
|
||||
<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-md mr-4" @reset="reset">
|
||||
<Head title="Users" />
|
||||
<h1 class="mb-8 text-3xl font-bold">Users</h1>
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<search-filter v-model="form.search" class="mr-4 w-full max-w-md" @reset="reset">
|
||||
<label class="block text-gray-700">Role:</label>
|
||||
<select v-model="form.role" class="mt-1 w-full form-select">
|
||||
<select v-model="form.role" class="form-select mt-1 w-full">
|
||||
<option :value="null" />
|
||||
<option value="user">User</option>
|
||||
<option value="owner">Owner</option>
|
||||
</select>
|
||||
<label class="mt-4 block text-gray-700">Trashed:</label>
|
||||
<select v-model="form.trashed" class="mt-1 w-full form-select">
|
||||
<label class="block mt-4 text-gray-700">Trashed:</label>
|
||||
<select v-model="form.trashed" class="form-select mt-1 w-full">
|
||||
<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')">
|
||||
<Link class="btn-indigo" href="/users/create">
|
||||
<span>Create</span>
|
||||
<span class="hidden md:inline">User</span>
|
||||
</inertia-link>
|
||||
<span class="hidden md:inline"> User</span>
|
||||
</Link>
|
||||
</div>
|
||||
<div class="bg-white rounded-md shadow overflow-x-auto">
|
||||
<table class="w-full whitespace-nowrap">
|
||||
<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>
|
||||
<th class="pb-4 pt-6 px-6">Name</th>
|
||||
<th class="pb-4 pt-6 px-6">Email</th>
|
||||
<th class="pb-4 pt-6 px-6" colspan="2">Role</th>
|
||||
</tr>
|
||||
<tr v-for="user in users" :key="user.id" class="hover:bg-gray-100 focus-within:bg-gray-100">
|
||||
<td class="border-t">
|
||||
<inertia-link class="px-6 py-4 flex items-center focus:text-indigo-500" :href="route('users.edit', user.id)">
|
||||
<img v-if="user.photo" class="block w-5 h-5 rounded-full mr-2 -my-2" :src="user.photo" />
|
||||
<Link class="flex items-center px-6 py-4 focus:text-indigo-500" :href="`/users/${user.id}/edit`">
|
||||
<img v-if="user.photo" class="block -my-2 mr-2 w-5 h-5 rounded-full" :src="user.photo" />
|
||||
{{ user.name }}
|
||||
<icon v-if="user.deleted_at" name="trash" class="flex-shrink-0 w-3 h-3 fill-gray-400 ml-2" />
|
||||
</inertia-link>
|
||||
<icon v-if="user.deleted_at" name="trash" class="flex-shrink-0 ml-2 w-3 h-3 fill-gray-400" />
|
||||
</Link>
|
||||
</td>
|
||||
<td class="border-t">
|
||||
<inertia-link class="px-6 py-4 flex items-center" :href="route('users.edit', user.id)" tabindex="-1">
|
||||
<Link class="flex items-center px-6 py-4" :href="`/users/${user.id}/edit`" tabindex="-1">
|
||||
{{ user.email }}
|
||||
</inertia-link>
|
||||
</Link>
|
||||
</td>
|
||||
<td class="border-t">
|
||||
<inertia-link class="px-6 py-4 flex items-center" :href="route('users.edit', user.id)" tabindex="-1">
|
||||
<Link class="flex items-center px-6 py-4" :href="`/users/${user.id}/edit`" tabindex="-1">
|
||||
{{ user.owner ? 'Owner' : 'User' }}
|
||||
</inertia-link>
|
||||
</Link>
|
||||
</td>
|
||||
<td class="border-t w-px">
|
||||
<inertia-link class="px-4 flex items-center" :href="route('users.edit', user.id)" tabindex="-1">
|
||||
<td class="w-px border-t">
|
||||
<Link class="flex items-center px-4" :href="`/users/${user.id}/edit`" tabindex="-1">
|
||||
<icon name="cheveron-right" class="block w-6 h-6 fill-gray-400" />
|
||||
</inertia-link>
|
||||
</Link>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="users.length === 0">
|
||||
<td class="border-t px-6 py-4" colspan="4">No users found.</td>
|
||||
<td class="px-6 py-4 border-t" colspan="4">No users found.</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
@ -61,6 +62,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { Head, Link } from '@inertiajs/inertia-vue3'
|
||||
import Icon from '@/Shared/Icon'
|
||||
import pickBy from 'lodash/pickBy'
|
||||
import Layout from '@/Shared/Layout'
|
||||
|
@ -69,9 +71,10 @@ import mapValues from 'lodash/mapValues'
|
|||
import SearchFilter from '@/Shared/SearchFilter'
|
||||
|
||||
export default {
|
||||
metaInfo: { title: 'Users' },
|
||||
components: {
|
||||
Head,
|
||||
Icon,
|
||||
Link,
|
||||
SearchFilter,
|
||||
},
|
||||
layout: Layout,
|
||||
|
@ -91,8 +94,8 @@ export default {
|
|||
watch: {
|
||||
form: {
|
||||
deep: true,
|
||||
handler: throttle(function() {
|
||||
this.$inertia.get(this.route('users'), pickBy(this.form), { preserveState: true })
|
||||
handler: throttle(function () {
|
||||
this.$inertia.get('/users', pickBy(this.form), { preserveState: true })
|
||||
}, 150),
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
<template>
|
||||
<button type="button" @click="show = true">
|
||||
<slot />
|
||||
<portal v-if="show" to="dropdown">
|
||||
<teleport 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="show = false" />
|
||||
<div ref="dropdown" style="position: absolute; z-index: 99999;" @click.stop="show = autoClose ? false : true">
|
||||
<div style="position: fixed; top: 0; right: 0; left: 0; bottom: 0; z-index: 99998; background: black; opacity: 0.2" @click="show = false" />
|
||||
<div ref="dropdown" style="position: absolute; z-index: 99999" @click.stop="show = !autoClose">
|
||||
<slot name="dropdown" />
|
||||
</div>
|
||||
</div>
|
||||
</portal>
|
||||
</teleport>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Popper from 'popper.js'
|
||||
import { createPopper } from '@popperjs/core'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
|
@ -21,10 +21,6 @@ export default {
|
|||
type: String,
|
||||
default: 'bottom-end',
|
||||
},
|
||||
boundary: {
|
||||
type: String,
|
||||
default: 'scrollParent',
|
||||
},
|
||||
autoClose: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
|
@ -39,11 +35,16 @@ export default {
|
|||
show(show) {
|
||||
if (show) {
|
||||
this.$nextTick(() => {
|
||||
this.popper = new Popper(this.$el, this.$refs.dropdown, {
|
||||
this.popper = createPopper(this.$el, this.$refs.dropdown, {
|
||||
placement: this.placement,
|
||||
modifiers: {
|
||||
preventOverflow: { boundariesElement: this.boundary },
|
||||
modifiers: [
|
||||
{
|
||||
name: 'preventOverflow',
|
||||
options: {
|
||||
altBoundary: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
} else if (this.popper) {
|
||||
|
@ -52,8 +53,8 @@ export default {
|
|||
},
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('keydown', e => {
|
||||
if (e.keyCode === 27) {
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
this.show = false
|
||||
}
|
||||
})
|
||||
|
|
|
@ -3,18 +3,14 @@
|
|||
<label v-if="label" class="form-label">{{ label }}:</label>
|
||||
<div class="form-input p-0" :class="{ error: errors.length }">
|
||||
<input ref="file" type="file" :accept="accept" class="hidden" @change="change" />
|
||||
<div v-if="!value" class="p-2">
|
||||
<button type="button" class="px-4 py-1 bg-gray-500 hover:bg-gray-700 rounded-sm text-xs font-medium text-white" @click="browse">
|
||||
Browse
|
||||
</button>
|
||||
<div v-if="!modelValue" class="p-2">
|
||||
<button type="button" class="px-4 py-1 text-white text-xs font-medium bg-gray-500 hover:bg-gray-700 rounded-sm" @click="browse">Browse</button>
|
||||
</div>
|
||||
<div v-else class="flex items-center justify-between p-2">
|
||||
<div class="flex-1 pr-1">
|
||||
{{ value.name }} <span class="text-gray-500 text-xs">({{ filesize(value.size) }})</span>
|
||||
{{ modelValue.name }} <span class="text-gray-500 text-xs">({{ filesize(modelValue.size) }})</span>
|
||||
</div>
|
||||
<button type="button" class="px-4 py-1 bg-gray-500 hover:bg-gray-700 rounded-sm text-xs font-medium text-white" @click="remove">
|
||||
Remove
|
||||
</button>
|
||||
<button type="button" class="px-4 py-1 text-white text-xs font-medium bg-gray-500 hover:bg-gray-700 rounded-sm" @click="remove">Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="errors.length" class="form-error">{{ errors[0] }}</div>
|
||||
|
@ -24,7 +20,7 @@
|
|||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: File,
|
||||
modelValue: File,
|
||||
label: String,
|
||||
accept: String,
|
||||
errors: {
|
||||
|
@ -32,8 +28,9 @@ export default {
|
|||
default: () => [],
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
watch: {
|
||||
value(value) {
|
||||
modelValue(value) {
|
||||
if (!value) {
|
||||
this.$refs.file.value = ''
|
||||
}
|
||||
|
@ -48,10 +45,10 @@ export default {
|
|||
this.$refs.file.click()
|
||||
},
|
||||
change(e) {
|
||||
this.$emit('input', e.target.files[0])
|
||||
this.$emit('update:modelValue', e.target.files[0])
|
||||
},
|
||||
remove() {
|
||||
this.$emit('input', null)
|
||||
this.$emit('update:modelValue', null)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
<template>
|
||||
<div>
|
||||
<div v-if="$page.props.flash.success && show" class="mb-8 flex items-center justify-between bg-green-500 rounded max-w-3xl">
|
||||
<div v-if="$page.props.flash.success && show" class="flex items-center justify-between mb-8 max-w-3xl bg-green-500 rounded">
|
||||
<div class="flex items-center">
|
||||
<svg class="ml-4 mr-2 flex-shrink-0 w-4 h-4 fill-white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><polygon points="0 11 2 9 7 14 18 3 20 5 7 18" /></svg>
|
||||
<svg class="flex-shrink-0 ml-4 mr-2 w-4 h-4 fill-white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><polygon points="0 11 2 9 7 14 18 3 20 5 7 18" /></svg>
|
||||
<div class="py-4 text-white text-sm font-medium">{{ $page.props.flash.success }}</div>
|
||||
</div>
|
||||
<button type="button" class="group mr-2 p-2" @click="show = false">
|
||||
<svg class="block w-2 h-2 fill-green-800 group-hover:fill-white" xmlns="http://www.w3.org/2000/svg" width="235.908" height="235.908" viewBox="278.046 126.846 235.908 235.908"><path d="M506.784 134.017c-9.56-9.56-25.06-9.56-34.62 0L396 210.18l-76.164-76.164c-9.56-9.56-25.06-9.56-34.62 0-9.56 9.56-9.56 25.06 0 34.62L361.38 244.8l-76.164 76.165c-9.56 9.56-9.56 25.06 0 34.62 9.56 9.56 25.06 9.56 34.62 0L396 279.42l76.164 76.165c9.56 9.56 25.06 9.56 34.62 0 9.56-9.56 9.56-25.06 0-34.62L430.62 244.8l76.164-76.163c9.56-9.56 9.56-25.06 0-34.62z" /></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="($page.props.flash.error || Object.keys($page.props.errors).length > 0) && show" class="mb-8 flex items-center justify-between bg-red-400 rounded max-w-3xl">
|
||||
<div v-if="($page.props.flash.error || Object.keys($page.props.errors).length > 0) && show" class="flex items-center justify-between mb-8 max-w-3xl bg-red-500 rounded">
|
||||
<div class="flex items-center">
|
||||
<svg class="ml-4 mr-2 flex-shrink-0 w-4 h-4 fill-white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zm1.41-1.41A8 8 0 1 0 15.66 4.34 8 8 0 0 0 4.34 15.66zm9.9-8.49L11.41 10l2.83 2.83-1.41 1.41L10 11.41l-2.83 2.83-1.41-1.41L8.59 10 5.76 7.17l1.41-1.41L10 8.59l2.83-2.83 1.41 1.41z" /></svg>
|
||||
<svg class="flex-shrink-0 ml-4 mr-2 w-4 h-4 fill-white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zm1.41-1.41A8 8 0 1 0 15.66 4.34 8 8 0 0 0 4.34 15.66zm9.9-8.49L11.41 10l2.83 2.83-1.41 1.41L10 11.41l-2.83 2.83-1.41-1.41L8.59 10 5.76 7.17l1.41-1.41L10 8.59l2.83-2.83 1.41 1.41z" /></svg>
|
||||
<div v-if="$page.props.flash.error" class="py-4 text-white text-sm font-medium">{{ $page.props.flash.error }}</div>
|
||||
<div v-else class="py-4 text-white text-sm font-medium">
|
||||
<span v-if="Object.keys($page.props.errors).length === 1">There is one form error.</span>
|
||||
|
|
|
@ -1,14 +1,9 @@
|
|||
<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-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>
|
||||
|
|
|
@ -1,41 +1,49 @@
|
|||
<template>
|
||||
<div>
|
||||
<portal-target name="dropdown" slim />
|
||||
<div id="dropdown" />
|
||||
<div class="md:flex md:flex-col">
|
||||
<div class="md:h-screen md:flex md:flex-col">
|
||||
<div class="md:flex md:flex-col md:h-screen">
|
||||
<div class="md:flex md:flex-shrink-0">
|
||||
<div class="bg-indigo-900 md:flex-shrink-0 md:w-56 px-6 py-4 flex items-center justify-between md:justify-center">
|
||||
<inertia-link class="mt-1" href="/">
|
||||
<div class="flex items-center justify-between px-6 py-4 bg-indigo-900 md:flex-shrink-0 md:justify-center md:w-56">
|
||||
<Link class="mt-1" href="/">
|
||||
<logo class="fill-white" width="120" height="28" />
|
||||
</inertia-link>
|
||||
</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-800 rounded">
|
||||
<template #default>
|
||||
<svg class="w-6 h-6 fill-white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z" /></svg>
|
||||
</template>
|
||||
<template #dropdown>
|
||||
<div class="mt-2 px-8 py-4 bg-indigo-800 rounded shadow-lg">
|
||||
<main-menu />
|
||||
</div>
|
||||
</template>
|
||||
</dropdown>
|
||||
</div>
|
||||
<div class="bg-white border-b w-full p-4 md:py-0 md:px-12 text-sm md:text-md flex justify-between items-center">
|
||||
<div class="mt-1 mr-4">{{ $page.props.auth.user.account.name }}</div>
|
||||
<div class="md:text-md flex items-center justify-between p-4 w-full text-sm bg-white border-b md:px-12 md:py-0">
|
||||
<div class="mr-4 mt-1">{{ 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-gray-700 group-hover:text-indigo-600 focus:text-indigo-600 mr-1 whitespace-nowrap">
|
||||
<span>{{ $page.props.auth.user.first_name }}</span>
|
||||
<span class="hidden md:inline">{{ $page.props.auth.user.last_name }}</span>
|
||||
<template #default>
|
||||
<div class="group flex items-center cursor-pointer select-none">
|
||||
<div class="mr-1 text-gray-700 group-hover:text-indigo-600 focus:text-indigo-600 whitespace-nowrap">
|
||||
<span>{{ auth.user.first_name }}</span>
|
||||
<span class="hidden md:inline"> {{ auth.user.last_name }}</span>
|
||||
</div>
|
||||
<icon class="w-5 h-5 group-hover:fill-indigo-600 fill-gray-700 focus:fill-indigo-600" name="cheveron-down" />
|
||||
<icon class="w-5 h-5 fill-gray-700 group-hover:fill-indigo-600 focus:fill-indigo-600" name="cheveron-down" />
|
||||
</div>
|
||||
<div slot="dropdown" class="mt-2 py-2 shadow-xl bg-white rounded text-sm">
|
||||
<inertia-link class="block px-6 py-2 hover:bg-indigo-500 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-500 hover:text-white" :href="route('users')">Manage Users</inertia-link>
|
||||
<inertia-link class="block px-6 py-2 hover:bg-indigo-500 hover:text-white w-full text-left" :href="route('logout')" method="delete" as="button">Logout</inertia-link>
|
||||
</template>
|
||||
<template #dropdown>
|
||||
<div class="mt-2 py-2 text-sm bg-white rounded shadow-xl">
|
||||
<Link class="block px-6 py-2 hover:text-white hover:bg-indigo-500" :href="`/users/${auth.user.id}/edit`">My Profile</Link>
|
||||
<Link class="block px-6 py-2 hover:text-white hover:bg-indigo-500" href="/users">Manage Users</Link>
|
||||
<Link class="block px-6 py-2 w-full text-left hover:text-white hover:bg-indigo-500" href="/logout" method="delete" as="button">Logout</Link>
|
||||
</div>
|
||||
</template>
|
||||
</dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<div class="md:flex md:flex-grow md:overflow-hidden">
|
||||
<main-menu class="hidden md:block bg-indigo-800 flex-shrink-0 w-56 p-12 overflow-y-auto" />
|
||||
<div class="md:flex-1 px-4 py-8 md:p-12 md:overflow-y-auto" scroll-region>
|
||||
<main-menu class="hidden flex-shrink-0 p-12 w-56 bg-indigo-800 overflow-y-auto md:block" />
|
||||
<div class="px-4 py-8 md:flex-1 md:p-12 md:overflow-y-auto" scroll-region>
|
||||
<flash-messages />
|
||||
<slot />
|
||||
</div>
|
||||
|
@ -46,6 +54,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { Link } from '@inertiajs/inertia-vue3'
|
||||
import Icon from '@/Shared/Icon'
|
||||
import Logo from '@/Shared/Logo'
|
||||
import Dropdown from '@/Shared/Dropdown'
|
||||
|
@ -57,8 +66,12 @@ export default {
|
|||
Dropdown,
|
||||
FlashMessages,
|
||||
Icon,
|
||||
Link,
|
||||
Logo,
|
||||
MainMenu,
|
||||
},
|
||||
props: {
|
||||
auth: Object,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,38 +1,40 @@
|
|||
<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-400 group-hover:fill-white'" />
|
||||
<Link class="group flex items-center py-3" href="/">
|
||||
<icon name="dashboard" class="mr-2 w-4 h-4" :class="isUrl('') ? 'fill-white' : 'fill-indigo-400 group-hover:fill-white'" />
|
||||
<div :class="isUrl('') ? 'text-white' : 'text-indigo-300 group-hover:text-white'">Dashboard</div>
|
||||
</inertia-link>
|
||||
</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-400 group-hover:fill-white'" />
|
||||
<Link class="group flex items-center py-3" href="/organizations">
|
||||
<icon name="office" class="mr-2 w-4 h-4" :class="isUrl('organizations') ? 'fill-white' : 'fill-indigo-400 group-hover:fill-white'" />
|
||||
<div :class="isUrl('organizations') ? 'text-white' : 'text-indigo-300 group-hover:text-white'">Organizations</div>
|
||||
</inertia-link>
|
||||
</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-400 group-hover:fill-white'" />
|
||||
<Link class="group flex items-center py-3" href="/contacts">
|
||||
<icon name="users" class="mr-2 w-4 h-4" :class="isUrl('contacts') ? 'fill-white' : 'fill-indigo-400 group-hover:fill-white'" />
|
||||
<div :class="isUrl('contacts') ? 'text-white' : 'text-indigo-300 group-hover:text-white'">Contacts</div>
|
||||
</inertia-link>
|
||||
</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-400 group-hover:fill-white'" />
|
||||
<Link class="group flex items-center py-3" href="/reports">
|
||||
<icon name="printer" class="mr-2 w-4 h-4" :class="isUrl('reports') ? 'fill-white' : 'fill-indigo-400 group-hover:fill-white'" />
|
||||
<div :class="isUrl('reports') ? 'text-white' : 'text-indigo-300 group-hover:text-white'">Reports</div>
|
||||
</inertia-link>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Link } from '@inertiajs/inertia-vue3'
|
||||
import Icon from '@/Shared/Icon'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Icon,
|
||||
Link,
|
||||
},
|
||||
methods: {
|
||||
isUrl(...urls) {
|
||||
|
@ -40,7 +42,7 @@ export default {
|
|||
if (urls[0] === '') {
|
||||
return currentUrl === ''
|
||||
}
|
||||
return urls.filter(url => currentUrl.startsWith(url)).length
|
||||
return urls.filter((url) => currentUrl.startsWith(url)).length
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -2,15 +2,20 @@
|
|||
<div v-if="links.length > 3">
|
||||
<div class="flex flex-wrap -mb-1">
|
||||
<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 leading-4 text-gray-400 border rounded" v-html="link.label" />
|
||||
<inertia-link v-else :key="key" class="mr-1 mb-1 px-4 py-3 text-sm leading-4 border rounded hover:bg-white focus:border-indigo-500 focus:text-indigo-500" :class="{ 'bg-white': link.active }" :href="link.url" v-html="link.label" />
|
||||
<div v-if="link.url === null" :key="key" class="mb-1 mr-1 px-4 py-3 text-gray-400 text-sm leading-4 border rounded" v-html="link.label" />
|
||||
<Link v-else :key="`link-${key}`" class="mb-1 mr-1 px-4 py-3 focus:text-indigo-500 text-sm leading-4 hover:bg-white border focus:border-indigo-500 rounded" :class="{ 'bg-white': link.active }" :href="link.url" v-html="link.label" />
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Link } from '@inertiajs/inertia-vue3'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Link,
|
||||
},
|
||||
props: {
|
||||
links: Array,
|
||||
},
|
||||
|
|
|
@ -1,20 +1,24 @@
|
|||
<template>
|
||||
<div class="flex items-center">
|
||||
<div class="flex w-full bg-white shadow rounded">
|
||||
<dropdown :auto-close="false" class="px-4 md:px-6 rounded-l border-r hover:bg-gray-100 focus:border-white focus:ring focus:z-10" placement="bottom-start">
|
||||
<div class="flex w-full bg-white rounded shadow">
|
||||
<dropdown :auto-close="false" class="focus:z-10 px-4 hover:bg-gray-100 border-r focus:border-white rounded-l focus:ring md:px-6" placement="bottom-start">
|
||||
<template #default>
|
||||
<div class="flex items-baseline">
|
||||
<span class="text-gray-700 hidden md:inline">Filter</span>
|
||||
<span class="hidden text-gray-700 md:inline">Filter</span>
|
||||
<svg class="w-2 h-2 fill-gray-700 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>
|
||||
<div slot="dropdown" class="mt-2 px-4 py-6 w-screen shadow-xl bg-white rounded" :style="{ maxWidth: `${maxWidth}px` }">
|
||||
</template>
|
||||
<template #dropdown>
|
||||
<div class="mt-2 px-4 py-6 w-screen bg-white rounded shadow-xl" :style="{ maxWidth: `${maxWidth}px` }">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
</dropdown>
|
||||
<input class="relative w-full px-6 py-3 rounded-r focus:ring" autocomplete="off" type="text" name="search" placeholder="Search…" :value="value" @input="$emit('input', $event.target.value)" />
|
||||
<input class="relative px-6 py-3 w-full rounded-r focus:shadow-outline" autocomplete="off" type="text" name="search" placeholder="Search…" :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" />
|
||||
</div>
|
||||
<button class="ml-3 text-sm text-gray-500 hover:text-gray-700 focus:text-indigo-500" type="button" @click="$emit('reset')">Reset</button>
|
||||
<button class="ml-3 text-gray-500 hover:text-gray-700 focus:text-indigo-500 text-sm" type="button" @click="$emit('reset')">Reset</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -26,11 +30,12 @@ export default {
|
|||
Dropdown,
|
||||
},
|
||||
props: {
|
||||
value: String,
|
||||
modelValue: String,
|
||||
maxWidth: {
|
||||
type: Number,
|
||||
default: 300,
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue', 'reset'],
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div>
|
||||
<div :class="$attrs.class">
|
||||
<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 }">
|
||||
<select :id="id" ref="input" v-model="selected" v-bind="{ ...$attrs, class: null }" class="form-select" :class="{ error: error }">
|
||||
<slot />
|
||||
</select>
|
||||
<div v-if="error" class="form-error">{{ error }}</div>
|
||||
|
@ -9,27 +9,31 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { v4 as uuid } from 'uuid'
|
||||
|
||||
export default {
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
default() {
|
||||
return `select-input-${this._uid}`
|
||||
return `select-input-${uuid()}`
|
||||
},
|
||||
},
|
||||
value: [String, Number, Boolean],
|
||||
label: String,
|
||||
class: String,
|
||||
error: String,
|
||||
label: String,
|
||||
modelValue: [String, Number, Boolean],
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
data() {
|
||||
return {
|
||||
selected: this.value,
|
||||
selected: this.modelValue,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
selected(selected) {
|
||||
this.$emit('input', selected)
|
||||
this.$emit('update:modelValue', selected)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
|
|
@ -1,29 +1,33 @@
|
|||
<template>
|
||||
<div>
|
||||
<div :class="$attrs.class">
|
||||
<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)" />
|
||||
<input :id="id" ref="input" v-bind="{ ...$attrs, class: null }" class="form-input" :class="{ error: error }" :type="type" :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" />
|
||||
<div v-if="error" class="form-error">{{ error }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { v4 as uuid } from 'uuid'
|
||||
|
||||
export default {
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
default() {
|
||||
return `text-input-${this._uid}`
|
||||
return `text-input-${uuid()}`
|
||||
},
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'text',
|
||||
},
|
||||
value: String,
|
||||
label: String,
|
||||
class: String,
|
||||
error: String,
|
||||
label: String,
|
||||
modelValue: String,
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
methods: {
|
||||
focus() {
|
||||
this.$refs.input.focus()
|
||||
|
|
|
@ -1,25 +1,29 @@
|
|||
<template>
|
||||
<div>
|
||||
<div :class="$attrs.class">
|
||||
<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)" />
|
||||
<textarea :id="id" ref="input" v-bind="{ ...$attrs, class: null }" class="form-textarea" :class="{ error: error }" :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" />
|
||||
<div v-if="error" class="form-error">{{ error }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { v4 as uuid } from 'uuid'
|
||||
|
||||
export default {
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
default() {
|
||||
return `textarea-input-${this._uid}`
|
||||
return `textarea-input-${uuid()}`
|
||||
},
|
||||
},
|
||||
value: String,
|
||||
label: String,
|
||||
class: String,
|
||||
error: String,
|
||||
label: String,
|
||||
modelValue: String,
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
methods: {
|
||||
focus() {
|
||||
this.$refs.input.focus()
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<template>
|
||||
<div class="p-4 bg-yellow-300 rounded flex items-center justify-between max-w-3xl">
|
||||
<div class="flex items-center justify-between p-4 max-w-3xl bg-yellow-400 rounded">
|
||||
<div class="flex items-center">
|
||||
<icon name="trash" class="flex-shrink-0 w-4 h-4 fill-yellow-800 mr-2" />
|
||||
<div class="text-sm font-medium text-yellow-800">
|
||||
<icon name="trash" class="flex-shrink-0 mr-2 w-4 h-4 fill-yellow-800" />
|
||||
<div class="text-yellow-800 text-sm font-medium">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
<button class="text-sm text-yellow-800 hover:underline" tabindex="-1" type="button" @click="$emit('restore')">Restore</button>
|
||||
<button class="text-yellow-800 hover:underline text-sm" tabindex="-1" type="button" @click="$emit('restore')">Restore</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -17,5 +17,6 @@ export default {
|
|||
components: {
|
||||
Icon,
|
||||
},
|
||||
emits: ['restore'],
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,24 +1,15 @@
|
|||
import Vue from 'vue'
|
||||
import VueMeta from 'vue-meta'
|
||||
import PortalVue from 'portal-vue'
|
||||
import { createApp, h } from 'vue'
|
||||
import { InertiaProgress } from '@inertiajs/progress'
|
||||
import { createInertiaApp } from '@inertiajs/inertia-vue'
|
||||
|
||||
Vue.config.productionTip = false
|
||||
Vue.mixin({ methods: { route: window.route } })
|
||||
Vue.use(PortalVue)
|
||||
Vue.use(VueMeta)
|
||||
import { createInertiaApp } from '@inertiajs/inertia-vue3'
|
||||
|
||||
InertiaProgress.init()
|
||||
|
||||
createInertiaApp({
|
||||
resolve: name => require(`./Pages/${name}`),
|
||||
setup({ el, app, props }) {
|
||||
new Vue({
|
||||
metaInfo: {
|
||||
titleTemplate: title => (title ? `${title} - Ping CRM` : 'Ping CRM'),
|
||||
},
|
||||
render: h => h(app, props),
|
||||
}).$mount(el)
|
||||
title: title => `${title} - Ping CRM`,
|
||||
setup({ el, App, props, plugin }) {
|
||||
createApp({ render: () => h(App, props) })
|
||||
.use(plugin)
|
||||
.mount(el)
|
||||
},
|
||||
})
|
||||
|
|
|
@ -14,6 +14,7 @@ return [
|
|||
*/
|
||||
|
||||
'failed' => 'These credentials do not match our records.',
|
||||
'password' => 'The provided password is incorrect.',
|
||||
'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
|
||||
|
||||
];
|
||||
|
|
|
@ -13,10 +13,10 @@ return [
|
|||
|
|
||||
*/
|
||||
|
||||
'password' => 'Passwords must be at least eight characters and match the confirmation.',
|
||||
'reset' => 'Your password has been reset!',
|
||||
'sent' => 'We have e-mailed your password reset link!',
|
||||
'sent' => 'We have emailed your password reset link!',
|
||||
'throttled' => 'Please wait before retrying.',
|
||||
'token' => 'This password reset token is invalid.',
|
||||
'user' => "We can't find a user with that e-mail address.",
|
||||
'user' => "We can't find a user with that email address.",
|
||||
|
||||
];
|
||||
|
|
|
@ -14,12 +14,13 @@ return [
|
|||
*/
|
||||
|
||||
'accepted' => 'The :attribute must be accepted.',
|
||||
'accepted_if' => 'The :attribute must be accepted when :other is :value.',
|
||||
'active_url' => 'The :attribute is not a valid URL.',
|
||||
'after' => 'The :attribute must be a date after :date.',
|
||||
'after_or_equal' => 'The :attribute must be a date after or equal to :date.',
|
||||
'alpha' => 'The :attribute may only contain letters.',
|
||||
'alpha_dash' => 'The :attribute may only contain letters, numbers, dashes and underscores.',
|
||||
'alpha_num' => 'The :attribute may only contain letters and numbers.',
|
||||
'alpha' => 'The :attribute must only contain letters.',
|
||||
'alpha_dash' => 'The :attribute must only contain letters, numbers, dashes and underscores.',
|
||||
'alpha_num' => 'The :attribute must only contain letters and numbers.',
|
||||
'array' => 'The :attribute must be an array.',
|
||||
'before' => 'The :attribute must be a date before :date.',
|
||||
'before_or_equal' => 'The :attribute must be a date before or equal to :date.',
|
||||
|
@ -31,15 +32,19 @@ return [
|
|||
],
|
||||
'boolean' => 'The :attribute field must be true or false.',
|
||||
'confirmed' => 'The :attribute confirmation does not match.',
|
||||
'current_password' => 'The password is incorrect.',
|
||||
'date' => 'The :attribute is not a valid date.',
|
||||
'date_equals' => 'The :attribute must be a date equal to :date.',
|
||||
'date_format' => 'The :attribute does not match the format :format.',
|
||||
'declined' => 'The :attribute must be declined.',
|
||||
'declined_if' => 'The :attribute must be declined when :other is :value.',
|
||||
'different' => 'The :attribute and :other must be different.',
|
||||
'digits' => 'The :attribute must be :digits digits.',
|
||||
'digits_between' => 'The :attribute must be between :min and :max digits.',
|
||||
'dimensions' => 'The :attribute has invalid image dimensions.',
|
||||
'distinct' => 'The :attribute field has a duplicate value.',
|
||||
'email' => 'The :attribute must be a valid email address.',
|
||||
'ends_with' => 'The :attribute must end with one of the following: :values.',
|
||||
'exists' => 'The selected :attribute is invalid.',
|
||||
'file' => 'The :attribute must be a file.',
|
||||
'filled' => 'The :attribute field must have a value.',
|
||||
|
@ -50,9 +55,9 @@ return [
|
|||
'array' => 'The :attribute must have more than :value items.',
|
||||
],
|
||||
'gte' => [
|
||||
'numeric' => 'The :attribute must be greater than or equal :value.',
|
||||
'file' => 'The :attribute must be greater than or equal :value kilobytes.',
|
||||
'string' => 'The :attribute must be greater than or equal :value characters.',
|
||||
'numeric' => 'The :attribute must be greater than or equal to :value.',
|
||||
'file' => 'The :attribute must be greater than or equal to :value kilobytes.',
|
||||
'string' => 'The :attribute must be greater than or equal to :value characters.',
|
||||
'array' => 'The :attribute must have :value items or more.',
|
||||
],
|
||||
'image' => 'The :attribute must be an image.',
|
||||
|
@ -70,16 +75,16 @@ return [
|
|||
'array' => 'The :attribute must have less than :value items.',
|
||||
],
|
||||
'lte' => [
|
||||
'numeric' => 'The :attribute must be less than or equal :value.',
|
||||
'file' => 'The :attribute must be less than or equal :value kilobytes.',
|
||||
'string' => 'The :attribute must be less than or equal :value characters.',
|
||||
'numeric' => 'The :attribute must be less than or equal to :value.',
|
||||
'file' => 'The :attribute must be less than or equal to :value kilobytes.',
|
||||
'string' => 'The :attribute must be less than or equal to :value characters.',
|
||||
'array' => 'The :attribute must not have more than :value items.',
|
||||
],
|
||||
'max' => [
|
||||
'numeric' => 'The :attribute may not be greater than :max.',
|
||||
'file' => 'The :attribute may not be greater than :max kilobytes.',
|
||||
'string' => 'The :attribute may not be greater than :max characters.',
|
||||
'array' => 'The :attribute may not have more than :max items.',
|
||||
'numeric' => 'The :attribute must not be greater than :max.',
|
||||
'file' => 'The :attribute must not be greater than :max kilobytes.',
|
||||
'string' => 'The :attribute must not be greater than :max characters.',
|
||||
'array' => 'The :attribute must not have more than :max items.',
|
||||
],
|
||||
'mimes' => 'The :attribute must be a file of type: :values.',
|
||||
'mimetypes' => 'The :attribute must be a file of type: :values.',
|
||||
|
@ -89,10 +94,16 @@ return [
|
|||
'string' => 'The :attribute must be at least :min characters.',
|
||||
'array' => 'The :attribute must have at least :min items.',
|
||||
],
|
||||
'multiple_of' => 'The :attribute must be a multiple of :value.',
|
||||
'not_in' => 'The selected :attribute is invalid.',
|
||||
'not_regex' => 'The :attribute format is invalid.',
|
||||
'numeric' => 'The :attribute must be a number.',
|
||||
'password' => 'The password is incorrect.',
|
||||
'present' => 'The :attribute field must be present.',
|
||||
'prohibited' => 'The :attribute field is prohibited.',
|
||||
'prohibited_if' => 'The :attribute field is prohibited when :other is :value.',
|
||||
'prohibited_unless' => 'The :attribute field is prohibited unless :other is in :values.',
|
||||
'prohibits' => 'The :attribute field prohibits :other from being present.',
|
||||
'regex' => 'The :attribute format is invalid.',
|
||||
'required' => 'The :attribute field is required.',
|
||||
'required_if' => 'The :attribute field is required when :other is :value.',
|
||||
|
@ -108,12 +119,12 @@ return [
|
|||
'string' => 'The :attribute must be :size characters.',
|
||||
'array' => 'The :attribute must contain :size items.',
|
||||
],
|
||||
'starts_with' => 'The :attribute must start with one of the following: :values',
|
||||
'starts_with' => 'The :attribute must start with one of the following: :values.',
|
||||
'string' => 'The :attribute must be a string.',
|
||||
'timezone' => 'The :attribute must be a valid zone.',
|
||||
'timezone' => 'The :attribute must be a valid timezone.',
|
||||
'unique' => 'The :attribute has already been taken.',
|
||||
'uploaded' => 'The :attribute failed to upload.',
|
||||
'url' => 'The :attribute format is invalid.',
|
||||
'url' => 'The :attribute must be a valid URL.',
|
||||
'uuid' => 'The :attribute must be a valid UUID.',
|
||||
|
||||
/*
|
||||
|
|
|
@ -13,11 +13,8 @@
|
|||
<script src="https://polyfill.io/v3/polyfill.min.js?features=String.prototype.startsWith" defer></script>
|
||||
|
||||
<script src="{{ mix('/js/app.js') }}" defer></script>
|
||||
@routes
|
||||
</head>
|
||||
<body class="font-sans leading-none text-gray-700 antialiased">
|
||||
|
||||
@inertia
|
||||
|
||||
@inertia
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -14,6 +14,6 @@ use Illuminate\Support\Facades\Route;
|
|||
|
|
||||
*/
|
||||
|
||||
Route::middleware('auth:api')->get('/user', function (Request $request) {
|
||||
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
|
||||
return $request->user();
|
||||
});
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Laravel - A PHP Framework For Web Artisans
|
||||
* Laravel - A PHP Framework For Web Artisans.
|
||||
*
|
||||
* @package Laravel
|
||||
* @author Taylor Otwell <taylor@laravel.com>
|
||||
*/
|
||||
|
||||
$uri = urldecode(
|
||||
parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)
|
||||
);
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
config.php
|
||||
routes.php
|
||||
schedule-*
|
||||
compiled.php
|
||||
services.json
|
||||
events.scanned.php
|
||||
routes.scanned.php
|
||||
config.php
|
||||
down
|
||||
events.scanned.php
|
||||
maintenance.php
|
||||
routes.php
|
||||
routes.scanned.php
|
||||
schedule-*
|
||||
services.json
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
const path = require('path')
|
||||
|
||||
// https://stefanbauer.me/tips-and-tricks/autocompletion-for-webpack-path-aliases-in-phpstorm-when-using-laravel-mix
|
||||
module.exports = {
|
||||
output: { chunkFilename: 'js/[name].js?id=[chunkhash]' },
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve('./resources/js'),
|
||||
},
|
||||
extensions: ['.js', '.vue', '.json'],
|
||||
},
|
||||
devServer: {
|
||||
allowedHosts: 'all',
|
||||
},
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
const path = require('path')
|
||||
const process = require('process')
|
||||
const mix = require('laravel-mix')
|
||||
const cssImport = require('postcss-import')
|
||||
const cssNesting = require('postcss-nesting')
|
||||
const webpackConfig = require('./webpack.config')
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
@ -16,8 +18,8 @@ const cssNesting = require('postcss-nesting')
|
|||
|
||||
mix
|
||||
.js('resources/js/app.js', 'public/js')
|
||||
.vue({ runtimeOnly: true })
|
||||
.alias({ '@': 'resources/js' })
|
||||
.vue({ runtimeOnly: (process.env.NODE_ENV || 'production') === 'production' })
|
||||
.webpackConfig(webpackConfig)
|
||||
.postCss('resources/css/app.css', 'public/css', [
|
||||
// prettier-ignore
|
||||
cssImport(),
|
||||
|
|
Loading…
Reference in New Issue