From 9849d48895599f37e0797a9f6b66e0c703a889bd Mon Sep 17 00:00:00 2001 From: Immanuel Onyeka <immanuel@onyeka.ca> Date: Thu, 10 Jun 2021 14:16:42 -0400 Subject: [PATCH] Display billing information in settings --- app/Http/Controllers/UserController.php | 5 +- .../2014_10_12_000000_create_users_table.php | 3 +- resources/js/panel/edit-cards.vue | 87 +++++++++++++++++++ resources/js/panel/settings.vue | 8 +- resources/scss/main.scss | 39 +++++++++ 5 files changed, 137 insertions(+), 5 deletions(-) create mode 100644 resources/js/panel/edit-cards.vue diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 648a70a..61aadab 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -34,7 +34,8 @@ class UserController extends Controller $user->active = true; $user->password = Hash::make($request->password); $user->customer_id = Customer::create(['email' => - $request->email], 'name' => $request->name, 'metadata' => ['user_id' => $user->id])->id; + $request->email, 'name' => $request->name, 'metadata' => ['user_id' + => $user->id]])->id; $user->save(); event(new Registered($user)); @@ -91,7 +92,6 @@ class UserController extends Controller //It should have an orderBy clause to make sure the most recent are first //This should limit non pending orders to 50. Should also return a json of all services public function getOrders(Request $request) { - /* Log::debug(Auth::user()->orders()->service_name); */ return Auth::user()->orders()->with('service')->withCasts(['updated_at' => 'datetime:d-m-Y'])->latest()->get(); } @@ -123,6 +123,7 @@ class UserController extends Controller public function resetEmail(Request $request) { Stripe::setApiKey(env('STRIPE_SECRET')); + if (! $request->hasValidSignature()) { abort(401); } 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 8f263a9..eae54c0 100644 --- a/database/migrations/2014_10_12_000000_create_users_table.php +++ b/database/migrations/2014_10_12_000000_create_users_table.php @@ -20,7 +20,8 @@ class CreateUsersTable extends Migration $table->string('customer_id')->unique(); $table->timestamp('email_verified_at')->nullable(); $table->string('password'); - $table->string('role'); + $table->enum('role', ['client', 'guest', 'admin']); + $table->string('payment_method'); $table->boolean('active')->default(true); $table->unsignedBigInteger('credits')->default(0); $table->rememberToken(); diff --git a/resources/js/panel/edit-cards.vue b/resources/js/panel/edit-cards.vue new file mode 100644 index 0000000..a156a7f --- /dev/null +++ b/resources/js/panel/edit-cards.vue @@ -0,0 +1,87 @@ +<template> +<div v-if="cards && cards.length > 0"> +<div class="saved-cards-heading"> +<h5>Card</h5> <h5>Default</h5> <h5>Delete</h5> +</div> +<div v-for="(card, index) in cards" :key="card.id" class="saved-card"> + <span>{{card.card.brand[0].toUpperCase() + card.card.brand.substring(1)}} + (••••{{card.card.last4}})</span> + <span><input :checked="index === 0" :value="card.id" name="selected-card" type="radio" + @change="change(card.id)"></span> + <span><img @click="remove(card.id)" src="../../images/close-icon-black.svg"/></span> +</div> +<p id="billing-error"></p> +</div> +</template> + +<script> +function get() { + fetch('/panel/cards', { + method: 'GET', + headers: {'Content-Type': 'application/json', + 'Accept': 'application/json', + 'X-XSRF-TOKEN': this.token} + }).then((response) => {response.json().then(data => { + this.cards = data.data + })}) +} + +function change(card) { + fetch('/panel/change-card', { + method: 'POST', + headers: {'Content-Type': 'application/json', + 'Accept': 'application/json', + 'X-XSRF-TOKEN': this.token}, + body: JSON.stringify({'card': card}) + }).then((response) => { + if (response.ok) { + console.log('ok') + response.json().then((data) => { + this.cards = data + }) + } else { + console.log('bad') + document.getElementById("billing-error").textContent = + `${response.status}: ${response.statusText}` + } + response.json().then(data => { + console.log(data)} + ) + }) +} + +function remove(card) { + fetch('/panel/delete-card', { + method: 'POST', + headers: {'Content-Type': 'application/json', + 'Accept': 'application/json', + 'X-XSRF-TOKEN': this.token}, + body: JSON.stringify({'card': card}) + }).then((response) => { + if (response.ok) { + console.log('ok') + response.json().then((data) => { + this.cards = data + }) + } else { + console.log('bad') + document.getElementById("billing-error").textContent = + `${response.status}: ${response.statusText}` + } + response.json().then(data => { + console.log(data)} + ) + }) +} + +export default { + data() { + return {cards: null} + }, + methods: {get, change, remove}, + created() { + this.get() + }, + props: ['token',], +} +</script> diff --git a/resources/js/panel/settings.vue b/resources/js/panel/settings.vue index 60fcf02..81668f9 100644 --- a/resources/js/panel/settings.vue +++ b/resources/js/panel/settings.vue @@ -1,8 +1,11 @@ <template> <div> - <section class="change-name-pane"> <h3>Settings</h3> + <section class="billing-pane"> <h4>Billing</h4> + <edit-cards :token="token"></edit-cards> + </section> + <section class="change-name-pane"> <h4>Name</h4> <input :value="user.name" name="name" id="changed_name" type="text"> <button @click="changeName">Save <loading src="../../images/loading-white.svg" alt=""></loading></button> @@ -27,6 +30,7 @@ <script> import Loading from '../icons/loading.vue' +import EditCards from './edit-cards.vue' function changeName() { let name = document.getElementById('changed_name').value @@ -107,7 +111,7 @@ function changePassword() { } export default { - components: {Loading}, + components: {Loading, EditCards}, methods: { changePassword, changeName, changeEmail }, diff --git a/resources/scss/main.scss b/resources/scss/main.scss index a183c43..c543bd5 100644 --- a/resources/scss/main.scss +++ b/resources/scss/main.scss @@ -1272,3 +1272,42 @@ div#card-errors { display: block; } } + +.saved-cards-heading { + display: flex; + align-items: center; + justify-content: space-around; + max-width: 25em; + margin: auto; + + h5 { + margin: 0; + } + +} + +.settings-page .saved-card { + max-width: 25em; + // align-items: start; + justify-content: space-between; + margin-top: 1em; + + > * { + min-width: 20%; + } + + img { + width: 15px; + display: block; + } + + input { + display: block; + margin: auto; + } +} + +.saved-card button { + width: 3em; + height: 2em; +}