From 79824835de3e635dd2dc32da13af9dd0df93c8fa Mon Sep 17 00:00:00 2001 From: Kevin MacMartin Date: Tue, 26 Jan 2016 23:20:08 -0500 Subject: [PATCH] Update + add in dashboard functionality, and document how to setup the dashboard in the readme --- .env.example | 7 +- .gitignore | 5 + app/Http/Controllers/Auth/AuthController.php | 30 +- app/Http/Controllers/ContactController.php | 3 +- app/Http/Controllers/DashboardController.php | 216 ++++++ .../Controllers/SubscriptionController.php | 39 + app/Http/Kernel.php | 6 +- app/Http/Middleware/Authenticate.php | 2 +- .../Middleware/RedirectIfAuthenticated.php | 2 +- app/Http/routes.php | 39 +- app/Models/Contact.php | 10 +- app/Models/Subscriptions.php | 15 + bower.json | 8 +- composer.json | 5 +- composer.lock | 704 ++++++++++++++++-- config/app.php | 12 +- config/excel.php | 683 +++++++++++++++++ config/image.php | 20 + config/laravel-head.php | 2 +- config/laravel-newsletter.php | 64 ++ ...15_12_17_232249_add_subscription_table.php | 33 + database/seeds/DatabaseSeeder.php | 2 +- gulpfile.js | 52 +- readme.md | 237 ++++++ resources/assets/js/app.js | 11 +- resources/assets/js/contact.js | 6 +- resources/assets/js/dashboard.js | 415 +++++++++++ resources/assets/js/subscription.js | 63 ++ resources/assets/less/app.less | 7 +- resources/assets/less/auth.less | 33 - resources/assets/less/dashboard.less | 356 +++++++++ resources/assets/less/elements/footer.less | 8 +- resources/assets/less/elements/nav.less | 102 ++- resources/assets/less/pages/contact.less | 53 ++ resources/assets/less/var.less | 20 +- resources/assets/less/website/contact.less | 49 -- resources/views/auth.blade.php | 11 - .../views/auth/emails/password.blade.php | 1 + resources/views/auth/login.blade.php | 81 +- .../views/auth/passwords/email.blade.php | 46 ++ .../views/auth/passwords/reset.blade.php | 70 ++ resources/views/auth/register.blade.php | 103 ++- resources/views/contact.blade.php | 11 - resources/views/dashboard/core.blade.php | 52 ++ resources/views/dashboard/edit-item.blade.php | 78 ++ resources/views/dashboard/edit-list.blade.php | 32 + .../views/dashboard/elements/menu.blade.php | 11 + .../views/dashboard/elements/nav.blade.php | 39 + resources/views/dashboard/home.blade.php | 6 + resources/views/dashboard/view.blade.php | 27 + resources/views/elements/footer.blade.php | 4 +- resources/views/elements/nav.blade.php | 10 +- resources/views/email/contact.blade.php | 2 +- resources/views/errors/404.blade.php | 48 +- resources/views/errors/503.blade.php | 48 +- .../views/errors/no-such-record.blade.php | 5 + resources/views/{ => layouts}/base.blade.php | 12 +- resources/views/layouts/dashboard.blade.php | 11 + resources/views/layouts/error.blade.php | 45 ++ resources/views/layouts/public.blade.php | 14 + resources/views/public.blade.php | 9 - resources/views/website/contact.blade.php | 12 +- resources/views/website/home.blade.php | 11 - resources/views/website/index.blade.php | 14 + 64 files changed, 3705 insertions(+), 427 deletions(-) create mode 100644 app/Http/Controllers/DashboardController.php create mode 100644 app/Http/Controllers/SubscriptionController.php create mode 100644 app/Models/Subscriptions.php create mode 100644 config/excel.php create mode 100644 config/image.php create mode 100644 config/laravel-newsletter.php create mode 100644 database/migrations/2015_12_17_232249_add_subscription_table.php create mode 100644 resources/assets/js/dashboard.js create mode 100644 resources/assets/js/subscription.js delete mode 100644 resources/assets/less/auth.less create mode 100644 resources/assets/less/dashboard.less create mode 100644 resources/assets/less/pages/contact.less delete mode 100644 resources/assets/less/website/contact.less delete mode 100644 resources/views/auth.blade.php create mode 100644 resources/views/auth/emails/password.blade.php create mode 100644 resources/views/auth/passwords/email.blade.php create mode 100644 resources/views/auth/passwords/reset.blade.php delete mode 100644 resources/views/contact.blade.php create mode 100644 resources/views/dashboard/core.blade.php create mode 100644 resources/views/dashboard/edit-item.blade.php create mode 100644 resources/views/dashboard/edit-list.blade.php create mode 100644 resources/views/dashboard/elements/menu.blade.php create mode 100644 resources/views/dashboard/elements/nav.blade.php create mode 100644 resources/views/dashboard/home.blade.php create mode 100644 resources/views/dashboard/view.blade.php create mode 100644 resources/views/errors/no-such-record.blade.php rename resources/views/{ => layouts}/base.blade.php (59%) create mode 100644 resources/views/layouts/dashboard.blade.php create mode 100644 resources/views/layouts/error.blade.php create mode 100644 resources/views/layouts/public.blade.php delete mode 100644 resources/views/public.blade.php delete mode 100644 resources/views/website/home.blade.php create mode 100644 resources/views/website/index.blade.php diff --git a/.env.example b/.env.example index 6bd521c..e102355 100644 --- a/.env.example +++ b/.env.example @@ -4,7 +4,7 @@ SITE_DESC="A website template" APP_ENV=local APP_DEBUG=true APP_KEY=random_string -APP_URL=http://template.hypothetic.al +APP_URL=http://localhost DB_HOST=localhost DB_DATABASE=hypothetical @@ -17,6 +17,9 @@ CACHE_DRIVER=file SESSION_DRIVER=file QUEUE_DRIVER=sync +MAILCHIMP_APIKEY= +MAILCHIMP_LISTID= + MAIL_SENDTO=null MAIL_DRIVER=smtp @@ -26,3 +29,5 @@ MAIL_ADDRESS=null MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=tls + +REGISTRATION=true diff --git a/.gitignore b/.gitignore index e833bd3..4d91a82 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,9 @@ /public/build /public/css /public/js +/public/fonts/FontAwesome.otf +/public/fonts/fontawesome-webfont.* +/public/fonts/glyphicons-halflings-regular.* +/public/uploads +/storage/exports .env diff --git a/app/Http/Controllers/Auth/AuthController.php b/app/Http/Controllers/Auth/AuthController.php index 417365a..690beb2 100644 --- a/app/Http/Controllers/Auth/AuthController.php +++ b/app/Http/Controllers/Auth/AuthController.php @@ -40,6 +40,20 @@ class AuthController extends Controller $this->middleware('guest', ['except' => 'logout']); } + /** + * Show the application registration form. + * + * @return \Illuminate\Http\Response + */ + public function showRegistrationForm() + { + if (env('REGISTRATION', false)) { + return view('auth.register'); + } else { + header('Location: /login'); + } + } + /** * Get a validator for an incoming registration request. * @@ -49,8 +63,8 @@ class AuthController extends Controller protected function validator(array $data) { return Validator::make($data, [ - 'name' => 'required|max:255', - 'email' => 'required|email|max:255|unique:users', + 'name' => 'required|max:255', + 'email' => 'required|email|max:255|unique:users', 'password' => 'required|confirmed|min:6', ]); } @@ -63,10 +77,12 @@ class AuthController extends Controller */ protected function create(array $data) { - return User::create([ - 'name' => $data['name'], - 'email' => $data['email'], - 'password' => bcrypt($data['password']), - ]); + if (env('REGISTRATION', false)) { + return User::create([ + 'name' => $data['name'], + 'email' => $data['email'], + 'password' => bcrypt($data['password']), + ]); + } } } diff --git a/app/Http/Controllers/ContactController.php b/app/Http/Controllers/ContactController.php index 7a53e80..68893f8 100644 --- a/app/Http/Controllers/ContactController.php +++ b/app/Http/Controllers/ContactController.php @@ -14,12 +14,11 @@ class ContactController extends Controller 'message' => 'required' ]); - $contact = new Contact; - $name = $request['name']; $email = $request['email']; $message = $request['message']; + $contact = new Contact; $contact->name = $name; $contact->email = $email; $contact->message = $message; diff --git a/app/Http/Controllers/DashboardController.php b/app/Http/Controllers/DashboardController.php new file mode 100644 index 0000000..57daa96 --- /dev/null +++ b/app/Http/Controllers/DashboardController.php @@ -0,0 +1,216 @@ +middleware('auth'); + } + + /** + * Show the application dashboard. + * + * @return Response + */ + public function index() + { + return view('dashboard.home', [ + 'heading' => 'Dashboard Home' + ]); + } + + /** + * Dashboard View + */ + public function getContact() + { + return view('dashboard.view', [ + 'heading' => 'Contact Form Submissions', + 'model' => 'contact', + 'rows' => Contact::getContactSubmissions(), + 'cols' => [ + [ 'Date', 'created_at' ], + [ 'Name', 'name' ], + [ 'Email', 'email' ], + [ 'Message', 'message' ] + ] + ]); + } + + public function getSubscriptions() + { + return view('dashboard.view', [ + 'heading' => 'Subscriptions', + 'model' => 'subscriptions', + 'rows' => Subscriptions::getSubscriptions(), + 'cols' => [ + [ 'Date', 'created_at' ], + [ 'Email', 'email' ], + [ 'Name', 'name' ], + [ 'Postal / Zip', 'location' ] + ] + ]); + } + + /** + * Dashboard Edit + */ + + /** + * Dashboard Export: Export data as a spreadsheet + */ + public function getExport($model) + { + // set the name of the spreadsheet + $sheet_name = ucfirst($model); + + // set the model using the 'model' request argument + switch ($model) { + case 'contact': + $headings = [ 'Date', 'Name', 'Email', 'Message' ]; + $items = Contact::select('created_at', 'name', 'email', 'message')->get(); + break; + case 'subscriptions': + $headings = [ 'Date', 'Email', 'Name', 'Postal / Zip' ]; + $items = Subscriptions::select('created_at', 'email', 'name', 'location')->get(); + break; + default: + abort(404); + } + + Excel::create($sheet_name, function($excel) use($sheet_name, $headings, $items) { + $excel->sheet($sheet_name, function($sheet) use($sheet_name, $headings, $items) { + $sheet->fromArray($items); + $sheet->row(1, $headings); + }); + })->store('xls')->export('xls'); + } + + /** + * Dashboard Image Upload: Upload images + */ + public function postImageUpload(Request $request) + { + if ($request->hasFile('file')) { + $file = base_path() . '/public/uploads/' . $request['model'] . '/' . $request['id'] . '.jpg'; + $image = Image::make($request->file('file')); + $image->save($file); + } else { + return 'file-upload-fail'; + } + + return 'success'; + } + + /** + * Dashboard Edit: Create and edit rows + */ + public function postEdit(Request $request) + { + $this->validate($request, [ + 'id' => 'required', + 'model' => 'required', + 'columns' => 'required' + ]); + + // store the id request variable for easy access + $id = $request['id']; + + // set the model using the 'model' request argument + switch ($request['model']) { + default: + return 'model-access-fail'; + } + + // populate the eloquent object with the remaining items in $request + foreach ($request['columns'] as $column) { + $item->$column = $request[$column]; + } + + // save the new or updated item + $item->save(); + + // return the id number in the format '^id:[0-9][0-9]*$' on success + return 'id:' . $item->id; + } + + /** + * Dashboard Reorder: Reorder rows + */ + public function postReorder(Request $request) + { + $this->validate($request, [ + 'order' => 'required', + 'column' => 'required', + 'model' => 'required' + ]); + + $order = $request['order']; + $column = $request['column']; + + // set the model using the 'model' request argument + switch ($request['model']) { + default: + return 'model-access-fail'; + } + + // update each row with the new order + foreach (array_keys($order) as $order_id) { + $item = $items::find($order_id); + $item->$column = $order[$order_id]; + $item->save(); + } + + return 'success'; + } + + /** + * Dashboard Delete: Delete rows + */ + public function deleteDelete(Request $request) + { + $this->validate($request, [ + 'id' => 'required', + 'model' => 'required' + ]); + + // set the model using the 'model' request argument + switch ($request['model']) { + default: + return 'model-access-fail'; + } + + // delete the row with the id using the 'id' request argument + if ($items::where('id', $request['id'])->exists()) { + $items::where('id', $request['id'])->delete(); + } else { + return 'row-delete-fail'; + } + + // delete the associated image if one exists + $image_file = base_path() . '/public/uploads/' . $request['model'] . '/' . $request['id'] . '.jpg'; + if (file_exists($image_file)) { + if (!unlink($image_file)) { + return 'image-delete-fail'; + } + } + + // Return a success + return 'success'; + } +} diff --git a/app/Http/Controllers/SubscriptionController.php b/app/Http/Controllers/SubscriptionController.php new file mode 100644 index 0000000..a4f4118 --- /dev/null +++ b/app/Http/Controllers/SubscriptionController.php @@ -0,0 +1,39 @@ +validate($request, [ + 'name' => 'required', + 'email' => 'required|email', + 'address' => array('required', 'regex:/^([A-Za-z][0-9][A-Za-z] *[0-9][A-Za-z][0-9]|[0-9][0-9][0-9][0-9][0-9])$/') + ]); + + $name = $request['name']; + $fname = preg_replace('/ .*$/', '', $name); + $lname = preg_match('/. ./', $name) === 1 ? preg_replace('/^[^ ][^ ]* /', '', $name) : ''; + $email = $request['email']; + $address = $request['address']; + + // Submit the subscription request + Newsletter::subscribe($email, [ + 'FNAME' => $fname, + 'LNAME' => $lname, + 'ADDRESS' => $address + ]); + + // Save to the database on success + $subscriptions = new Subscriptions; + $subscriptions->name = $name; + $subscriptions->email = $email; + $subscriptions->location = $address; + $subscriptions->save(); + + return 'success'; + } +} diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index f0d8083..0538bfc 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -44,9 +44,9 @@ class Kernel extends HttpKernel * @var array */ protected $routeMiddleware = [ - 'auth' => \App\Http\Middleware\Authenticate::class, + 'auth' => \App\Http\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, - 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, - 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, + 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, + 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, ]; } diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php index faaeb04..ba1f1c3 100644 --- a/app/Http/Middleware/Authenticate.php +++ b/app/Http/Middleware/Authenticate.php @@ -21,7 +21,7 @@ class Authenticate if ($request->ajax()) { return response('Unauthorized.', 401); } else { - return redirect()->guest('auth/login'); + return redirect()->guest('/login'); } } diff --git a/app/Http/Middleware/RedirectIfAuthenticated.php b/app/Http/Middleware/RedirectIfAuthenticated.php index e27860e..b194092 100644 --- a/app/Http/Middleware/RedirectIfAuthenticated.php +++ b/app/Http/Middleware/RedirectIfAuthenticated.php @@ -18,7 +18,7 @@ class RedirectIfAuthenticated public function handle($request, Closure $next, $guard = null) { if (Auth::guard($guard)->check()) { - return redirect('/'); + return redirect('/dashboard'); } return $next($request); diff --git a/app/Http/routes.php b/app/Http/routes.php index ac15a50..02432ff 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -1,34 +1,47 @@ ['web']], function () { +Route::group(['middleware' => 'web'], function () { /* |-------------------------------------------------------------------------- | Public Routes |-------------------------------------------------------------------------- */ - Route::get('/', function () { - return view('website.home'); + Route::get('/', function() { + Head::setTitle('Home'); + return view('website.index'); }); Route::get('/contact', function() { + Head::setTitle('Contact'); return view('website.contact'); }); - Route::post('/contact-submit', 'ContactController@postContactSubmit'); - /* |-------------------------------------------------------------------------- - | Content Management Routes + | Post Routes |-------------------------------------------------------------------------- */ - // Authentication - Route::get('auth/login', 'Auth\AuthController@getLogin'); - Route::post('auth/login', 'Auth\AuthController@postLogin'); - Route::get('auth/logout', 'Auth\AuthController@getLogout'); + Route::post('/contact-submit', 'ContactController@postContactSubmit'); + Route::post('/subscription-submit', 'SubscriptionController@postSubscriptionSubmit'); - // Registration - Route::get('auth/register', 'Auth\AuthController@getRegister'); - Route::post('auth/register', 'Auth\AuthController@postRegister'); + /* + |-------------------------------------------------------------------------- + | Authentication Routes + |-------------------------------------------------------------------------- + */ + + Route::auth(); + + /* + |-------------------------------------------------------------------------- + | Dashboard Routes + |-------------------------------------------------------------------------- + */ + + Route::group(['prefix' => 'dashboard'], function() { + Route::get('/', 'DashboardController@index'); + Route::controller('', DashboardController::class); + }); }); diff --git a/app/Models/Contact.php b/app/Models/Contact.php index 308c273..c4302bb 100644 --- a/app/Models/Contact.php +++ b/app/Models/Contact.php @@ -1,6 +1,4 @@ -get(); + } } diff --git a/app/Models/Subscriptions.php b/app/Models/Subscriptions.php new file mode 100644 index 0000000..fab3987 --- /dev/null +++ b/app/Models/Subscriptions.php @@ -0,0 +1,15 @@ +get(); + } +} diff --git a/bower.json b/bower.json index 6b805c3..8df5d81 100644 --- a/bower.json +++ b/bower.json @@ -7,6 +7,12 @@ "description": "A hypothetical website template", "license": "MIT", "dependencies": { - "bootstrap": "~3.3.6" + "bootstrap": "~3.3.6", + "font-awesome": "~4.5.0", + "awesome-bootstrap-checkbox": "~0.3.5", + "Sortable": "~1.4.2", + "simplemde": "~1.9.0", + "datetimepicker": "~2.4.5", + "SpinKit": "~1.2.5" } } diff --git a/composer.json b/composer.json index 639f33d..0f4b389 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,10 @@ "laravel/framework": "5.2.*", "radic/blade-extensions": "~6.0", "erusev/parsedown": "~1.5", - "gwnobots/laravel-head": "dev-master" + "gwnobots/laravel-head": "dev-master", + "spatie/laravel-newsletter": "^2.2", + "intervention/image": "^2.3", + "maatwebsite/excel": "~2.0.0" }, "require-dev": { "fzaninotto/faker": "~1.4", diff --git a/composer.lock b/composer.lock index b0fa76a..9999d28 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "909b8a65ce3c9081674735483be15837", - "content-hash": "a72eccf8b74633ef449571150228ba0e", + "hash": "1c79203a020edce6fbdbf7c0f5ce5905", + "content-hash": "b4d80c4f47f54b01adf0017122832a48", "packages": [ { "name": "anahkiasen/underscore-php", @@ -357,6 +357,64 @@ ], "time": "2015-03-11 15:46:37" }, + { + "name": "guzzlehttp/psr7", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "4d0bdbe1206df7440219ce14c972aa57cc5e4982" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/4d0bdbe1206df7440219ce14c972aa57cc5e4982", + "reference": "4d0bdbe1206df7440219ce14c972aa57cc5e4982", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "psr/http-message": "~1.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "PSR-7 message implementation", + "keywords": [ + "http", + "message", + "stream", + "uri" + ], + "time": "2015-11-03 01:34:55" + }, { "name": "gwnobots/laravel-head", "version": "dev-master", @@ -405,6 +463,68 @@ ], "time": "2015-03-09 05:14:57" }, + { + "name": "intervention/image", + "version": "2.3.5", + "source": { + "type": "git", + "url": "https://github.com/Intervention/image.git", + "reference": "9f29360b8ab94585cb9e80cf9045abd5b85feb89" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Intervention/image/zipball/9f29360b8ab94585cb9e80cf9045abd5b85feb89", + "reference": "9f29360b8ab94585cb9e80cf9045abd5b85feb89", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "guzzlehttp/psr7": "~1.1", + "php": ">=5.4.0" + }, + "require-dev": { + "mockery/mockery": "~0.9.2", + "phpunit/phpunit": "3.*" + }, + "suggest": { + "ext-gd": "to use GD library based image processing.", + "ext-imagick": "to use Imagick based image processing.", + "intervention/imagecache": "Caching extension for the Intervention Image library" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-4": { + "Intervention\\Image\\": "src/Intervention/Image" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Oliver Vogel", + "email": "oliver@olivervogel.net", + "homepage": "http://olivervogel.net/" + } + ], + "description": "Image handling and manipulation library with support for Laravel integration", + "homepage": "http://image.intervention.io/", + "keywords": [ + "gd", + "image", + "imagick", + "laravel", + "thumbnail", + "watermark" + ], + "time": "2016-01-02 19:15:13" + }, { "name": "jakub-onderka/php-console-color", "version": "0.1", @@ -552,16 +672,16 @@ }, { "name": "laravel/framework", - "version": "v5.2.6", + "version": "v5.2.10", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "b774c304ee81fcf5402fa06b261b364ba8f29cf0" + "reference": "93dc5b0089eef468157fd7200e575c3861ec59a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/b774c304ee81fcf5402fa06b261b364ba8f29cf0", - "reference": "b774c304ee81fcf5402fa06b261b364ba8f29cf0", + "url": "https://api.github.com/repos/laravel/framework/zipball/93dc5b0089eef468157fd7200e575c3861ec59a5", + "reference": "93dc5b0089eef468157fd7200e575c3861ec59a5", "shasum": "" }, "require": { @@ -676,7 +796,61 @@ "framework", "laravel" ], - "time": "2015-12-31 17:41:58" + "time": "2016-01-13 20:29:10" + }, + { + "name": "laravelcollective/html", + "version": "v5.2.1", + "source": { + "type": "git", + "url": "https://github.com/LaravelCollective/html.git", + "reference": "add2c74b144d0a25ba27a1506b7ce84e6bcadd64" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/LaravelCollective/html/zipball/add2c74b144d0a25ba27a1506b7ce84e6bcadd64", + "reference": "add2c74b144d0a25ba27a1506b7ce84e6bcadd64", + "shasum": "" + }, + "require": { + "illuminate/http": "5.2.*", + "illuminate/routing": "5.2.*", + "illuminate/session": "5.2.*", + "illuminate/support": "5.2.*", + "illuminate/view": "5.2.*", + "php": ">=5.5.9" + }, + "require-dev": { + "illuminate/database": "5.2.*", + "mockery/mockery": "~0.9", + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Collective\\Html\\": "src/" + }, + "files": [ + "src/helpers.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylorotwell@gmail.com" + }, + { + "name": "Adam Engebretson", + "email": "adam@laravelcollective.com" + } + ], + "description": "HTML and Form Builders for the Laravel Framework", + "homepage": "http://laravelcollective.com", + "time": "2016-01-15 21:59:03" }, { "name": "league/flysystem", @@ -762,6 +936,119 @@ ], "time": "2015-12-19 20:16:43" }, + { + "name": "maatwebsite/excel", + "version": "v2.0.9", + "source": { + "type": "git", + "url": "https://github.com/Maatwebsite/Laravel-Excel.git", + "reference": "bd9428da19fb3de9bbdd80f18f31b744485dc250" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Maatwebsite/Laravel-Excel/zipball/bd9428da19fb3de9bbdd80f18f31b744485dc250", + "reference": "bd9428da19fb3de9bbdd80f18f31b744485dc250", + "shasum": "" + }, + "require": { + "illuminate/cache": "~5.0|~5.1", + "illuminate/config": "~5.0|~5.1", + "illuminate/filesystem": "~5.0|~5.1", + "illuminate/support": "~5.0|~5.1", + "nesbot/carbon": "~1.0", + "php": ">=5.3.0", + "phpoffice/phpexcel": "~1.8.0", + "tijsverkoyen/css-to-inline-styles": "~1.5" + }, + "require-dev": { + "mockery/mockery": "~0.9", + "orchestra/testbench": "~3.0.0", + "phpseclib/phpseclib": ">=0.3.7", + "phpunit/phpunit": "~4.0" + }, + "suggest": { + "illuminate/http": "~5.0|~5.1", + "illuminate/routing": "~5.0|~5.1", + "illuminate/view": "~5.0|~5.1" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/Maatwebsite/Excel", + "tests/TestCase.php" + ], + "psr-0": { + "Maatwebsite\\Excel\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL" + ], + "authors": [ + { + "name": "Maatwebsite.nl", + "email": "patrick@maatwebsite.nl" + } + ], + "description": "An eloquent way of importing and exporting Excel and CSV in Laravel 4 with the power of PHPExcel", + "keywords": [ + "PHPExcel", + "batch", + "csv", + "excel", + "export", + "import", + "laravel" + ], + "time": "2015-10-26 10:15:37" + }, + { + "name": "mailchimp/mailchimp", + "version": "2.0.6", + "source": { + "type": "git", + "url": "https://bitbucket.org/mailchimp/mailchimp-api-php.git", + "reference": "7ac99b5ac746d5875c5c350ad7e3b83674c83ec1" + }, + "dist": { + "type": "zip", + "url": "https://bitbucket.org/mailchimp/mailchimp-api-php/get/7ac99b5ac746d5875c5c350ad7e3b83674c83ec1.zip", + "reference": "7ac99b5ac746d5875c5c350ad7e3b83674c83ec1", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "apigen/apigen": "dev-master" + }, + "type": "library", + "autoload": { + "psr-0": { + "Mailchimp": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "MailChimp Devs", + "email": "api@mailchimp.com", + "homepage": "http://mailchimp.com", + "role": "Developer" + } + ], + "description": "API client library for the MailChimp bulk email as a service platform", + "homepage": "https://bitbucket.org/mailchimp/mailchimp-api-php", + "keywords": [ + "api", + "email" + ], + "time": "2014-10-30 20:38:12" + }, { "name": "monolog/monolog", "version": "1.17.2", @@ -983,16 +1270,16 @@ }, { "name": "paragonie/random_compat", - "version": "1.1.4", + "version": "1.1.5", "source": { "type": "git", "url": "https://github.com/paragonie/random_compat.git", - "reference": "d762ee5b099a29044603cd4649851e81aa66cb47" + "reference": "dd8998b7c846f6909f4e7a5f67fabebfc412a4f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/d762ee5b099a29044603cd4649851e81aa66cb47", - "reference": "d762ee5b099a29044603cd4649851e81aa66cb47", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/dd8998b7c846f6909f4e7a5f67fabebfc412a4f7", + "reference": "dd8998b7c846f6909f4e7a5f67fabebfc412a4f7", "shasum": "" }, "require": { @@ -1027,7 +1314,7 @@ "pseudorandom", "random" ], - "time": "2015-12-10 14:48:13" + "time": "2016-01-06 13:31:20" }, { "name": "patchwork/utf8", @@ -1088,6 +1375,112 @@ ], "time": "2015-12-15 15:34:15" }, + { + "name": "phpoffice/phpexcel", + "version": "1.8.1", + "source": { + "type": "git", + "url": "https://github.com/PHPOffice/PHPExcel.git", + "reference": "372c7cbb695a6f6f1e62649381aeaa37e7e70b32" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPOffice/PHPExcel/zipball/372c7cbb695a6f6f1e62649381aeaa37e7e70b32", + "reference": "372c7cbb695a6f6f1e62649381aeaa37e7e70b32", + "shasum": "" + }, + "require": { + "ext-xml": "*", + "ext-xmlwriter": "*", + "php": ">=5.2.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "PHPExcel": "Classes/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL" + ], + "authors": [ + { + "name": "Maarten Balliauw", + "homepage": "http://blog.maartenballiauw.be" + }, + { + "name": "Mark Baker" + }, + { + "name": "Franck Lefevre", + "homepage": "http://blog.rootslabs.net" + }, + { + "name": "Erik Tilt" + } + ], + "description": "PHPExcel - OpenXML - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine", + "homepage": "http://phpexcel.codeplex.com", + "keywords": [ + "OpenXML", + "excel", + "php", + "spreadsheet", + "xls", + "xlsx" + ], + "time": "2015-05-01 07:00:55" + }, + { + "name": "psr/http-message", + "version": "1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/85d63699f0dbedb190bbd4b0d2b9dc707ea4c298", + "reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "time": "2015-05-04 20:22:00" + }, { "name": "psr/log", "version": "1.0.0", @@ -1257,16 +1650,16 @@ }, { "name": "sebwite/support", - "version": "1.0.5", + "version": "1.0.10", "source": { "type": "git", "url": "https://bitbucket.org/sebwitepackages/sebwite-support.git", - "reference": "f731cbdf8f7c608fd79c6373b5e50c4c9efdfd9e" + "reference": "e93caad0a5a1c89847d2c0c02e49f986945fce45" }, "dist": { "type": "zip", - "url": "https://bitbucket.org/sebwitepackages/sebwite-support/get/f731cbdf8f7c608fd79c6373b5e50c4c9efdfd9e.zip", - "reference": "f731cbdf8f7c608fd79c6373b5e50c4c9efdfd9e", + "url": "https://bitbucket.org/sebwitepackages/sebwite-support/get/e93caad0a5a1c89847d2c0c02e49f986945fce45.zip", + "reference": "e93caad0a5a1c89847d2c0c02e49f986945fce45", "shasum": "" }, "require": { @@ -1277,8 +1670,11 @@ "illuminate/console": "~5.1", "illuminate/filesystem": "~5.1", "illuminate/support": "~5.1", + "laravelcollective/html": "~5.1", "php": ">=5.5.9", - "symfony/filesystem": "~2.7", + "radic/blade-extensions": "~6.1", + "symfony/filesystem": "~2.7|~3.0", + "symfony/options-resolver": "~2.0|~3.0", "webmozart/path-util": "~1.0" }, "require-dev": { @@ -1313,7 +1709,58 @@ "structure", "support" ], - "time": "2015-12-28 19:20:17" + "time": "2016-01-13 11:02:15" + }, + { + "name": "spatie/laravel-newsletter", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-newsletter.git", + "reference": "cdfe4de92f4c822904bfaee5f85a98d495ca56d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-newsletter/zipball/cdfe4de92f4c822904bfaee5f85a98d495ca56d3", + "reference": "cdfe4de92f4c822904bfaee5f85a98d495ca56d3", + "shasum": "" + }, + "require": { + "illuminate/support": "~5.0", + "mailchimp/mailchimp": "~2.0", + "php": ">=5.4.0" + }, + "require-dev": { + "mockery/mockery": "0.9.*", + "phpunit/phpunit": "4.*", + "scrutinizer/ocular": "~1.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\Newsletter\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://murze.be", + "role": "Developer" + } + ], + "description": "Manage newsletters in Laravel 5", + "homepage": "https://github.com/spatie/laravel-newsletter", + "keywords": [ + "laravel", + "mailchimp", + "newsletter" + ], + "time": "2015-12-21 22:48:10" }, { "name": "swiftmailer/swiftmailer", @@ -1428,6 +1875,59 @@ "homepage": "https://symfony.com", "time": "2015-12-22 10:39:06" }, + { + "name": "symfony/css-selector", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "4613311fd46e146f506403ce2f8a0c71d402d2a3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/4613311fd46e146f506403ce2f8a0c71d402d2a3", + "reference": "4613311fd46e146f506403ce2f8a0c71d402d2a3", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony CssSelector Component", + "homepage": "https://symfony.com", + "time": "2015-12-05 17:45:07" + }, { "name": "symfony/debug", "version": "v3.0.1", @@ -1547,25 +2047,25 @@ }, { "name": "symfony/filesystem", - "version": "v2.8.1", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "a7ad724530a764d70c168d321ac226ba3d2f10fc" + "reference": "c2e59d11dccd135dc8f00ee97f34fe1de842e70c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/a7ad724530a764d70c168d321ac226ba3d2f10fc", - "reference": "a7ad724530a764d70c168d321ac226ba3d2f10fc", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/c2e59d11dccd135dc8f00ee97f34fe1de842e70c", + "reference": "c2e59d11dccd135dc8f00ee97f34fe1de842e70c", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": ">=5.5.9" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1592,7 +2092,7 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2015-12-22 10:25:57" + "time": "2015-12-22 10:39:06" }, { "name": "symfony/finder", @@ -1777,6 +2277,60 @@ "homepage": "https://symfony.com", "time": "2015-12-26 16:46:13" }, + { + "name": "symfony/options-resolver", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "8e68c053a39e26559357cc742f01a7182ce40785" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/8e68c053a39e26559357cc742f01a7182ce40785", + "reference": "8e68c053a39e26559357cc742f01a7182ce40785", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony OptionsResolver Component", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "time": "2015-11-18 13:48:51" + }, { "name": "symfony/polyfill-mbstring", "version": "v1.0.1", @@ -2194,6 +2748,53 @@ ], "time": "2015-12-05 11:13:14" }, + { + "name": "tijsverkoyen/css-to-inline-styles", + "version": "1.5.5", + "source": { + "type": "git", + "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", + "reference": "9753fc340726e327e4d48b7c0604f85475ae0bc3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/9753fc340726e327e4d48b7c0604f85475ae0bc3", + "reference": "9753fc340726e327e4d48b7c0604f85475ae0bc3", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "symfony/css-selector": "~2.1|~3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.5.x-dev" + } + }, + "autoload": { + "psr-4": { + "TijsVerkoyen\\CssToInlineStyles\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD" + ], + "authors": [ + { + "name": "Tijs Verkoyen", + "email": "css_to_inline_styles@verkoyen.eu", + "role": "Developer" + } + ], + "description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.", + "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", + "time": "2015-12-08 16:14:14" + }, { "name": "vlucas/phpdotenv", "version": "v2.2.0", @@ -3352,59 +3953,6 @@ "homepage": "https://github.com/sebastianbergmann/version", "time": "2015-06-21 13:59:46" }, - { - "name": "symfony/css-selector", - "version": "v3.0.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/css-selector.git", - "reference": "4613311fd46e146f506403ce2f8a0c71d402d2a3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/4613311fd46e146f506403ce2f8a0c71d402d2a3", - "reference": "4613311fd46e146f506403ce2f8a0c71d402d2a3", - "shasum": "" - }, - "require": { - "php": ">=5.5.9" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\CssSelector\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jean-François Simon", - "email": "jeanfrancois.simon@sensiolabs.com" - }, - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony CssSelector Component", - "homepage": "https://symfony.com", - "time": "2015-12-05 17:45:07" - }, { "name": "symfony/dom-crawler", "version": "v3.0.1", diff --git a/config/app.php b/config/app.php index 3309908..d4f079e 100644 --- a/config/app.php +++ b/config/app.php @@ -160,6 +160,9 @@ return [ */ Radic\BladeExtensions\BladeExtensionsServiceProvider::class, Gwnobots\LaravelHead\LaravelHeadServiceProvider::class, + Spatie\Newsletter\NewsletterServiceProvider::class, + Intervention\Image\ImageServiceProvider::class, + Maatwebsite\Excel\ExcelServiceProvider::class, ], @@ -206,6 +209,13 @@ return [ 'Validator' => Illuminate\Support\Facades\Validator::class, 'View' => Illuminate\Support\Facades\View::class, - ], + /* + * Custom Class Aliases... + */ + 'Newsletter' => Spatie\Newsletter\NewsletterFacade::class, + 'Image' => Intervention\Image\Facades\Image::class, + 'Excel' => Maatwebsite\Excel\Facades\Excel::class, + + ] ]; diff --git a/config/excel.php b/config/excel.php new file mode 100644 index 0000000..60df6ef --- /dev/null +++ b/config/excel.php @@ -0,0 +1,683 @@ + array( + + /* + |-------------------------------------------------------------------------- + | Enable/Disable cell caching + |-------------------------------------------------------------------------- + */ + 'enable' => true, + + /* + |-------------------------------------------------------------------------- + | Caching driver + |-------------------------------------------------------------------------- + | + | Set the caching driver + | + | Available methods: + | memory|gzip|serialized|igbinary|discISAM|apc|memcache|temp|wincache|sqlite|sqlite3 + | + */ + 'driver' => 'memory', + + /* + |-------------------------------------------------------------------------- + | Cache settings + |-------------------------------------------------------------------------- + */ + 'settings' => array( + + 'memoryCacheSize' => '32MB', + 'cacheTime' => 600 + + ), + + /* + |-------------------------------------------------------------------------- + | Memcache settings + |-------------------------------------------------------------------------- + */ + 'memcache' => array( + + 'host' => 'localhost', + 'port' => 11211, + + ), + + /* + |-------------------------------------------------------------------------- + | Cache dir (for discISAM) + |-------------------------------------------------------------------------- + */ + + 'dir' => storage_path('cache') + ), + + 'properties' => array( + 'creator' => env('SITE_NAME', 'Dashboard'), + 'lastModifiedBy' => env('SITE_NAME', 'Dashboard'), + 'title' => 'Spreadsheet Export', + 'description' => 'Exported Spreadsheet Data', + 'subject' => 'Spreadsheet export', + 'keywords' => 'excel, export', + 'category' => 'Excel', + 'manager' => env('SITE_NAME', 'Dashboard'), + 'company' => env('SITE_NAME', 'Dashboard'), + ), + + /* + |-------------------------------------------------------------------------- + | Sheets settings + |-------------------------------------------------------------------------- + */ + 'sheets' => array( + + /* + |-------------------------------------------------------------------------- + | Default page setup + |-------------------------------------------------------------------------- + */ + 'pageSetup' => array( + 'orientation' => 'portrait', + 'paperSize' => '9', + 'scale' => '100', + 'fitToPage' => false, + 'fitToHeight' => true, + 'fitToWidth' => true, + 'columnsToRepeatAtLeft' => array('', ''), + 'rowsToRepeatAtTop' => array(0, 0), + 'horizontalCentered' => false, + 'verticalCentered' => false, + 'printArea' => null, + 'firstPageNumber' => null, + ), + ), + + /* + |-------------------------------------------------------------------------- + | Creator + |-------------------------------------------------------------------------- + | + | The default creator of a new Excel file + | + */ + + 'creator' => env('SITE_NAME', 'Dashboard'), + + 'csv' => array( + /* + |-------------------------------------------------------------------------- + | Delimiter + |-------------------------------------------------------------------------- + | + | The default delimiter which will be used to read out a CSV file + | + */ + + 'delimiter' => ',', + + /* + |-------------------------------------------------------------------------- + | Enclosure + |-------------------------------------------------------------------------- + */ + + 'enclosure' => '"', + + /* + |-------------------------------------------------------------------------- + | Line endings + |-------------------------------------------------------------------------- + */ + + 'line_ending' => "\r\n" + ), + + 'export' => array( + + /* + |-------------------------------------------------------------------------- + | Autosize columns + |-------------------------------------------------------------------------- + | + | Disable/enable column autosize or set the autosizing for + | an array of columns ( array('A', 'B') ) + | + */ + 'autosize' => true, + + /* + |-------------------------------------------------------------------------- + | Autosize method + |-------------------------------------------------------------------------- + | + | --> PHPExcel_Shared_Font::AUTOSIZE_METHOD_APPROX + | The default is based on an estimate, which does its calculation based + | on the number of characters in the cell value (applying any calculation + | and format mask, and allowing for wordwrap and rotation) and with an + | "arbitrary" adjustment based on the font (Arial, Calibri or Verdana, + | defaulting to Calibri if any other font is used) and a proportional + | adjustment for the font size. + | + | --> PHPExcel_Shared_Font::AUTOSIZE_METHOD_EXACT + | The second method is more accurate, based on actual style formatting as + | well (bold, italic, etc), and is calculated by generating a gd2 imagettf + | bounding box and using its dimensions to determine the size; but this + | method is significantly slower, and its accuracy is still dependent on + | having the appropriate fonts installed. + | + */ + 'autosize-method' => PHPExcel_Shared_Font::AUTOSIZE_METHOD_APPROX, + + /* + |-------------------------------------------------------------------------- + | Auto generate table heading + |-------------------------------------------------------------------------- + | + | If set to true, the array indices (or model attribute names) + | will automatically be used as first row (table heading) + | + */ + 'generate_heading_by_indices' => true, + + /* + |-------------------------------------------------------------------------- + | Auto set alignment on merged cells + |-------------------------------------------------------------------------- + */ + 'merged_cell_alignment' => 'left', + + /* + |-------------------------------------------------------------------------- + | Pre-calculate formulas during export + |-------------------------------------------------------------------------- + */ + 'calculate' => false, + + /* + |-------------------------------------------------------------------------- + | Include Charts during export + |-------------------------------------------------------------------------- + */ + 'includeCharts' => false, + + /* + |-------------------------------------------------------------------------- + | Default sheet settings + |-------------------------------------------------------------------------- + */ + 'sheets' => array( + + /* + |-------------------------------------------------------------------------- + | Default page margin + |-------------------------------------------------------------------------- + | + | 1) When set to false, default margins will be used + | 2) It's possible to enter a single margin which will + | be used for all margins. + | 3) Alternatively you can pass an array with 4 margins + | Default order: array(top, right, bottom, left) + | + */ + 'page_margin' => false, + + /* + |-------------------------------------------------------------------------- + | Value in source array that stands for blank cell + |-------------------------------------------------------------------------- + */ + 'nullValue' => null, + + /* + |-------------------------------------------------------------------------- + | Insert array starting from this cell address as the top left coordinate + |-------------------------------------------------------------------------- + */ + 'startCell' => 'A1', + + /* + |-------------------------------------------------------------------------- + | Apply strict comparison when testing for null values in the array + |-------------------------------------------------------------------------- + */ + 'strictNullComparison' => false + ), + + /* + |-------------------------------------------------------------------------- + | Store settings + |-------------------------------------------------------------------------- + */ + + 'store' => array( + + /* + |-------------------------------------------------------------------------- + | Path + |-------------------------------------------------------------------------- + | + | The path we want to save excel file to + | + */ + 'path' => storage_path('exports'), + + /* + |-------------------------------------------------------------------------- + | Return info + |-------------------------------------------------------------------------- + | + | Whether we want to return information about the stored file or not + | + */ + 'returnInfo' => false + + ), + + /* + |-------------------------------------------------------------------------- + | PDF Settings + |-------------------------------------------------------------------------- + */ + 'pdf' => array( + + /* + |-------------------------------------------------------------------------- + | PDF Drivers + |-------------------------------------------------------------------------- + | Supported: DomPDF, tcPDF, mPDF + */ + 'driver' => 'DomPDF', + + /* + |-------------------------------------------------------------------------- + | PDF Driver settings + |-------------------------------------------------------------------------- + */ + 'drivers' => array( + + /* + |-------------------------------------------------------------------------- + | DomPDF settings + |-------------------------------------------------------------------------- + */ + 'DomPDF' => array( + 'path' => base_path('vendor/dompdf/dompdf/') + ), + + /* + |-------------------------------------------------------------------------- + | tcPDF settings + |-------------------------------------------------------------------------- + */ + 'tcPDF' => array( + 'path' => base_path('vendor/tecnick.com/tcpdf/') + ), + + /* + |-------------------------------------------------------------------------- + | mPDF settings + |-------------------------------------------------------------------------- + */ + 'mPDF' => array( + 'path' => base_path('vendor/mpdf/mpdf/') + ), + ) + ) + ), + + 'filters' => array( + /* + |-------------------------------------------------------------------------- + | Register read filters + |-------------------------------------------------------------------------- + */ + + 'registered' => array( + 'chunk' => 'Maatwebsite\Excel\Filters\ChunkReadFilter' + ), + + /* + |-------------------------------------------------------------------------- + | Enable certain filters for every file read + |-------------------------------------------------------------------------- + */ + + 'enabled' => array() + ), + + 'import' => array( + + /* + |-------------------------------------------------------------------------- + | Has heading + |-------------------------------------------------------------------------- + | + | The sheet has a heading (first) row which we can use as attribute names + | + | Options: true|false|slugged|slugged_with_count|ascii|numeric|hashed|trans|original + | + */ + + 'heading' => 'slugged', + + /* + |-------------------------------------------------------------------------- + | First Row with data or heading of data + |-------------------------------------------------------------------------- + | + | If the heading row is not the first row, or the data doesn't start + | on the first row, here you can change the start row. + | + */ + + 'startRow' => 1, + + /* + |-------------------------------------------------------------------------- + | Cell name word separator + |-------------------------------------------------------------------------- + | + | The default separator which is used for the cell names + | Note: only applies to 'heading' settings 'true' && 'slugged' + | + */ + + 'separator' => '_', + + /* + |-------------------------------------------------------------------------- + | Include Charts during import + |-------------------------------------------------------------------------- + */ + + 'includeCharts' => false, + + /* + |-------------------------------------------------------------------------- + | Sheet heading conversion + |-------------------------------------------------------------------------- + | + | Convert headings to ASCII + | Note: only applies to 'heading' settings 'true' && 'slugged' + | + */ + + 'to_ascii' => true, + + /* + |-------------------------------------------------------------------------- + | Import encoding + |-------------------------------------------------------------------------- + */ + + 'encoding' => array( + + 'input' => 'UTF-8', + 'output' => 'UTF-8' + + ), + + /* + |-------------------------------------------------------------------------- + | Calculate + |-------------------------------------------------------------------------- + | + | By default cells with formulas will be calculated. + | + */ + + 'calculate' => true, + + /* + |-------------------------------------------------------------------------- + | Ignore empty cells + |-------------------------------------------------------------------------- + | + | By default empty cells are not ignored + | + */ + + 'ignoreEmpty' => false, + + /* + |-------------------------------------------------------------------------- + | Force sheet collection + |-------------------------------------------------------------------------- + | + | For a sheet collection even when there is only 1 sheets. + | When set to false and only 1 sheet found, the parsed file will return + | a row collection instead of a sheet collection. + | When set to true, it will return a sheet collection instead. + | + */ + 'force_sheets_collection' => false, + + /* + |-------------------------------------------------------------------------- + | Date format + |-------------------------------------------------------------------------- + | + | The format dates will be parsed to + | + */ + + 'dates' => array( + + /* + |-------------------------------------------------------------------------- + | Enable/disable date formatting + |-------------------------------------------------------------------------- + */ + 'enabled' => true, + + /* + |-------------------------------------------------------------------------- + | Default date format + |-------------------------------------------------------------------------- + | + | If set to false, a carbon object will return + | + */ + 'format' => false, + + /* + |-------------------------------------------------------------------------- + | Date columns + |-------------------------------------------------------------------------- + */ + 'columns' => array() + ), + + /* + |-------------------------------------------------------------------------- + | Import sheets by config + |-------------------------------------------------------------------------- + */ + 'sheets' => array( + + /* + |-------------------------------------------------------------------------- + | Example sheet + |-------------------------------------------------------------------------- + | + | Example sheet "test" will grab the firstname at cell A2 + | + */ + + 'test' => array( + + 'firstname' => 'A2' + + ) + + ) + ), + + 'views' => array( + + /* + |-------------------------------------------------------------------------- + | Styles + |-------------------------------------------------------------------------- + | + | The default styles which will be used when parsing a view + | + */ + + 'styles' => array( + + /* + |-------------------------------------------------------------------------- + | Table headings + |-------------------------------------------------------------------------- + */ + 'th' => array( + 'font' => array( + 'bold' => true, + 'size' => 12, + ) + ), + + /* + |-------------------------------------------------------------------------- + | Strong tags + |-------------------------------------------------------------------------- + */ + 'strong' => array( + 'font' => array( + 'bold' => true, + 'size' => 12, + ) + ), + + /* + |-------------------------------------------------------------------------- + | Bold tags + |-------------------------------------------------------------------------- + */ + 'b' => array( + 'font' => array( + 'bold' => true, + 'size' => 12, + ) + ), + + /* + |-------------------------------------------------------------------------- + | Italic tags + |-------------------------------------------------------------------------- + */ + 'i' => array( + 'font' => array( + 'italic' => true, + 'size' => 12, + ) + ), + + /* + |-------------------------------------------------------------------------- + | Heading 1 + |-------------------------------------------------------------------------- + */ + 'h1' => array( + 'font' => array( + 'bold' => true, + 'size' => 24, + ) + ), + + /* + |-------------------------------------------------------------------------- + | Heading 2 + |-------------------------------------------------------------------------- + */ + 'h2' => array( + 'font' => array( + 'bold' => true, + 'size' => 18, + ) + ), + + /* + |-------------------------------------------------------------------------- + | Heading 2 + |-------------------------------------------------------------------------- + */ + 'h3' => array( + 'font' => array( + 'bold' => true, + 'size' => 13.5, + ) + ), + + /* + |-------------------------------------------------------------------------- + | Heading 4 + |-------------------------------------------------------------------------- + */ + 'h4' => array( + 'font' => array( + 'bold' => true, + 'size' => 12, + ) + ), + + /* + |-------------------------------------------------------------------------- + | Heading 5 + |-------------------------------------------------------------------------- + */ + 'h5' => array( + 'font' => array( + 'bold' => true, + 'size' => 10, + ) + ), + + /* + |-------------------------------------------------------------------------- + | Heading 6 + |-------------------------------------------------------------------------- + */ + 'h6' => array( + 'font' => array( + 'bold' => true, + 'size' => 7.5, + ) + ), + + /* + |-------------------------------------------------------------------------- + | Hyperlinks + |-------------------------------------------------------------------------- + */ + 'a' => array( + 'font' => array( + 'underline' => true, + 'color' => array('argb' => 'FF0000FF'), + ) + ), + + /* + |-------------------------------------------------------------------------- + | Horizontal rules + |-------------------------------------------------------------------------- + */ + 'hr' => array( + 'borders' => array( + 'bottom' => array( + 'style' => 'thin', + 'color' => array('FF000000') + ), + ) + ) + ) + + ) + +); diff --git a/config/image.php b/config/image.php new file mode 100644 index 0000000..b106809 --- /dev/null +++ b/config/image.php @@ -0,0 +1,20 @@ + 'gd' + +); diff --git a/config/laravel-head.php b/config/laravel-head.php index 0850a7c..906d104 100644 --- a/config/laravel-head.php +++ b/config/laravel-head.php @@ -102,7 +102,7 @@ return array( | */ - 'responsive' => true, + 'responsive' => false, /* |-------------------------------------------------------------------------- diff --git a/config/laravel-newsletter.php b/config/laravel-newsletter.php new file mode 100644 index 0000000..f526045 --- /dev/null +++ b/config/laravel-newsletter.php @@ -0,0 +1,64 @@ + [ + + /* + * The api key of a MailChimp account. You can find yours here: + * https://us10.admin.mailchimp.com/account/api-key-popup/ + */ + 'apiKey' => env('MAILCHIMP_APIKEY'), + + /* + * Here you can define properties of the lists you want to + * send campaigns. + */ + 'lists' => [ + + /* + * This key is used to identify this list. It can be used + * in the various methods provided by this package. + * + * You can set it to any string you want and you can add + * as many lists as you want. + */ + 'subscribers' => [ + + /* + * A mail chimp list id. Check the mailchimp docs if you don't know + * how to get this value: + * http://kb.mailchimp.com/lists/managing-subscribers/find-your-list-id + */ + 'id' => env('MAILCHIMP_LISTID'), + + /* + * These values will be used when creating a new campaign. + */ + 'createCampaign' => [ + 'fromEmail' => '', + 'fromName' => '', + 'toName' => '', + ], + + /* + * These values will be used when subscribing to a list. + */ + 'subscribe' => [ + 'emailType' => 'html', + 'requireDoubleOptin' => false, + 'updateExistingUser' => false, + ], + + /* + * These values will be used when unsubscribing from a list. + */ + 'unsubscribe' => [ + 'deletePermanently' => false, + 'sendGoodbyeEmail' => false, + 'sendUnsubscribeEmail' => false, + ], + ], + ], + ], +]; diff --git a/database/migrations/2015_12_17_232249_add_subscription_table.php b/database/migrations/2015_12_17_232249_add_subscription_table.php new file mode 100644 index 0000000..f2b43f2 --- /dev/null +++ b/database/migrations/2015_12_17_232249_add_subscription_table.php @@ -0,0 +1,33 @@ +increments('id'); + $table->string('name'); + $table->string('email'); + $table->string('location'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('subscriptions'); + } +} diff --git a/database/seeds/DatabaseSeeder.php b/database/seeds/DatabaseSeeder.php index 2a28edd..0b788d2 100644 --- a/database/seeds/DatabaseSeeder.php +++ b/database/seeds/DatabaseSeeder.php @@ -11,6 +11,6 @@ class DatabaseSeeder extends Seeder */ public function run() { - // $this->call(UserTableSeeder::class); + } } diff --git a/gulpfile.js b/gulpfile.js index 8bc7630..a1cd65a 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,6 +1,6 @@ process.env.DISABLE_NOTIFIER = true; -var gulp = require("gulp"), +var gulp = require('gulp'), elixir = require('laravel-elixir'), lessglob = require('less-plugin-glob'); @@ -12,37 +12,55 @@ if (!elixir.config.production) elixir.config.autoprefix = { remove: false, cascade: false, - browsers: ['last 2 versions'] + browsers: [ 'last 2 versions' ] }; -// javascript files in resources/assets/js/ +// javascript files for the dashboard in resources/assets/js/ +var jsDashboard = [ + 'dashboard.js' +]; + +// javascript files for the public site in resources/assets/js/ var jsLocal = [ 'site-vars.js', 'contact.js', + 'subscription.js', 'app.js' ]; -// javascript files in bower_components/ +// javascript files in bower_components/ for libraries var jsBower = [ 'jquery/dist/jquery.min.js', - 'bootstrap/dist/js/bootstrap.min.js' + 'bootstrap/dist/js/bootstrap.min.js', +]; + +var jsDashboardBower = [ + 'Sortable/Sortable.js', + 'datetimepicker/jquery.datetimepicker.js', + 'simplemde/dist/simplemde.min.js' ]; // less import path locations other than resources/assets/less/ -var lessPaths = [ - 'bower_components/bootstrap/less' -]; +var lessPaths = [ 'bower_components' ]; elixir(function(mix) { - // elixir mix functions - mix.copy('bower_components/bootstrap/dist/fonts/bootstrap/**', 'public/fonts') - .less('app.less', 'public/css/app.css', { - paths: lessPaths, - plugins: [lessglob] - }) - .scripts(jsLocal, 'public/js/app.js', 'resources/assets/js/') - .scripts(jsBower, 'public/js/lib.js', 'bower_components/') - .version(['css/app.css', 'js/app.js', 'js/lib.js']); + // compile the project + mix + .copy('bower_components/bootstrap/dist/fonts/**', 'public/fonts') + .copy('bower_components/font-awesome/fonts/**', 'public/fonts') + .less('dashboard.less', 'public/css/dashboard.css', { + paths: lessPaths, + plugins: [ lessglob ] + }) + .less('app.less', 'public/css/app.css', { + paths: lessPaths, + plugins: [ lessglob ] + }) + .scripts(jsLocal, 'public/js/app.js', 'resources/assets/js/') + .scripts(jsDashboard, 'public/js/dashboard.js', 'resources/assets/js/') + .scripts(jsBower, 'public/js/lib.js', 'bower_components/') + .scripts(jsDashboardBower, 'public/js/lib-dashboard.js', 'bower_components/') + .version([ 'css/dashboard.css', 'css/app.css', 'js/dashboard.js', 'js/app.js', 'js/lib.js', 'js/lib-dashboard.js' ]); // start livereload when not production if (!elixir.config.production) diff --git a/readme.md b/readme.md index f10f7ee..3d8be18 100644 --- a/readme.md +++ b/readme.md @@ -2,3 +2,240 @@ A hypothetical website template +## Dashboard + +Unless otherwise stated all examples in this section are to be added to `app/Http/Controllers/DashboardController.php`. + +### Adding a Viewable Model to the Dashboard + +#### Viewable List of Rows + +First add a function to generate the page: + +```php + public function getContact() + { + return view('dashboard.view', [ + 'heading' => 'Contact Form Submissions', + 'model' => 'contact', + 'rows' => Contact::getContactSubmissions(), + 'cols' => [ + [ 'Date', 'created_at' ], + [ 'Name', 'name' ], + [ 'Email', 'email' ], + [ 'Message', 'message' ] + ] + ]); + } +``` + +* `heading`: The title that will appear for this page +* `model`: The model that will be accessed on this page +* `rows`: A function returning an array containing the data to be shown on this page +* `cols`: An array containing a set of arrays where the first element of each is the visible column name and the second is the column name in the array + +#### Export Functionality + +Viewable models must have an entry in the switch statement of the `getExport` function to make the export button work: + +```php + switch ($model) { + case 'contact': + $headings = [ 'Date', 'Name', 'Email', 'Message' ]; + $items = Contact::select('created_at', 'name', 'email', 'message')->get(); + break; + default: + abort(404); + } +``` + +* `$headings`: The visible column names in the same order as the array containing the items to be exported +* `$items`: A function returning an array containing the data to be exported + +### Adding an Editable Model to the Dashboard + +#### Editable List of Rows + +##### Editable List for Unsortable Model + +```php + public function getShows() + { + return view('dashboard.edit-list', [ + 'heading' => 'Shows', + 'model' => 'shows', + 'rows' => Shows::getShowsList(), + 'column' => 'title', + 'sortcol' => 'false' + ]); + } +``` + +##### Editable List for Sortable Model + +**NOTE**: Sortable models must have an entry configured in the `postReorder` function (details below) + +```php + public function getNews() + { + return view('dashboard.edit-list', [ + 'heading' => 'News', + 'model' => 'news', + 'rows' => News::getNewsList(), + 'column' => 'title', + 'sortcol' => 'order' + ]); + } +``` + +* `heading`: The title that will appear for this page +* `model`: The model that will be accessed on this page +* `rows`: A function returning an array containing the data to be shown on this page +* `column`: The column name in the array that contains the data to display in each row +* `sortcol`: The name of the column containing the sort order or `'false'` to disable + +#### Delete Functionality + +Editable models must have an entry in the switch statement of the `deleteDelete` function to make deletion functionality work: + +```php + switch ($request['model']) { + case 'shows': + $items = new Shows(); + break; + case 'news': + $items = new News(); + break; + default: + return 'model-access-fail'; + } +``` + +#### Editable Item + +This function should be named the same as the one above except with `Edit` at the end + +##### Editable Item for Unsortable Model + +```php + public function getShowsEdit($id = 'new') + { + if ($id != 'new') { + if (Shows::where('id', $id)->exists()) { + $item = Shows::where('id', $id)->first(); + } else { + return view('errors.no-such-record'); + } + } else { + $item = null; + } + + return view('dashboard.edit-item', [ + 'heading' => 'Shows', + 'model' => 'shows', + 'id' => $id, + 'item' => $item, + 'help_text' => 'NOTE: This is some help text for the current page', + 'columns' => [ + [ 'name' => 'venue', 'type' => 'text' ], + [ 'name' => 'date', 'type' => 'date' ], + [ 'name' => 'address', 'type' => 'text' ], + [ 'name' => 'phone', 'type' => 'text' ], + [ 'name' => 'website', 'type' => 'text' ], + [ 'name' => 'cover', 'type' => 'text' ], + [ 'name' => 'description', 'type' => 'mkd' ] + ] + ]); + } +``` + +##### Editable Item for Sortable Model + +```php + public function getNewsEdit($id = 'new') + { + if ($id != 'new') { + if (News::where('id', $id)->exists()) { + $item = News::where('id', $id)->first(); + } else { + return view('errors.no-such-record'); + } + } else { + $item = new News(); + $item['order'] = News::count(); + } + + return view('dashboard.edit-item', [ + 'heading' => 'News', + 'model' => 'news', + 'id' => $id, + 'item' => $item, + 'imgup' => true, + 'columns' => [ + [ 'name' => 'title', 'type' => 'text', 'label' => 'The Title' ], + [ 'name' => 'iframe', 'type' => 'text' ], + [ 'name' => 'story', 'type' => 'mkd' ], + [ 'name' => 'order', 'type' => 'hidden' ] + ] + ]); + } +``` + +* `heading`: The title that will appear for this page +* `model`: The model that will be accessed on this page +* `id`: Always set this to `$id` +* `item`: Always set this to `$item` +* `imgup`: Set this to `true` to enable image upload, otherwise set to `false` +* `help_text`: An optional value that will add a box containing help text above the form if set +* `columns`: An array containing a set of arrays where: + * `name` is the name of the column to be edited + * `type` is the type of column (details below) + * `label` is an optional value that overrides the visible column name + +###### Editable Column Types + +The following is a list of possible `types` in the `columns` array for Editable Items: + +* `text`: Text input field for text data +* `mkd`: Markdown editor for text data containing markdown +* `date`: Date and time selection tool for date/time data +* `hidden`: Fields that will contain values to pass to the update function but won't appear on the page (this must be used for the sort column) + +#### Edit Item Functionality + +Editable models must have an entry in the switch statement of the `postEdit` function to make create and edit functionality work: + +```php + switch ($request['model']) { + case 'shows': + $item = $id == 'new' ? new Shows : Shows::find($id); + break; + case 'news': + $item = $id == 'new' ? new News : News::find($id); + break; + default: + return 'model-access-fail'; + } +``` + +#### Additional Requirement for Sortable Models + +Sortable models must have an entry in the switch statement of the `postReorder` function to make sorting functionality work: + +```php + switch ($request['model']) { + case 'news': + $items = new News(); + break; + default: + return 'model-access-fail'; + } +``` + +#### Additional Requirements for Image Upload + +If the value of `imgup` has been set to `true`, ensure `public/uploads/model_name` exists (where `model_name` is the name of the given model) and contains a `.gitkeep` that exists in version control. + +By default, uploaded images are saved in JPEG format with the value of the `id` column of the respective row as its name and `.jpg` as its file extension. + +When a row is deleted, its respective image will be deleted as well if it exists. diff --git a/resources/assets/js/app.js b/resources/assets/js/app.js index aca44a6..21938e0 100644 --- a/resources/assets/js/app.js +++ b/resources/assets/js/app.js @@ -1,6 +1,11 @@ // run once the document is ready $(document).ready(function() { - // initialize the contact form - if ($('#contact-form').length) - contactFormInit(); + switch (SiteVars.page) { + case '': + subscriptionFormInit(); + break; + case 'contact': + contactFormInit(); + break; + } }); diff --git a/resources/assets/js/contact.js b/resources/assets/js/contact.js index d31b8b5..396a7d9 100644 --- a/resources/assets/js/contact.js +++ b/resources/assets/js/contact.js @@ -1,6 +1,7 @@ -// contact page functionality +// contact form functionality function contactFormInit() { var $form = $('#contact-form'), + $input = $form.find(':input'), $notify = $('#notification'), contact = {}, submitting = false; @@ -16,6 +17,7 @@ function contactFormInit() { $('#submit').on('click', function(e) { e.preventDefault(); + var $submit = $(this); if (!submitting) { submitting = true; @@ -30,6 +32,8 @@ function contactFormInit() { $notify.removeClass('visible'); if (response === 'success') { + $input.attr('disabled', true); + $submit.addClass('disabled'); $notify.text('Thanks for your message!').addClass('success').addClass('visible'); } else { var responseJSON = response.responseJSON, diff --git a/resources/assets/js/dashboard.js b/resources/assets/js/dashboard.js new file mode 100644 index 0000000..63578ad --- /dev/null +++ b/resources/assets/js/dashboard.js @@ -0,0 +1,415 @@ +// declare a reverse function for jquery +jQuery.fn.reverse = [].reverse; + +// show the confirmation modal and run the supplied command if confirm is pressed +function askConfirmation(message, command) { + var $confirmationModal = $('#confirmation-modal'), + $heading = $confirmationModal.find('.panel-heading'), + $cancelButton = $confirmationModal.find('.btn.cancel-button'), + $confirmButton = $confirmationModal.find('.btn.confirm-button'), + fadeTime = 250; + + // close the confirmation modal and unbind its events + var closeConfirmationModal = function() { + // unbind events + $(document).off('keyup', escapeModal); + $cancelButton.off('click', closeConfirmationModal); + $confirmButton.off('click', confirmModal); + + // clear the heading + $heading.empty(); + + // hide the modal + $confirmationModal.css({ opacity: 0 }); + setTimeout(function() { $confirmationModal.css({ visibility: 'hidden' }); }, fadeTime); + }; + + // close the modal if the escape button is pressed + var escapeModal = function(e) { + if (e.keyCode == 27) + closeConfirmationModal(); + }; + + // functionality to run when clicking the confirm button + var confirmModal = function() { + command(); + closeConfirmationModal(); + }; + + // hide the modal when the cancel button is pressed + $cancelButton.on('click', closeConfirmationModal); + + // hide the modal when the escape key is pressed + $(document).on('keyup', escapeModal); + + // run the command and hide the modal when the confirm button is pressed + $confirmButton.on('click', confirmModal); + + // set the heading with the supplied message + $heading.text(message); + + // show the confirmation modal + $confirmationModal.css({ + visibility: 'visible', + opacity: 1 + }); +} + +// show the alert modal and display the provided message until accept is pressed +function showAlert(message, command) { + var $alertModal = $('#alert-modal'), + $message = $alertModal.find('.message'), + $acceptButton = $alertModal.find('.btn.accept-button'), + fadeTime = 250; + + // close the alert modal and unbind its events + var closeAlertModal = function() { + // unbind events + $(document).off('keyup', escapeModal); + $acceptButton.off('click', closeAlertModal); + + // clear the message + $message.empty(); + + // hide the modal + $alertModal.css({ opacity: 0 }); + setTimeout(function() { $alertModal.css({ visibility: 'hidden' }); }, fadeTime); + + // if a command was passed run it now + if (command !== undefined) + command(); + }; + + // close the modal if the escape button is pressed + var escapeModal = function(e) { + if (e.keyCode == 27) + closeAlertModal(); + }; + + // hide the modal when the escape key is pressed + $(document).on('keyup', escapeModal); + + // hide the modal when the accept button is pressed + $acceptButton.on('click', closeAlertModal); + + // set the message with the supplied message + $message.text(message); + + // show the alert modal + $alertModal.css({ + visibility: 'visible', + opacity: 1 + }); +} + +// initialize edit list functionality +function editListInit() { + var editList = document.getElementById('edit-list'), + $editList = $(editList), + model = $editList.data('model'); + + // initialize new button functionality + var newButtonInit = function() { + var $newButton = $('.btn.new-button'); + + $newButton.on('click', function() { + window.location.href = '/dashboard/' + model + '-edit/new'; + }); + }; + + // initialize edit button functionality + var editButtonInit = function() { + var $editButtons = $('.btn.edit-button'); + + $editButtons.on('click', function() { + var $this = $(this), + $listItem = $this.closest('.list-group-item'), + itemId = $listItem.data('id'); + + // go to the edit page + window.location.href = '/dashboard/' + model + '-edit/' + itemId; + }); + }; + + // initialize delete button functionality + var deleteButtonInit = function() { + var $deleteButtons = $('.btn.delete-button'); + + $deleteButtons.on('click', function() { + var $this = $(this), + $listItem = $this.closest('.list-group-item'), + itemId = $listItem.data('id'); + + askConfirmation('Are you sure you want to delete this?', function() { + $.ajax({ + type: 'DELETE', + url: '/dashboard/delete', + data: { + model: model, + id: itemId, + _token: $('#token').val() + } + }).always(function(response) { + if (response === 'success') { + $listItem.slideUp(150, function() { $listItem.remove(); }); + } else { + showAlert('ERROR: Failed to delete record'); + } + }); + }); + }); + }; + + // initialize sort functionality if data-sort is set + var sortRowInit = function() { + if ($editList.attr('data-sort')) { + var sortCol = $editList.data('sort'), + sortOrder = {}; + + var sortable = Sortable.create(editList, { + handle: '.sort-icon', + onUpdate: function() { + // update the sortOrder object based on the updated order + $editList.find('.list-group-item').reverse().each(function(index) { + var thisId = $(this).data('id'); + sortOrder[thisId] = index; + }); + + $.ajax({ + type: 'POST', + url: '/dashboard/reorder', + data: { + model: model, + order: sortOrder, + column: sortCol, + _token: $('#token').val() + } + }).always(function(response) { + if (response !== 'success') { + showAlert('ERROR: Sorting failed', function() { + document.location.reload(true); + }); + } + }); + } + }); + } + }; + + newButtonInit(); + editButtonInit(); + deleteButtonInit(); + sortRowInit(); +} + +function editItemInit() { + var $editItem = $('#edit-item'), + $submit = $('#submit'), + $backButton = $('#back'), + $textInputs = $('.text-input'), + $dateTimePickers = $('.date-time-picker'), + $mkdEditors = $('.mkd-editor'), + $imgUpload = $('#image-upload'), + $token = $('#_token'), + $spinner = $('#loading-modal'), + fadeTime = 250, + model = $editItem.data('model'), + id = $editItem.data('id'), + allowTimes = [], + simplemde = [], + formData = {}, + submitting = false; + + // show the loading modal + var showLoadingModal = function() { + $spinner.css({ + visibility: 'visible', + opacity: 1 + }); + }; + + // hide the loading modal + var hideLoadingModal = function() { + $spinner.css({ opacity: 0 }); + setTimeout(function() { $spinner.css({ visibility: 'hidden' }); }, fadeTime); + }; + + // fill the formData object with data from all the form fields + var getFormData = function() { + // function to add a column and value to the formData object + var addFormData = function(column, value) { + // add the value to a key with the column name + formData[column] = value; + + // add the column to the array of columns + formData.columns.push(column); + }; + + // reset the formData object + formData = {}; + + // add the database model row id and _token + formData.model = model; + formData.id = id; + formData._token = $token.val(); + + // create an empty array to contain the list of columns + formData.columns = []; + + // add values from the contents of text-input class elements + $textInputs.each(function() { + var $this = $(this), + column = $this.attr('id'), + value = $this.val(); + + addFormData(column, value); + }); + + // add values from the contents of date-time-picker class elements + $dateTimePickers.each(function() { + var $this = $(this), + column = $this.attr('id'), + value = $this.val() + ':00'; + + addFormData(column, value); + }); + + // add values from the contents of the markdown editor for mkd-editor class elements + $mkdEditors.each(function() { + var $this = $(this), + column = $this.attr('id'), + value = simplemde[column].value(); + + addFormData(column, value); + }); + }; + + var uploadImage = function(row_id) { + // functionality to run on success + var returnSuccess = function() { + hideLoadingModal(); + window.location.href = '/dashboard/' + model; + }; + + // add the image from the image upload box for image-upload class elements + if ($imgUpload.length && $imgUpload.val() !== '') { + var file = new FormData(); + + // add the file, id and model to the formData variable + file.append('file', $imgUpload[0].files[0]); + file.append('id', row_id); + file.append('model', model); + + $.ajax({ + type: 'POST', + url: '/dashboard/image-upload', + data: file, + processData: false, + contentType: false, + beforeSend: function(xhr) { xhr.setRequestHeader('X-CSRF-TOKEN', $token.val()); } + }).always(function(response) { + if (response === 'success') { + returnSuccess(); + } else { + hideLoadingModal(); + showAlert('ERROR: Failed to upload image'); + submitting = false; + } + }); + } else { + returnSuccess(); + } + }; + + // allow start time selection to start on the hour and every 15 minutes after + for (var hours=0; hours<=23; hours++) { + for (var minutes=0; minutes<=3; minutes++) { + allowTimes.push(hours + ':' + (minutes === 0 ? '00' : minutes * 15)); + } + } + + // enable the datetimepicker for each element with the date-time-picker class + $dateTimePickers.each(function() { + $(this).datetimepicker({ + format: 'Y-m-d H:i', + allowTimes: allowTimes, + step: 15 + }); + }); + + // enable the markdown editor for each element with the mkd-editor class + $mkdEditors.each(function() { + var $this = $(this), + column = $this.attr('id'); + + simplemde[column] = new SimpleMDE({ + element: this, + toolbar: [ + 'bold', + 'italic', + '|', + 'heading-1', + 'heading-2', + 'heading-3', + '|', + 'quote', + 'unordered-list', + 'ordered-list', + 'link' + ], + blockStyles: { italic: '_' }, + autoDownloadFontAwesome: false, + tabSize: 4 + }); + + setTimeout(function() { + simplemde[column].value($this.attr('value')); + }, 100); + }); + + // initialize back button + $backButton.on('click', function() { + if (!submitting) { + askConfirmation('Cancel and return to the ' + model + ' list?', function() { + window.location.href = '/dashboard/' + model; + }); + } + }); + + // initialize submit button + $submit.on('click', function() { + if (!submitting) { + submitting = true; + + // show the loading modal + showLoadingModal(); + + // populate the formData object + getFormData(); + + // submit the update + $.ajax({ + type: 'POST', + url: '/dashboard/edit', + data: formData + }).always(function(response) { + if (/^id:[0-9][0-9]*$/.test(response)) { + uploadImage(response.replace(/^id:/, '')); + } else { + hideLoadingModal(); + showAlert('ERROR: Failed to update record'); + submitting = false; + } + }); + } + }); +} + +// run once the document is ready +$(document).ready(function() { + if ($('#edit-list').length) { + editListInit(); + } else if ($('#edit-item').length) { + editItemInit(); + } +}); diff --git a/resources/assets/js/subscription.js b/resources/assets/js/subscription.js new file mode 100644 index 0000000..d2808cd --- /dev/null +++ b/resources/assets/js/subscription.js @@ -0,0 +1,63 @@ +// subscription form functionality +function subscriptionFormInit() { + var $form = $('#subscription-form'), + $input = $form.find(':input'), + $notify = $('#notification'), + subscribe = {}, + submitting = false; + + var getSubscribeData = function() { + subscribe = { + name: $('#name').val(), + email: $('#email').val(), + address: $('#address').val(), + _token: $('#token').val() + }; + }; + + $('#submit').on('click', function(e) { + e.preventDefault(); + var $submit = $(this); + + if (!submitting) { + submitting = true; + getSubscribeData(); + + $.ajax({ + type: 'POST', + url: '/subscription-submit', + data: subscribe + }).always(function(response) { + $form.find('.error').removeClass('error'); + $notify.removeClass('visible').removeClass('error'); + + if (response === 'success') { + $form.addClass('success'); + + setTimeout(function() { + $notify.text('Thanks for subscribing!').addClass('success').addClass('visible'); + $input.fadeOut(150); + }, 1000); + } else { + var responseJSON = response.responseJSON, + errors = 0; + + // add the error class to fields that haven't been filled out + for (var prop in responseJSON) { + if (responseJSON.hasOwnProperty(prop)) { + $('#' + prop).addClass('error'); + errors++; + } + } + + // if there are no errors with form fields then there must have been an API error + if (errors === 0) + $notify.text('An error occurred. Are you already subscribed?').addClass('error').addClass('visible'); + + // re-enable submitting + submitting = false; + } + }); + } + }); +} diff --git a/resources/assets/less/app.less b/resources/assets/less/app.less index 2815932..84b37fb 100644 --- a/resources/assets/less/app.less +++ b/resources/assets/less/app.less @@ -1,11 +1,8 @@ // Core -@import "bootstrap"; +@import "bootstrap/less/bootstrap"; @import "var"; @import "fonts"; -// Base -@import "auth"; - // Supplementary @import "elements/**"; @import "pages/**"; @@ -21,6 +18,8 @@ html, body { height: 100%; } +html.no-scroll { overflow-y: hidden; } + body { min-width: 300px; margin: 0; diff --git a/resources/assets/less/auth.less b/resources/assets/less/auth.less deleted file mode 100644 index 893ee20..0000000 --- a/resources/assets/less/auth.less +++ /dev/null @@ -1,33 +0,0 @@ -.auth-container { - form { - margin-top: 50px; - padding: 25px 25px 50px 25px; - border-radius: 5px; - border: 1px solid darken(@c_accent, 5%); - background-color: @c_accent; - - .form-field { - label { - float: left; - min-width: 125px; - } - - .input { - display: block; - overflow: hidden; - margin-bottom: 10px; - - input { - width: 100%; - height: 24px; - } - } - } - - button { - position: absolute; - right: 41px; - bottom: 25px; - } - } -} diff --git a/resources/assets/less/dashboard.less b/resources/assets/less/dashboard.less new file mode 100644 index 0000000..28dc3f8 --- /dev/null +++ b/resources/assets/less/dashboard.less @@ -0,0 +1,356 @@ +// Core +@import "bootstrap/less/bootstrap"; +@import "font-awesome/less/font-awesome"; +@import "awesome-bootstrap-checkbox/awesome-bootstrap-checkbox"; +@import (inline) "datetimepicker/jquery.datetimepicker.css"; +@import (inline) "simplemde/dist/simplemde.min.css"; +@import (inline) "SpinKit/css/spinners/11-folding-cube.css"; +@import "var"; +@import "fonts"; +@import "classes"; +@fa-font-path: "../../fonts"; + +* { + outline: none !important; + .font_sans; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +body { min-width: 350px; } + +nav.navbar { + background-color: @c_dashboard_dark; + .navbar-brand { font-weight: bold; } + + #spark-navbar-collapse { + box-shadow: none; + border: none; + + .dropdown { + .dropdown-toggle .caret { margin-left: 4px; } + + &.open .dropdown-toggle { + background-color: darken(@c_dashboard_dark, 5%); + color: @c_dashboard_light; + } + + .dropdown-menu { + top: calc(100% ~"-" 1px); + right: -1px; + background-color: @c_dashboard_dark; + & > li > a:hover, & > li > a:focus { background-color: transparent; } + & > li > a { &, &:hover, &:focus { color: #fff; } } + } + } + } + + .navbar-brand, .navbar-default .navbar-nav > li > a, .navbar-nav > li > a { + &, &:hover, &:focus { color: @c_dashboard_light; } + } + + .navbar-toggle { + position: relative; + bottom: 0px; + left: 10px; + border: none; + &, &:hover, &:focus { background-color: transparent; } + + .icon-bar { + width: 27px; + height: 4px; + background-color: @c_dashboard_light; + transition: background-color 100ms; + } + + &:hover .icon-bar { background-color: darken(@c_dashboard_dark, 15%); } + } +} + +.panel-default { + margin-top: 20px; + margin-bottom: 40px; + + .panel-body { + padding-bottom: 0px; + background-color: lighten(@c_dashboard_light, 1%); + + .help-text { + padding: 5px 10px; + margin-top: 10px; + margin-bottom: 10px; + border-radius: 5px; + border: 1px solid darken(@c_dashboard_dark, 5%); + background-color: @c_dashboard_dark; + color: @c_text_light; + } + } + + & > .panel-heading { + border-top: 1px solid darken(@c_dashboard_dark, 5%); + border-right: 1px solid darken(@c_dashboard_dark, 5%); + border-left: 1px solid darken(@c_dashboard_dark, 5%); + background-color: @c_dashboard_dark; + color: @c_dashboard_light; + font-weight: bold; + + .btn { + float: right; + position: relative; + bottom: 3px; + min-width: 70px; + height: 26px; + padding-top: 1px; + padding-bottom: 2px; + } + + a { + text-decoration: none; + color: @c_text; + } + } +} + +.form-control { + &:focus { + border-color: @c_dashboard_dark; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px fade(@c_dashboard_dark, 60%); + } +} + +.btn { + border-color: darken(@c_dashboard_dark, 5%); + background-color: @c_dashboard_dark; + transition: background-color 100ms; + &:hover { background-color: darken(@c_dashboard_dark, 5%); } + &:focus { background-color: lighten(@c_dashboard_dark, 5%); } + + &.btn-warning { + border-color: darken(@c_dashboard_edit, 10%); + background-color: @c_dashboard_edit; + &:hover { background-color: darken(@c_dashboard_edit, 5%); } + &:focus { background-color: lighten(@c_dashboard_edit, 5%); } + } + + &.btn-danger { + border-color: darken(@c_dashboard_delete, 10%); + background-color: @c_dashboard_delete; + &:hover { background-color: darken(@c_dashboard_delete, 5%); } + &:focus { background-color: lighten(@c_dashboard_delete, 5%); } + } + + &.btn-default { + border-color: darken(@c_dashboard_light, 10%); + background-color: @c_dashboard_light; + color: @c_text; + &:hover { background-color: darken(@c_dashboard_light, 5%); } + &:focus { background-color: lighten(@c_dashboard_light, 5%); } + } + + &.btn-link { color: @c_dashboard_light; } + &, &:hover, &:focus { text-decoration: none; } +} + +.table { + & > thead > tr > th { border-bottom: 1px solid #666; } + + @media (max-width: (@screen-sm - 1)) { + tr.heading-row { display: none; } + tr:not(:first-child) { border-top: 1px solid #ddd; } + + & > tbody > tr > td { + &:first-child { padding-top: 20px; } + &:last-child { padding-bottom: 20px; } + } + + & > tbody > tr:first-child > td:first-child { padding-top: 0px; } + & > tbody > tr:last-child > td:last-child { padding-bottom: 0px; } + + & > tbody > tr > td { + display: block; + border-top: none; + } + } + + @media (min-width: @screen-sm) { + & > tbody > tr > td { padding: 20px 8px; } + & > tbody > tr:last-child > td { padding-bottom: 0px; } + .mobile-heading { display: none; } + } +} + +.list-group { + margin-bottom: 0px; + padding-bottom: 15px; + + &.linked-list { + margin-bottom: 0px; + + .list-group-item { + padding: 0px; + + a { + display: block; + padding: 10px 15px; + color: @c_dashboard_dark; + background-color: #fff; + transition: background-color 100ms, color 100ms; + &:focus, &:hover { text-decoration: none; } + + &:hover { + color: @c_dashboard_light; + background-color: @c_dashboard_dark; + } + } + + &:first-child a { + border-top-right-radius: 4px; + border-top-left-radius: 4px; + } + + &:last-child a { + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; + } + } + } + + &.edit-list { + .list-group-item { + position: relative; + padding-top: 4px; + padding-bottom: 4px; + + .sort-icon { + display: inline-block; + margin-right: 10px; + cursor: grab; + cursor: -webkit-grabbing; + } + + .title-column { + padding-top: 6px; + padding-bottom: 6px; + padding-left: 0px; + } + + .button-column { + text-align: right; + padding-right: 0px; + padding-left: 0px; + + .btn { + min-width: 70px; + height: 26px; + margin: 3px; + padding-top: 1px; + padding-bottom: 2px; + } + } + } + } +} + +.edit-item { + margin-top: 10px; + .CodeMirror, .CodeMirror-scroll { min-height: 100px; } + .date-time-picker { cursor: pointer; } + + label { + height: 32px; + line-height: 32px; + } + + input { + display: block; + width: 100%; + margin-bottom: 15px; + } + + .current-image { + width: 125px; + height: 125px; + background-size: contain; + background-position: left center; + background-repeat: no-repeat; + } + + .back-button { float: left; } + .submit-button { float: right; } + + .back-button, .submit-button { + margin: 25px 15px 15px 15px; + + @media (max-width: (@screen-sm - 1)) { + float: none; + width: calc(100% ~"-" 30px); + &:first-child { margin: 25px 15px 5px 15px; } + &:last-child { margin: 5px 15px 25px 15px; } + } + } +} + +#loading-modal { + position: fixed; + top: 0px; + left: 0px; + width: 100%; + height: 100%; + background-color: fade(lighten(@c_dashboard_light, 1%), 40%); + transition: opacity 250ms; + opacity: 0; + visibility: hidden; + z-index: 1000; + + .spinner-container { + position: absolute; + top: 50%; + left: 50%; + transform: translateX(-50%) translateY(-50%); + } +} + +.modal { + display: table; + position: fixed; + top: 0px; + left: 0px; + width: 100%; + height: 100%; + background-color: fade(lighten(@c_dashboard_light, 1%), 40%); + transition: opacity 250ms; + opacity: 0; + visibility: hidden; + z-index: 1000; + + .modal-container { + display: table-cell; + vertical-align: middle; + text-align: center; + .panel { margin: 0px; } + + .btn { + display: inline-block; + margin: 20px 15px; + } + } + + &#alert-modal { + .modal-container { + .panel { position: relative; } + + .message { + min-height: 50px; + padding: 15px 106px 10px 20px; + text-align: left; + } + + .btn { + position: absolute; + right: 0px; + top: 50%; + transform: translateY(-50%); + } + } + } +} diff --git a/resources/assets/less/elements/footer.less b/resources/assets/less/elements/footer.less index 2e7f561..7a13e37 100644 --- a/resources/assets/less/elements/footer.less +++ b/resources/assets/less/elements/footer.less @@ -1,3 +1,9 @@ footer { - + width: 100%; + height: 32px; + line-height: 32px; + padding-left: 10px; + background-color: #000; + color: @c_text_light; + font-size: 11px; } diff --git a/resources/assets/less/elements/nav.less b/resources/assets/less/elements/nav.less index c86e384..9f8d423 100644 --- a/resources/assets/less/elements/nav.less +++ b/resources/assets/less/elements/nav.less @@ -1,18 +1,55 @@ nav.navbar { - background-color: @c_base; + height: @nav_height; + margin-bottom: 0px; + padding-right: 10px; + background: linear-gradient(to right, transparent 0%, fade(@c_base, 40%) 25%, fade(@c_base, 50%) 50%, @c_base 100%); border: none; border-radius: 0px; z-index: 1; + @media (max-width: @grid-float-breakpoint-max) { + height: @mobile_nav_height; + background: fade(@c_base, 55%); + } + .navbar-logo { - top: 25px; - left: 15px; - width: 150px; - height: 150px; + position: fixed; + top: 45px; + left: 63px; + width: 145px; + height: 145px; background-image: url('/img/logo.png'); background-size: contain; background-position: center center; background-repeat: no-repeat; + cursor: pointer; + transition: transform 200ms; + &:hover { transform: scale(1.03); } + + &:active { + transition: transform 100ms; + transform: scale(1.01); + } + + @media (max-width: @grid-float-breakpoint-max) { + top: 5px; + left: 10px; + width: 40px; + height: 40px; + } + + @media (min-width: @grid-float-breakpoint) { + @logoAnimSpeed: 400ms; + transition: transform 200ms, top @logoAnimSpeed, left @logoAnimSpeed, width @logoAnimSpeed, height @logoAnimSpeed; + &:active { transition: transform 100ms, top @logoAnimSpeed, left @logoAnimSpeed, width @logoAnimSpeed, height @logoAnimSpeed; } + + &.scaled { + top: 10px; + left: 15px; + width: @nav_height - 20; + height: @nav_height - 20; + } + } } #navbar { @@ -22,20 +59,69 @@ nav.navbar { ul.nav { li { a { + padding: 0px 15px; + user-select: none; color: @c_text_light; - font-weight: bold; text-decoration: none; + text-transform: uppercase; + font-size: 36px; + font-weight: 500; + font-stretch: ultra-condensed; + line-height: @nav_height; + transition: color 100ms; + @media (max-width: 1250px) { font-size: 28px; } + @media (max-width: 1050px) { font-size: 24px; } + + &:after { + content: ""; + display: block; + position: absolute; + bottom: calc(50% ~"-" 0.8em); + left: 50%; + transform: translateX(-50%); + width: calc(100% ~"-" 20px); + height: 3px; + background-color: @c_text_light; + opacity: 0; + transition: opacity 200ms; + @media (max-width: @grid-float-breakpoint-max) { bottom: 0px; } + } } - &:hover a { color: @c_text; } - &.active a { color: @c_text; } &.active a, &.active:hover a, &:hover a, &:focus a { background-color: transparent; } + &:hover a { color: darken(@c_text_light, 25%); } + &.active a:after { opacity: 1; } + } + } + + @media (max-width: @grid-float-breakpoint-max) { + position: relative; + bottom: 4px; + width: 100vw; + padding-bottom: 5px; + background-color: fade(@c_base, 55%); + + ul.nav li a { + line-height: 40px; + font-size: 18px; } } } .navbar-toggle { + position: relative; + bottom: 2px; + left: 20px; border: none; &, &:hover, &:focus { background-color: transparent; } + + .icon-bar { + width: 27px; + height: 4px; + background-color: @c_text_light; + transition: background-color 150ms; + } + + &:hover .icon-bar { background-color: darken(@c_text_light, 25%); } } } diff --git a/resources/assets/less/pages/contact.less b/resources/assets/less/pages/contact.less new file mode 100644 index 0000000..ba7b08f --- /dev/null +++ b/resources/assets/less/pages/contact.less @@ -0,0 +1,53 @@ +body.contact { + #contact-form { + @trans-speed: 100ms; + margin-top: 35px; + margin-bottom: 20px; + + input, textarea { + width: 100%; + margin-bottom: 20px; + padding: 5px 10px; + font-size: 14px; + background-color: rgba(255, 255, 255, 0.8); + border: 2px solid fade(@c_text, 25%); + transition: border @trans-speed; + &:focus { border: 2px solid fade(@c_base, 60%); } + &.error { border: 2px solid @c_error; } + } + + textarea { + resize: none; + height: 150px; + } + + #submit { + text-align: center; + font-weight: bold; + color: @c_text_light; + background-color: lighten(@c_base, 5%); + transition: background-color @trans-speed; + &:hover { background-color: @c_base; } + &.disabled { background-color: @c_base; } + } + + #notification { + margin: 0px auto 15px auto; + padding: 5px 10px; + color: @c_text_light; + text-align: center; + font-size: 14px; + opacity: 0; + background-color: lighten(@c_error, 15%); + transition: opacity @trans-speed; + span { font-weight: bold; } + &.visible { opacity: 1; } + + &.success { + background-color: transparent; + color: @c_text; + font-weight: bold; + } + } + } +} diff --git a/resources/assets/less/var.less b/resources/assets/less/var.less index 73bca33..40f30f1 100644 --- a/resources/assets/less/var.less +++ b/resources/assets/less/var.less @@ -1,3 +1,12 @@ +/* + | + | Overrides + | + */ + +@grid-float-breakpoint: @screen-sm; +@grid-float-breakpoint-max: (@grid-float-breakpoint - 1); + /* | | Custom Variables @@ -8,5 +17,14 @@ @c_text: #111; // text @c_text_light: #fff; // light text @c_base: #0088cc; // base -@c_accent: #f5f5f5; // accent +@c_accent: #000; // accent @c_error: #ff0000; // error + +@c_dashboard_dark: #3e6087; +@c_dashboard_light: #f1f1f1; +@c_dashboard_edit: #87823e; +@c_dashboard_delete: #87483e; + +// Sizes +@nav_height: 96px; +@mobile_nav_height: 50px; diff --git a/resources/assets/less/website/contact.less b/resources/assets/less/website/contact.less deleted file mode 100644 index acf3f9b..0000000 --- a/resources/assets/less/website/contact.less +++ /dev/null @@ -1,49 +0,0 @@ -#contact-form { - margin-top: 35px; - margin-bottom: 20px; - - input, textarea { - width: 100%; - margin-bottom: 20px; - padding: 5px 10px; - font-size: 14px; - background-color: rgba(255, 255, 255, 0.8); - border: 2px solid fade(@c_text, 25%); - transition: border 100ms; - &:focus { border: 2px solid fade(@c_base, 60%); } - &.error { border: 2px solid @c_error; } - } - - textarea { - resize: none; - height: 150px; - } - - #submit { - text-align: center; - font-weight: bold; - color: @c_text_light; - background-color: lighten(@c_base, 5%); - transition: background-color 100ms; - &:hover { background-color: @c_base; } - } - - #notification { - margin: 0px auto 15px auto; - padding: 5px 10px; - color: @c_text_light; - text-align: center; - font-size: 14px; - opacity: 0; - background-color: lighten(@c_error, 15%); - transition: opacity 100ms; - &.visible { opacity: 1; } - span { font-weight: bold; } - - &.success { - background-color: transparent; - color: @c_text; - font-weight: bold; - } - } -} diff --git a/resources/views/auth.blade.php b/resources/views/auth.blade.php deleted file mode 100644 index c4ba305..0000000 --- a/resources/views/auth.blade.php +++ /dev/null @@ -1,11 +0,0 @@ -@extends('base') - -@section('page-content') -
-
-
- @yield('auth-form') -
-
-
-@endsection diff --git a/resources/views/auth/emails/password.blade.php b/resources/views/auth/emails/password.blade.php new file mode 100644 index 0000000..c4a5834 --- /dev/null +++ b/resources/views/auth/emails/password.blade.php @@ -0,0 +1 @@ +Click here to reset your password: {{ url('password/reset', $token).'?email='.urlencode($user->getEmailForPasswordReset()) }} diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php index ddc8c03..8ccfae1 100644 --- a/resources/views/auth/login.blade.php +++ b/resources/views/auth/login.blade.php @@ -1,24 +1,65 @@ -@extends('auth') +@extends('layouts.dashboard') -@section('auth-form') -
- {!! csrf_field() !!} +@section('content') +
+
+
+
+
Login
+
+ + {!! csrf_field() !!} -
- -
+
+ + +
+ + + @if ($errors->has('email')) + + {{ $errors->first('email') }} + + @endif +
+
+ +
+ + +
+ + + @if ($errors->has('password')) + + {{ $errors->first('password') }} + + @endif +
+
+ +
+
+
+ + +
+
+
+ +
+
+ + + Forgot Your Password? +
+
+ +
+
+
- -
- -
-
- -
- - -
- - - +
@endsection diff --git a/resources/views/auth/passwords/email.blade.php b/resources/views/auth/passwords/email.blade.php new file mode 100644 index 0000000..98df540 --- /dev/null +++ b/resources/views/auth/passwords/email.blade.php @@ -0,0 +1,46 @@ +@extends('layouts.dashboard') + +@section('content') +
+
+
+
+
Reset Password
+
+ @if (session('status')) +
+ {{ session('status') }} +
+ @endif + +
+ {!! csrf_field() !!} + +
+ + +
+ + + @if ($errors->has('email')) + + {{ $errors->first('email') }} + + @endif +
+
+ +
+
+ +
+
+
+
+
+
+
+
+@endsection diff --git a/resources/views/auth/passwords/reset.blade.php b/resources/views/auth/passwords/reset.blade.php new file mode 100644 index 0000000..3ed9731 --- /dev/null +++ b/resources/views/auth/passwords/reset.blade.php @@ -0,0 +1,70 @@ +@extends('layouts.dashboard') + +@section('content') +
+
+
+
+
Reset Password
+ +
+
+ {!! csrf_field() !!} + + + +
+ + +
+ + + @if ($errors->has('email')) + + {{ $errors->first('email') }} + + @endif +
+
+ +
+ + +
+ + + @if ($errors->has('password')) + + {{ $errors->first('password') }} + + @endif +
+
+ +
+ +
+ + + @if ($errors->has('password_confirmation')) + + {{ $errors->first('password_confirmation') }} + + @endif +
+
+ +
+
+ +
+
+
+
+
+
+
+
+@endsection diff --git a/resources/views/auth/register.blade.php b/resources/views/auth/register.blade.php index 8e3e94b..096dd62 100644 --- a/resources/views/auth/register.blade.php +++ b/resources/views/auth/register.blade.php @@ -1,29 +1,82 @@ -@extends('auth') +@extends('layouts.dashboard') -@section('auth-form') -
- {!! csrf_field() !!} +@section('content') +
+
+
+
+
Register
+
+ + {!! csrf_field() !!} -
- -
+
+ + +
+ + + @if ($errors->has('name')) + + {{ $errors->first('name') }} + + @endif +
+
+ +
+ + +
+ + + @if ($errors->has('email')) + + {{ $errors->first('email') }} + + @endif +
+
+ +
+ + +
+ + + @if ($errors->has('password')) + + {{ $errors->first('password') }} + + @endif +
+
+ +
+ + +
+ + + @if ($errors->has('password_confirmation')) + + {{ $errors->first('password_confirmation') }} + + @endif +
+
+ +
+
+ +
+
+ +
+
+
- -
- -
-
- -
- -
-
- -
- -
-
- - - +
@endsection diff --git a/resources/views/contact.blade.php b/resources/views/contact.blade.php deleted file mode 100644 index 8d80d8c..0000000 --- a/resources/views/contact.blade.php +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - -

Name: {{ $contact['name'] }}

-

Email: {{ $contact['email'] }}

-

{{ $contact['message'] }}

- - diff --git a/resources/views/dashboard/core.blade.php b/resources/views/dashboard/core.blade.php new file mode 100644 index 0000000..37420b4 --- /dev/null +++ b/resources/views/dashboard/core.blade.php @@ -0,0 +1,52 @@ +@extends('layouts.dashboard') + +@section('content') +
+
+
+
+
+ {{ $heading }} + @yield('dashboard-heading') +
+ +
+ @yield('dashboard-body') +
+
+
+
+
+ + + + +@endsection diff --git a/resources/views/dashboard/edit-item.blade.php b/resources/views/dashboard/edit-item.blade.php new file mode 100644 index 0000000..c441354 --- /dev/null +++ b/resources/views/dashboard/edit-item.blade.php @@ -0,0 +1,78 @@ +@extends('dashboard.core') + +@section('dashboard-body') + @if(!empty($help_text)) +
+
+
+
+ {!! $help_text !!} +
+
+
+
+ @endif + +
+ + +
+
+ @foreach($columns as $column) + @set('value', empty($item->$column['name']) ? '' : $item->$column['name']) + + @if($column['type'] == 'hidden') + + @else +
+ +
+ +
+ @if($column['type'] == 'text') + + @elseif($column['type'] == 'date') + + @elseif($column['type'] == 'mkd') + + @endif +
+ @endif + @endforeach + + @if(!empty($imgup) && $imgup) +
+ +
+ +
+ + + @set('current_image', "/uploads/$model/$id.jpg") + @if(file_exists(base_path() . '/public' . $current_image)) +
+ @else +
(No Image Set)
+ @endif +
+ @endif +
+ +
+ + +
+
+ + +
+
+
+
+
+
+
+
+
+
+@endsection diff --git a/resources/views/dashboard/edit-list.blade.php b/resources/views/dashboard/edit-list.blade.php new file mode 100644 index 0000000..ce814a4 --- /dev/null +++ b/resources/views/dashboard/edit-list.blade.php @@ -0,0 +1,32 @@ +@extends('dashboard.core') + +@section('dashboard-heading') + +@endsection + +@section('dashboard-body') + @set('sort_data', $sortcol != 'false' ? "data-sort=$sortcol" : '') + @set('sort_icon', $sortcol != 'false' ? '' : '') + +
    + + + @foreach($rows as $row) +
  • +
    +
    +
    + {!! $sort_icon !!} + {{ $row[$column] }} +
    + +
    + + +
    +
    +
    +
  • + @endforeach +
+@endsection diff --git a/resources/views/dashboard/elements/menu.blade.php b/resources/views/dashboard/elements/menu.blade.php new file mode 100644 index 0000000..d1b7549 --- /dev/null +++ b/resources/views/dashboard/elements/menu.blade.php @@ -0,0 +1,11 @@ +@set('menu', [ + [ 'Contact', 'contact' ], + [ 'Subscriptions', 'subscriptions' ], + [ 'Shows', 'shows' ], + [ 'News', 'news' ], + [ 'Videos', 'videos' ] +]) + +@foreach($menu as $menu_item) +
  • {{ $menu_item[0] }}
  • +@endforeach diff --git a/resources/views/dashboard/elements/nav.blade.php b/resources/views/dashboard/elements/nav.blade.php new file mode 100644 index 0000000..6b41ef5 --- /dev/null +++ b/resources/views/dashboard/elements/nav.blade.php @@ -0,0 +1,39 @@ + diff --git a/resources/views/dashboard/home.blade.php b/resources/views/dashboard/home.blade.php new file mode 100644 index 0000000..a4ba775 --- /dev/null +++ b/resources/views/dashboard/home.blade.php @@ -0,0 +1,6 @@ +@extends('dashboard.core') + +@section('dashboard-body') + @set('menu_class', 'list-group-item') +
      @include('dashboard.elements.menu')
    +@endsection diff --git a/resources/views/dashboard/view.blade.php b/resources/views/dashboard/view.blade.php new file mode 100644 index 0000000..d396212 --- /dev/null +++ b/resources/views/dashboard/view.blade.php @@ -0,0 +1,27 @@ +@extends('dashboard.core') + +@section('dashboard-heading') + +@endsection + +@section('dashboard-body') + + + + @foreach($cols as $column) + + @endforeach + + + + + @foreach($rows as $row) + + @foreach($cols as $column) + + @endforeach + + @endforeach + +
    {{ $column[0] }}
    {{ $column[0] }}: {{ $row[$column[1]] }}
    +@endsection diff --git a/resources/views/elements/footer.blade.php b/resources/views/elements/footer.blade.php index 0ed3a6c..14c9e32 100644 --- a/resources/views/elements/footer.blade.php +++ b/resources/views/elements/footer.blade.php @@ -1,3 +1 @@ -
    - -
    +
    Copyright © {{ date('Y') }} {{ env('SITE_NAME') }}
    diff --git a/resources/views/elements/nav.blade.php b/resources/views/elements/nav.blade.php index 9f8bccb..711b4ca 100644 --- a/resources/views/elements/nav.blade.php +++ b/resources/views/elements/nav.blade.php @@ -1,19 +1,17 @@ -