@@ -34,7 +34,8 @@ class UserController extends Controller | |||||
$user->active = true; | $user->active = true; | ||||
$user->password = Hash::make($request->password); | $user->password = Hash::make($request->password); | ||||
$user->customer_id = Customer::create(['email' => | $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(); | $user->save(); | ||||
event(new Registered($user)); | 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 | //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 | //This should limit non pending orders to 50. Should also return a json of all services | ||||
public function getOrders(Request $request) { | public function getOrders(Request $request) { | ||||
/* Log::debug(Auth::user()->orders()->service_name); */ | |||||
return Auth::user()->orders()->with('service')->withCasts(['updated_at' | return Auth::user()->orders()->with('service')->withCasts(['updated_at' | ||||
=> 'datetime:d-m-Y'])->latest()->get(); | => 'datetime:d-m-Y'])->latest()->get(); | ||||
} | } | ||||
@@ -123,6 +123,7 @@ class UserController extends Controller | |||||
public function resetEmail(Request $request) { | public function resetEmail(Request $request) { | ||||
Stripe::setApiKey(env('STRIPE_SECRET')); | Stripe::setApiKey(env('STRIPE_SECRET')); | ||||
if (! $request->hasValidSignature()) { | if (! $request->hasValidSignature()) { | ||||
abort(401); | abort(401); | ||||
} | } | ||||
@@ -20,7 +20,8 @@ class CreateUsersTable extends Migration | |||||
$table->string('customer_id')->unique(); | $table->string('customer_id')->unique(); | ||||
$table->timestamp('email_verified_at')->nullable(); | $table->timestamp('email_verified_at')->nullable(); | ||||
$table->string('password'); | $table->string('password'); | ||||
$table->string('role'); | |||||
$table->enum('role', ['client', 'guest', 'admin']); | |||||
$table->string('payment_method'); | |||||
$table->boolean('active')->default(true); | $table->boolean('active')->default(true); | ||||
$table->unsignedBigInteger('credits')->default(0); | $table->unsignedBigInteger('credits')->default(0); | ||||
$table->rememberToken(); | $table->rememberToken(); | ||||
@@ -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> |
@@ -1,8 +1,11 @@ | |||||
<template> | <template> | ||||
<div> | <div> | ||||
<section class="change-name-pane"> | |||||
<h3>Settings</h3> | <h3>Settings</h3> | ||||
<section class="billing-pane"> | |||||
<h4>Billing</h4> | <h4>Billing</h4> | ||||
<edit-cards :token="token"></edit-cards> | |||||
</section> | |||||
<section class="change-name-pane"> | |||||
<h4>Name</h4> | <h4>Name</h4> | ||||
<input :value="user.name" name="name" id="changed_name" type="text"> | <input :value="user.name" name="name" id="changed_name" type="text"> | ||||
<button @click="changeName">Save <loading src="../../images/loading-white.svg" alt=""></loading></button> | <button @click="changeName">Save <loading src="../../images/loading-white.svg" alt=""></loading></button> | ||||
@@ -27,6 +30,7 @@ | |||||
<script> | <script> | ||||
import Loading from '../icons/loading.vue' | import Loading from '../icons/loading.vue' | ||||
import EditCards from './edit-cards.vue' | |||||
function changeName() { | function changeName() { | ||||
let name = document.getElementById('changed_name').value | let name = document.getElementById('changed_name').value | ||||
@@ -107,7 +111,7 @@ function changePassword() { | |||||
} | } | ||||
export default { | export default { | ||||
components: {Loading}, | |||||
components: {Loading, EditCards}, | |||||
methods: { | methods: { | ||||
changePassword, changeName, changeEmail | changePassword, changeName, changeEmail | ||||
}, | }, | ||||
@@ -1272,3 +1272,42 @@ div#card-errors { | |||||
display: block; | 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; | |||||
} |