parent
51e306bcb9
commit
716af9ab04
@ -0,0 +1,125 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Laravel\Cashier\Console\WebhookCommand;
|
||||||
|
use Laravel\Cashier\Invoices\DompdfInvoiceRenderer;
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Stripe Keys
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The Stripe publishable key and secret key give you access to Stripe's
|
||||||
|
| API. The "publishable" key is typically used when interacting with
|
||||||
|
| Stripe.js while the "secret" key accesses private API endpoints.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'key' => env('STRIPE_KEY'),
|
||||||
|
|
||||||
|
'secret' => env('STRIPE_SECRET'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Cashier Path
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This is the base URI path where Cashier's views, such as the payment
|
||||||
|
| verification screen, will be available from. You're free to tweak
|
||||||
|
| this path according to your preferences and application design.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'path' => env('CASHIER_PATH', 'stripe'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Stripe Webhooks
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Your Stripe webhook secret is used to prevent unauthorized requests to
|
||||||
|
| your Stripe webhook handling controllers. The tolerance setting will
|
||||||
|
| check the drift between the current time and the signed request's.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'webhook' => [
|
||||||
|
'secret' => env('STRIPE_WEBHOOK_SECRET'),
|
||||||
|
'tolerance' => env('STRIPE_WEBHOOK_TOLERANCE', 300),
|
||||||
|
'events' => WebhookCommand::DEFAULT_EVENTS,
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Currency
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This is the default currency that will be used when generating charges
|
||||||
|
| from your application. Of course, you are welcome to use any of the
|
||||||
|
| various world currencies that are currently supported via Stripe.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'currency' => env('CASHIER_CURRENCY', 'usd'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Currency Locale
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This is the default locale in which your money values are formatted in
|
||||||
|
| for display. To utilize other locales besides the default en locale
|
||||||
|
| verify you have the "intl" PHP extension installed on the system.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'currency_locale' => env('CASHIER_CURRENCY_LOCALE', 'en'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Payment Confirmation Notification
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| If this setting is enabled, Cashier will automatically notify customers
|
||||||
|
| whose payments require additional verification. You should listen to
|
||||||
|
| Stripe's webhooks in order for this feature to function correctly.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'payment_notification' => env('CASHIER_PAYMENT_NOTIFICATION'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Invoice Settings
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The following options determine how Cashier invoices are converted from
|
||||||
|
| HTML into PDFs. You're free to change the options based on the needs
|
||||||
|
| of your application or your preferences regarding invoice styling.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'invoices' => [
|
||||||
|
'renderer' => env('CASHIER_INVOICE_RENDERER', DompdfInvoiceRenderer::class),
|
||||||
|
|
||||||
|
'options' => [
|
||||||
|
// Supported: 'letter', 'legal', 'A4'
|
||||||
|
'paper' => env('CASHIER_PAPER', 'letter'),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Stripe Logger
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This setting defines which logging channel will be used by the Stripe
|
||||||
|
| library to write log messages. You are free to specify any of your
|
||||||
|
| logging channels listed inside the "logging" configuration file.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'logger' => env('CASHIER_LOGGER'),
|
||||||
|
|
||||||
|
];
|
@ -0,0 +1,80 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Spatie\FlareClient\FlareMiddleware\AddGitInformation;
|
||||||
|
use Spatie\FlareClient\FlareMiddleware\RemoveRequestIp;
|
||||||
|
use Spatie\FlareClient\FlareMiddleware\CensorRequestBodyFields;
|
||||||
|
use Spatie\FlareClient\FlareMiddleware\CensorRequestHeaders;
|
||||||
|
use Spatie\LaravelIgnition\FlareMiddleware\AddDumps;
|
||||||
|
use Spatie\LaravelIgnition\FlareMiddleware\AddEnvironmentInformation;
|
||||||
|
use Spatie\LaravelIgnition\FlareMiddleware\AddExceptionInformation;
|
||||||
|
use Spatie\LaravelIgnition\FlareMiddleware\AddJobs;
|
||||||
|
use Spatie\LaravelIgnition\FlareMiddleware\AddLogs;
|
||||||
|
use Spatie\LaravelIgnition\FlareMiddleware\AddQueries;
|
||||||
|
use Spatie\LaravelIgnition\FlareMiddleware\AddNotifierName;
|
||||||
|
|
||||||
|
return [
|
||||||
|
/*
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Flare API key
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Specify Flare's API key below to enable error reporting to the service.
|
||||||
|
|
|
||||||
|
| More info: https://flareapp.io/docs/general/projects
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'key' => env('FLARE_KEY'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Middleware
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| These middleware will modify the contents of the report sent to Flare.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'flare_middleware' => [
|
||||||
|
RemoveRequestIp::class,
|
||||||
|
AddGitInformation::class,
|
||||||
|
AddNotifierName::class,
|
||||||
|
AddEnvironmentInformation::class,
|
||||||
|
AddExceptionInformation::class,
|
||||||
|
AddDumps::class,
|
||||||
|
AddLogs::class => [
|
||||||
|
'maximum_number_of_collected_logs' => 200,
|
||||||
|
],
|
||||||
|
AddQueries::class => [
|
||||||
|
'maximum_number_of_collected_queries' => 200,
|
||||||
|
'report_query_bindings' => true,
|
||||||
|
],
|
||||||
|
AddJobs::class => [
|
||||||
|
'max_chained_job_reporting_depth' => 5,
|
||||||
|
],
|
||||||
|
CensorRequestBodyFields::class => [
|
||||||
|
'censor_fields' => [
|
||||||
|
'password',
|
||||||
|
'password_confirmation',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
CensorRequestHeaders::class => [
|
||||||
|
'headers' => [
|
||||||
|
'API-KEY',
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Reporting log statements
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| If this setting is `false` log statements won't be sent as events to Flare,
|
||||||
|
| no matter which error level you specified in the Flare log channel.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'send_logs_as_events' => true,
|
||||||
|
];
|
@ -0,0 +1,241 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Spatie\Ignition\Solutions\SolutionProviders\BadMethodCallSolutionProvider;
|
||||||
|
use Spatie\Ignition\Solutions\SolutionProviders\MergeConflictSolutionProvider;
|
||||||
|
use Spatie\Ignition\Solutions\SolutionProviders\UndefinedPropertySolutionProvider;
|
||||||
|
use Spatie\LaravelIgnition\Recorders\DumpRecorder\DumpRecorder;
|
||||||
|
use Spatie\LaravelIgnition\Recorders\JobRecorder\JobRecorder;
|
||||||
|
use Spatie\LaravelIgnition\Recorders\LogRecorder\LogRecorder;
|
||||||
|
use Spatie\LaravelIgnition\Recorders\QueryRecorder\QueryRecorder;
|
||||||
|
use Spatie\LaravelIgnition\Solutions\SolutionProviders\DefaultDbNameSolutionProvider;
|
||||||
|
use Spatie\LaravelIgnition\Solutions\SolutionProviders\GenericLaravelExceptionSolutionProvider;
|
||||||
|
use Spatie\LaravelIgnition\Solutions\SolutionProviders\IncorrectValetDbCredentialsSolutionProvider;
|
||||||
|
use Spatie\LaravelIgnition\Solutions\SolutionProviders\InvalidRouteActionSolutionProvider;
|
||||||
|
use Spatie\LaravelIgnition\Solutions\SolutionProviders\MissingAppKeySolutionProvider;
|
||||||
|
use Spatie\LaravelIgnition\Solutions\SolutionProviders\MissingColumnSolutionProvider;
|
||||||
|
use Spatie\LaravelIgnition\Solutions\SolutionProviders\MissingImportSolutionProvider;
|
||||||
|
use Spatie\LaravelIgnition\Solutions\SolutionProviders\MissingLivewireComponentSolutionProvider;
|
||||||
|
use Spatie\LaravelIgnition\Solutions\SolutionProviders\MissingMixManifestSolutionProvider;
|
||||||
|
use Spatie\LaravelIgnition\Solutions\SolutionProviders\MissingViteManifestSolutionProvider;
|
||||||
|
use Spatie\LaravelIgnition\Solutions\SolutionProviders\RunningLaravelDuskInProductionProvider;
|
||||||
|
use Spatie\LaravelIgnition\Solutions\SolutionProviders\TableNotFoundSolutionProvider;
|
||||||
|
use Spatie\LaravelIgnition\Solutions\SolutionProviders\UndefinedViewVariableSolutionProvider;
|
||||||
|
use Spatie\LaravelIgnition\Solutions\SolutionProviders\UnknownValidationSolutionProvider;
|
||||||
|
use Spatie\LaravelIgnition\Solutions\SolutionProviders\ViewNotFoundSolutionProvider;
|
||||||
|
use Spatie\LaravelIgnition\Solutions\SolutionProviders\OpenAiSolutionProvider;
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Editor
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Choose your preferred editor to use when clicking any edit button.
|
||||||
|
|
|
||||||
|
| Supported: "phpstorm", "vscode", "vscode-insiders", "textmate", "emacs",
|
||||||
|
| "sublime", "atom", "nova", "macvim", "idea", "netbeans",
|
||||||
|
| "xdebug"
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'editor' => env('IGNITION_EDITOR', 'phpstorm'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Theme
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here you may specify which theme Ignition should use.
|
||||||
|
|
|
||||||
|
| Supported: "light", "dark", "auto"
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'theme' => env('IGNITION_THEME', 'auto'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Sharing
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| You can share local errors with colleagues or others around the world.
|
||||||
|
| Sharing is completely free and doesn't require an account on Flare.
|
||||||
|
|
|
||||||
|
| If necessary, you can completely disable sharing below.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'enable_share_button' => env('IGNITION_SHARING_ENABLED', true),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Register Ignition commands
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Ignition comes with an additional make command that lets you create
|
||||||
|
| new solution classes more easily. To keep your default Laravel
|
||||||
|
| installation clean, this command is not registered by default.
|
||||||
|
|
|
||||||
|
| You can enable the command registration below.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'register_commands' => env('REGISTER_IGNITION_COMMANDS', false),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Solution Providers
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| You may specify a list of solution providers (as fully qualified class
|
||||||
|
| names) that shouldn't be loaded. Ignition will ignore these classes
|
||||||
|
| and possible solutions provided by them will never be displayed.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'solution_providers' => [
|
||||||
|
// from spatie/ignition
|
||||||
|
BadMethodCallSolutionProvider::class,
|
||||||
|
MergeConflictSolutionProvider::class,
|
||||||
|
UndefinedPropertySolutionProvider::class,
|
||||||
|
|
||||||
|
// from spatie/laravel-ignition
|
||||||
|
IncorrectValetDbCredentialsSolutionProvider::class,
|
||||||
|
MissingAppKeySolutionProvider::class,
|
||||||
|
DefaultDbNameSolutionProvider::class,
|
||||||
|
TableNotFoundSolutionProvider::class,
|
||||||
|
MissingImportSolutionProvider::class,
|
||||||
|
InvalidRouteActionSolutionProvider::class,
|
||||||
|
ViewNotFoundSolutionProvider::class,
|
||||||
|
RunningLaravelDuskInProductionProvider::class,
|
||||||
|
MissingColumnSolutionProvider::class,
|
||||||
|
UnknownValidationSolutionProvider::class,
|
||||||
|
MissingMixManifestSolutionProvider::class,
|
||||||
|
MissingViteManifestSolutionProvider::class,
|
||||||
|
MissingLivewireComponentSolutionProvider::class,
|
||||||
|
UndefinedViewVariableSolutionProvider::class,
|
||||||
|
GenericLaravelExceptionSolutionProvider::class,
|
||||||
|
OpenAiSolutionProvider::class,
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Ignored Solution Providers
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| You may specify a list of solution providers (as fully qualified class
|
||||||
|
| names) that shouldn't be loaded. Ignition will ignore these classes
|
||||||
|
| and possible solutions provided by them will never be displayed.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'ignored_solution_providers' => [
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Runnable Solutions
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Some solutions that Ignition displays are runnable and can perform
|
||||||
|
| various tasks. By default, runnable solutions are only enabled when your
|
||||||
|
| app has debug mode enabled and the environment is `local` or
|
||||||
|
| `development`.
|
||||||
|
|
|
||||||
|
| Using the `IGNITION_ENABLE_RUNNABLE_SOLUTIONS` environment variable, you
|
||||||
|
| can override this behaviour and enable or disable runnable solutions
|
||||||
|
| regardless of the application's environment.
|
||||||
|
|
|
||||||
|
| Default: env('IGNITION_ENABLE_RUNNABLE_SOLUTIONS')
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'enable_runnable_solutions' => env('IGNITION_ENABLE_RUNNABLE_SOLUTIONS'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Remote Path Mapping
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| If you are using a remote dev server, like Laravel Homestead, Docker, or
|
||||||
|
| even a remote VPS, it will be necessary to specify your path mapping.
|
||||||
|
|
|
||||||
|
| Leaving one, or both of these, empty or null will not trigger the remote
|
||||||
|
| URL changes and Ignition will treat your editor links as local files.
|
||||||
|
|
|
||||||
|
| "remote_sites_path" is an absolute base path for your sites or projects
|
||||||
|
| in Homestead, Vagrant, Docker, or another remote development server.
|
||||||
|
|
|
||||||
|
| Example value: "/home/vagrant/Code"
|
||||||
|
|
|
||||||
|
| "local_sites_path" is an absolute base path for your sites or projects
|
||||||
|
| on your local computer where your IDE or code editor is running on.
|
||||||
|
|
|
||||||
|
| Example values: "/Users/<name>/Code", "C:\Users\<name>\Documents\Code"
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'remote_sites_path' => env('IGNITION_REMOTE_SITES_PATH', base_path()),
|
||||||
|
'local_sites_path' => env('IGNITION_LOCAL_SITES_PATH', ''),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Housekeeping Endpoint Prefix
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Ignition registers a couple of routes when it is enabled. Below you may
|
||||||
|
| specify a route prefix that will be used to host all internal links.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'housekeeping_endpoint_prefix' => '_ignition',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Settings File
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Ignition allows you to save your settings to a specific global file.
|
||||||
|
|
|
||||||
|
| If no path is specified, a file with settings will be saved to the user's
|
||||||
|
| home directory. The directory depends on the OS and its settings but it's
|
||||||
|
| typically `~/.ignition.json`. In this case, the settings will be applied
|
||||||
|
| to all of your projects where Ignition is used and the path is not
|
||||||
|
| specified.
|
||||||
|
|
|
||||||
|
| However, if you want to store your settings on a project basis, or you
|
||||||
|
| want to keep them in another directory, you can specify a path where
|
||||||
|
| the settings file will be saved. The path should be an existing directory
|
||||||
|
| with correct write access.
|
||||||
|
| For example, create a new `ignition` folder in the storage directory and
|
||||||
|
| use `storage_path('ignition')` as the `settings_file_path`.
|
||||||
|
|
|
||||||
|
| Default value: '' (empty string)
|
||||||
|
*/
|
||||||
|
|
||||||
|
'settings_file_path' => '',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Recorders
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Ignition registers a couple of recorders when it is enabled. Below you may
|
||||||
|
| specify a recorders will be used to record specific events.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'recorders' => [
|
||||||
|
DumpRecorder::class,
|
||||||
|
JobRecorder::class,
|
||||||
|
LogRecorder::class,
|
||||||
|
QueryRecorder::class
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When a key is set, we'll send your exceptions to Open AI to generate a solution
|
||||||
|
*/
|
||||||
|
'open_ai_key' => env('IGNITION_OPEN_AI_KEY'),
|
||||||
|
];
|
@ -0,0 +1,158 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Class Namespace
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This value sets the root namespace for Livewire component classes in
|
||||||
|
| your application. This value affects component auto-discovery and
|
||||||
|
| any Livewire file helper commands, like `artisan make:livewire`.
|
||||||
|
|
|
||||||
|
| After changing this item, run: `php artisan livewire:discover`.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'class_namespace' => 'App\\Http\\Livewire',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| View Path
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This value sets the path for Livewire component views. This affects
|
||||||
|
| file manipulation helper commands like `artisan make:livewire`.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'view_path' => resource_path('views/livewire'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Layout
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| The default layout view that will be used when rendering a component via
|
||||||
|
| Route::get('/some-endpoint', SomeComponent::class);. In this case the
|
||||||
|
| the view returned by SomeComponent will be wrapped in "layouts.app"
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'layout' => 'layouts.app',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Livewire Assets URL
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This value sets the path to Livewire JavaScript assets, for cases where
|
||||||
|
| your app's domain root is not the correct path. By default, Livewire
|
||||||
|
| will load its JavaScript assets from the app's "relative root".
|
||||||
|
|
|
||||||
|
| Examples: "/assets", "myurl.com/app".
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'asset_url' => null,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Livewire App URL
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This value should be used if livewire assets are served from CDN.
|
||||||
|
| Livewire will communicate with an app through this url.
|
||||||
|
|
|
||||||
|
| Examples: "https://my-app.com", "myurl.com/app".
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'app_url' => null,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Livewire Endpoint Middleware Group
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This value sets the middleware group that will be applied to the main
|
||||||
|
| Livewire "message" endpoint (the endpoint that gets hit everytime
|
||||||
|
| a Livewire component updates). It is set to "web" by default.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'middleware_group' => 'web',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Livewire Temporary File Uploads Endpoint Configuration
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Livewire handles file uploads by storing uploads in a temporary directory
|
||||||
|
| before the file is validated and stored permanently. All file uploads
|
||||||
|
| are directed to a global endpoint for temporary storage. The config
|
||||||
|
| items below are used for customizing the way the endpoint works.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'temporary_file_upload' => [
|
||||||
|
'disk' => null, // Example: 'local', 's3' Default: 'default'
|
||||||
|
'rules' => null, // Example: ['file', 'mimes:png,jpg'] Default: ['required', 'file', 'max:12288'] (12MB)
|
||||||
|
'directory' => null, // Example: 'tmp' Default 'livewire-tmp'
|
||||||
|
'middleware' => null, // Example: 'throttle:5,1' Default: 'throttle:60,1'
|
||||||
|
'preview_mimes' => [ // Supported file types for temporary pre-signed file URLs.
|
||||||
|
'png', 'gif', 'bmp', 'svg', 'wav', 'mp4',
|
||||||
|
'mov', 'avi', 'wmv', 'mp3', 'm4a',
|
||||||
|
'jpg', 'jpeg', 'mpga', 'webp', 'wma',
|
||||||
|
],
|
||||||
|
'max_upload_time' => 5, // Max duration (in minutes) before an upload gets invalidated.
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Manifest File Path
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This value sets the path to the Livewire manifest file.
|
||||||
|
| The default should work for most cases (which is
|
||||||
|
| "<app_root>/bootstrap/cache/livewire-components.php"), but for specific
|
||||||
|
| cases like when hosting on Laravel Vapor, it could be set to a different value.
|
||||||
|
|
|
||||||
|
| Example: for Laravel Vapor, it would be "/tmp/storage/bootstrap/cache/livewire-components.php".
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'manifest_path' => null,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Back Button Cache
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This value determines whether the back button cache will be used on pages
|
||||||
|
| that contain Livewire. By disabling back button cache, it ensures that
|
||||||
|
| the back button shows the correct state of components, instead of
|
||||||
|
| potentially stale, cached data.
|
||||||
|
|
|
||||||
|
| Setting it to "false" (default) will disable back button cache.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'back_button_cache' => false,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Render On Redirect
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This value determines whether Livewire will render before it's redirected
|
||||||
|
| or not. Setting it to "false" (default) will mean the render method is
|
||||||
|
| skipped when redirecting. And "true" will mean the render method is
|
||||||
|
| run before redirecting. Browsers bfcache can store a potentially
|
||||||
|
| stale view if render is skipped on redirect.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'render_on_redirect' => false,
|
||||||
|
|
||||||
|
];
|
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'layout' => 'layouts.app', // You must set it. Example: 'layout' => 'adminlte::page'
|
||||||
|
'userModelClass' => App\Models\User::class, // You can change it
|
||||||
|
'adminUserId' => 1, // You must set it. This is the initial user id, which must be an administrator, at least at the first stage.
|
||||||
|
'routesMainPermission' => Itstructure\LaRbac\Models\Permission::ADMINISTRATE_PERMISSION, // You can change it
|
||||||
|
'routesAuthMiddlewares' => ['auth'], // You can change it
|
||||||
|
'memberNameAttributeKey' => function ($row) { // You can change it. And can simply set 'memberNameAttributeKey' => 'name'
|
||||||
|
return $row->name;
|
||||||
|
},
|
||||||
|
'rowsPerPage' => 10,
|
||||||
|
];
|
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Console Commands
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This option allows you to add additional Artisan commands that should
|
||||||
|
| be available within the Tinker environment. Once the command is in
|
||||||
|
| this array you may execute the command in Tinker using its name.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'commands' => [
|
||||||
|
// App\Console\Commands\ExampleCommand::class,
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Auto Aliased Classes
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Tinker will not automatically alias classes in your vendor namespaces
|
||||||
|
| but you may explicitly allow a subset of classes to get aliased by
|
||||||
|
| adding the names of each of those classes to the following list.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'alias' => [
|
||||||
|
//
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Classes That Should Not Be Aliased
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Typically, Tinker automatically aliases classes as you require them in
|
||||||
|
| Tinker. However, you may wish to never alias certain classes, which
|
||||||
|
| you may accomplish by listing the classes in the following array.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'dont_alias' => [
|
||||||
|
'App\Nova',
|
||||||
|
],
|
||||||
|
|
||||||
|
];
|
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->string('stripe_id')->nullable()->index();
|
||||||
|
$table->string('pm_type')->nullable();
|
||||||
|
$table->string('pm_last_four', 4)->nullable();
|
||||||
|
$table->timestamp('trial_ends_at')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->dropColumn([
|
||||||
|
'stripe_id',
|
||||||
|
'pm_type',
|
||||||
|
'pm_last_four',
|
||||||
|
'trial_ends_at',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('subscriptions', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->foreignId('user_id');
|
||||||
|
$table->string('name');
|
||||||
|
$table->string('stripe_id')->unique();
|
||||||
|
$table->string('stripe_status');
|
||||||
|
$table->string('stripe_price')->nullable();
|
||||||
|
$table->integer('quantity')->nullable();
|
||||||
|
$table->timestamp('trial_ends_at')->nullable();
|
||||||
|
$table->timestamp('ends_at')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->index(['user_id', 'stripe_status']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('subscriptions');
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('subscription_items', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->foreignId('subscription_id');
|
||||||
|
$table->string('stripe_id')->unique();
|
||||||
|
$table->string('stripe_product');
|
||||||
|
$table->string('stripe_price');
|
||||||
|
$table->integer('quantity')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->unique(['subscription_id', 'stripe_price']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('subscription_items');
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('teams', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->foreignId('user_id')->index();
|
||||||
|
$table->string('name');
|
||||||
|
$table->boolean('personal_team');
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('teams');
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('team_user', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->foreignId('team_id');
|
||||||
|
$table->foreignId('user_id');
|
||||||
|
$table->string('role')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->unique(['team_id', 'user_id']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('team_user');
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('team_invitations', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->foreignId('team_id')->constrained()->cascadeOnDelete();
|
||||||
|
$table->string('email');
|
||||||
|
$table->string('role')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->unique(['team_id', 'email']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('team_invitations');
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class CreateRolesTable
|
||||||
|
*
|
||||||
|
* @author Andrey Girnik <girnikandrey@gmail.com>
|
||||||
|
*/
|
||||||
|
class CreateRolesTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('roles', function (Blueprint $table) {
|
||||||
|
$table->increments('id');
|
||||||
|
$table->string('name', 191);
|
||||||
|
$table->string('slug', 191)->unique();
|
||||||
|
$table->string('description', 191);
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('roles');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Itstructure\LaRbac\Helpers\Helper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class CreateUserRoleTable
|
||||||
|
*
|
||||||
|
* @author Andrey Girnik <girnikandrey@gmail.com>
|
||||||
|
*/
|
||||||
|
class CreateUserRoleTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $userModelClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UserController constructor.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->userModelClass = config('rbac.userModelClass');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
* @throws Exception
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Helper::checkUserModel($this->userModelClass);
|
||||||
|
|
||||||
|
/* @var Illuminate\Foundation\Auth\User $userNewModel */
|
||||||
|
$userNewModel = new $this->userModelClass();
|
||||||
|
|
||||||
|
Helper::checkUserModelKeyType($userNewModel);
|
||||||
|
|
||||||
|
$userModelTable = $userNewModel->getTable();
|
||||||
|
|
||||||
|
$userModelKeyName = $userNewModel->getAuthIdentifierName();
|
||||||
|
|
||||||
|
$userTablePrimaryType = Schema::getConnection()->getDoctrineColumn($userModelTable, $userModelKeyName)->getType()->getName();
|
||||||
|
|
||||||
|
Helper::checkUserTablePrimaryType($userTablePrimaryType, $userModelKeyName, $userModelTable);
|
||||||
|
|
||||||
|
Schema::create('user_role', function (Blueprint $table) use ($userModelKeyName, $userModelTable, $userTablePrimaryType) {
|
||||||
|
switch ($userTablePrimaryType) {
|
||||||
|
case 'bigint':
|
||||||
|
$table->unsignedBigInteger('user_id');
|
||||||
|
break;
|
||||||
|
case 'integer':
|
||||||
|
$table->unsignedInteger('user_id');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$table->unsignedInteger('role_id');
|
||||||
|
$table->timestamps();
|
||||||
|
$table->unique(['user_id','role_id']);
|
||||||
|
$table->foreign('user_id')->references($userModelKeyName)->on($userModelTable)->onDelete('cascade');
|
||||||
|
$table->foreign('role_id')->references('id')->on('roles')->onDelete('cascade');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('user_role');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class CreatePermissionsTable
|
||||||
|
*
|
||||||
|
* @author Andrey Girnik <girnikandrey@gmail.com>
|
||||||
|
*/
|
||||||
|
class CreatePermissionsTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('permissions', function (Blueprint $table) {
|
||||||
|
$table->increments('id');
|
||||||
|
$table->string('name', 191);
|
||||||
|
$table->string('slug', 191)->unique();
|
||||||
|
$table->string('description', 191);
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('permissions');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class CreateRolePermissionTable
|
||||||
|
*
|
||||||
|
* @author Andrey Girnik <girnikandrey@gmail.com>
|
||||||
|
*/
|
||||||
|
class CreateRolePermissionTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('role_permission', function (Blueprint $table) {
|
||||||
|
$table->unsignedInteger('role_id');
|
||||||
|
$table->unsignedInteger('permission_id');
|
||||||
|
$table->timestamps();
|
||||||
|
$table->unique(['permission_id','role_id']);
|
||||||
|
$table->foreign('role_id')->references('id')->on('roles')->onDelete('cascade');
|
||||||
|
$table->foreign('permission_id')->references('id')->on('permissions')->onDelete('cascade');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('role_permission');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class LaRbacDatabaseSeeder
|
||||||
|
*
|
||||||
|
* @author Andrey Girnik <girnikandrey@gmail.com>
|
||||||
|
*/
|
||||||
|
class LaRbacDatabaseSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Seed the application's database with RBAC data.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
$this->call(PermissionSeeder::class);
|
||||||
|
$this->call(RoleSeeder::class);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
use Itstructure\LaRbac\Models\Permission;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class PermissionSeeder
|
||||||
|
*
|
||||||
|
* @author Andrey Girnik <girnikandrey@gmail.com>
|
||||||
|
*/
|
||||||
|
class PermissionSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
$this->createRecord(
|
||||||
|
Permission::ADMINISTRATE_PERMISSION,
|
||||||
|
'Total administrate of users and content'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->createRecord(
|
||||||
|
Permission::VIEW_RECORD_PERMISSION,
|
||||||
|
'Permission to view record'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->createRecord(
|
||||||
|
Permission::CREATE_RECORD_PERMISSION,
|
||||||
|
'Permission to create record'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->createRecord(
|
||||||
|
Permission::UPDATE_RECORD_PERMISSION,
|
||||||
|
'Permission to update record'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->createRecord(
|
||||||
|
Permission::DELETE_RECORD_PERMISSION,
|
||||||
|
'Permission to delete record'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->createRecord(
|
||||||
|
Permission::PUBLISH_RECORD_PERMISSION,
|
||||||
|
'Permission to publish record'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create permission record in database.
|
||||||
|
* @param string $slug
|
||||||
|
* @param string $description
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function createRecord(string $slug, string $description): void
|
||||||
|
{
|
||||||
|
Permission::create([
|
||||||
|
'name' => str_replace('-', ' ', ucfirst($slug)),
|
||||||
|
'slug' => $slug,
|
||||||
|
'description' => $description,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
<?php
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
use Itstructure\LaRbac\Models\{Role, Permission};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class RoleSeeder
|
||||||
|
*
|
||||||
|
* @author Andrey Girnik <girnikandrey@gmail.com>
|
||||||
|
*/
|
||||||
|
class RoleSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
$totalPermissionId = Permission::where('slug', Permission::ADMINISTRATE_PERMISSION)->firstOrFail()->id;
|
||||||
|
$viewPermissionId = Permission::where('slug', Permission::VIEW_RECORD_PERMISSION)->firstOrFail()->id;
|
||||||
|
$createPermissionId = Permission::where('slug', Permission::CREATE_RECORD_PERMISSION)->firstOrFail()->id;
|
||||||
|
$updatePermissionId = Permission::where('slug', Permission::UPDATE_RECORD_PERMISSION)->firstOrFail()->id;
|
||||||
|
$deletePermissionId = Permission::where('slug', Permission::DELETE_RECORD_PERMISSION)->firstOrFail()->id;
|
||||||
|
$publishPermissionId = Permission::where('slug', Permission::PUBLISH_RECORD_PERMISSION)->firstOrFail()->id;
|
||||||
|
|
||||||
|
$this->createRecord(
|
||||||
|
Role::ADMIN_ROLE,
|
||||||
|
'Administrator',
|
||||||
|
[
|
||||||
|
$totalPermissionId,
|
||||||
|
$viewPermissionId,
|
||||||
|
$createPermissionId,
|
||||||
|
$updatePermissionId,
|
||||||
|
$deletePermissionId,
|
||||||
|
$publishPermissionId
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->createRecord(
|
||||||
|
Role::MANAGER_ROLE,
|
||||||
|
'Content manager',
|
||||||
|
[
|
||||||
|
$viewPermissionId,
|
||||||
|
$createPermissionId,
|
||||||
|
$updatePermissionId,
|
||||||
|
$deletePermissionId,
|
||||||
|
$publishPermissionId
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->createRecord(
|
||||||
|
Role::EDITOR_ROLE,
|
||||||
|
'Record editor',
|
||||||
|
[
|
||||||
|
$viewPermissionId,
|
||||||
|
$createPermissionId,
|
||||||
|
$updatePermissionId,
|
||||||
|
$deletePermissionId,
|
||||||
|
$publishPermissionId
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->createRecord(
|
||||||
|
Role::USER_ROLE,
|
||||||
|
'Simple user',
|
||||||
|
[
|
||||||
|
$viewPermissionId
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create role record in database.
|
||||||
|
* @param string $slug
|
||||||
|
* @param string $description
|
||||||
|
* @param array $permissions
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function createRecord(string $slug, string $description, array $permissions): void
|
||||||
|
{
|
||||||
|
Role::create([
|
||||||
|
'name' => ucfirst($slug),
|
||||||
|
'slug' => $slug,
|
||||||
|
'description' => $description,
|
||||||
|
])->permissions()
|
||||||
|
->attach($permissions);
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@
|
|||||||
|
{"/livewire.js":"/livewire.js?id=90730a3b0e7144480175"}
|
@ -0,0 +1,11 @@
|
|||||||
|
<template>
|
||||||
|
<div class="min-h-screen flex flex-col sm:justify-center items-center pt-6 sm:pt-0 bg-gray-100 dark:bg-gray-900">
|
||||||
|
<div>
|
||||||
|
<slot name="logo" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="w-full sm:max-w-md mt-6 px-6 py-4 bg-white dark:bg-gray-800 shadow-md overflow-hidden sm:rounded-lg">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
@ -0,0 +1,17 @@
|
|||||||
|
<script setup>
|
||||||
|
import { Link } from '@inertiajs/vue3';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Link :href="'/'">
|
||||||
|
<svg
|
||||||
|
class="w-16 h-16"
|
||||||
|
viewBox="0 0 48 48"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path d="M11.395 44.428C4.557 40.198 0 32.632 0 24 0 10.745 10.745 0 24 0a23.891 23.891 0 0113.997 4.502c-.2 17.907-11.097 33.245-26.602 39.926z" fill="#6875F5" />
|
||||||
|
<path d="M14.134 45.885A23.914 23.914 0 0024 48c13.255 0 24-10.745 24-24 0-3.516-.756-6.856-2.115-9.866-4.659 15.143-16.608 27.092-31.75 31.751z" fill="#6875F5" />
|
||||||
|
</svg>
|
||||||
|
</Link>
|
||||||
|
</template>
|
@ -0,0 +1,36 @@
|
|||||||
|
<script setup>
|
||||||
|
import { computed } from 'vue';
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:checked']);
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
checked: {
|
||||||
|
type: [Array, Boolean],
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const proxyChecked = computed({
|
||||||
|
get() {
|
||||||
|
return props.checked;
|
||||||
|
},
|
||||||
|
|
||||||
|
set(val) {
|
||||||
|
emit('update:checked', val);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<input
|
||||||
|
v-model="proxyChecked"
|
||||||
|
type="checkbox"
|
||||||
|
:value="value"
|
||||||
|
class="rounded dark:bg-gray-900 border-gray-300 dark:border-gray-700 text-indigo-600 shadow-sm focus:ring-indigo-500 dark:focus:ring-indigo-600 dark:focus:ring-offset-gray-800"
|
||||||
|
>
|
||||||
|
</template>
|
@ -0,0 +1,63 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { Head, useForm } from '@inertiajs/vue3';
|
||||||
|
import AuthenticationCard from '@/Components/AuthenticationCard.vue';
|
||||||
|
import AuthenticationCardLogo from '@/Components/AuthenticationCardLogo.vue';
|
||||||
|
import InputError from '@/Components/InputError.vue';
|
||||||
|
import InputLabel from '@/Components/InputLabel.vue';
|
||||||
|
import PrimaryButton from '@/Components/PrimaryButton.vue';
|
||||||
|
import TextInput from '@/Components/TextInput.vue';
|
||||||
|
|
||||||
|
const form = useForm({
|
||||||
|
password: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const passwordInput = ref(null);
|
||||||
|
|
||||||
|
const submit = () => {
|
||||||
|
form.post(route('password.confirm'), {
|
||||||
|
onFinish: () => {
|
||||||
|
form.reset();
|
||||||
|
|
||||||
|
passwordInput.value.focus();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Head title="Secure Area" />
|
||||||
|
|
||||||
|
<AuthenticationCard>
|
||||||
|
<template #logo>
|
||||||
|
<AuthenticationCardLogo />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="mb-4 text-sm text-gray-600 dark:text-gray-400">
|
||||||
|
This is a secure area of the application. Please confirm your password before continuing.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form @submit.prevent="submit">
|
||||||
|
<div>
|
||||||
|
<InputLabel for="password" value="Password" />
|
||||||
|
<TextInput
|
||||||
|
id="password"
|
||||||
|
ref="passwordInput"
|
||||||
|
v-model="form.password"
|
||||||
|
type="password"
|
||||||
|
class="mt-1 block w-full"
|
||||||
|
required
|
||||||
|
autocomplete="current-password"
|
||||||
|
autofocus
|
||||||
|
/>
|
||||||
|
<InputError class="mt-2" :message="form.errors.password" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex justify-end mt-4">
|
||||||
|
<PrimaryButton class="ml-4" :class="{ 'opacity-25': form.processing }" :disabled="form.processing">
|
||||||
|
Confirm
|
||||||
|
</PrimaryButton>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</AuthenticationCard>
|
||||||
|
</template>
|
@ -0,0 +1,61 @@
|
|||||||
|
<script setup>
|
||||||
|
import { Head, useForm } from '@inertiajs/vue3';
|
||||||
|
import AuthenticationCard from '@/Components/AuthenticationCard.vue';
|
||||||
|
import AuthenticationCardLogo from '@/Components/AuthenticationCardLogo.vue';
|
||||||
|
import InputError from '@/Components/InputError.vue';
|
||||||
|
import InputLabel from '@/Components/InputLabel.vue';
|
||||||
|
import PrimaryButton from '@/Components/PrimaryButton.vue';
|
||||||
|
import TextInput from '@/Components/TextInput.vue';
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
status: String,
|
||||||
|
});
|
||||||
|
|
||||||
|
const form = useForm({
|
||||||
|
email: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const submit = () => {
|
||||||
|
form.post(route('password.email'));
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Head title="Forgot Password" />
|
||||||
|
|
||||||
|
<AuthenticationCard>
|
||||||
|
<template #logo>
|
||||||
|
<AuthenticationCardLogo />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="mb-4 text-sm text-gray-600 dark:text-gray-400">
|
||||||
|
Forgot your password? No problem. Just let us know your email address and we will email you a password reset link that will allow you to choose a new one.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="status" class="mb-4 font-medium text-sm text-green-600 dark:text-green-400">
|
||||||
|
{{ status }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form @submit.prevent="submit">
|
||||||
|
<div>
|
||||||
|
<InputLabel for="email" value="Email" />
|
||||||
|
<TextInput
|
||||||
|
id="email"
|
||||||
|
v-model="form.email"
|
||||||
|
type="email"
|
||||||
|
class="mt-1 block w-full"
|
||||||
|
required
|
||||||
|
autofocus
|
||||||
|
autocomplete="username"
|
||||||
|
/>
|
||||||
|
<InputError class="mt-2" :message="form.errors.email" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center justify-end mt-4">
|
||||||
|
<PrimaryButton :class="{ 'opacity-25': form.processing }" :disabled="form.processing">
|
||||||
|
Email Password Reset Link
|
||||||
|
</PrimaryButton>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</AuthenticationCard>
|
||||||
|
</template>
|
@ -0,0 +1,90 @@
|
|||||||
|
<script setup>
|
||||||
|
import { Head, Link, useForm } from '@inertiajs/vue3';
|
||||||
|
import AuthenticationCard from '@/Components/AuthenticationCard.vue';
|
||||||
|
import AuthenticationCardLogo from '@/Components/AuthenticationCardLogo.vue';
|
||||||
|
import Checkbox from '@/Components/Checkbox.vue';
|
||||||
|
import InputError from '@/Components/InputError.vue';
|
||||||
|
import InputLabel from '@/Components/InputLabel.vue';
|
||||||
|
import PrimaryButton from '@/Components/PrimaryButton.vue';
|
||||||
|
import TextInput from '@/Components/TextInput.vue';
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
canResetPassword: Boolean,
|
||||||
|
status: String,
|
||||||
|
});
|
||||||
|
|
||||||
|
const form = useForm({
|
||||||
|
email: '',
|
||||||
|
password: '',
|
||||||
|
remember: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const submit = () => {
|
||||||
|
form.transform(data => ({
|
||||||
|
...data,
|
||||||
|
remember: form.remember ? 'on' : '',
|
||||||
|
})).post(route('login'), {
|
||||||
|
onFinish: () => form.reset('password'),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Head title="Log in" />
|
||||||
|
|
||||||
|
<AuthenticationCard>
|
||||||
|
<template #logo>
|
||||||
|
<AuthenticationCardLogo />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div v-if="status" class="mb-4 font-medium text-sm text-green-600 dark:text-green-400">
|
||||||
|
{{ status }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form @submit.prevent="submit">
|
||||||
|
<div>
|
||||||
|
<InputLabel for="email" value="Email" />
|
||||||
|
<TextInput
|
||||||
|
id="email"
|
||||||
|
v-model="form.email"
|
||||||
|
type="email"
|
||||||
|
class="mt-1 block w-full"
|
||||||
|
required
|
||||||
|
autofocus
|
||||||
|
autocomplete="username"
|
||||||
|
/>
|
||||||
|
<InputError class="mt-2" :message="form.errors.email" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-4">
|
||||||
|
<InputLabel for="password" value="Password" />
|
||||||
|
<TextInput
|
||||||
|
id="password"
|
||||||
|
v-model="form.password"
|
||||||
|
type="password"
|
||||||
|
class="mt-1 block w-full"
|
||||||
|
required
|
||||||
|
autocomplete="current-password"
|
||||||
|
/>
|
||||||
|
<InputError class="mt-2" :message="form.errors.password" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="block mt-4">
|
||||||
|
<label class="flex items-center">
|
||||||
|
<Checkbox v-model:checked="form.remember" name="remember" />
|
||||||
|
<span class="ml-2 text-sm text-gray-600 dark:text-gray-400">Remember me</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center justify-end mt-4">
|
||||||
|
<Link v-if="canResetPassword" :href="route('password.request')" class="underline text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-800">
|
||||||
|
Forgot your password?
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<PrimaryButton class="ml-4" :class="{ 'opacity-25': form.processing }" :disabled="form.processing">
|
||||||
|
Log in
|
||||||
|
</PrimaryButton>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</AuthenticationCard>
|
||||||
|
</template>
|
@ -0,0 +1,112 @@
|
|||||||
|
<script setup>
|
||||||
|
import { Head, Link, useForm } from '@inertiajs/vue3';
|
||||||
|
import AuthenticationCard from '@/Components/AuthenticationCard.vue';
|
||||||
|
import AuthenticationCardLogo from '@/Components/AuthenticationCardLogo.vue';
|
||||||
|
import Checkbox from '@/Components/Checkbox.vue';
|
||||||
|
import InputError from '@/Components/InputError.vue';
|
||||||
|
import InputLabel from '@/Components/InputLabel.vue';
|
||||||
|
import PrimaryButton from '@/Components/PrimaryButton.vue';
|
||||||
|
import TextInput from '@/Components/TextInput.vue';
|
||||||
|
|
||||||
|
const form = useForm({
|
||||||
|
name: '',
|
||||||
|
email: '',
|
||||||
|
password: '',
|
||||||
|
password_confirmation: '',
|
||||||
|
terms: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const submit = () => {
|
||||||
|
form.post(route('register'), {
|
||||||
|
onFinish: () => form.reset('password', 'password_confirmation'),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Head title="Register" />
|
||||||
|
|
||||||
|
<AuthenticationCard>
|
||||||
|
<template #logo>
|
||||||
|
<AuthenticationCardLogo />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<form @submit.prevent="submit">
|
||||||
|
<div>
|
||||||
|
<InputLabel for="name" value="Name" />
|
||||||
|
<TextInput
|
||||||
|
id="name"
|
||||||
|
v-model="form.name"
|
||||||
|
type="text"
|
||||||
|
class="mt-1 block w-full"
|
||||||
|
required
|
||||||
|
autofocus
|
||||||
|
autocomplete="name"
|
||||||
|
/>
|
||||||
|
<InputError class="mt-2" :message="form.errors.name" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-4">
|
||||||
|
<InputLabel for="email" value="Email" />
|
||||||
|
<TextInput
|
||||||
|
id="email"
|
||||||
|
v-model="form.email"
|
||||||
|
type="email"
|
||||||
|
class="mt-1 block w-full"
|
||||||
|
required
|
||||||
|
autocomplete="username"
|
||||||
|
/>
|
||||||
|
<InputError class="mt-2" :message="form.errors.email" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-4">
|
||||||
|
<InputLabel for="password" value="Password" />
|
||||||
|
<TextInput
|
||||||
|
id="password"
|
||||||
|
v-model="form.password"
|
||||||
|
type="password"
|
||||||
|
class="mt-1 block w-full"
|
||||||
|
required
|
||||||
|
autocomplete="new-password"
|
||||||
|
/>
|
||||||
|
<InputError class="mt-2" :message="form.errors.password" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-4">
|
||||||
|
<InputLabel for="password_confirmation" value="Confirm Password" />
|
||||||
|
<TextInput
|
||||||
|
id="password_confirmation"
|
||||||
|
v-model="form.password_confirmation"
|
||||||
|
type="password"
|
||||||
|
class="mt-1 block w-full"
|
||||||
|
required
|
||||||
|
autocomplete="new-password"
|
||||||
|
/>
|
||||||
|
<InputError class="mt-2" :message="form.errors.password_confirmation" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="$page.props.jetstream.hasTermsAndPrivacyPolicyFeature" class="mt-4">
|
||||||
|
<InputLabel for="terms">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<Checkbox id="terms" v-model:checked="form.terms" name="terms" required />
|
||||||
|
|
||||||
|
<div class="ml-2">
|
||||||
|
I agree to the <a target="_blank" :href="route('terms.show')" class="underline text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-800">Terms of Service</a> and <a target="_blank" :href="route('policy.show')" class="underline text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-800">Privacy Policy</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<InputError class="mt-2" :message="form.errors.terms" />
|
||||||
|
</InputLabel>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center justify-end mt-4">
|
||||||
|
<Link :href="route('login')" class="underline text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-800">
|
||||||
|
Already registered?
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<PrimaryButton class="ml-4" :class="{ 'opacity-25': form.processing }" :disabled="form.processing">
|
||||||
|
Register
|
||||||
|
</PrimaryButton>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</AuthenticationCard>
|
||||||
|
</template>
|
@ -0,0 +1,85 @@
|
|||||||
|
<script setup>
|
||||||
|
import { Head, useForm } from '@inertiajs/vue3';
|
||||||
|
import AuthenticationCard from '@/Components/AuthenticationCard.vue';
|
||||||
|
import AuthenticationCardLogo from '@/Components/AuthenticationCardLogo.vue';
|
||||||
|
import InputError from '@/Components/InputError.vue';
|
||||||
|
import InputLabel from '@/Components/InputLabel.vue';
|
||||||
|
import PrimaryButton from '@/Components/PrimaryButton.vue';
|
||||||
|
import TextInput from '@/Components/TextInput.vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
email: String,
|
||||||
|
token: String,
|
||||||
|
});
|
||||||
|
|
||||||
|
const form = useForm({
|
||||||
|
token: props.token,
|
||||||
|
email: props.email,
|
||||||
|
password: '',
|
||||||
|
password_confirmation: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const submit = () => {
|
||||||
|
form.post(route('password.update'), {
|
||||||
|
onFinish: () => form.reset('password', 'password_confirmation'),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Head title="Reset Password" />
|
||||||
|
|
||||||
|
<AuthenticationCard>
|
||||||
|
<template #logo>
|
||||||
|
<AuthenticationCardLogo />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<form @submit.prevent="submit">
|
||||||
|
<div>
|
||||||
|
<InputLabel for="email" value="Email" />
|
||||||
|
<TextInput
|
||||||
|
id="email"
|
||||||
|
v-model="form.email"
|
||||||
|
type="email"
|
||||||
|
class="mt-1 block w-full"
|
||||||
|
required
|
||||||
|
autofocus
|
||||||
|
autocomplete="username"
|
||||||
|
/>
|
||||||
|
<InputError class="mt-2" :message="form.errors.email" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-4">
|
||||||
|
<InputLabel for="password" value="Password" />
|
||||||
|
<TextInput
|
||||||
|
id="password"
|
||||||
|
v-model="form.password"
|
||||||
|
type="password"
|
||||||
|
class="mt-1 block w-full"
|
||||||
|
required
|
||||||
|
autocomplete="new-password"
|
||||||
|
/>
|
||||||
|
<InputError class="mt-2" :message="form.errors.password" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-4">
|
||||||
|
<InputLabel for="password_confirmation" value="Confirm Password" />
|
||||||
|
<TextInput
|
||||||
|
id="password_confirmation"
|
||||||
|
v-model="form.password_confirmation"
|
||||||
|
type="password"
|
||||||
|
class="mt-1 block w-full"
|
||||||
|
required
|
||||||
|
autocomplete="new-password"
|
||||||
|
/>
|
||||||
|
<InputError class="mt-2" :message="form.errors.password_confirmation" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center justify-end mt-4">
|
||||||
|
<PrimaryButton :class="{ 'opacity-25': form.processing }" :disabled="form.processing">
|
||||||
|
Reset Password
|
||||||
|
</PrimaryButton>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</AuthenticationCard>
|
||||||
|
</template>
|
@ -0,0 +1,104 @@
|
|||||||
|
<script setup>
|
||||||
|
import { nextTick, ref } from 'vue';
|
||||||
|
import { Head, useForm } from '@inertiajs/vue3';
|
||||||
|
import AuthenticationCard from '@/Components/AuthenticationCard.vue';
|
||||||
|
import AuthenticationCardLogo from '@/Components/AuthenticationCardLogo.vue';
|
||||||
|
import InputError from '@/Components/InputError.vue';
|
||||||
|
import InputLabel from '@/Components/InputLabel.vue';
|
||||||
|
import PrimaryButton from '@/Components/PrimaryButton.vue';
|
||||||
|
import TextInput from '@/Components/TextInput.vue';
|
||||||
|
|
||||||
|
const recovery = ref(false);
|
||||||
|
|
||||||
|
const form = useForm({
|
||||||
|
code: '',
|
||||||
|
recovery_code: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const recoveryCodeInput = ref(null);
|
||||||
|
const codeInput = ref(null);
|
||||||
|
|
||||||
|
const toggleRecovery = async () => {
|
||||||
|
recovery.value ^= true;
|
||||||
|
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
if (recovery.value) {
|
||||||
|
recoveryCodeInput.value.focus();
|
||||||
|
form.code = '';
|
||||||
|
} else {
|
||||||
|
codeInput.value.focus();
|
||||||
|
form.recovery_code = '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const submit = () => {
|
||||||
|
form.post(route('two-factor.login'));
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Head title="Two-factor Confirmation" />
|
||||||
|
|
||||||
|
<AuthenticationCard>
|
||||||
|
<template #logo>
|
||||||
|
<AuthenticationCardLogo />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="mb-4 text-sm text-gray-600 dark:text-gray-400">
|
||||||
|
<template v-if="! recovery">
|
||||||
|
Please confirm access to your account by entering the authentication code provided by your authenticator application.
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
Please confirm access to your account by entering one of your emergency recovery codes.
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form @submit.prevent="submit">
|
||||||
|
<div v-if="! recovery">
|
||||||
|
<InputLabel for="code" value="Code" />
|
||||||
|
<TextInput
|
||||||
|
id="code"
|
||||||
|
ref="codeInput"
|
||||||
|
v-model="form.code"
|
||||||
|
type="text"
|
||||||
|
inputmode="numeric"
|
||||||
|
class="mt-1 block w-full"
|
||||||
|
autofocus
|
||||||
|
autocomplete="one-time-code"
|
||||||
|
/>
|
||||||
|
<InputError class="mt-2" :message="form.errors.code" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else>
|
||||||
|
<InputLabel for="recovery_code" value="Recovery Code" />
|
||||||
|
<TextInput
|
||||||
|
id="recovery_code"
|
||||||
|
ref="recoveryCodeInput"
|
||||||
|
v-model="form.recovery_code"
|
||||||
|
type="text"
|
||||||
|
class="mt-1 block w-full"
|
||||||
|
autocomplete="one-time-code"
|
||||||
|
/>
|
||||||
|
<InputError class="mt-2" :message="form.errors.recovery_code" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center justify-end mt-4">
|
||||||
|
<button type="button" class="text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 underline cursor-pointer" @click.prevent="toggleRecovery">
|
||||||
|
<template v-if="! recovery">
|
||||||
|
Use a recovery code
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
Use an authentication code
|
||||||
|
</template>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<PrimaryButton class="ml-4" :class="{ 'opacity-25': form.processing }" :disabled="form.processing">
|
||||||
|
Log in
|
||||||
|
</PrimaryButton>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</AuthenticationCard>
|
||||||
|
</template>
|
@ -0,0 +1,62 @@
|
|||||||
|
<script setup>
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { Head, Link, useForm } from '@inertiajs/vue3';
|
||||||
|
import AuthenticationCard from '@/Components/AuthenticationCard.vue';
|
||||||
|
import AuthenticationCardLogo from '@/Components/AuthenticationCardLogo.vue';
|
||||||
|
import PrimaryButton from '@/Components/PrimaryButton.vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
status: String,
|
||||||
|
});
|
||||||
|
|
||||||
|
const form = useForm({});
|
||||||
|
|
||||||
|
const submit = () => {
|
||||||
|
form.post(route('verification.send'));
|
||||||
|
};
|
||||||
|
|
||||||
|
const verificationLinkSent = computed(() => props.status === 'verification-link-sent');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Head title="Email Verification" />
|
||||||
|
|
||||||
|
<AuthenticationCard>
|
||||||
|
<template #logo>
|
||||||
|
<AuthenticationCardLogo />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="mb-4 text-sm text-gray-600 dark:text-gray-400">
|
||||||
|
Before continuing, could you verify your email address by clicking on the link we just emailed to you? If you didn't receive the email, we will gladly send you another.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="verificationLinkSent" class="mb-4 font-medium text-sm text-green-600 dark:text-green-400">
|
||||||
|
A new verification link has been sent to the email address you provided in your profile settings.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form @submit.prevent="submit">
|
||||||
|
<div class="mt-4 flex items-center justify-between">
|
||||||
|
<PrimaryButton :class="{ 'opacity-25': form.processing }" :disabled="form.processing">
|
||||||
|
Resend Verification Email
|
||||||
|
</PrimaryButton>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Link
|
||||||
|
:href="route('profile.show')"
|
||||||
|
class="underline text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-800"
|
||||||
|
>
|
||||||
|
Edit Profile</Link>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
:href="route('logout')"
|
||||||
|
method="post"
|
||||||
|
as="button"
|
||||||
|
class="underline text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-800 ml-2"
|
||||||
|
>
|
||||||
|
Log Out
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</AuthenticationCard>
|
||||||
|
</template>
|
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
'page-info' => 'Showing :start to :end of :total entries',
|
||||||
|
'select' => 'Select',
|
||||||
|
'actions' => 'Actions',
|
||||||
|
'delete' => 'Delete',
|
||||||
|
'deletion' => 'Deletion',
|
||||||
|
'search' => 'Search',
|
||||||
|
'reset' => 'Reset',
|
||||||
|
'send' => 'Send',
|
||||||
|
];
|
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
// MAIN
|
||||||
|
'name' => 'Name',
|
||||||
|
'description' => 'Description',
|
||||||
|
'slug' => 'Slug',
|
||||||
|
'attribute' => 'Attribute',
|
||||||
|
'value' => 'Value',
|
||||||
|
'created' => 'Created',
|
||||||
|
'create' => 'Create',
|
||||||
|
'edit' => 'Edit',
|
||||||
|
'delete' => 'Delete',
|
||||||
|
'delete_confirm' => 'Are you sure you want to delete?',
|
||||||
|
'generated_automatically' => 'Generated automatically',
|
||||||
|
];
|
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
// PERMISSIONS
|
||||||
|
'permissions' => 'Permissions',
|
||||||
|
'create_permission' => 'Create permission',
|
||||||
|
'edit_permission' => 'Edit permission',
|
||||||
|
'delete_permission' => 'Delete permission',
|
||||||
|
'permission_details' => 'Permission details',
|
||||||
|
];
|
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
// ROLES
|
||||||
|
'roles' => 'Roles',
|
||||||
|
'create_role' => 'Create role',
|
||||||
|
'edit_role' => 'Edit role',
|
||||||
|
'delete_role' => 'Delete role',
|
||||||
|
'role_details' => 'Role details',
|
||||||
|
];
|
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
// USERS
|
||||||
|
'users' => 'Users',
|
||||||
|
'user_details' => 'User details',
|
||||||
|
'assign_roles' => 'Assign roles',
|
||||||
|
'assign_roles_for_user' => 'Assign roles for user',
|
||||||
|
'delete_user' => 'Delete user',
|
||||||
|
'name' => 'Name',
|
||||||
|
];
|
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
// VALIDATION
|
||||||
|
'required' => 'The ":attribute" field is required.',
|
||||||
|
'string' => 'The ":attribute" field must be a string.',
|
||||||
|
'regex' => 'The ":attribute" field must contain latin characters.',
|
||||||
|
'min' => 'The ":attribute" field must contain at least :min characters.',
|
||||||
|
'max' => 'The ":attribute" field may contain no more :max characters.',
|
||||||
|
'unique' => 'The ":attribute" already exists with this value ":input" in database.',
|
||||||
|
'required_to_delete' => 'Select items to delete.',
|
||||||
|
];
|
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
// MAIN
|
||||||
|
'name' => 'Название',
|
||||||
|
'description' => 'Описание',
|
||||||
|
'slug' => 'Слаг',
|
||||||
|
'attribute' => 'Атрибут',
|
||||||
|
'value' => 'Значение',
|
||||||
|
'created' => 'Создано',
|
||||||
|
'create' => 'Создать',
|
||||||
|
'edit' => 'Редактировать',
|
||||||
|
'delete' => 'Удалить',
|
||||||
|
'delete_confirm' => 'Вы уверены что хотите это удалить?',
|
||||||
|
'generated_automatically' => 'Сгенерирован автоматически',
|
||||||
|
];
|
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
// PERMISSIONS
|
||||||
|
'permissions' => 'Полномочия',
|
||||||
|
'create_permission' => 'Создать полномочие',
|
||||||
|
'edit_permission' => 'Редактировать полномочие',
|
||||||
|
'delete_permission' => 'Удалить полномочие',
|
||||||
|
'permission_details' => 'Детали полномочия',
|
||||||
|
];
|
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
// ROLES
|
||||||
|
'roles' => 'Роли',
|
||||||
|
'create_role' => 'Создать роль',
|
||||||
|
'edit_role' => 'Редактировать роль',
|
||||||
|
'delete_role' => 'Удалить роль',
|
||||||
|
'role_details' => 'Детали роли',
|
||||||
|
];
|
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
// USERS
|
||||||
|
'users' => 'Пользователи',
|
||||||
|
'user_details' => 'Детали пользователя',
|
||||||
|
'assign_roles' => 'Назначить роли',
|
||||||
|
'assign_roles_for_user' => 'Назначить роли пользователю',
|
||||||
|
'delete_user' => 'Удалить пользователя',
|
||||||
|
'name' => 'Имя',
|
||||||
|
];
|
@ -0,0 +1,5 @@
|
|||||||
|
@extends('errors::minimal')
|
||||||
|
|
||||||
|
@section('title', __('Unauthorized'))
|
||||||
|
@section('code', '401')
|
||||||
|
@section('message', __('Unauthorized'))
|
@ -0,0 +1,5 @@
|
|||||||
|
@extends('errors::minimal')
|
||||||
|
|
||||||
|
@section('title', __('Payment Required'))
|
||||||
|
@section('code', '402')
|
||||||
|
@section('message', __('Payment Required'))
|
@ -0,0 +1,5 @@
|
|||||||
|
@extends('errors::minimal')
|
||||||
|
|
||||||
|
@section('title', __('Forbidden'))
|
||||||
|
@section('code', '403')
|
||||||
|
@section('message', __($exception->getMessage() ?: 'Forbidden'))
|
@ -0,0 +1,5 @@
|
|||||||
|
@extends('errors::minimal')
|
||||||
|
|
||||||
|
@section('title', __('Not Found'))
|
||||||
|
@section('code', '404')
|
||||||
|
@section('message', __('Not Found'))
|
@ -0,0 +1,5 @@
|
|||||||
|
@extends('errors::minimal')
|
||||||
|
|
||||||
|
@section('title', __('Page Expired'))
|
||||||
|
@section('code', '419')
|
||||||
|
@section('message', __('Page Expired'))
|
@ -0,0 +1,5 @@
|
|||||||
|
@extends('errors::minimal')
|
||||||
|
|
||||||
|
@section('title', __('Too Many Requests'))
|
||||||
|
@section('code', '429')
|
||||||
|
@section('message', __('Too Many Requests'))
|
@ -0,0 +1,5 @@
|
|||||||
|
@extends('errors::minimal')
|
||||||
|
|
||||||
|
@section('title', __('Server Error'))
|
||||||
|
@section('code', '500')
|
||||||
|
@section('message', __('Server Error'))
|
@ -0,0 +1,5 @@
|
|||||||
|
@extends('errors::minimal')
|
||||||
|
|
||||||
|
@section('title', __('Service Unavailable'))
|
||||||
|
@section('code', '503')
|
||||||
|
@section('message', __('Service Unavailable'))
|
@ -0,0 +1,53 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
|
<title>@yield('title')</title>
|
||||||
|
|
||||||
|
<!-- Styles -->
|
||||||
|
<style>
|
||||||
|
html, body {
|
||||||
|
background-color: #fff;
|
||||||
|
color: #636b6f;
|
||||||
|
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||||
|
font-weight: 100;
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-height {
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-center {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.position-ref {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 36px;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="flex-center position-ref full-height">
|
||||||
|
<div class="content">
|
||||||
|
<div class="title">
|
||||||
|
@yield('message')
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,385 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" class="h-full">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
||||||
|
<title>{{ __('Payment Confirmation') }} - {{ config('app.name', 'Laravel') }}</title>
|
||||||
|
|
||||||
|
<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.min.js"></script>
|
||||||
|
<script src="https://js.stripe.com/v3"></script>
|
||||||
|
</head>
|
||||||
|
<body class="font-sans text-gray-600 bg-gray-100 leading-normal p-4 h-full">
|
||||||
|
<div id="app" class="h-full md:flex md:justify-center md:items-center">
|
||||||
|
<div class="w-full max-w-lg">
|
||||||
|
<h1 class="text-4xl font-bold text-center p-4 sm:p-6 mt-4">
|
||||||
|
Your {{ $amount }} payment
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<!-- Status Messages -->
|
||||||
|
<p class="flex items-center bg-red-100 border border-red-200 px-5 py-2 rounded-lg text-red-500" v-if="errorMessage">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="flex-shrink-0 w-6 h-6">
|
||||||
|
<path class="fill-current text-red-300" d="M12 2a10 10 0 1 1 0 20 10 10 0 0 1 0-20z"/>
|
||||||
|
<path class="fill-current text-red-500" d="M12 18a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm1-5.9c-.13 1.2-1.88 1.2-2 0l-.5-5a1 1 0 0 1 1-1.1h1a1 1 0 0 1 1 1.1l-.5 5z"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<span class="ml-3">@{{ errorMessage }}</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="bg-white rounded-lg shadow-xl p-4 sm:p-6 mt-4">
|
||||||
|
<div v-if="paymentIntent.status === 'succeeded'">
|
||||||
|
<h2 class="text-xl mb-4 text-gray-600">
|
||||||
|
Payment Successful
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<p class="mb-6">
|
||||||
|
This payment was successfully confirmed.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else-if="paymentIntent.status === 'processing'">
|
||||||
|
<h2 class="text-xl mb-4 text-gray-600">
|
||||||
|
Payment Processing
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<p class="mb-6">
|
||||||
|
This payment is currently processing. Refresh this page from time to time to see its status.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else-if="paymentIntent.status === 'canceled'">
|
||||||
|
<h2 class="text-xl mb-4 text-gray-600">
|
||||||
|
Payment Canceled
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<p class="mb-6">
|
||||||
|
This payment was canceled.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else>
|
||||||
|
<!-- Payment Method Form -->
|
||||||
|
<div v-if="paymentIntent.status === 'requires_payment_method'" class="mb-3">
|
||||||
|
<!-- Instructions -->
|
||||||
|
<h2 class="text-xl mb-4 text-gray-600">
|
||||||
|
Confirm Your Payment
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<p class="mb-6">
|
||||||
|
A valid payment method is needed to process your payment. Please confirm your payment by filling out your payment details below.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- Payment Method -->
|
||||||
|
<label for="paymentMethod" class="inline-block text-sm text-gray-700 font-semibold mb-2">
|
||||||
|
Payment Method
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div v-if="paymentMethods.length > 1">
|
||||||
|
<p class="text-sm mb-3">
|
||||||
|
Please select the payment method which you'd like to use.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<select
|
||||||
|
id="paymentMethod"
|
||||||
|
required
|
||||||
|
class="inline-block bg-gray-100 border border-gray-300 rounded-lg w-full px-4 py-3 mb-3 focus:outline-none"
|
||||||
|
v-model="paymentMethod"
|
||||||
|
@change="configureStripeElements"
|
||||||
|
>
|
||||||
|
<option v-for="option in paymentMethods" v-bind:value="option">
|
||||||
|
@{{ option.title }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<p class="text-sm mb-3">
|
||||||
|
Your payment will be processed by @{{ paymentMethodTitle }}.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Name -->
|
||||||
|
<label for="name" class="inline-block text-sm text-gray-700 font-semibold mb-2">
|
||||||
|
Full name
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<input
|
||||||
|
id="name"
|
||||||
|
type="text" placeholder="Jane Doe"
|
||||||
|
required
|
||||||
|
class="inline-block bg-gray-100 border border-gray-300 rounded-lg w-full px-4 py-3 mb-3 focus:outline-none"
|
||||||
|
v-model="name"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- E-mail Address -->
|
||||||
|
<label for="email" class="inline-block text-sm text-gray-700 font-semibold mb-2">
|
||||||
|
E-mail address
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<input
|
||||||
|
id="email"
|
||||||
|
type="text" placeholder="jane@example.com"
|
||||||
|
required
|
||||||
|
class="inline-block bg-gray-100 border border-gray-300 rounded-lg w-full px-4 py-3 mb-3 focus:outline-none"
|
||||||
|
v-model="email"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div v-if="paymentElement">
|
||||||
|
<!-- Stripe Payment Element -->
|
||||||
|
<label for="payment-element" class="inline-block text-sm text-gray-700 font-semibold mb-2">
|
||||||
|
Payment details
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div id="payment-element" ref="paymentElement" class="bg-gray-100 border border-gray-300 rounded-lg p-4 mb-6"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="(paymentMethod || {}).remember">
|
||||||
|
<!-- Remember Payment Method -->
|
||||||
|
<label for="remember" class="inline-block text-sm text-gray-700 mb-2">
|
||||||
|
<input
|
||||||
|
id="remember"
|
||||||
|
type="checkbox"
|
||||||
|
required
|
||||||
|
class="inline-block mr-1 focus:outline-none"
|
||||||
|
v-model="remember"
|
||||||
|
/>
|
||||||
|
|
||||||
|
Remember payment method for future usage
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<p v-if="['bancontact', 'ideal', 'sepa_debit'].includes(paymentMethod.type)" class="text-xs text-gray-400 mb-6">
|
||||||
|
By providing your payment information and confirming this payment, you authorise (A) and Stripe, our payment service provider, to send instructions to your bank to debit your account and (B) your bank to debit your account in accordance with those instructions. As part of your rights, you are entitled to a refund from your bank under the terms and conditions of your agreement with your bank. A refund must be claimed within 8 weeks starting from the date on which your account was debited. Your rights are explained in a statement that you can obtain from your bank. You agree to receive notifications for future debits up to 2 days before they occur.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Confirm Payment Method Button -->
|
||||||
|
<button
|
||||||
|
class="inline-block w-full px-4 py-3 mb-4 text-white rounded-lg hover:bg-blue-500"
|
||||||
|
:class="{ 'bg-blue-400': isPaymentProcessing, 'bg-blue-600': ! isPaymentProcessing }"
|
||||||
|
@click="confirmPaymentMethod"
|
||||||
|
:disabled="isPaymentProcessing"
|
||||||
|
>
|
||||||
|
<span v-if="isPaymentProcessing">
|
||||||
|
Processing...
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
Confirm your {{ $amount }} payment with @{{ paymentMethodTitle }}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button @click="goBack" ref="goBackButton" data-redirect="{{ $redirect }}"
|
||||||
|
class="inline-block w-full px-4 py-3 bg-gray-100 hover:bg-gray-200 text-center text-gray-600 rounded-lg">
|
||||||
|
Go back
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="text-center text-gray-500 text-sm mt-4 pb-4">
|
||||||
|
© {{ date('Y') }} {{ config('app.name') }}. All rights reserved.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
window.stripe = Stripe('{{ $stripeKey }}');
|
||||||
|
|
||||||
|
new Vue({
|
||||||
|
el: '#app',
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
paymentIntent: @json($paymentIntent),
|
||||||
|
paymentMethod: null,
|
||||||
|
name: '{{ optional($customer)->stripeName() }}',
|
||||||
|
email: '{{ optional($customer)->stripeEmail() }}',
|
||||||
|
paymentElement: null,
|
||||||
|
remember: false,
|
||||||
|
isPaymentProcessing: false,
|
||||||
|
errorMessage: '{{ $errorMessage }}'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted: function () {
|
||||||
|
this.configurePayment(this.paymentIntent);
|
||||||
|
this.configureStripeElements();
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
paymentMethodTitle() {
|
||||||
|
return this.paymentMethod ? this.paymentMethod.title : '';
|
||||||
|
},
|
||||||
|
|
||||||
|
paymentMethods() {
|
||||||
|
const methods = [
|
||||||
|
{ title: 'Card', type: 'card', remember: true, redirects: false, element: 'card' },
|
||||||
|
{ title: 'Alipay', type: 'alipay' },
|
||||||
|
{ title: 'BECS Direct Debit', type: 'au_becs_debit', remember: true, redirects: false, element: 'auBankAccount' },
|
||||||
|
{ title: 'Bancontact', type: 'bancontact', remember: true },
|
||||||
|
{ title: 'EPS', type: 'eps', element: 'epsBank' },
|
||||||
|
{ title: 'Giropay', type: 'giropay' },
|
||||||
|
{ title: 'iDEAL', type: 'ideal', remember: true, element: 'idealBank' },
|
||||||
|
{ title: 'SEPA Debit', type: 'sepa_debit', remember: true, redirects: false, element: 'iban', options: { supportedCountries: ['SEPA'] }}
|
||||||
|
].map(paymentMethod => {
|
||||||
|
return { remember: false, redirects: true, options: {}, ...paymentMethod }
|
||||||
|
})
|
||||||
|
|
||||||
|
return methods.filter(method => this.paymentIntent.payment_method_types.includes(method.type))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
configurePayment: function (paymentIntent) {
|
||||||
|
// Set the payment intent object...
|
||||||
|
this.paymentIntent = paymentIntent;
|
||||||
|
|
||||||
|
// Set the allowed payment methods based on the payment method types of the intent...
|
||||||
|
const paymentMethodTypes = paymentIntent.payment_method_types;
|
||||||
|
|
||||||
|
// If the previously set payment method isn't available anymore,
|
||||||
|
// update it to either the current one or the first available one...
|
||||||
|
if (this.paymentMethod === null || ! paymentMethodTypes.includes(this.paymentMethod.type)) {
|
||||||
|
const type = this.paymentMethod === null
|
||||||
|
? ('{{ $paymentMethod }}' ? '{{ $paymentMethod }}' : paymentMethodTypes[0])
|
||||||
|
: (((this.paymentIntent || {}).payment_method || {}).type ?? paymentMethodTypes[0]);
|
||||||
|
|
||||||
|
this.paymentMethod = this.paymentMethods.filter(
|
||||||
|
paymentMethod => paymentMethod.type === type
|
||||||
|
)[0];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
configureStripeElements: function () {
|
||||||
|
// Stripe Elements are only needed when a payment method is required.
|
||||||
|
if (this.paymentIntent.status !== 'requires_payment_method') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the Stripe element based on the currently selected payment method...
|
||||||
|
if (this.paymentMethod.element) {
|
||||||
|
const elements = stripe.elements();
|
||||||
|
|
||||||
|
this.paymentElement = elements.create(
|
||||||
|
this.paymentMethod.element, this.paymentMethod.options ?? {}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.paymentElement = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.paymentElement) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
// Clear the payment element first, otherwise Stripe Elements will emit a warning...
|
||||||
|
document.getElementById("payment-element").innerHTML = "";
|
||||||
|
|
||||||
|
this.paymentElement.mount('#payment-element');
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
confirmPaymentMethod: function () {
|
||||||
|
this.isPaymentProcessing = true;
|
||||||
|
this.errorMessage = '';
|
||||||
|
|
||||||
|
const secret = this.paymentIntent.client_secret;
|
||||||
|
let data = {
|
||||||
|
setup_future_usage: this.paymentMethod.remember && this.remember
|
||||||
|
? 'off_session'
|
||||||
|
: null,
|
||||||
|
payment_method: {
|
||||||
|
billing_details: { name: this.name, email: this.email }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let paymentPromise;
|
||||||
|
|
||||||
|
// Set a return url to redirect the user back to the payment
|
||||||
|
// page after handling the off session payment confirmation.
|
||||||
|
if (this.paymentMethod.redirects) {
|
||||||
|
data.return_url = '{{ route('cashier.payment', $paymentIntent['id']).'?redirect='.$redirect }}';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.paymentMethod.type === 'card') {
|
||||||
|
if (this.paymentIntent.status === 'requires_payment_method') {
|
||||||
|
data.payment_method.card = this.paymentElement;
|
||||||
|
} else if (
|
||||||
|
this.paymentIntent.status === 'requires_action' ||
|
||||||
|
this.paymentIntent.status === 'requires_confirmation'
|
||||||
|
) {
|
||||||
|
data.payment_method = this.paymentIntent.payment_method.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
paymentPromise = stripe.confirmCardPayment(secret, data);
|
||||||
|
} else if (this.paymentMethod.type === 'alipay') {
|
||||||
|
paymentPromise = stripe.confirmAlipayPayment(secret, data);
|
||||||
|
} else if (this.paymentMethod.type === 'au_becs_debit') {
|
||||||
|
if (this.paymentIntent.status === 'requires_payment_method') {
|
||||||
|
data.payment_method.au_becs_debit = this.paymentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
paymentPromise = stripe.confirmAuBecsDebitPayment(secret, data);
|
||||||
|
} else if (this.paymentMethod.type === 'bancontact') {
|
||||||
|
paymentPromise = stripe.confirmBancontactPayment(secret, data);
|
||||||
|
} else if (this.paymentMethod.type === 'eps') {
|
||||||
|
if (this.paymentIntent.status === 'requires_payment_method') {
|
||||||
|
data.payment_method.eps = this.paymentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
paymentPromise = stripe.confirmEpsPayment(secret, data);
|
||||||
|
} else if (this.paymentMethod.type === 'giropay') {
|
||||||
|
paymentPromise = stripe.confirmGiropayPayment(secret, data);
|
||||||
|
} else if (this.paymentMethod.type === 'ideal') {
|
||||||
|
if (this.paymentIntent.status === 'requires_payment_method') {
|
||||||
|
data.payment_method.ideal = this.paymentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
paymentPromise = stripe.confirmIdealPayment(secret, data);
|
||||||
|
} else if (this.paymentMethod.type === 'sepa_debit') {
|
||||||
|
if (this.paymentIntent.status === 'requires_payment_method') {
|
||||||
|
data.payment_method.sepa_debit = this.paymentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
paymentPromise = stripe.confirmSepaDebitPayment(secret, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
paymentPromise.then(result => this.confirmCallback(result));
|
||||||
|
},
|
||||||
|
|
||||||
|
confirmCallback: function (result) {
|
||||||
|
this.isPaymentProcessing = false;
|
||||||
|
|
||||||
|
if (result.error) {
|
||||||
|
if (result.error.code === '{{ Stripe\ErrorObject::CODE_PARAMETER_INVALID_EMPTY }}') {
|
||||||
|
this.errorMessage = 'Please provide your name and e-mail address.';
|
||||||
|
} else {
|
||||||
|
this.errorMessage = result.error.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.error.payment_intent) {
|
||||||
|
this.configurePayment(result.error.payment_intent);
|
||||||
|
|
||||||
|
this.configureStripeElements();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.configurePayment(result.paymentIntent);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
goBack: function () {
|
||||||
|
const button = this.$refs.goBackButton;
|
||||||
|
const redirect = new URL(button.dataset.redirect);
|
||||||
|
|
||||||
|
redirect.searchParams.append(
|
||||||
|
'success', this.paymentIntent.status === 'succeeded' ? 'true' : 'false'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this.errorMessage) {
|
||||||
|
redirect.searchParams.append('message', this.errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.location.href = redirect;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,323 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
|
<title>Invoice</title>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background: #fff none;
|
||||||
|
font-family: DejaVu Sans, 'sans-serif';
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
padding-top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table th {
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 8px 8px 8px 0;
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table tr.row td {
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table td {
|
||||||
|
padding: 8px 8px 8px 0;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table th:last-child,
|
||||||
|
.table td:last-child {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dates {
|
||||||
|
color: #555;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<table style="margin-left: auto; margin-right: auto;" width="100%">
|
||||||
|
<tr valign="top">
|
||||||
|
<td width="160">
|
||||||
|
<span style="font-size: 28px;">
|
||||||
|
Receipt
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- Invoice Info -->
|
||||||
|
<p>
|
||||||
|
@isset ($product)
|
||||||
|
<strong>Product:</strong> {{ $product }}<br>
|
||||||
|
@endisset
|
||||||
|
|
||||||
|
<strong>Date:</strong> {{ $invoice->date()->toFormattedDateString() }}<br>
|
||||||
|
|
||||||
|
@if ($dueDate = $invoice->dueDate())
|
||||||
|
<strong>Due date:</strong> {{ $dueDate->toFormattedDateString() }}<br>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if ($invoiceId = $id ?? $invoice->number)
|
||||||
|
<strong>Invoice Number:</strong> {{ $invoiceId }}<br>
|
||||||
|
@endif
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<!-- Account Name / Header Image -->
|
||||||
|
<td align="right">
|
||||||
|
<span style="font-size: 28px; color: #ccc;">
|
||||||
|
<strong>{{ $header ?? $vendor ?? $invoice->account_name }}</strong>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr valign="top">
|
||||||
|
<td width="50%">
|
||||||
|
<!-- Account Details -->
|
||||||
|
<strong>{{ $vendor ?? $invoice->account_name }}</strong><br>
|
||||||
|
|
||||||
|
@isset($street)
|
||||||
|
{{ $street }}<br>
|
||||||
|
@endisset
|
||||||
|
|
||||||
|
@isset($location)
|
||||||
|
{{ $location }}<br>
|
||||||
|
@endisset
|
||||||
|
|
||||||
|
@isset($country)
|
||||||
|
{{ $country }}<br>
|
||||||
|
@endisset
|
||||||
|
|
||||||
|
@isset($phone)
|
||||||
|
{{ $phone }}<br>
|
||||||
|
@endisset
|
||||||
|
|
||||||
|
@isset($email)
|
||||||
|
{{ $email }}<br>
|
||||||
|
@endisset
|
||||||
|
|
||||||
|
@isset($url)
|
||||||
|
<a href="{{ $url }}">{{ $url }}</a><br>
|
||||||
|
@endisset
|
||||||
|
|
||||||
|
@isset($vendorVat)
|
||||||
|
{{ $vendorVat }}<br>
|
||||||
|
@else
|
||||||
|
@foreach ($invoice->accountTaxIds() as $taxId)
|
||||||
|
{{ $taxId->value }}<br>
|
||||||
|
@endforeach
|
||||||
|
@endisset
|
||||||
|
</td>
|
||||||
|
<td width="50%">
|
||||||
|
<!-- Customer Details -->
|
||||||
|
<strong>Recipient</strong><br>
|
||||||
|
|
||||||
|
{{ $invoice->customer_name ?? $invoice->customer_email }}<br>
|
||||||
|
|
||||||
|
@if ($address = $invoice->customer_address)
|
||||||
|
@if ($address->line1)
|
||||||
|
{{ $address->line1 }}<br>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if ($address->line2)
|
||||||
|
{{ $address->line2 }}<br>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if ($address->city)
|
||||||
|
{{ $address->city }}<br>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if ($address->state || $address->postal_code)
|
||||||
|
{{ implode(' ', [$address->state, $address->postal_code]) }}<br>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if ($address->country)
|
||||||
|
{{ $address->country }}<br>
|
||||||
|
@endif
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if ($invoice->customer_phone)
|
||||||
|
{{ $invoice->customer_phone }}<br>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if ($invoice->customer_name)
|
||||||
|
{{ $invoice->customer_email }}<br>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@foreach ($invoice->customerTaxIds() as $taxId)
|
||||||
|
{{ $taxId->value }}<br>
|
||||||
|
@endforeach
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr valign="top">
|
||||||
|
<td colspan="2">
|
||||||
|
<!-- Memo / Description -->
|
||||||
|
@if ($invoice->description)
|
||||||
|
<p>
|
||||||
|
{{ $invoice->description }}
|
||||||
|
</p>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<!-- Extra / VAT Information -->
|
||||||
|
@if (isset($vat))
|
||||||
|
<p>
|
||||||
|
{{ $vat }}
|
||||||
|
</p>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">
|
||||||
|
<!-- Invoice Table -->
|
||||||
|
<table width="100%" class="table" border="0">
|
||||||
|
<tr>
|
||||||
|
<th align="left">Description</th>
|
||||||
|
<th align="left">Qty</th>
|
||||||
|
<th align="left">Unit price</th>
|
||||||
|
|
||||||
|
@if ($invoice->hasTax())
|
||||||
|
<th align="right">Tax</th>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<th align="right">Amount</th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<!-- Display The Invoice Line Items -->
|
||||||
|
@foreach ($invoice->invoiceLineItems() as $item)
|
||||||
|
<tr class="row">
|
||||||
|
<td>
|
||||||
|
{{ $item->description }}
|
||||||
|
|
||||||
|
@if ($item->hasPeriod() && ! $item->periodStartAndEndAreEqual())
|
||||||
|
<br><span class="dates">
|
||||||
|
{{ $item->startDate() }} - {{ $item->endDate() }}
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>{{ $item->quantity }}</td>
|
||||||
|
<td>{{ $item->unitAmountExcludingTax() }}</td>
|
||||||
|
|
||||||
|
@if ($invoice->hasTax())
|
||||||
|
<td align="right">
|
||||||
|
@if ($inclusiveTaxPercentage = $item->inclusiveTaxPercentage())
|
||||||
|
{{ $inclusiveTaxPercentage }}% incl.
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if ($item->hasBothInclusiveAndExclusiveTax())
|
||||||
|
+
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if ($exclusiveTaxPercentage = $item->exclusiveTaxPercentage())
|
||||||
|
{{ $exclusiveTaxPercentage }}%
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<td align="right">{{ $item->total() }}</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
<!-- Display The Subtotal -->
|
||||||
|
@if ($invoice->hasDiscount() || $invoice->hasTax() || $invoice->hasStartingBalance())
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td colspan="{{ $invoice->hasTax() ? 3 : 2 }}">Subtotal</td>
|
||||||
|
<td align="right">{{ $invoice->subtotal() }}</td>
|
||||||
|
</tr>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<!-- Display The Discount -->
|
||||||
|
@if ($invoice->hasDiscount())
|
||||||
|
@foreach ($invoice->discounts() as $discount)
|
||||||
|
@php($coupon = $discount->coupon())
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td colspan="{{ $invoice->hasTax() ? 3 : 2 }}" align="right">
|
||||||
|
@if ($coupon->isPercentage())
|
||||||
|
{{ $coupon->name() }} ({{ $coupon->percentOff() }}% Off)
|
||||||
|
@else
|
||||||
|
{{ $coupon->name() }} ({{ $coupon->amountOff() }} Off)
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td align="right">-{{ $invoice->discountFor($discount) }}</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<!-- Display The Taxes -->
|
||||||
|
@unless ($invoice->isNotTaxExempt())
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td colspan="{{ $invoice->hasTax() ? 3 : 2 }}" align="right">
|
||||||
|
@if ($invoice->isTaxExempt())
|
||||||
|
Tax is exempted
|
||||||
|
@else
|
||||||
|
Tax to be paid on reverse charge basis
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
<td align="right"></td>
|
||||||
|
</tr>
|
||||||
|
@else
|
||||||
|
@foreach ($invoice->taxes() as $tax)
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td colspan="3">
|
||||||
|
{{ $tax->display_name }} {{ $tax->jurisdiction ? ' - '.$tax->jurisdiction : '' }}
|
||||||
|
({{ $tax->percentage }}%{{ $tax->isInclusive() ? ' incl.' : '' }})
|
||||||
|
</td>
|
||||||
|
<td align="right">{{ $tax->amount() }}</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
@endunless
|
||||||
|
|
||||||
|
<!-- Display The Final Total -->
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td colspan="{{ $invoice->hasTax() ? 3 : 2 }}">
|
||||||
|
Total
|
||||||
|
</td>
|
||||||
|
<td align="right">
|
||||||
|
{{ $invoice->realTotal() }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<!-- Applied Balance -->
|
||||||
|
@if ($invoice->hasAppliedBalance())
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td colspan="{{ $invoice->hasTax() ? 3 : 2 }}">
|
||||||
|
Applied balance
|
||||||
|
</td>
|
||||||
|
<td align="right">{{ $invoice->appliedBalance() }}</td>
|
||||||
|
</tr>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<!-- Display The Amount Due -->
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td colspan="{{ $invoice->hasTax() ? 3 : 2 }}">
|
||||||
|
<strong>Amount due</strong>
|
||||||
|
</td>
|
||||||
|
<td align="right">
|
||||||
|
<strong>{{ $invoice->amountDue() }}</strong>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,9 @@
|
|||||||
|
<div class="col-lg-{!! $bootstrapColWidth !!}">
|
||||||
|
<a class="btn btn-danger" href="{!! $url !!}" @if(!empty($htmlAttributes)) {!! $htmlAttributes !!} @endif >
|
||||||
|
<svg width="1.2em" height="1.5em" viewBox="0 0 16 16" class="bi bi-trash" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z"/>
|
||||||
|
<path fill-rule="evenodd" d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4L4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z"/>
|
||||||
|
</svg>
|
||||||
|
</a> 
|
||||||
|
</div>
|
||||||
|
<div class="d-lg-none"> </div>
|
@ -0,0 +1,9 @@
|
|||||||
|
<div class="col-lg-{!! $bootstrapColWidth !!}">
|
||||||
|
<a class="btn btn-success" href="{!! $url !!}" @if(!empty($htmlAttributes)) {!! $htmlAttributes !!} @endif >
|
||||||
|
<svg width="1.2em" height="1.5em" viewBox="0 0 16 16" class="bi bi-pencil-square" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M15.502 1.94a.5.5 0 0 1 0 .706L14.459 3.69l-2-2L13.502.646a.5.5 0 0 1 .707 0l1.293 1.293zm-1.75 2.456l-2-2L4.939 9.21a.5.5 0 0 0-.121.196l-.805 2.414a.25.25 0 0 0 .316.316l2.414-.805a.5.5 0 0 0 .196-.12l6.813-6.814z"/>
|
||||||
|
<path fill-rule="evenodd" d="M1 13.5A1.5 1.5 0 0 0 2.5 15h11a1.5 1.5 0 0 0 1.5-1.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5v-11a.5.5 0 0 1 .5-.5H9a.5.5 0 0 0 0-1H2.5A1.5 1.5 0 0 0 1 2.5v11z"/>
|
||||||
|
</svg>
|
||||||
|
</a> 
|
||||||
|
</div>
|
||||||
|
<div class="d-lg-none"> </div>
|
@ -0,0 +1,9 @@
|
|||||||
|
<div class="col-lg-{!! $bootstrapColWidth !!}">
|
||||||
|
<a class="btn btn-primary" href="{!! $url !!}" @if(!empty($htmlAttributes)) {!! $htmlAttributes !!} @endif >
|
||||||
|
<svg width="1.2em" height="1.5em" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M10.5 8a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0z"/>
|
||||||
|
<path fill-rule="evenodd" d="M0 8s3-5.5 8-5.5S16 8 16 8s-3 5.5-8 5.5S0 8 0 8zm8 3.5a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7z"/>
|
||||||
|
</svg>
|
||||||
|
</a> 
|
||||||
|
</div>
|
||||||
|
<div class="d-lg-none"> </div>
|
@ -0,0 +1,9 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="col-12 text-center">
|
||||||
|
<svg width="2em" height="2em" viewBox="0 0 16 16" class="bi bi-x-circle" fill="red" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" d="M8 15A7 7 0 1 0 8 1a7 7 0 0 0 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"/>
|
||||||
|
<path fill-rule="evenodd" d="M11.854 4.146a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708-.708l7-7a.5.5 0 0 1 .708 0z"/>
|
||||||
|
<path fill-rule="evenodd" d="M4.146 4.146a.5.5 0 0 0 0 .708l7 7a.5.5 0 0 0 .708-.708l-7-7a.5.5 0 0 0-.708 0z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,9 @@
|
|||||||
|
@php
|
||||||
|
/** @var string $field */
|
||||||
|
/** @var mixed $value */
|
||||||
|
@endphp
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 text-center">
|
||||||
|
<input type="checkbox" class="form-control form-control-sm" name="{{ $field }}[]" value="{{ $value }}" role="grid-view-checkbox-item" />
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,11 @@
|
|||||||
|
@php
|
||||||
|
/** @var string $name */
|
||||||
|
/** @var array $data */
|
||||||
|
/** @var mixed $value */
|
||||||
|
@endphp
|
||||||
|
<select class="form-control" name="filters[{{ $name }}]" role="grid-view-filter-item">
|
||||||
|
<option value="">{!! trans('grid_view::grid.select') !!}</option>
|
||||||
|
@foreach($data as $key => $val)
|
||||||
|
<option value="{!! $key !!}" @if($value !== null && $value == $key) selected="selected" @endif >{!! $val !!}</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
@ -0,0 +1 @@
|
|||||||
|
 
|
@ -0,0 +1,5 @@
|
|||||||
|
@php
|
||||||
|
/** @var string $name */
|
||||||
|
/** @var string $value */
|
||||||
|
@endphp
|
||||||
|
<input type="text" class="form-control" name="filters[{{ $name }}]" value="{{ $value }}" role="grid-view-filter-item">
|
@ -0,0 +1,138 @@
|
|||||||
|
@php
|
||||||
|
/** @var \Itstructure\GridView\Columns\BaseColumn[] $columnObjects */
|
||||||
|
/** @var \Illuminate\Pagination\LengthAwarePaginator $paginator */
|
||||||
|
/** @var boolean $useFilters */
|
||||||
|
$checkboxesExist = false;
|
||||||
|
@endphp
|
||||||
|
<style>
|
||||||
|
.table-bordered tfoot tr td {
|
||||||
|
border-width: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
@if($title)
|
||||||
|
<h2 class="card-title">{!! $title !!}</h2>
|
||||||
|
@endif
|
||||||
|
<div class="float-right">
|
||||||
|
@if ($paginator->onFirstPage())
|
||||||
|
{!! trans('grid_view::grid.page-info', [
|
||||||
|
'start' => '<b>1</b>',
|
||||||
|
'end' => '<b>' . $paginator->perPage() . '</b>',
|
||||||
|
'total' => '<b>' . $paginator->total() . '</b>',
|
||||||
|
]) !!}
|
||||||
|
@elseif ($paginator->currentPage() == $paginator->lastPage())
|
||||||
|
{!! trans('grid_view::grid.page-info', [
|
||||||
|
'start' => '<b>' . (($paginator->currentPage() - 1) * $paginator->perPage() + 1) . '</b>',
|
||||||
|
'end' => '<b>' . $paginator->total() . '</b>',
|
||||||
|
'total' => '<b>' . $paginator->total() . '</b>',
|
||||||
|
]) !!}
|
||||||
|
@else
|
||||||
|
{!! trans('grid_view::grid.page-info', [
|
||||||
|
'start' => '<b>' . (($paginator->currentPage() - 1) * $paginator->perPage() + 1) . '</b>',
|
||||||
|
'end' => '<b>' . (($paginator->currentPage()) * $paginator->perPage()) . '</b>',
|
||||||
|
'total' => '<b>' . $paginator->total() . '</b>',
|
||||||
|
]) !!}
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<table class="table @if($tableBordered) table-bordered @endif @if($tableStriped) table-striped @endif @if($tableHover) table-hover @endif @if($tableSmall) table-sm @endif">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="5%">#</th>
|
||||||
|
@foreach($columnObjects as $column_obj)
|
||||||
|
<th {!! $column_obj->buildHtmlAttributes() !!}>
|
||||||
|
|
||||||
|
@if($column_obj->getSort() === false || $column_obj instanceof \Itstructure\GridView\Columns\ActionColumn)
|
||||||
|
{{ $column_obj->getLabel() }}
|
||||||
|
|
||||||
|
@elseif($column_obj instanceof \Itstructure\GridView\Columns\CheckboxColumn)
|
||||||
|
@php($checkboxesExist = true)
|
||||||
|
@if($useFilters)
|
||||||
|
{{ $column_obj->getLabel() }}
|
||||||
|
@else
|
||||||
|
<input type="checkbox" id="grid_view_checkbox_main" class="form-control form-control-sm" @if($paginator->count() == 0) disabled="disabled" @endif />
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@else
|
||||||
|
<a href="{{ \Itstructure\GridView\Helpers\SortHelper::getSortableLink(request(), $column_obj) }}">{{ $column_obj->getLabel() }}</a>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
</th>
|
||||||
|
@endforeach
|
||||||
|
</tr>
|
||||||
|
@if ($useFilters)
|
||||||
|
<tr>
|
||||||
|
<form action="{{ $filtersFormAction }}" method="get" id="grid_view_filters_form">
|
||||||
|
<td></td>
|
||||||
|
@foreach($columnObjects as $column_obj)
|
||||||
|
<td>
|
||||||
|
@if($column_obj instanceof \Itstructure\GridView\Columns\CheckboxColumn)
|
||||||
|
<input type="checkbox" id="grid_view_checkbox_main" class="form-control form-control-sm" @if($paginator->count() == 0) disabled="disabled" @endif />
|
||||||
|
@else
|
||||||
|
{!! $column_obj->getFilter()->render() !!}
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
@endforeach
|
||||||
|
<input type="submit" class="d-none">
|
||||||
|
</form>
|
||||||
|
</tr>
|
||||||
|
@endif
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<form action="{{ $rowsFormAction }}" method="post" id="grid_view_rows_form">
|
||||||
|
<tbody>
|
||||||
|
@foreach($paginator->items() as $key => $row)
|
||||||
|
<tr>
|
||||||
|
<td>{{ ($paginator->currentPage() - 1) * $paginator->perPage() + $key + 1 }}</td>
|
||||||
|
@foreach($columnObjects as $column_obj)
|
||||||
|
<td>{!! $column_obj->render($row) !!}</td>
|
||||||
|
@endforeach
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<td colspan="{{ count($columnObjects) + 1 }}">
|
||||||
|
<div class="mx-1">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 col-xl-8 text-center text-xl-left">
|
||||||
|
{{ $paginator->render('grid_view::pagination') }}
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-xl-4 text-center text-xl-right">
|
||||||
|
@if ($useFilters)
|
||||||
|
<button id="grid_view_search_button" type="button" class="btn btn-primary">{{ $searchButtonLabel }}</button>
|
||||||
|
<button id="grid_view_reset_button" type="button" class="btn btn-warning">{{ $resetButtonLabel }}</button>
|
||||||
|
@endif
|
||||||
|
@if (($checkboxesExist || $useSendButtonAnyway) && $paginator->count() > 0)
|
||||||
|
<button type="submit" class="btn btn-danger">{{ $sendButtonLabel }}</button>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
<input type="hidden" value="{!! csrf_token() !!}" name="_token">
|
||||||
|
</form>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
|
$('#grid_view_checkbox_main').click(function (event) {
|
||||||
|
$('input[role="grid-view-checkbox-item"]').prop('checked', event.target.checked);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#grid_view_search_button').click(function () {
|
||||||
|
$('#grid_view_filters_form').submit();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#grid_view_reset_button').click(function () {
|
||||||
|
$('input[role="grid-view-filter-item"]').val('');
|
||||||
|
$('select[role="grid-view-filter-item"]').prop('selectedIndex', 0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
@ -0,0 +1,51 @@
|
|||||||
|
@php
|
||||||
|
/**
|
||||||
|
* @var \Illuminate\Pagination\LengthAwarePaginator $paginator
|
||||||
|
* @var array[] $elements
|
||||||
|
*/
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
@if ($paginator->hasPages())
|
||||||
|
<nav class="d-inline-flex d-xl-flex">
|
||||||
|
<ul class="pagination">
|
||||||
|
@if ($paginator->onFirstPage())
|
||||||
|
<li class="page-item disabled">
|
||||||
|
<a class="page-link" href="#">«</a>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li class="page-item">
|
||||||
|
<a class="page-link" href="{{ \Itstructure\GridView\Helpers\UrlSliderHelper::previousPageUrl(request(), $paginator) }}">«</a>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Pagination Elements --}}
|
||||||
|
@foreach($elements as $key => $element)
|
||||||
|
{{-- Array Of Links --}}
|
||||||
|
@if (is_array($element))
|
||||||
|
@foreach($element as $page => $url)
|
||||||
|
@if ($page == $paginator->currentPage())
|
||||||
|
<li class="page-item active"><span class="page-link">{{ $page }}</span></li>
|
||||||
|
@else
|
||||||
|
<li class="page-item"><a class="page-link" href="{{ \Itstructure\GridView\Helpers\UrlSliderHelper::toPageUrl(request(), $paginator, $page) }}">{{ $page }}</a></li>
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
{{-- "Three Dots" Separator --}}
|
||||||
|
@elseif(is_string($element))
|
||||||
|
<li class="page-item disabled"><span class="page-link">{{ $element }}</span></li>
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
@if ($paginator->hasMorePages())
|
||||||
|
<li class="page-item">
|
||||||
|
<a class="page-link" href="{{ \Itstructure\GridView\Helpers\UrlSliderHelper::nextPageUrl(request(), $paginator) }}">»</a>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li class="page-item disabled">
|
||||||
|
<a class="page-link" href="#">»</a>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
@endif
|
@ -0,0 +1,50 @@
|
|||||||
|
<div>
|
||||||
|
@if ($paginator->hasPages())
|
||||||
|
@php(isset($this->numberOfPaginatorsRendered[$paginator->getPageName()]) ? $this->numberOfPaginatorsRendered[$paginator->getPageName()]++ : $this->numberOfPaginatorsRendered[$paginator->getPageName()] = 1)
|
||||||
|
|
||||||
|
<nav>
|
||||||
|
<ul class="pagination">
|
||||||
|
{{-- Previous Page Link --}}
|
||||||
|
@if ($paginator->onFirstPage())
|
||||||
|
<li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.previous')">
|
||||||
|
<span class="page-link" aria-hidden="true">‹</span>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li class="page-item">
|
||||||
|
<button type="button" dusk="previousPage{{ $paginator->getPageName() == 'page' ? '' : '.' . $paginator->getPageName() }}" class="page-link" wire:click="previousPage('{{ $paginator->getPageName() }}')" wire:loading.attr="disabled" rel="prev" aria-label="@lang('pagination.previous')">‹</button>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Pagination Elements --}}
|
||||||
|
@foreach ($elements as $element)
|
||||||
|
{{-- "Three Dots" Separator --}}
|
||||||
|
@if (is_string($element))
|
||||||
|
<li class="page-item disabled" aria-disabled="true"><span class="page-link">{{ $element }}</span></li>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Array Of Links --}}
|
||||||
|
@if (is_array($element))
|
||||||
|
@foreach ($element as $page => $url)
|
||||||
|
@if ($page == $paginator->currentPage())
|
||||||
|
<li class="page-item active" wire:key="paginator-{{ $paginator->getPageName() }}-{{ $this->numberOfPaginatorsRendered[$paginator->getPageName()] }}-page-{{ $page }}" aria-current="page"><span class="page-link">{{ $page }}</span></li>
|
||||||
|
@else
|
||||||
|
<li class="page-item" wire:key="paginator-{{ $paginator->getPageName() }}-{{ $this->numberOfPaginatorsRendered[$paginator->getPageName()] }}-page-{{ $page }}"><button type="button" class="page-link" wire:click="gotoPage({{ $page }}, '{{ $paginator->getPageName() }}')">{{ $page }}</button></li>
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
{{-- Next Page Link --}}
|
||||||
|
@if ($paginator->hasMorePages())
|
||||||
|
<li class="page-item">
|
||||||
|
<button type="button" dusk="nextPage{{ $paginator->getPageName() == 'page' ? '' : '.' . $paginator->getPageName() }}" class="page-link" wire:click="nextPage('{{ $paginator->getPageName() }}')" wire:loading.attr="disabled" rel="next" aria-label="@lang('pagination.next')">›</button>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.next')">
|
||||||
|
<span class="page-link" aria-hidden="true">›</span>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
@endif
|
||||||
|
</div>
|
@ -0,0 +1,41 @@
|
|||||||
|
<div>
|
||||||
|
@if ($paginator->hasPages())
|
||||||
|
<nav>
|
||||||
|
<ul class="pagination">
|
||||||
|
{{-- Previous Page Link --}}
|
||||||
|
@if ($paginator->onFirstPage())
|
||||||
|
<li class="page-item disabled" aria-disabled="true">
|
||||||
|
<span class="page-link">@lang('pagination.previous')</span>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
@if(method_exists($paginator,'getCursorName'))
|
||||||
|
<li class="page-item">
|
||||||
|
<button dusk="previousPage" type="button" class="page-link" wire:click="setPage('{{$paginator->previousCursor()->encode()}}','{{ $paginator->getCursorName() }}')" wire:loading.attr="disabled" rel="prev">@lang('pagination.previous')</button>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li class="page-item">
|
||||||
|
<button type="button" dusk="previousPage{{ $paginator->getPageName() == 'page' ? '' : '.' . $paginator->getPageName() }}" class="page-link" wire:click="previousPage('{{ $paginator->getPageName() }}')" wire:loading.attr="disabled" rel="prev">@lang('pagination.previous')</button>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Next Page Link --}}
|
||||||
|
@if ($paginator->hasMorePages())
|
||||||
|
@if(method_exists($paginator,'getCursorName'))
|
||||||
|
<li class="page-item">
|
||||||
|
<button dusk="nextPage" type="button" class="page-link" wire:click="setPage('{{$paginator->nextCursor()->encode()}}','{{ $paginator->getCursorName() }}')" wire:loading.attr="disabled" rel="next">@lang('pagination.next')</button>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li class="page-item">
|
||||||
|
<button type="button" dusk="nextPage{{ $paginator->getPageName() == 'page' ? '' : '.' . $paginator->getPageName() }}" class="page-link" wire:click="nextPage('{{ $paginator->getPageName() }}')" wire:loading.attr="disabled" rel="next">@lang('pagination.next')</button>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
@else
|
||||||
|
<li class="page-item disabled" aria-disabled="true">
|
||||||
|
<span class="page-link">@lang('pagination.next')</span>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
@endif
|
||||||
|
</div>
|
@ -0,0 +1,43 @@
|
|||||||
|
<div>
|
||||||
|
@if ($paginator->hasPages())
|
||||||
|
<nav role="navigation" aria-label="Pagination Navigation" class="flex justify-between">
|
||||||
|
<span>
|
||||||
|
{{-- Previous Page Link --}}
|
||||||
|
@if ($paginator->onFirstPage())
|
||||||
|
<span class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5 rounded-md select-none">
|
||||||
|
{!! __('pagination.previous') !!}
|
||||||
|
</span>
|
||||||
|
@else
|
||||||
|
@if(method_exists($paginator,'getCursorName'))
|
||||||
|
<button type="button" dusk="previousPage" wire:click="setPage('{{$paginator->previousCursor()->encode()}}','{{ $paginator->getCursorName() }}')" wire:loading.attr="disabled" class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 rounded-md hover:text-gray-500 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150">
|
||||||
|
{!! __('pagination.previous') !!}
|
||||||
|
</button>
|
||||||
|
@else
|
||||||
|
<button type="button" wire:click="previousPage('{{ $paginator->getPageName() }}')" wire:loading.attr="disabled" dusk="previousPage{{ $paginator->getPageName() == 'page' ? '' : '.' . $paginator->getPageName() }}" class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 rounded-md hover:text-gray-500 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150">
|
||||||
|
{!! __('pagination.previous') !!}
|
||||||
|
</button>
|
||||||
|
@endif
|
||||||
|
@endif
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span>
|
||||||
|
{{-- Next Page Link --}}
|
||||||
|
@if ($paginator->hasMorePages())
|
||||||
|
@if(method_exists($paginator,'getCursorName'))
|
||||||
|
<button type="button" dusk="nextPage" wire:click="setPage('{{$paginator->nextCursor()->encode()}}','{{ $paginator->getCursorName() }}')" wire:loading.attr="disabled" class="relative inline-flex items-center px-4 py-2 ml-3 text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 rounded-md hover:text-gray-500 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150">
|
||||||
|
{!! __('pagination.next') !!}
|
||||||
|
</button>
|
||||||
|
@else
|
||||||
|
<button type="button" wire:click="nextPage('{{ $paginator->getPageName() }}')" wire:loading.attr="disabled" dusk="nextPage{{ $paginator->getPageName() == 'page' ? '' : '.' . $paginator->getPageName() }}" class="relative inline-flex items-center px-4 py-2 ml-3 text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 rounded-md hover:text-gray-500 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150">
|
||||||
|
{!! __('pagination.next') !!}
|
||||||
|
</button>
|
||||||
|
@endif
|
||||||
|
@else
|
||||||
|
<span class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5 rounded-md select-none">
|
||||||
|
{!! __('pagination.next') !!}
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</span>
|
||||||
|
</nav>
|
||||||
|
@endif
|
||||||
|
</div>
|
@ -0,0 +1,116 @@
|
|||||||
|
<div>
|
||||||
|
@if ($paginator->hasPages())
|
||||||
|
@php(isset($this->numberOfPaginatorsRendered[$paginator->getPageName()]) ? $this->numberOfPaginatorsRendered[$paginator->getPageName()]++ : $this->numberOfPaginatorsRendered[$paginator->getPageName()] = 1)
|
||||||
|
|
||||||
|
<nav role="navigation" aria-label="Pagination Navigation" class="flex items-center justify-between">
|
||||||
|
<div class="flex justify-between flex-1 sm:hidden">
|
||||||
|
<span>
|
||||||
|
@if ($paginator->onFirstPage())
|
||||||
|
<span class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5 rounded-md select-none">
|
||||||
|
{!! __('pagination.previous') !!}
|
||||||
|
</span>
|
||||||
|
@else
|
||||||
|
<button type="button" wire:click="previousPage('{{ $paginator->getPageName() }}')" wire:loading.attr="disabled" dusk="previousPage{{ $paginator->getPageName() == 'page' ? '' : '.' . $paginator->getPageName() }}.before" class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 rounded-md hover:text-gray-500 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150">
|
||||||
|
{!! __('pagination.previous') !!}
|
||||||
|
</button>
|
||||||
|
@endif
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span>
|
||||||
|
@if ($paginator->hasMorePages())
|
||||||
|
<button type="button" wire:click="nextPage('{{ $paginator->getPageName() }}')" wire:loading.attr="disabled" dusk="nextPage{{ $paginator->getPageName() == 'page' ? '' : '.' . $paginator->getPageName() }}.before" class="relative inline-flex items-center px-4 py-2 ml-3 text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 rounded-md hover:text-gray-500 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150">
|
||||||
|
{!! __('pagination.next') !!}
|
||||||
|
</button>
|
||||||
|
@else
|
||||||
|
<span class="relative inline-flex items-center px-4 py-2 ml-3 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5 rounded-md select-none">
|
||||||
|
{!! __('pagination.next') !!}
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between">
|
||||||
|
<div>
|
||||||
|
<p class="text-sm text-gray-700 leading-5">
|
||||||
|
<span>{!! __('Showing') !!}</span>
|
||||||
|
<span class="font-medium">{{ $paginator->firstItem() }}</span>
|
||||||
|
<span>{!! __('to') !!}</span>
|
||||||
|
<span class="font-medium">{{ $paginator->lastItem() }}</span>
|
||||||
|
<span>{!! __('of') !!}</span>
|
||||||
|
<span class="font-medium">{{ $paginator->total() }}</span>
|
||||||
|
<span>{!! __('results') !!}</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<span class="relative z-0 inline-flex rounded-md shadow-sm">
|
||||||
|
<span>
|
||||||
|
{{-- Previous Page Link --}}
|
||||||
|
@if ($paginator->onFirstPage())
|
||||||
|
<span aria-disabled="true" aria-label="{{ __('pagination.previous') }}">
|
||||||
|
<span class="relative inline-flex items-center px-2 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default rounded-l-md leading-5" aria-hidden="true">
|
||||||
|
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path fill-rule="evenodd" d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
@else
|
||||||
|
<button type="button" wire:click="previousPage('{{ $paginator->getPageName() }}')" dusk="previousPage{{ $paginator->getPageName() == 'page' ? '' : '.' . $paginator->getPageName() }}.after" rel="prev" class="relative inline-flex items-center px-2 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 rounded-l-md leading-5 hover:text-gray-400 focus:z-10 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-100 active:text-gray-500 transition ease-in-out duration-150" aria-label="{{ __('pagination.previous') }}">
|
||||||
|
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path fill-rule="evenodd" d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
@endif
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{{-- Pagination Elements --}}
|
||||||
|
@foreach ($elements as $element)
|
||||||
|
{{-- "Three Dots" Separator --}}
|
||||||
|
@if (is_string($element))
|
||||||
|
<span aria-disabled="true">
|
||||||
|
<span class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-gray-700 bg-white border border-gray-300 cursor-default leading-5 select-none">{{ $element }}</span>
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Array Of Links --}}
|
||||||
|
@if (is_array($element))
|
||||||
|
@foreach ($element as $page => $url)
|
||||||
|
<span wire:key="paginator-{{ $paginator->getPageName() }}-{{ $this->numberOfPaginatorsRendered[$paginator->getPageName()] }}-page{{ $page }}">
|
||||||
|
@if ($page == $paginator->currentPage())
|
||||||
|
<span aria-current="page">
|
||||||
|
<span class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5 select-none">{{ $page }}</span>
|
||||||
|
</span>
|
||||||
|
@else
|
||||||
|
<button type="button" wire:click="gotoPage({{ $page }}, '{{ $paginator->getPageName() }}')" class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 hover:text-gray-500 focus:z-10 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150" aria-label="{{ __('Go to page :page', ['page' => $page]) }}">
|
||||||
|
{{ $page }}
|
||||||
|
</button>
|
||||||
|
@endif
|
||||||
|
</span>
|
||||||
|
@endforeach
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
<span>
|
||||||
|
{{-- Next Page Link --}}
|
||||||
|
@if ($paginator->hasMorePages())
|
||||||
|
<button type="button" wire:click="nextPage('{{ $paginator->getPageName() }}')" dusk="nextPage{{ $paginator->getPageName() == 'page' ? '' : '.' . $paginator->getPageName() }}.after" rel="next" class="relative inline-flex items-center px-2 py-2 -ml-px text-sm font-medium text-gray-500 bg-white border border-gray-300 rounded-r-md leading-5 hover:text-gray-400 focus:z-10 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-100 active:text-gray-500 transition ease-in-out duration-150" aria-label="{{ __('pagination.next') }}">
|
||||||
|
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
@else
|
||||||
|
<span aria-disabled="true" aria-label="{{ __('pagination.next') }}">
|
||||||
|
<span class="relative inline-flex items-center px-2 py-2 -ml-px text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default rounded-r-md leading-5" aria-hidden="true">
|
||||||
|
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
@endif
|
||||||
|
</div>
|
@ -0,0 +1,24 @@
|
|||||||
|
@props([
|
||||||
|
'url',
|
||||||
|
'color' => 'primary',
|
||||||
|
'align' => 'center',
|
||||||
|
])
|
||||||
|
<table class="action" align="{{ $align }}" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||||
|
<tr>
|
||||||
|
<td align="{{ $align }}">
|
||||||
|
<table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation">
|
||||||
|
<tr>
|
||||||
|
<td align="{{ $align }}">
|
||||||
|
<table border="0" cellpadding="0" cellspacing="0" role="presentation">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a href="{{ $url }}" class="button button-{{ $color }}" target="_blank" rel="noopener">{{ $slot }}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
@ -0,0 +1,11 @@
|
|||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<table class="footer" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation">
|
||||||
|
<tr>
|
||||||
|
<td class="content-cell" align="center">
|
||||||
|
{{ Illuminate\Mail\Markdown::parse($slot) }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
@ -0,0 +1,12 @@
|
|||||||
|
@props(['url'])
|
||||||
|
<tr>
|
||||||
|
<td class="header">
|
||||||
|
<a href="{{ $url }}" style="display: inline-block;">
|
||||||
|
@if (trim($slot) === 'Laravel')
|
||||||
|
<img src="https://laravel.com/img/notification-logo.png" class="logo" alt="Laravel Logo">
|
||||||
|
@else
|
||||||
|
{{ $slot }}
|
||||||
|
@endif
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
@ -0,0 +1,57 @@
|
|||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<title>{{ config('app.name') }}</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
<meta name="color-scheme" content="light">
|
||||||
|
<meta name="supported-color-schemes" content="light">
|
||||||
|
<style>
|
||||||
|
@media only screen and (max-width: 600px) {
|
||||||
|
.inner-body {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 500px) {
|
||||||
|
.button {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<table class="wrapper" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||||
|
<tr>
|
||||||
|
<td align="center">
|
||||||
|
<table class="content" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||||
|
{{ $header ?? '' }}
|
||||||
|
|
||||||
|
<!-- Email Body -->
|
||||||
|
<tr>
|
||||||
|
<td class="body" width="100%" cellpadding="0" cellspacing="0" style="border: hidden !important;">
|
||||||
|
<table class="inner-body" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation">
|
||||||
|
<!-- Body content -->
|
||||||
|
<tr>
|
||||||
|
<td class="content-cell">
|
||||||
|
{{ Illuminate\Mail\Markdown::parse($slot) }}
|
||||||
|
|
||||||
|
{{ $subcopy ?? '' }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{{ $footer ?? '' }}
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,27 @@
|
|||||||
|
<x-mail::layout>
|
||||||
|
{{-- Header --}}
|
||||||
|
<x-slot:header>
|
||||||
|
<x-mail::header :url="config('app.url')">
|
||||||
|
{{ config('app.name') }}
|
||||||
|
</x-mail::header>
|
||||||
|
</x-slot:header>
|
||||||
|
|
||||||
|
{{-- Body --}}
|
||||||
|
{{ $slot }}
|
||||||
|
|
||||||
|
{{-- Subcopy --}}
|
||||||
|
@isset($subcopy)
|
||||||
|
<x-slot:subcopy>
|
||||||
|
<x-mail::subcopy>
|
||||||
|
{{ $subcopy }}
|
||||||
|
</x-mail::subcopy>
|
||||||
|
</x-slot:subcopy>
|
||||||
|
@endisset
|
||||||
|
|
||||||
|
{{-- Footer --}}
|
||||||
|
<x-slot:footer>
|
||||||
|
<x-mail::footer>
|
||||||
|
© {{ date('Y') }} {{ config('app.name') }}. @lang('All rights reserved.')
|
||||||
|
</x-mail::footer>
|
||||||
|
</x-slot:footer>
|
||||||
|
</x-mail::layout>
|
@ -0,0 +1,14 @@
|
|||||||
|
<table class="panel" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||||
|
<tr>
|
||||||
|
<td class="panel-content">
|
||||||
|
<table width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||||
|
<tr>
|
||||||
|
<td class="panel-item">
|
||||||
|
{{ Illuminate\Mail\Markdown::parse($slot) }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
@ -0,0 +1,7 @@
|
|||||||
|
<table class="subcopy" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{{ Illuminate\Mail\Markdown::parse($slot) }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
@ -0,0 +1,3 @@
|
|||||||
|
<div class="table">
|
||||||
|
{{ Illuminate\Mail\Markdown::parse($slot) }}
|
||||||
|
</div>
|
@ -0,0 +1,290 @@
|
|||||||
|
/* Base */
|
||||||
|
|
||||||
|
body,
|
||||||
|
body *:not(html):not(style):not(br):not(tr):not(code) {
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif,
|
||||||
|
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
-webkit-text-size-adjust: none;
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: #718096;
|
||||||
|
height: 100%;
|
||||||
|
line-height: 1.4;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
p,
|
||||||
|
ul,
|
||||||
|
ol,
|
||||||
|
blockquote {
|
||||||
|
line-height: 1.4;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #3869d4;
|
||||||
|
}
|
||||||
|
|
||||||
|
a img {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Typography */
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: #3d4852;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 0;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 0;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 0;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.5em;
|
||||||
|
margin-top: 0;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.sub {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Layout */
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
-premailer-cellpadding: 0;
|
||||||
|
-premailer-cellspacing: 0;
|
||||||
|
-premailer-width: 100%;
|
||||||
|
background-color: #edf2f7;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
-premailer-cellpadding: 0;
|
||||||
|
-premailer-cellspacing: 0;
|
||||||
|
-premailer-width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header */
|
||||||
|
|
||||||
|
.header {
|
||||||
|
padding: 25px 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header a {
|
||||||
|
color: #3d4852;
|
||||||
|
font-size: 19px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Logo */
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
height: 75px;
|
||||||
|
max-height: 75px;
|
||||||
|
width: 75px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Body */
|
||||||
|
|
||||||
|
.body {
|
||||||
|
-premailer-cellpadding: 0;
|
||||||
|
-premailer-cellspacing: 0;
|
||||||
|
-premailer-width: 100%;
|
||||||
|
background-color: #edf2f7;
|
||||||
|
border-bottom: 1px solid #edf2f7;
|
||||||
|
border-top: 1px solid #edf2f7;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inner-body {
|
||||||
|
-premailer-cellpadding: 0;
|
||||||
|
-premailer-cellspacing: 0;
|
||||||
|
-premailer-width: 570px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-color: #e8e5ef;
|
||||||
|
border-radius: 2px;
|
||||||
|
border-width: 1px;
|
||||||
|
box-shadow: 0 2px 0 rgba(0, 0, 150, 0.025), 2px 4px 0 rgba(0, 0, 150, 0.015);
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0;
|
||||||
|
width: 570px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Subcopy */
|
||||||
|
|
||||||
|
.subcopy {
|
||||||
|
border-top: 1px solid #e8e5ef;
|
||||||
|
margin-top: 25px;
|
||||||
|
padding-top: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subcopy p {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footer */
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
-premailer-cellpadding: 0;
|
||||||
|
-premailer-cellspacing: 0;
|
||||||
|
-premailer-width: 570px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0;
|
||||||
|
text-align: center;
|
||||||
|
width: 570px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer p {
|
||||||
|
color: #b0adc5;
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer a {
|
||||||
|
color: #b0adc5;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tables */
|
||||||
|
|
||||||
|
.table table {
|
||||||
|
-premailer-cellpadding: 0;
|
||||||
|
-premailer-cellspacing: 0;
|
||||||
|
-premailer-width: 100%;
|
||||||
|
margin: 30px auto;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table th {
|
||||||
|
border-bottom: 1px solid #edeff2;
|
||||||
|
margin: 0;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table td {
|
||||||
|
color: #74787e;
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 18px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-cell {
|
||||||
|
max-width: 100vw;
|
||||||
|
padding: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buttons */
|
||||||
|
|
||||||
|
.action {
|
||||||
|
-premailer-cellpadding: 0;
|
||||||
|
-premailer-cellspacing: 0;
|
||||||
|
-premailer-width: 100%;
|
||||||
|
margin: 30px auto;
|
||||||
|
padding: 0;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
-webkit-text-size-adjust: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #fff;
|
||||||
|
display: inline-block;
|
||||||
|
overflow: hidden;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-blue,
|
||||||
|
.button-primary {
|
||||||
|
background-color: #2d3748;
|
||||||
|
border-bottom: 8px solid #2d3748;
|
||||||
|
border-left: 18px solid #2d3748;
|
||||||
|
border-right: 18px solid #2d3748;
|
||||||
|
border-top: 8px solid #2d3748;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-green,
|
||||||
|
.button-success {
|
||||||
|
background-color: #48bb78;
|
||||||
|
border-bottom: 8px solid #48bb78;
|
||||||
|
border-left: 18px solid #48bb78;
|
||||||
|
border-right: 18px solid #48bb78;
|
||||||
|
border-top: 8px solid #48bb78;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-red,
|
||||||
|
.button-error {
|
||||||
|
background-color: #e53e3e;
|
||||||
|
border-bottom: 8px solid #e53e3e;
|
||||||
|
border-left: 18px solid #e53e3e;
|
||||||
|
border-right: 18px solid #e53e3e;
|
||||||
|
border-top: 8px solid #e53e3e;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Panels */
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
border-left: #2d3748 solid 4px;
|
||||||
|
margin: 21px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-content {
|
||||||
|
background-color: #edf2f7;
|
||||||
|
color: #718096;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-content p {
|
||||||
|
color: #718096;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-item {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-item p:last-of-type {
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Utilities */
|
||||||
|
|
||||||
|
.break-all {
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
{{ $slot }}: {{ $url }}
|
@ -0,0 +1 @@
|
|||||||
|
{{ $slot }}
|
@ -0,0 +1 @@
|
|||||||
|
[{{ $slot }}]({{ $url }})
|
@ -0,0 +1,9 @@
|
|||||||
|
{!! strip_tags($header) !!}
|
||||||
|
|
||||||
|
{!! strip_tags($slot) !!}
|
||||||
|
@isset($subcopy)
|
||||||
|
|
||||||
|
{!! strip_tags($subcopy) !!}
|
||||||
|
@endisset
|
||||||
|
|
||||||
|
{!! strip_tags($footer) !!}
|
@ -0,0 +1,27 @@
|
|||||||
|
<x-mail::layout>
|
||||||
|
{{-- Header --}}
|
||||||
|
<x-slot:header>
|
||||||
|
<x-mail::header :url="config('app.url')">
|
||||||
|
{{ config('app.name') }}
|
||||||
|
</x-mail::header>
|
||||||
|
</x-slot:header>
|
||||||
|
|
||||||
|
{{-- Body --}}
|
||||||
|
{{ $slot }}
|
||||||
|
|
||||||
|
{{-- Subcopy --}}
|
||||||
|
@isset($subcopy)
|
||||||
|
<x-slot:subcopy>
|
||||||
|
<x-mail::subcopy>
|
||||||
|
{{ $subcopy }}
|
||||||
|
</x-mail::subcopy>
|
||||||
|
</x-slot:subcopy>
|
||||||
|
@endisset
|
||||||
|
|
||||||
|
{{-- Footer --}}
|
||||||
|
<x-slot:footer>
|
||||||
|
<x-mail::footer>
|
||||||
|
© {{ date('Y') }} {{ config('app.name') }}. @lang('All rights reserved.')
|
||||||
|
</x-mail::footer>
|
||||||
|
</x-slot:footer>
|
||||||
|
</x-mail::layout>
|
@ -0,0 +1 @@
|
|||||||
|
{{ $slot }}
|
@ -0,0 +1 @@
|
|||||||
|
{{ $slot }}
|
@ -0,0 +1 @@
|
|||||||
|
{{ $slot }}
|
@ -0,0 +1,58 @@
|
|||||||
|
<x-mail::message>
|
||||||
|
{{-- Greeting --}}
|
||||||
|
@if (! empty($greeting))
|
||||||
|
# {{ $greeting }}
|
||||||
|
@else
|
||||||
|
@if ($level === 'error')
|
||||||
|
# @lang('Whoops!')
|
||||||
|
@else
|
||||||
|
# @lang('Hello!')
|
||||||
|
@endif
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Intro Lines --}}
|
||||||
|
@foreach ($introLines as $line)
|
||||||
|
{{ $line }}
|
||||||
|
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
{{-- Action Button --}}
|
||||||
|
@isset($actionText)
|
||||||
|
<?php
|
||||||
|
$color = match ($level) {
|
||||||
|
'success', 'error' => $level,
|
||||||
|
default => 'primary',
|
||||||
|
};
|
||||||
|
?>
|
||||||
|
<x-mail::button :url="$actionUrl" :color="$color">
|
||||||
|
{{ $actionText }}
|
||||||
|
</x-mail::button>
|
||||||
|
@endisset
|
||||||
|
|
||||||
|
{{-- Outro Lines --}}
|
||||||
|
@foreach ($outroLines as $line)
|
||||||
|
{{ $line }}
|
||||||
|
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
{{-- Salutation --}}
|
||||||
|
@if (! empty($salutation))
|
||||||
|
{{ $salutation }}
|
||||||
|
@else
|
||||||
|
@lang('Regards'),<br>
|
||||||
|
{{ config('app.name') }}
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Subcopy --}}
|
||||||
|
@isset($actionText)
|
||||||
|
<x-slot:subcopy>
|
||||||
|
@lang(
|
||||||
|
"If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\n".
|
||||||
|
'into your web browser:',
|
||||||
|
[
|
||||||
|
'actionText' => $actionText,
|
||||||
|
]
|
||||||
|
) <span class="break-all">[{{ $displayableActionUrl }}]({{ $actionUrl }})</span>
|
||||||
|
</x-slot:subcopy>
|
||||||
|
@endisset
|
||||||
|
</x-mail::message>
|
@ -0,0 +1,46 @@
|
|||||||
|
@if ($paginator->hasPages())
|
||||||
|
<nav>
|
||||||
|
<ul class="pagination">
|
||||||
|
{{-- Previous Page Link --}}
|
||||||
|
@if ($paginator->onFirstPage())
|
||||||
|
<li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.previous')">
|
||||||
|
<span class="page-link" aria-hidden="true">‹</span>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li class="page-item">
|
||||||
|
<a class="page-link" href="{{ $paginator->previousPageUrl() }}" rel="prev" aria-label="@lang('pagination.previous')">‹</a>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Pagination Elements --}}
|
||||||
|
@foreach ($elements as $element)
|
||||||
|
{{-- "Three Dots" Separator --}}
|
||||||
|
@if (is_string($element))
|
||||||
|
<li class="page-item disabled" aria-disabled="true"><span class="page-link">{{ $element }}</span></li>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Array Of Links --}}
|
||||||
|
@if (is_array($element))
|
||||||
|
@foreach ($element as $page => $url)
|
||||||
|
@if ($page == $paginator->currentPage())
|
||||||
|
<li class="page-item active" aria-current="page"><span class="page-link">{{ $page }}</span></li>
|
||||||
|
@else
|
||||||
|
<li class="page-item"><a class="page-link" href="{{ $url }}">{{ $page }}</a></li>
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
{{-- Next Page Link --}}
|
||||||
|
@if ($paginator->hasMorePages())
|
||||||
|
<li class="page-item">
|
||||||
|
<a class="page-link" href="{{ $paginator->nextPageUrl() }}" rel="next" aria-label="@lang('pagination.next')">›</a>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.next')">
|
||||||
|
<span class="page-link" aria-hidden="true">›</span>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
@endif
|
@ -0,0 +1,88 @@
|
|||||||
|
@if ($paginator->hasPages())
|
||||||
|
<nav class="d-flex justify-items-center justify-content-between">
|
||||||
|
<div class="d-flex justify-content-between flex-fill d-sm-none">
|
||||||
|
<ul class="pagination">
|
||||||
|
{{-- Previous Page Link --}}
|
||||||
|
@if ($paginator->onFirstPage())
|
||||||
|
<li class="page-item disabled" aria-disabled="true">
|
||||||
|
<span class="page-link">@lang('pagination.previous')</span>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li class="page-item">
|
||||||
|
<a class="page-link" href="{{ $paginator->previousPageUrl() }}" rel="prev">@lang('pagination.previous')</a>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Next Page Link --}}
|
||||||
|
@if ($paginator->hasMorePages())
|
||||||
|
<li class="page-item">
|
||||||
|
<a class="page-link" href="{{ $paginator->nextPageUrl() }}" rel="next">@lang('pagination.next')</a>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li class="page-item disabled" aria-disabled="true">
|
||||||
|
<span class="page-link">@lang('pagination.next')</span>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-none flex-sm-fill d-sm-flex align-items-sm-center justify-content-sm-between">
|
||||||
|
<div>
|
||||||
|
<p class="small text-muted">
|
||||||
|
{!! __('Showing') !!}
|
||||||
|
<span class="fw-semibold">{{ $paginator->firstItem() }}</span>
|
||||||
|
{!! __('to') !!}
|
||||||
|
<span class="fw-semibold">{{ $paginator->lastItem() }}</span>
|
||||||
|
{!! __('of') !!}
|
||||||
|
<span class="fw-semibold">{{ $paginator->total() }}</span>
|
||||||
|
{!! __('results') !!}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<ul class="pagination">
|
||||||
|
{{-- Previous Page Link --}}
|
||||||
|
@if ($paginator->onFirstPage())
|
||||||
|
<li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.previous')">
|
||||||
|
<span class="page-link" aria-hidden="true">‹</span>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li class="page-item">
|
||||||
|
<a class="page-link" href="{{ $paginator->previousPageUrl() }}" rel="prev" aria-label="@lang('pagination.previous')">‹</a>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Pagination Elements --}}
|
||||||
|
@foreach ($elements as $element)
|
||||||
|
{{-- "Three Dots" Separator --}}
|
||||||
|
@if (is_string($element))
|
||||||
|
<li class="page-item disabled" aria-disabled="true"><span class="page-link">{{ $element }}</span></li>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Array Of Links --}}
|
||||||
|
@if (is_array($element))
|
||||||
|
@foreach ($element as $page => $url)
|
||||||
|
@if ($page == $paginator->currentPage())
|
||||||
|
<li class="page-item active" aria-current="page"><span class="page-link">{{ $page }}</span></li>
|
||||||
|
@else
|
||||||
|
<li class="page-item"><a class="page-link" href="{{ $url }}">{{ $page }}</a></li>
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
{{-- Next Page Link --}}
|
||||||
|
@if ($paginator->hasMorePages())
|
||||||
|
<li class="page-item">
|
||||||
|
<a class="page-link" href="{{ $paginator->nextPageUrl() }}" rel="next" aria-label="@lang('pagination.next')">›</a>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.next')">
|
||||||
|
<span class="page-link" aria-hidden="true">›</span>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
@endif
|
@ -0,0 +1,46 @@
|
|||||||
|
@if ($paginator->hasPages())
|
||||||
|
<nav>
|
||||||
|
<ul class="pagination">
|
||||||
|
{{-- Previous Page Link --}}
|
||||||
|
@if ($paginator->onFirstPage())
|
||||||
|
<li class="disabled" aria-disabled="true" aria-label="@lang('pagination.previous')">
|
||||||
|
<span aria-hidden="true">‹</span>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li>
|
||||||
|
<a href="{{ $paginator->previousPageUrl() }}" rel="prev" aria-label="@lang('pagination.previous')">‹</a>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Pagination Elements --}}
|
||||||
|
@foreach ($elements as $element)
|
||||||
|
{{-- "Three Dots" Separator --}}
|
||||||
|
@if (is_string($element))
|
||||||
|
<li class="disabled" aria-disabled="true"><span>{{ $element }}</span></li>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Array Of Links --}}
|
||||||
|
@if (is_array($element))
|
||||||
|
@foreach ($element as $page => $url)
|
||||||
|
@if ($page == $paginator->currentPage())
|
||||||
|
<li class="active" aria-current="page"><span>{{ $page }}</span></li>
|
||||||
|
@else
|
||||||
|
<li><a href="{{ $url }}">{{ $page }}</a></li>
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
{{-- Next Page Link --}}
|
||||||
|
@if ($paginator->hasMorePages())
|
||||||
|
<li>
|
||||||
|
<a href="{{ $paginator->nextPageUrl() }}" rel="next" aria-label="@lang('pagination.next')">›</a>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li class="disabled" aria-disabled="true" aria-label="@lang('pagination.next')">
|
||||||
|
<span aria-hidden="true">›</span>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
@endif
|
@ -0,0 +1,36 @@
|
|||||||
|
@if ($paginator->hasPages())
|
||||||
|
<div class="ui pagination menu" role="navigation">
|
||||||
|
{{-- Previous Page Link --}}
|
||||||
|
@if ($paginator->onFirstPage())
|
||||||
|
<a class="icon item disabled" aria-disabled="true" aria-label="@lang('pagination.previous')"> <i class="left chevron icon"></i> </a>
|
||||||
|
@else
|
||||||
|
<a class="icon item" href="{{ $paginator->previousPageUrl() }}" rel="prev" aria-label="@lang('pagination.previous')"> <i class="left chevron icon"></i> </a>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Pagination Elements --}}
|
||||||
|
@foreach ($elements as $element)
|
||||||
|
{{-- "Three Dots" Separator --}}
|
||||||
|
@if (is_string($element))
|
||||||
|
<a class="icon item disabled" aria-disabled="true">{{ $element }}</a>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Array Of Links --}}
|
||||||
|
@if (is_array($element))
|
||||||
|
@foreach ($element as $page => $url)
|
||||||
|
@if ($page == $paginator->currentPage())
|
||||||
|
<a class="item active" href="{{ $url }}" aria-current="page">{{ $page }}</a>
|
||||||
|
@else
|
||||||
|
<a class="item" href="{{ $url }}">{{ $page }}</a>
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
{{-- Next Page Link --}}
|
||||||
|
@if ($paginator->hasMorePages())
|
||||||
|
<a class="icon item" href="{{ $paginator->nextPageUrl() }}" rel="next" aria-label="@lang('pagination.next')"> <i class="right chevron icon"></i> </a>
|
||||||
|
@else
|
||||||
|
<a class="icon item disabled" aria-disabled="true" aria-label="@lang('pagination.next')"> <i class="right chevron icon"></i> </a>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
@endif
|
@ -0,0 +1,27 @@
|
|||||||
|
@if ($paginator->hasPages())
|
||||||
|
<nav>
|
||||||
|
<ul class="pagination">
|
||||||
|
{{-- Previous Page Link --}}
|
||||||
|
@if ($paginator->onFirstPage())
|
||||||
|
<li class="page-item disabled" aria-disabled="true">
|
||||||
|
<span class="page-link">@lang('pagination.previous')</span>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li class="page-item">
|
||||||
|
<a class="page-link" href="{{ $paginator->previousPageUrl() }}" rel="prev">@lang('pagination.previous')</a>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Next Page Link --}}
|
||||||
|
@if ($paginator->hasMorePages())
|
||||||
|
<li class="page-item">
|
||||||
|
<a class="page-link" href="{{ $paginator->nextPageUrl() }}" rel="next">@lang('pagination.next')</a>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li class="page-item disabled" aria-disabled="true">
|
||||||
|
<span class="page-link">@lang('pagination.next')</span>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
@endif
|
@ -0,0 +1,29 @@
|
|||||||
|
@if ($paginator->hasPages())
|
||||||
|
<nav role="navigation" aria-label="Pagination Navigation">
|
||||||
|
<ul class="pagination">
|
||||||
|
{{-- Previous Page Link --}}
|
||||||
|
@if ($paginator->onFirstPage())
|
||||||
|
<li class="page-item disabled" aria-disabled="true">
|
||||||
|
<span class="page-link">{!! __('pagination.previous') !!}</span>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li class="page-item">
|
||||||
|
<a class="page-link" href="{{ $paginator->previousPageUrl() }}" rel="prev">
|
||||||
|
{!! __('pagination.previous') !!}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Next Page Link --}}
|
||||||
|
@if ($paginator->hasMorePages())
|
||||||
|
<li class="page-item">
|
||||||
|
<a class="page-link" href="{{ $paginator->nextPageUrl() }}" rel="next">{!! __('pagination.next') !!}</a>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li class="page-item disabled" aria-disabled="true">
|
||||||
|
<span class="page-link">{!! __('pagination.next') !!}</span>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
@endif
|
@ -0,0 +1,19 @@
|
|||||||
|
@if ($paginator->hasPages())
|
||||||
|
<nav>
|
||||||
|
<ul class="pagination">
|
||||||
|
{{-- Previous Page Link --}}
|
||||||
|
@if ($paginator->onFirstPage())
|
||||||
|
<li class="disabled" aria-disabled="true"><span>@lang('pagination.previous')</span></li>
|
||||||
|
@else
|
||||||
|
<li><a href="{{ $paginator->previousPageUrl() }}" rel="prev">@lang('pagination.previous')</a></li>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Next Page Link --}}
|
||||||
|
@if ($paginator->hasMorePages())
|
||||||
|
<li><a href="{{ $paginator->nextPageUrl() }}" rel="next">@lang('pagination.next')</a></li>
|
||||||
|
@else
|
||||||
|
<li class="disabled" aria-disabled="true"><span>@lang('pagination.next')</span></li>
|
||||||
|
@endif
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
@endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue