From e02d57ad1702c8a9d1cdba1bb5892bab4718fe32 Mon Sep 17 00:00:00 2001 From: Kevin MacMartin Date: Wed, 25 Apr 2018 23:27:45 -0400 Subject: [PATCH] Move the user api_token migration to its original migration, add website and social media columns to the original user migration, hook up user profile edit functionality on the dashboard settings page, name the user password function userPasswordUpdateInit to reflect its form name, clean up the dashboard settings layout and improve it on mobile, add a repeating pattern background to the dashboard so it's not so sparse, and sync the traditional-bootstrap routes/web.php with the main one --- app/Http/Controllers/DashboardController.php | 18 + .../2014_10_12_000000_create_users_table.php | 8 +- ...21_193450_add_api_token_to_users_table.php | 32 -- public/img/dashboard/star-bg.svg | 1 + resources/assets/js/dashboard.js | 86 +++- resources/assets/sass/dashboard.scss | 456 +++++++++++------- .../views/dashboard/pages/settings.blade.php | 89 +++- resources/views/templates/dashboard.blade.php | 1 + routes/web.php | 1 + traditional-bootstrap/routes/web.php | 16 +- 10 files changed, 461 insertions(+), 247 deletions(-) delete mode 100644 database/migrations/2017_11_21_193450_add_api_token_to_users_table.php create mode 100644 public/img/dashboard/star-bg.svg diff --git a/app/Http/Controllers/DashboardController.php b/app/Http/Controllers/DashboardController.php index e02dbd4..678be9d 100644 --- a/app/Http/Controllers/DashboardController.php +++ b/app/Http/Controllers/DashboardController.php @@ -392,6 +392,24 @@ class DashboardController extends Controller { } } + // User Profile Update + public function postUserProfileUpdate(Request $request) + { + $this->validate($request, [ + 'name' => 'required|string|max:255' + ]); + + $user = User::find(Auth::id()); + $user->name = $request['name']; + $user->website = $request['website']; + $user->facebook = $request['facebook']; + $user->soundcloud = $request['soundcloud']; + $user->instagram = $request['instagram']; + $user->twitter = $request['twitter']; + $user->save(); + return 'success'; + } + // User Profile Image Upload public function postUserProfileImageUpload(Request $request) { diff --git a/database/migrations/2014_10_12_000000_create_users_table.php b/database/migrations/2014_10_12_000000_create_users_table.php index 07f6139..3c4b267 100644 --- a/database/migrations/2014_10_12_000000_create_users_table.php +++ b/database/migrations/2014_10_12_000000_create_users_table.php @@ -15,9 +15,15 @@ class CreateUsersTable extends Migration { Schema::create('users', function(Blueprint $table) { $table->increments('id'); - $table->string('name'); $table->string('email')->unique(); + $table->string('name'); + $table->string('website')->nullable(); + $table->string('facebook')->nullable(); + $table->string('twitter')->nullable(); + $table->string('instagram')->nullable(); + $table->string('soundcloud')->nullable(); $table->string('password'); + $table->string('api_token', 60)->unique(); $table->rememberToken(); $table->timestamps(); }); diff --git a/database/migrations/2017_11_21_193450_add_api_token_to_users_table.php b/database/migrations/2017_11_21_193450_add_api_token_to_users_table.php deleted file mode 100644 index 7655086..0000000 --- a/database/migrations/2017_11_21_193450_add_api_token_to_users_table.php +++ /dev/null @@ -1,32 +0,0 @@ -string('api_token', 60)->unique(); - }); - } - - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::table('users', function(Blueprint $table) { - $table->dropColumn('api_token'); - }); - } -} diff --git a/public/img/dashboard/star-bg.svg b/public/img/dashboard/star-bg.svg new file mode 100644 index 0000000..dca0cdf --- /dev/null +++ b/public/img/dashboard/star-bg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/assets/js/dashboard.js b/resources/assets/js/dashboard.js index c2c99aa..d5c2f19 100644 --- a/resources/assets/js/dashboard.js +++ b/resources/assets/js/dashboard.js @@ -703,8 +703,82 @@ function userProfileImageInit() { }); } -function userPasswordInit() { - const $form = $("#user-password"), +function userProfileUpdateInit() { + const $form = $("#user-profile-update"), + $submit = $form.find(".submit-button"), + $inputs = $form.find("input"), + $name = $("#name"), + $website = $("#website"), + $facebook = $("#facebook"), + $soundcloud = $("#soundcloud"), + $instagram = $("#instagram"), + $twitter = $("#twitter"), + $token = $("#token"); + + let formData = {}, + submitting = false; + + const getFormData = function() { + formData = { + name: $name.val(), + website: $website.val(), + facebook: $facebook.val(), + soundcloud: $soundcloud.val(), + instagram: $instagram.val(), + twitter: $twitter.val(), + _token: $token.val() + }; + }; + + // remove the error class from an input and enable submit when its value changes + $inputs.on("input change", function() { + $submit.removeClass("no-input"); + $(this).removeClass("error"); + }); + + // initialize submit button + $submit.on("click", function() { + if (!submitting) { + submitting = true; + + // remove the error class from inputs + $inputs.removeClass("error"); + + // show the loading modal + showLoadingModal(); + + // populate the formData object + getFormData(); + + // submit the update + $.ajax({ + type: "POST", + url: "/dashboard/user/profile-update", + data: formData + }).always(function(response) { + hideLoadingModal(); + submitting = false; + + if (response === "success") { + $submit.addClass("no-input"); + showAlert("User profile updated successfully"); + } else { + // add the error class to fields that haven't been filled correctly + for (let errorName in response.responseJSON.errors) { + if ($form.find(`[name='${errorName}']`).length) { + $form.find(`[name='${errorName}']`).addClass("error"); + } + } + + showAlert("Error updating user profile"); + } + }); + } + }); +} + +function userPasswordUpdateInit() { + const $form = $("#user-password-update"), $submit = $form.find(".submit-button"), $inputs = $form.find("input"), $oldpass = $("#oldpass"), @@ -806,7 +880,11 @@ $(document).ready(function() { userProfileImageInit(); } - if ($("#user-password").length) { - userPasswordInit(); + if ($("#user-profile-update").length) { + userProfileUpdateInit(); + } + + if ($("#user-password-update").length) { + userPasswordUpdateInit(); } }); diff --git a/resources/assets/sass/dashboard.scss b/resources/assets/sass/dashboard.scss index acc0a82..dc5bf0c 100644 --- a/resources/assets/sass/dashboard.scss +++ b/resources/assets/sass/dashboard.scss @@ -39,6 +39,7 @@ body { } .site-content { + position: relative; display: flex; min-height: 100vh; flex-direction: column; @@ -48,6 +49,33 @@ body { } } +.dashboard-background { + position: absolute; + top: 0px; + left: 0px; + width: 100%; + height: 100%; + background-image: url("/img/dashboard/star-bg.svg"); + background-position: center top; + background-size: 65px auto; + background-repeat: repeat; + + &:after { + content: ""; + position: absolute; + top: 0px; + left: 0px; + width: 100%; + height: 100%; + background-color: fade-out(#fff, 0.02); + } +} + +.navbar, .dashboard-footer { + z-index: 10; + position: relative; +} + .navbar { margin-bottom: $grid-gutter-width; border: 0; @@ -652,223 +680,283 @@ body { } } -.edit-item { - $label-height: 30px; +form { margin-top: 10px; - .CodeMirror { - height: 300px; - padding: 5px; - - &-code { - margin-bottom: 10px; - } - } - - .picker__holder { - overflow-y: hidden; - - .picker__button--today { - white-space: nowrap; - } - - .picker__select--year, .picker__select--month, .picker__month, .picker__day, .picker__weekday, .picker__footer { - @include media-breakpoint-down(sm) { - font-size: 16px; - } - } - - .picker__select--year, .picker__select--month { - width: auto; - height: 1.5em; - padding: 0px; - } - } - - label { - min-height: $label-height; - line-height: $label-height; - - @include media-breakpoint-up(md) { - margin-bottom: 0px; - } - } - - .text-display, .mkd-editor-container, input, select { + .form-title { + @include font-sans-semibold; + margin-top: 0px; margin-bottom: 15px; - } - - input { - display: block; - width: 100%; - - &:not([type="file"]) { - padding: 5px 8px; - border: 1px solid darken($c-dashboard-light, 10%); - border-radius: 2px; - transition: border-color 150ms; - - &.error { - border-color: $c-dashboard-error; - } - } - - &[type="file"] { - height: $label-height; - font-size: 14px; - } - - &.date-picker { - cursor: pointer; - } - } - - .current-image { - margin-bottom: 15px; - display: block; - width: 125px; - max-width: 100%; - } - - .edit-button { - margin-bottom: ($grid-gutter-width / 2); - display: inline-block; - padding: 5px 10px; - border-radius: 5px; + font-size: 14px; text-transform: uppercase; - transition: background-color 150ms; - cursor: pointer; - - &:hover, &:focus { - text-decoration: none; - } - - &.view { - border: 1px solid darken($c-dashboard-dark, 5%); - background-color: $c-dashboard-dark; - color: $c-text-light; - - &:hover { - background-color: lighten($c-dashboard-dark, 5%); - } - } - - &.delete { - border: 1px solid darken($c-dashboard-delete, 5%); - background-color: $c-dashboard-delete; - color: $c-text-light; - - &:hover { - background-color: lighten($c-dashboard-delete, 5%); - } - } } - .back-button { - float: left; - } + &.edit-item { + $label-height: 30px; - .submit-button { - float: right; - transition: opacity 150ms; + .CodeMirror { + height: 300px; + padding: 5px; - &.no-input { - opacity: 0.65; - pointer-events: none; - } - } - - .back-button, .submit-button { - margin: 20px 15px 15px 15px; - - @include media-breakpoint-down(sm) { - float: none; - width: calc(100% - 30px); - - &:first-child { - margin-top: 20px; - margin-bottom: 5px; - } - - &:last-child { - margin-top: 5px; - margin-bottom: 20px; + &-code { + margin-bottom: 10px; } } - &.no-horizontal-margins { - margin-right: 0px; - margin-left: 0px; + .picker__holder { + overflow-y: hidden; - @include media-breakpoint-down(sm) { - width: 100%; + .picker__button--today { + white-space: nowrap; + } + + .picker__select--year, .picker__select--month, .picker__month, .picker__day, .picker__weekday, .picker__footer { + @include media-breakpoint-down(sm) { + font-size: 16px; + } + } + + .picker__select--year, .picker__select--month { + width: auto; + height: 1.5em; + padding: 0px; } } - } - &.user-profile-image { - display: block; - width: 100%; - max-width: 150px; + label { + min-height: $label-height; + line-height: $label-height; - @include media-breakpoint-down(sm) { - margin: $grid-gutter-width auto; + @include media-breakpoint-up(md) { + margin-bottom: 0px; + } } - .image-display { - @include aspect-ratio(1, 1); - position: relative; + .text-display, .mkd-editor-container, input, select { + margin-bottom: 15px; + } + + input { + display: block; width: 100%; - border: 1px solid darken($c-dashboard-light, 10%); - border-radius: 3px; - background-position: center center; - background-size: cover; - background-repeat: no-repeat; - } - .image-buttons { - margin-top: 20px; - display: flex; - justify-content: space-around; + &:not([type="file"]) { + padding: 5px 8px; + border: 1px solid darken($c-dashboard-light, 10%); + border-radius: 2px; + transition: border-color 150ms; - input { - display: none; + &.error { + border-color: $c-dashboard-error; + } } - .image-upload-button, .image-delete-button { - display: block; - width: 40px; - height: 40px; - min-height: 0; - border: 1px solid darken($c-dashboard-light, 14%); - border-radius: 3px; - background-color: darken($c-dashboard-light, 10%); - background-position: center center; - background-size: 50% auto; - background-repeat: no-repeat; - font-size: 0px; - line-height: 1; + &[type="file"] { + height: $label-height; + font-size: 14px; + } + + &.date-picker { cursor: pointer; + } + } + + .current-image { + margin-bottom: 15px; + display: block; + width: 125px; + max-width: 100%; + } + + .edit-button { + margin-bottom: ($grid-gutter-width / 2); + display: inline-block; + padding: 5px 10px; + border-radius: 5px; + text-transform: uppercase; + transition: background-color 150ms; + cursor: pointer; + + &:hover, &:focus { + text-decoration: none; + } + + &.view { + border: 1px solid darken($c-dashboard-dark, 5%); + background-color: $c-dashboard-dark; + color: $c-text-light; &:hover { - background-color: darken($c-dashboard-light, 7%); + background-color: lighten($c-dashboard-dark, 5%); } } - .image-upload-button { - background-image: url("/img/dashboard/icons/upload.svg"); - transition: background-color 150ms; + &.delete { + border: 1px solid darken($c-dashboard-delete, 5%); + background-color: $c-dashboard-delete; + color: $c-text-light; + + &:hover { + background-color: lighten($c-dashboard-delete, 5%); + } + } + } + + .back-button { + float: left; + } + + .submit-button { + float: right; + transition: opacity 150ms; + + &.no-input { + opacity: 0.65; + pointer-events: none; + } + } + + .back-button, .submit-button { + margin: 20px 15px 15px 15px; + + @include media-breakpoint-down(sm) { + float: none; + width: calc(100% - 30px); + + &:first-child { + margin-top: 20px; + margin-bottom: 5px; + } + + &:last-child { + margin-top: 5px; + margin-bottom: 20px; + } + } + } + } +} + +.dashboard-settings-container { + @include media-breakpoint-up(lg) { + display: flex; + flex-direction: row; + } + + form { + &.user-profile-image { + display: block; + width: 100%; + max-width: 150px; + + @include media-breakpoint-down(md) { + margin: $grid-gutter-width auto; } - .image-delete-button { - background-image: url("/img/dashboard/icons/trash-alt.svg"); - opacity: 1; - transition: background-color 150ms, opacity 150ms; + @include media-breakpoint-up(lg) { + flex-shrink: 0; + } - &.inactive { - opacity: 0.35; - pointer-events: none; + .image-display { + @include aspect-ratio(1, 1); + position: relative; + width: 100%; + border: 1px solid darken($c-dashboard-light, 10%); + border-radius: 3px; + background-position: center center; + background-size: cover; + background-repeat: no-repeat; + } + + .image-buttons { + margin-top: 20px; + display: flex; + justify-content: space-around; + + input { + display: none; } + + .image-upload-button, .image-delete-button { + display: block; + width: 40px; + height: 40px; + min-height: 0; + border: 1px solid darken($c-dashboard-light, 14%); + border-radius: 3px; + background-color: darken($c-dashboard-light, 10%); + background-position: center center; + background-size: 50% auto; + background-repeat: no-repeat; + font-size: 0px; + line-height: 1; + cursor: pointer; + + &:hover { + background-color: darken($c-dashboard-light, 7%); + } + } + + .image-upload-button { + background-image: url("/img/dashboard/icons/upload.svg"); + transition: background-color 150ms; + } + + .image-delete-button { + background-image: url("/img/dashboard/icons/trash-alt.svg"); + opacity: 1; + transition: background-color 150ms, opacity 150ms; + + &.inactive { + opacity: 0.35; + pointer-events: none; + } + } + } + } + + &.user-profile-update { + @include media-breakpoint-down(md) { + border-top: 1px solid darken($c-dashboard-light, 10%); + border-bottom: 1px solid darken($c-dashboard-light, 10%); + } + + @include media-breakpoint-up(lg) { + margin-right: $grid-gutter-width; + margin-left: $grid-gutter-width; + width: 100%; + padding: 0px $grid-gutter-width; + flex-grow: 1; + border-right: 1px solid darken($c-dashboard-light, 10%); + border-left: 1px solid darken($c-dashboard-light, 10%); + } + } + + &.user-password-update { + @include media-breakpoint-up(lg) { + width: 225px; + flex-shrink: 0; + } + } + + .form-title { + @include media-breakpoint-down(md) { + margin-top: 25px; + margin-bottom: 20px; + text-align: center; + } + } + + .submit-button { + @include media-breakpoint-down(sm) { + margin-right: auto; + margin-left: auto; + width: 100%; + } + + @include media-breakpoint-up(md) { + float: none; + margin: ($grid-gutter-width / 2) 0px; } } } diff --git a/resources/views/dashboard/pages/settings.blade.php b/resources/views/dashboard/pages/settings.blade.php index c4ae57d..3b6843a 100644 --- a/resources/views/dashboard/pages/settings.blade.php +++ b/resources/views/dashboard/pages/settings.blade.php @@ -7,32 +7,75 @@
-
-
diff --git a/resources/views/templates/dashboard.blade.php b/resources/views/templates/dashboard.blade.php index 38dd14c..b917375 100644 --- a/resources/views/templates/dashboard.blade.php +++ b/resources/views/templates/dashboard.blade.php @@ -8,6 +8,7 @@ @endsection @section('page-top') +
@include('dashboard.sections.nav') @endsection diff --git a/routes/web.php b/routes/web.php index 2e2a1f7..468e1e6 100644 --- a/routes/web.php +++ b/routes/web.php @@ -46,6 +46,7 @@ Route::group([ 'prefix' => 'dashboard' ], function() { // Dashboard Settings Route::get('/settings', 'DashboardController@getSettings'); Route::post('/user/password-update', 'DashboardController@postUserPasswordUpdate'); + Route::post('/user/profile-update', 'DashboardController@postUserProfileUpdate'); Route::post('/user/profile-image-upload', 'DashboardController@postUserProfileImageUpload'); Route::delete('/user/profile-image-delete', 'DashboardController@deleteUserProfileImageDelete'); diff --git a/traditional-bootstrap/routes/web.php b/traditional-bootstrap/routes/web.php index 179f009..e1c1173 100644 --- a/traditional-bootstrap/routes/web.php +++ b/traditional-bootstrap/routes/web.php @@ -29,19 +29,29 @@ Route::get('/logout', 'Auth\LoginController@logout'); */ Route::group([ 'prefix' => 'dashboard' ], function() { + // Dashboard CMS Route::get('/', 'DashboardController@getIndex'); - Route::get('/credits', 'DashboardController@getCredits'); Route::get('/view/{model}', 'DashboardController@getView'); Route::get('/edit/{model}', 'DashboardController@getEditList'); Route::get('/edit/{model}/{id}', 'DashboardController@getEditItem'); Route::get('/export/{model}', 'DashboardController@getExport'); + Route::post('/reorder', 'DashboardController@postReorder'); + Route::post('/update', 'DashboardController@postUpdate'); Route::post('/image-upload', 'DashboardController@postImageUpload'); Route::post('/file-upload', 'DashboardController@postFileUpload'); - Route::post('/update', 'DashboardController@postUpdate'); - Route::post('/reorder', 'DashboardController@postReorder'); Route::delete('/delete', 'DashboardController@deleteDelete'); Route::delete('/image-delete', 'DashboardController@deleteImageDelete'); Route::delete('/file-delete', 'DashboardController@deleteFileDelete'); + + // Dashboard Settings + Route::get('/settings', 'DashboardController@getSettings'); + Route::post('/user/password-update', 'DashboardController@postUserPasswordUpdate'); + Route::post('/user/profile-update', 'DashboardController@postUserProfileUpdate'); + Route::post('/user/profile-image-upload', 'DashboardController@postUserProfileImageUpload'); + Route::delete('/user/profile-image-delete', 'DashboardController@deleteUserProfileImageDelete'); + + // Credits Page + Route::get('/credits', 'DashboardController@getCredits'); }); /*