├── _config.yml ├── resources └── views │ ├── text │ ├── alert │ │ └── box.blade.php │ └── invoice │ │ ├── attributes.blade.php │ │ └── table.blade.php │ ├── templates │ ├── alert.blade.php │ ├── action.blade.php │ └── invoice.blade.php │ └── html │ ├── alert │ └── box.blade.php │ ├── invoice │ ├── attributes.blade.php │ └── table.blade.php │ └── themes │ └── tuxedo.css ├── .gitignore ├── .travis.yml ├── src ├── Mailables │ ├── ActionMailable.php │ ├── AlertMailable.php │ └── InvoiceMailable.php ├── Traits │ ├── HasGreeting.php │ ├── HasLine.php │ └── HasAction.php ├── TuxedoMessage.php └── TuxedoServiceProvider.php ├── phpunit.xml.dist ├── composer.json ├── LICENSE ├── tests └── Unit │ ├── ActionMailableTest.php │ ├── AlertMailableTest.php │ └── InvoiceMailableTest.php └── readme.md /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /resources/views/text/alert/box.blade.php: -------------------------------------------------------------------------------- 1 | foo -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.lock 3 | .phpunit.result.cache -------------------------------------------------------------------------------- /resources/views/text/invoice/attributes.blade.php: -------------------------------------------------------------------------------- 1 | {{ $slot }} -------------------------------------------------------------------------------- /resources/views/text/invoice/table.blade.php: -------------------------------------------------------------------------------- 1 | {{ Illuminate\Mail\Markdown::parse($slot) }} -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.0 5 | - 7.1 6 | 7 | sudo: false 8 | 9 | cache: 10 | directories: 11 | - $HOME/.composer/cache 12 | 13 | before_install: 14 | - travis_retry composer self-update 15 | - travis_retry composer update --no-interaction --prefer-dist 16 | 17 | script: vendor/bin/phpunit 18 | -------------------------------------------------------------------------------- /resources/views/templates/alert.blade.php: -------------------------------------------------------------------------------- 1 | @component('mail::message') 2 | 3 | @component('mail::alert.box', ['type' => $type]) 4 | {{ $text }} 5 | @endcomponent 6 | 7 | # {{ $greeting }} 8 | 9 | @foreach($outroLines as $line) 10 | {{ $line }} 11 | @endforeach 12 | 13 | Regards,
14 | {{ config('app.name') }} 15 | 16 | @endcomponent -------------------------------------------------------------------------------- /resources/views/html/alert/box.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 |
4 | 5 | 6 | 7 | 8 |
{{ Illuminate\Mail\Markdown::parse($slot) }}
9 |
12 | -------------------------------------------------------------------------------- /src/Mailables/ActionMailable.php: -------------------------------------------------------------------------------- 1 | greeting = $greeting; 24 | 25 | return $this; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /resources/views/templates/action.blade.php: -------------------------------------------------------------------------------- 1 | @component('mail::message') 2 | 3 | # {{ $greeting }} 4 | 5 | @foreach($introLines as $line) 6 | {{ $line }} 7 | @endforeach 8 | 9 | @component('mail::button', ['url' => $actionUrl, 'color' => $color]) 10 | {{ $actionText }} 11 | @endcomponent 12 | 13 | @foreach($outroLines as $line) 14 | {{ $line }} 15 | @endforeach 16 | 17 | @component('mail::subcopy') 18 | If you’re having trouble with the button above, copy and paste the URL below into your web browser. 19 | 20 | {{ $actionUrl }} 21 | @endcomponent 22 | 23 | @endcomponent -------------------------------------------------------------------------------- /resources/views/html/invoice/attributes.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | 14 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
Amount Due: ${{ $total }}
Due By: {{ $dueDate }}
12 |
-------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | src/ 15 | 16 | 17 | 18 | 19 | ./tests/Unit 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tomirons/tuxedo", 3 | "description": "Simple transactional email classes/templates for Laravel 5.4 mailables", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Tom Irons", 8 | "email": "tom.irons@hotmail.com" 9 | } 10 | ], 11 | "require": { 12 | "php": "^7.3", 13 | "illuminate/database": "^6.0|^7.0|^8.0", 14 | "illuminate/mail": "^6.0|^7.0|^8.0", 15 | "illuminate/support": "^6.0|^7.0|^8.0" 16 | }, 17 | "require-dev": { 18 | "mockery/mockery": "^1.4", 19 | "phpunit/phpunit": "^9.2" 20 | }, 21 | "autoload": { 22 | "psr-4": { 23 | "TomIrons\\Tuxedo\\": "src/" 24 | } 25 | }, 26 | "extra": { 27 | "branch-alias": { 28 | "dev-master": "2.x-dev" 29 | }, 30 | "laravel": { 31 | "providers": [ 32 | "TomIrons\\Tuxedo\\TuxedoServiceProvider" 33 | ] 34 | } 35 | }, 36 | "minimum-stability": "dev", 37 | "prefer-stable": true 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Tom Irons 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/TuxedoMessage.php: -------------------------------------------------------------------------------- 1 | greeting = $greeting; 40 | 41 | return $this; 42 | } 43 | 44 | /** 45 | * Set the salutation of the message. 46 | * 47 | * @param string $salutation 48 | * 49 | * @return $this 50 | */ 51 | public function salutation($salutation) 52 | { 53 | $this->salutation = $salutation; 54 | 55 | return $this; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/TuxedoServiceProvider.php: -------------------------------------------------------------------------------- 1 | loadViewsFrom(__DIR__.'/../resources/views', 'tuxedo'); 17 | 18 | $this->alterConfiguration(); 19 | } 20 | 21 | /** 22 | * Register the service provider. 23 | * 24 | * @return void 25 | */ 26 | public function register() 27 | { 28 | if ($this->app->runningInConsole()) { 29 | $this->publishes([ 30 | __DIR__.'/../resources/views' => $this->app->resourcePath('views/vendor/tuxedo'), 31 | ], 'tuxedo-mail'); 32 | } 33 | } 34 | 35 | /** 36 | * Modify the markdown configuration. 37 | * 38 | * @return void 39 | */ 40 | public function alterConfiguration() 41 | { 42 | config()->set('mail.markdown.paths', array_merge([__DIR__.'/../resources/views'], config('mail.markdown.paths'))); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Traits/HasLine.php: -------------------------------------------------------------------------------- 1 | actionText) { 31 | $this->introLines[] = $this->formatLine($line); 32 | } else { 33 | $this->outroLines[] = $this->formatLine($line); 34 | } 35 | 36 | return $this; 37 | } 38 | 39 | /** 40 | * Format the given line of text. 41 | * 42 | * @param string|array $line 43 | * 44 | * @return $this 45 | */ 46 | protected function formatLine($line) 47 | { 48 | if (is_array($line)) { 49 | return implode(' ', array_map('trim', $line)); 50 | } 51 | 52 | return trim(implode(' ', array_map('trim', preg_split('/\\r\\n|\\r|\\n/', $line)))); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /resources/views/templates/invoice.blade.php: -------------------------------------------------------------------------------- 1 | @component('mail::message') 2 | 3 | # {{ $greeting }} 4 | 5 | @foreach($introLines as $line) 6 | {{ $line }} 7 | 8 | @endforeach 9 | 10 | @component('mail::invoice.attributes', ['total' => $tableData['total'], 'dueDate' => $dueDate]) 11 | # Amount Due: {{ $tableData['total'] }}
12 | # Due By: {{ $dueDate }} 13 | @endcomponent 14 | 15 | @component('mail::button', ['url' => $actionUrl, 'color' => $color]) 16 | {{ $actionText }} 17 | @endcomponent 18 | 19 | @component('mail::invoice.table', ['data' => $tableData]) 20 | | Description | Amount | 21 | | ----------- | ------ | 22 | @foreach($tableData['items'] as $item) 23 | | {{ $item[$tableData['keys']['name']] }} | {{ $item[$tableData['keys']['price']] }} | 24 | @endforeach 25 | @if ($tableData['shipping'] > 0) 26 | | Shipping | {{ $tableData['shipping'] }} | 27 | @endif 28 | @if ($tableData['tax'] > 0) 29 | | Tax | {{ $tableData['tax'] }} | 30 | @endif 31 | | Total | {{ $tableData['total'] }} | 32 | @endcomponent 33 | 34 | @foreach($outroLines as $line) 35 | {{ $line }} 36 | 37 | @endforeach 38 | 39 | @if($salutation) 40 | {{ $salutation }} 41 | @else 42 | Regards,
{{ config('app.name') }} 43 | @endif 44 | 45 | @component('mail::subcopy') 46 | If you’re having trouble with the button above, copy and paste the URL below into your web browser. 47 | 48 | {{ $actionUrl }} 49 | @endcomponent 50 | 51 | @endcomponent -------------------------------------------------------------------------------- /tests/Unit/ActionMailableTest.php: -------------------------------------------------------------------------------- 1 | mailable = new ActionMailable(); 17 | } 18 | 19 | public function testInfoMethod() 20 | { 21 | $this->assertInstanceOf(ActionMailable::class, $this->mailable->info()); 22 | } 23 | 24 | public function testSuccessMethod() 25 | { 26 | $this->assertInstanceOf(ActionMailable::class, $this->mailable->success()); 27 | } 28 | 29 | public function testErrorMethod() 30 | { 31 | $this->assertInstanceOf(ActionMailable::class, $this->mailable->error()); 32 | } 33 | 34 | public function testColorMethod() 35 | { 36 | $this->assertInstanceOf(ActionMailable::class, $this->mailable->color('blue')); 37 | } 38 | 39 | public function testActionMethod() 40 | { 41 | $this->assertInstanceOf(ActionMailable::class, $this->mailable->action('Click Me', 'http://example.com')); 42 | } 43 | 44 | public function testLineMethod() 45 | { 46 | $this->assertInstanceOf(ActionMailable::class, $this->mailable->line('Some line of text to tell you what exactly is going on.')); 47 | } 48 | 49 | public function testGrettingMethod() 50 | { 51 | $this->assertInstanceOf(ActionMailable::class, $this->mailable->greeting('Hello!')); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tests/Unit/AlertMailableTest.php: -------------------------------------------------------------------------------- 1 | mailable = new AlertMailable(); 17 | } 18 | 19 | public function testInfoMethod() 20 | { 21 | $this->assertInstanceOf(AlertMailable::class, $this->mailable->info()); 22 | } 23 | 24 | public function testWarningMethod() 25 | { 26 | $this->assertInstanceOf(AlertMailable::class, $this->mailable->warning()); 27 | } 28 | 29 | public function testSuccessMethod() 30 | { 31 | $this->assertInstanceOf(AlertMailable::class, $this->mailable->success()); 32 | } 33 | 34 | public function testErrorMethod() 35 | { 36 | $this->assertInstanceOf(AlertMailable::class, $this->mailable->error()); 37 | } 38 | 39 | public function testTypeMethod() 40 | { 41 | $this->assertInstanceOf(AlertMailable::class, $this->mailable->type('info')); 42 | } 43 | 44 | public function testMessageMethod() 45 | { 46 | $this->assertInstanceOf(AlertMailable::class, $this->mailable->message('Something has gone wrong, please contact support.')); 47 | } 48 | 49 | public function testGrettingMethod() 50 | { 51 | $this->assertInstanceOf(AlertMailable::class, $this->mailable->greeting('Hello!')); 52 | } 53 | 54 | public function testLineMethod() 55 | { 56 | $this->assertInstanceOf(AlertMailable::class, $this->mailable->line('Some line of text to tell you what exactly is going on.')); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /tests/Unit/InvoiceMailableTest.php: -------------------------------------------------------------------------------- 1 | mailable = new InvoiceMailable(); 17 | } 18 | 19 | public function testIdMethod() 20 | { 21 | $this->assertInstanceOf(InvoiceMailable::class, $this->mailable->id(123456)); 22 | } 23 | 24 | public function testDateMethod() 25 | { 26 | date_default_timezone_set('America/Detroit'); 27 | $this->assertInstanceOf(InvoiceMailable::class, $this->mailable->date(date('l, M j Y \a\t g:i a'))); 28 | } 29 | 30 | public function testDueMethod() 31 | { 32 | date_default_timezone_set('America/Detroit'); 33 | $this->assertInstanceOf(InvoiceMailable::class, $this->mailable->due(date('l, M j Y \a\t g:i a', strtotime('+7 days')))); 34 | } 35 | 36 | public function testItemsMethod() 37 | { 38 | $this->assertInstanceOf(InvoiceMailable::class, $this->mailable->items([ 39 | ['product_name' => 'Example Product', 'product_price' => 123.99], 40 | ['product_name' => 'Second Product', 'product_price' => 321.99], 41 | ])); 42 | } 43 | 44 | public function testCalcuateMethod() 45 | { 46 | $this->mailable->items([ 47 | ['product_name' => 'Example Product', 'product_price' => 123.99], 48 | ['product_name' => 'Second Product', 'product_price' => 321.99], 49 | ]); 50 | $this->assertInstanceOf(InvoiceMailable::class, $this->mailable->calculate(3, 15)); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Traits/HasAction.php: -------------------------------------------------------------------------------- 1 | color('blue'); 36 | } 37 | 38 | /** 39 | * Indicate that the message gives information about a successful operation. 40 | * 41 | * @return $this 42 | */ 43 | public function success() 44 | { 45 | return $this->color('green'); 46 | } 47 | 48 | /** 49 | * Indicate that the message gives information about an error. 50 | * 51 | * @return $this 52 | */ 53 | public function error() 54 | { 55 | return $this->color('red'); 56 | } 57 | 58 | /** 59 | * Set the color of the message (blue, green, red). 60 | * 61 | * @param string $level 62 | * 63 | * @return $this 64 | */ 65 | public function color($color) 66 | { 67 | $this->color = $color; 68 | 69 | return $this; 70 | } 71 | 72 | /** 73 | * Configure the "call to action" button. 74 | * 75 | * @param string $text 76 | * @param string $url 77 | * 78 | * @return $this 79 | */ 80 | public function action($text, $url) 81 | { 82 | $this->actionText = $text; 83 | $this->actionUrl = $url; 84 | 85 | return $this; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/Mailables/AlertMailable.php: -------------------------------------------------------------------------------- 1 | type = $type; 43 | 44 | return $this; 45 | } 46 | 47 | /** 48 | * Set the alert "message" for the message. 49 | * 50 | * @param string $text 51 | * 52 | * @return $this 53 | */ 54 | public function message($text) 55 | { 56 | $this->text = $text; 57 | 58 | return $this; 59 | } 60 | 61 | /** 62 | * Set the type of the alert to "info". 63 | * 64 | * @return $this 65 | */ 66 | public function info() 67 | { 68 | return $this->type('info'); 69 | } 70 | 71 | /** 72 | * Set the type of the alert to "warning". 73 | * 74 | * @return $this 75 | */ 76 | public function warning() 77 | { 78 | return $this->type('warning'); 79 | } 80 | 81 | /** 82 | * Set the type of the alert to "success". 83 | * 84 | * @return $this 85 | */ 86 | public function success() 87 | { 88 | return $this->type('success'); 89 | } 90 | 91 | /** 92 | * Set the type of the alert to "error". 93 | * 94 | * @return $this 95 | */ 96 | public function error() 97 | { 98 | return $this->type('error'); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /resources/views/html/invoice/table.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | 10 | 56 | 57 |
4 |

{{ $data['id'] }}

6 |

{{ $data['date'] }}

7 |
11 | 12 | 13 | 16 | 19 | 20 | @foreach ($data['items'] as $item) 21 | 22 | 23 | 24 | 25 | @endforeach 26 | @if ($data['shipping'] > 0) 27 | 28 | 31 | 34 | 35 | @endif 36 | @if ($data['tax'] > 0) 37 | 38 | 41 | 44 | 45 | @endif 46 | 47 | 50 | 53 | 54 |
14 |

Description

15 |
17 |

Amount

18 |
{{ $item[$data['keys']['name']] }}{{ $item[$data['keys']['price']] }}
39 |

Tax

40 |
42 |

{{ $data['tax'] }}

43 |
48 |

Total

49 |
51 |

{{ $data['total'] }}

52 |
55 |
58 | -------------------------------------------------------------------------------- /src/Mailables/InvoiceMailable.php: -------------------------------------------------------------------------------- 1 | 'product_name', 'price' => 'product_price']; 77 | 78 | /** 79 | * Information to display in the table. 80 | * 81 | * @var array 82 | */ 83 | public $tableData; 84 | 85 | /** 86 | * Set the id of the invoice. 87 | * 88 | * @param int $id 89 | * 90 | * @return $this 91 | */ 92 | public function id($id) 93 | { 94 | $this->id = $id; 95 | 96 | return $this; 97 | } 98 | 99 | /** 100 | * Set the due date for the invoice. 101 | * 102 | * @param string $date 103 | * 104 | * @return $this 105 | */ 106 | public function due($date) 107 | { 108 | $this->dueDate = $date; 109 | 110 | return $this; 111 | } 112 | 113 | /** 114 | * Set the customer information for the invoice. 115 | * 116 | * @param string $date 117 | * 118 | * @return $this 119 | */ 120 | public function date($date) 121 | { 122 | $this->date = $date; 123 | 124 | return $this; 125 | } 126 | 127 | /** 128 | * Add multiple item's to the invoice. 129 | * 130 | * @param Collection|array $items 131 | * 132 | * @return $this 133 | */ 134 | public function items($items) 135 | { 136 | if (!$items instanceof Collection) { 137 | $items = collect($items); 138 | } 139 | 140 | foreach ($items as $item) { 141 | $this->item($item[$this->keys['name']], $item[$this->keys['price']]); 142 | } 143 | 144 | return $this; 145 | } 146 | 147 | /** 148 | * Add an item to the invoice. 149 | * 150 | * @param string $name 151 | * @param string|int $price 152 | */ 153 | private function item($name, $price) 154 | { 155 | if (!$this->items instanceof Collection) { 156 | $this->items = new Collection(); 157 | } 158 | 159 | $this->items->push([ 160 | 'product_name' => $name, 161 | 'product_price' => $price, 162 | ]); 163 | } 164 | 165 | /** 166 | * Calculate the subtotal, tax, and total. 167 | * 168 | * @param int $taxPercent 169 | * @param int $shipping 170 | * 171 | * @return $this 172 | */ 173 | public function calculate($taxPercent = 0, $shipping = 0) 174 | { 175 | $subtotal = $this->items->sum('product_price'); 176 | 177 | $this->tax = $subtotal * ($taxPercent / 100); 178 | 179 | $this->shipping = $shipping; 180 | 181 | $this->total = $subtotal + $this->tax + $this->shipping; 182 | 183 | return $this->dataToArray(); 184 | } 185 | 186 | /** 187 | * Set the data to use in the table. 188 | * 189 | * @return $this 190 | */ 191 | private function dataToArray() 192 | { 193 | $this->tableData = [ 194 | 'id' => $this->id, 195 | 'date' => $this->date, 196 | 'items' => $this->items, 197 | 'shipping' => $this->shipping ? number_format($this->shipping, 2) : null, 198 | 'tax' => $this->tax ? number_format($this->tax, 2) : null, 199 | 'total' => number_format($this->total, 2), 200 | 'keys' => $this->keys, 201 | ]; 202 | 203 | return $this; 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Tuxedo 2 | [![Version](https://img.shields.io/packagist/v/tomirons/tuxedo.svg)](https://packagist.org/packages/tomirons/tuxedo) 3 | [![License](https://poser.pugx.org/tomirons/tuxedo/license.svg)](https://packagist.org/packages/tomirons/tuxedo) 4 | [![Total Downloads](https://img.shields.io/packagist/dt/tomirons/tuxedo.svg)](https://packagist.org/packages/tomirons/tuxedo) 5 | [![Build Status](https://travis-ci.org/tomirons/tuxedo.svg?branch=master)](https://travis-ci.org/tomirons/tuxedo) 6 | 7 | Tuxedo is an easy way to send transactional emails with Laravel's `Mail` classes, with the templates already done for you. 8 | 9 | ## Contents 10 | 11 | - [Installation](#installation) 12 | - [Classes](#classes) 13 | - [ActionMailable](#actionmailable) 14 | - [AlertMailable](#alertmailable) 15 | - [InvoiceMailable](#invoicemailable) 16 | - [License](#license) 17 | 18 | ## Installation 19 | 1) Run the following command: 20 | 21 | ````shell 22 | $ composer require tomirons/tuxedo 23 | ```` 24 | 25 | 2) Open your `config/app.php` and add the following class to your `providers` array: 26 | 27 | ````php 28 | TomIrons\Tuxedo\TuxedoServiceProvider::class 29 | ```` 30 | 31 | 3) (Optional) If you would like to edit the templates, run the following command to publish them 32 | 33 | ````shell 34 | php artisan vendor:publish --provider=TomIrons\Tuxedo\TuxedoServiceProvider 35 | ```` 36 | 37 | ## Classes 38 | There are currently 3 different types of classes you can extend. `ActionMailable`, `AlertMailable`, and `InvoiceMailable`, and each have their own special properties and methods. 39 | 40 | #### Global Methods 41 | These methods are available in **ALL** classes. 42 | - `greeting($greeting)` - Sets the greeting for the message. 43 | - `salutation($salutation)` - Sets the salutation for the message. 44 | - `line($line)` - Add a line of text to the message. 45 | 46 | ### ActionMailable 47 | 48 | #### Methods 49 | - `color($color)` - Sets the color of the button. Available options are `blue`, `green`, and `red`. 50 | - `action($text, $url)` - Sets the button text and url. 51 | - `success()` - Sets the button color to `green`. 52 | - `error()` - Sets the button color to `red`. 53 | - `info()` - Sets the button color to `blue`. 54 | 55 | #### Example 56 | ````php 57 | greeting('Hello!') 88 | ->line('Some line of text to tell you what exactly is going on.') 89 | ->action('Click here to do something fun', url('/')) 90 | ->line('Some other information to be displayed after the button.') 91 | ->salutation('Regards, Example App'); 92 | } 93 | } 94 | ```` 95 | 96 | #### Screenshot 97 | ![Action](https://i.imgur.com/1VHPO0c.png) 98 | 99 | ### AlertMailable 100 | 101 | #### Methods 102 | - `info()` - Sets the type of the alert to `info`. 103 | - `warning()` - Sets the type of the alert to `warning`. 104 | - `success()` - Sets the type of the alert to `success`. 105 | - `error()` - Sets the type of the alert to `error`. 106 | - `type($type)` - Sets the type of alert, options are `info`, `success`, `warning`, and `error`. 107 | - `message($message)` - Sets the message to display in the alert. 108 | 109 | #### Example 110 | ````php 111 | greeting('Hello!') 142 | ->info() 143 | ->message('Some text goes here to inform the user') 144 | ->line('Some line of text to tell you what exactly is going on.') 145 | ->salutation('Regards, Example App'); 146 | } 147 | } 148 | ```` 149 | 150 | #### Screenshot 151 | ![Alert](https://i.imgur.com/ckOLIxT.png) 152 | 153 | ### InvoiceMailable 154 | 155 | #### Properties 156 | - `$keys|array` - Set which keys to use when looking for an item's name and price. 157 | 158 | #### Methods 159 | - `id($id)` - Sets the invoice ID. 160 | - `date($date)` - Sets the date to display at the top of the invoice table. 161 | - `due($date)` - Sets the due date of the invoice. 162 | - `items($items)` - Add an list of items to the invoice. Acceptable variable types are `Collection` and `array`. 163 | - `calculate($taxPercent, $shipping)` - Calculates the tax and final total, **MUST** be called after items have been added. 164 | 165 | #### Example 166 | ````php 167 | id(123456) 199 | ->greeting('Hi John Doe!') 200 | ->date(Carbon::now()->format('l, M j Y \a\t g:i a')) 201 | ->due(Carbon::now()->addDays(7)->format('l, M j Y \a\t g:i a')) 202 | ->action('Click me to pay', url('/')) 203 | ->items([ 204 | ['product_name' => 'Example Product', 'product_price' => 123.99], 205 | ['product_name' => 'Second Product', 'product_price' => 321.99] 206 | ]) 207 | ->calculate(3, 15) 208 | ->salutation('Regards, Example App'); 209 | } 210 | } 211 | ```` 212 | 213 | #### Screenshot 214 | ![Invoice](https://i.imgur.com/d5S8gJl.png) 215 | 216 | ## License 217 | Tuxedo is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT) 218 | -------------------------------------------------------------------------------- /resources/views/html/themes/tuxedo.css: -------------------------------------------------------------------------------- 1 | /* Base */ 2 | 3 | body, body *:not(html):not(style):not(br):not(tr):not(code) { 4 | font-family: Avenir, Helvetica, sans-serif; 5 | box-sizing: border-box; 6 | } 7 | 8 | body { 9 | background-color: #F2F4F6; 10 | color: #74787E; 11 | height: 100%; 12 | line-height: 1.4; 13 | margin: 0; 14 | width: 100% !important; 15 | -webkit-text-size-adjust: none; 16 | } 17 | 18 | p, 19 | ul, 20 | ol, 21 | blockquote { 22 | line-height: 1.4; 23 | text-align: left; 24 | } 25 | 26 | a { 27 | color: #3869D4; 28 | } 29 | 30 | a img { 31 | border: none; 32 | } 33 | 34 | /* Typography */ 35 | 36 | h1 { 37 | color: #2F3133; 38 | font-size: 19px; 39 | font-weight: bold; 40 | margin-top: 0; 41 | text-align: left; 42 | } 43 | 44 | h2 { 45 | color: #2F3133; 46 | font-size: 16px; 47 | font-weight: bold; 48 | margin-top: 0; 49 | text-align: left; 50 | } 51 | 52 | h3 { 53 | color: #2F3133; 54 | font-size: 14px; 55 | font-weight: bold; 56 | margin-top: 0; 57 | text-align: left; 58 | } 59 | 60 | p { 61 | color: #74787E; 62 | font-size: 16px; 63 | line-height: 1.5em; 64 | margin-top: 0; 65 | text-align: left; 66 | } 67 | 68 | p.sub { 69 | font-size: 12px; 70 | } 71 | 72 | /* Layout */ 73 | 74 | .wrapper { 75 | background-color: #f5f8fa; 76 | margin: 0; 77 | padding: 0; 78 | width: 100%; 79 | -premailer-cellpadding: 0; 80 | -premailer-cellspacing: 0; 81 | -premailer-width: 100%; 82 | } 83 | 84 | .content { 85 | margin: 0; 86 | padding: 0; 87 | width: 100%; 88 | -premailer-cellpadding: 0; 89 | -premailer-cellspacing: 0; 90 | -premailer-width: 100%; 91 | } 92 | 93 | /* Header */ 94 | 95 | .header { 96 | padding: 25px 0; 97 | text-align: center; 98 | } 99 | 100 | .header a { 101 | color: #bbbfc3; 102 | font-size: 19px; 103 | font-weight: bold; 104 | text-decoration: none; 105 | text-shadow: 0 1px 0 white; 106 | } 107 | 108 | /* Body */ 109 | 110 | .body { 111 | background-color: #FFFFFF; 112 | border-bottom: 1px solid #EDEFF2; 113 | border-top: 1px solid #EDEFF2; 114 | margin: 0; 115 | padding: 0; 116 | width: 100%; 117 | -premailer-cellpadding: 0; 118 | -premailer-cellspacing: 0; 119 | -premailer-width: 100%; 120 | } 121 | 122 | .inner-body { 123 | background-color: #FFFFFF; 124 | margin: 0 auto; 125 | padding: 0; 126 | width: 570px; 127 | -premailer-cellpadding: 0; 128 | -premailer-cellspacing: 0; 129 | -premailer-width: 570px; 130 | } 131 | 132 | /* Subcopy */ 133 | 134 | .subcopy { 135 | border-top: 1px solid #EDEFF2; 136 | margin-top: 25px; 137 | padding-top: 25px; 138 | } 139 | 140 | .subcopy p { 141 | font-size: 12px; 142 | } 143 | 144 | /* Footer */ 145 | 146 | .footer { 147 | margin: 0 auto; 148 | padding: 0; 149 | text-align: center; 150 | width: 570px; 151 | -premailer-cellpadding: 0; 152 | -premailer-cellspacing: 0; 153 | -premailer-width: 570px; 154 | } 155 | 156 | .footer p { 157 | color: #AEAEAE; 158 | font-size: 12px; 159 | text-align: center; 160 | } 161 | 162 | /* Tables */ 163 | 164 | .table table { 165 | margin: 30px auto; 166 | width: 100%; 167 | -premailer-cellpadding: 0; 168 | -premailer-cellspacing: 0; 169 | -premailer-width: 100%; 170 | } 171 | 172 | .table th { 173 | border-bottom: 1px solid #EDEFF2; 174 | padding-bottom: 8px; 175 | } 176 | 177 | .table td { 178 | color: #74787E; 179 | font-size: 15px; 180 | line-height: 18px; 181 | padding: 10px 0; 182 | } 183 | 184 | .content-cell { 185 | padding: 35px; 186 | } 187 | 188 | /* Buttons */ 189 | 190 | .action { 191 | margin: 30px auto; 192 | padding: 0; 193 | text-align: center; 194 | width: 100%; 195 | -premailer-cellpadding: 0; 196 | -premailer-cellspacing: 0; 197 | -premailer-width: 100%; 198 | } 199 | 200 | .button { 201 | border-radius: 3px; 202 | box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16); 203 | color: #FFF; 204 | display: inline-block; 205 | text-decoration: none; 206 | -webkit-text-size-adjust: none; 207 | } 208 | 209 | .button-blue { 210 | background-color: #3097D1; 211 | border-top: 10px solid #3097D1; 212 | border-right: 18px solid #3097D1; 213 | border-bottom: 10px solid #3097D1; 214 | border-left: 18px solid #3097D1; 215 | } 216 | 217 | .button-green { 218 | background-color: #2ab27b; 219 | border-top: 10px solid #2ab27b; 220 | border-right: 18px solid #2ab27b; 221 | border-bottom: 10px solid #2ab27b; 222 | border-left: 18px solid #2ab27b; 223 | } 224 | 225 | .button-red { 226 | background-color: #bf5329; 227 | border-top: 10px solid #bf5329; 228 | border-right: 18px solid #bf5329; 229 | border-bottom: 10px solid #bf5329; 230 | border-left: 18px solid #bf5329; 231 | } 232 | 233 | /* Panels */ 234 | 235 | .panel { 236 | margin: 0 0 21px; 237 | } 238 | 239 | .panel-content { 240 | background-color: #EDEFF2; 241 | padding: 16px; 242 | } 243 | 244 | .panel-item { 245 | padding: 0; 246 | } 247 | 248 | .panel-item p:last-of-type { 249 | margin-bottom: 0; 250 | padding-bottom: 0; 251 | } 252 | 253 | /* Alerts */ 254 | 255 | .alert { 256 | margin: 0 0 21px; 257 | } 258 | 259 | .alert-content { 260 | background-color: #EDEFF2; 261 | padding: 16px; 262 | } 263 | 264 | .alert-item { 265 | padding: 0; 266 | } 267 | 268 | .alert-item p:last-of-type { 269 | margin-bottom: 0; 270 | padding-bottom: 0; 271 | color: #fff; 272 | } 273 | 274 | .alert-info { 275 | background-color: #3097D1; 276 | } 277 | 278 | .alert-warning { 279 | background-color: #bf9d31; 280 | } 281 | 282 | .alert-success { 283 | background-color: #2ab27b; 284 | } 285 | 286 | .alert-error { 287 | background-color: #bf5329; 288 | } 289 | 290 | /* Promotions */ 291 | 292 | .promotion { 293 | background-color: #FFFFFF; 294 | border: 2px dashed #9BA2AB; 295 | margin: 0; 296 | margin-bottom: 25px; 297 | margin-top: 25px; 298 | padding: 24px; 299 | width: 100%; 300 | -premailer-cellpadding: 0; 301 | -premailer-cellspacing: 0; 302 | -premailer-width: 100%; 303 | } 304 | 305 | .promotion h1 { 306 | text-align: center; 307 | } 308 | 309 | .promotion p { 310 | font-size: 15px; 311 | text-align: center; 312 | } 313 | 314 | /* Attribute list ------------------------------ */ 315 | 316 | .attributes { 317 | margin: 0 0 21px; 318 | } 319 | 320 | .attributes_content { 321 | background-color: #EDEFF2; 322 | padding: 16px; 323 | } 324 | 325 | .attributes_item { 326 | padding: 0; 327 | } 328 | 329 | /* Related Items ------------------------------ */ 330 | 331 | .related { 332 | width: 100%; 333 | margin: 0; 334 | padding: 25px 0 0 0; 335 | -premailer-width: 100%; 336 | -premailer-cellpadding: 0; 337 | -premailer-cellspacing: 0; 338 | } 339 | 340 | .related_item { 341 | padding: 10px 0; 342 | color: #74787E; 343 | font-size: 15px; 344 | line-height: 18px; 345 | } 346 | 347 | .related_item-title { 348 | display: block; 349 | margin: .5em 0 0; 350 | } 351 | 352 | .related_item-thumb { 353 | display: block; 354 | padding-bottom: 10px; 355 | } 356 | 357 | .related_heading { 358 | border-top: 1px solid #EDEFF2; 359 | text-align: center; 360 | padding: 25px 0 10px; 361 | } 362 | 363 | /* Discount Code ------------------------------ */ 364 | 365 | .discount { 366 | width: 100%; 367 | margin: 0; 368 | padding: 24px; 369 | -premailer-width: 100%; 370 | -premailer-cellpadding: 0; 371 | -premailer-cellspacing: 0; 372 | background-color: #EDEFF2; 373 | border: 2px dashed #9BA2AB; 374 | } 375 | 376 | .discount_heading { 377 | text-align: center; 378 | } 379 | 380 | .discount_body { 381 | text-align: center; 382 | font-size: 15px; 383 | } 384 | 385 | /* Social Icons ------------------------------ */ 386 | 387 | .social { 388 | width: auto; 389 | } 390 | 391 | .social td { 392 | padding: 0; 393 | width: auto; 394 | } 395 | 396 | .social_icon { 397 | height: 20px; 398 | margin: 0 8px 10px 8px; 399 | padding: 0; 400 | } 401 | 402 | /* Data table ------------------------------ */ 403 | 404 | .purchase { 405 | width: 100%; 406 | margin: 0; 407 | padding: 35px 0; 408 | -premailer-width: 100%; 409 | -premailer-cellpadding: 0; 410 | -premailer-cellspacing: 0; 411 | } 412 | 413 | .purchase_content { 414 | width: 100%; 415 | margin: 0; 416 | padding: 25px 0 0 0; 417 | -premailer-width: 100%; 418 | -premailer-cellpadding: 0; 419 | -premailer-cellspacing: 0; 420 | } 421 | 422 | .purchase_item { 423 | padding: 10px 0; 424 | color: #74787E; 425 | font-size: 15px; 426 | line-height: 18px; 427 | } 428 | 429 | .purchase_heading { 430 | padding-bottom: 8px; 431 | border-bottom: 1px solid #EDEFF2; 432 | } 433 | 434 | .purchase_heading p { 435 | margin: 0; 436 | color: #9BA2AB; 437 | font-size: 12px; 438 | } 439 | 440 | .purchase_footer { 441 | padding-top: 15px; 442 | border-top: 1px solid #EDEFF2; 443 | } 444 | 445 | .purchase_total, .purchase_tax, .purchase_shipping { 446 | margin: 0; 447 | text-align: right; 448 | font-weight: bold; 449 | color: #2F3133; 450 | } 451 | 452 | .purchase_tax, .purchase_shipping { 453 | font-weight: normal; 454 | color: #484c52; 455 | } 456 | 457 | .purchase_total--label, .purchase_tax--label, .purchase_shipping--label { 458 | padding: 0 15px 0 0; 459 | } 460 | 461 | /* Utilities ------------------------------ */ 462 | 463 | .align-right { 464 | text-align: right; 465 | } 466 | 467 | .align-left { 468 | text-align: left; 469 | } 470 | 471 | .align-center { 472 | text-align: center; 473 | } --------------------------------------------------------------------------------