diff options
author | Mateja Marić <mail@matejamaric.com> | 2021-03-22 15:21:40 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-03-22 15:21:40 +0100 |
commit | 6ab102bc4be617255d5eab77faebb1cada65b370 (patch) | |
tree | 09fcff5953cc6ca3702335e717ca72f865124d1c | |
parent | 21e9e94e76a21516edc3d1e4d0462a0ba75aafa4 (diff) | |
parent | db976a9fb0434df0095ea9f1b8858213d57e7535 (diff) | |
download | yota-laravel-master.tar.gz yota-laravel-master.zip |
Use Vue.js and Vuex.
-rw-r--r-- | app/Http/Controllers/SpecialCallsController.php | 7 | ||||
-rw-r--r-- | package-lock.json | 6 | ||||
-rw-r--r-- | package.json | 3 | ||||
-rw-r--r-- | public/js/activities.js | 49 | ||||
-rw-r--r-- | public/js/reservations.js | 119 | ||||
-rw-r--r-- | public/js/reserve.js | 9 | ||||
-rw-r--r-- | resources/js/app.js | 19 | ||||
-rw-r--r-- | resources/js/bootstrap.js | 8 | ||||
-rw-r--r-- | resources/js/components/activities.vue | 53 | ||||
-rw-r--r-- | resources/js/components/call-sign-description.vue | 56 | ||||
-rw-r--r-- | resources/js/components/call-sign-filter.vue | 44 | ||||
-rw-r--r-- | resources/js/components/reservation.vue | 59 | ||||
-rw-r--r-- | resources/js/components/reservations.vue | 62 | ||||
-rw-r--r-- | resources/js/store.js | 96 | ||||
-rw-r--r-- | resources/views/layouts/app.blade.php | 3 | ||||
-rw-r--r-- | resources/views/pages/activities.blade.php | 25 | ||||
-rw-r--r-- | resources/views/pages/reservations.blade.php | 94 | ||||
-rw-r--r-- | resources/views/pages/reserve.blade.php | 17 | ||||
-rw-r--r-- | routes/web.php | 1 |
19 files changed, 419 insertions, 311 deletions
diff --git a/app/Http/Controllers/SpecialCallsController.php b/app/Http/Controllers/SpecialCallsController.php index 025d955..a26af78 100644 --- a/app/Http/Controllers/SpecialCallsController.php +++ b/app/Http/Controllers/SpecialCallsController.php @@ -13,6 +13,13 @@ use App\Models\SpecialCall; class SpecialCallsController extends Controller { + public function index(Request $request) + { + //$data = SpecialCall::select('sign')->get()->toArray(); + $data = SpecialCall::all()->toArray(); + return $data; + } + public function create(Request $request) { $data = SpecialCall::all(); diff --git a/package-lock.json b/package-lock.json index 9c0ccea..5f8bfbe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9198,6 +9198,12 @@ "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", "dev": true }, + "vuex": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.2.tgz", + "integrity": "sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw==", + "dev": true + }, "watchpack": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", diff --git a/package.json b/package.json index 3556d45..5c158cb 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "sass": "^1.32.8", "sass-loader": "^8.0.2", "vue": "^2.6.12", - "vue-template-compiler": "^2.6.12" + "vue-template-compiler": "^2.6.12", + "vuex": "^3.6.2" } } diff --git a/public/js/activities.js b/public/js/activities.js deleted file mode 100644 index eacf4d3..0000000 --- a/public/js/activities.js +++ /dev/null @@ -1,49 +0,0 @@ -$.ajaxSetup({ - headers: { - 'X-CSRF-TOKEN': $('input[name="csrf-token"]').attr('content') - } -}); - -jQuery('select#call-sign').change(fillTable); -jQuery(document).ready(fillTable); - -function fillTable() { - tableData = jQuery('table#ajax-table>tbody').first(); - tableData.html('<tr><td class="font-weight-bold text-center" colspan="7">Loading...</td></tr>'); - - sign = jQuery('select#call-sign').first().val(); - - descDiv = jQuery('div#sign-desc-div'); - if (sign === 'all') { - descDiv.empty(); - } else { - jQuery.get('/special-calls/show/' + sign, function (data, status) { - descDiv.html('<div class="card mt-1"><div class="card-body pb-1">' + data + '</div></div>'); - console.log(data); - }); - } - - jQuery.post('/api/activities', {'call-sign': sign}, function (data, status) { - if (status === 'success') { - if (data.data.length > 0) { - tableData.empty(); - for (i = 0, len = data.data.length; i < len; i++) { - tr = '<tr><td>' + data.data[i].operatorCall + '</td>' + - '<td>' + data.data[i].fromTime + '</td>' + - '<td>' + data.data[i].toTime + '</td>' + - '<td>' + data.data[i].specialCall + '</td>' + - '<td>' + data.data[i].frequencies + '</td>' + - '<td>' + data.data[i].modes + '</td>' + - '<td>' + data.data[i].qso + '</td></tr>'; - tableData.append(tr); - } - } - else { - tableData.html('<tr><td class="font-weight-bold text-center" colspan="6">No data...</td></tr>'); - } - } - else { - tableData.html('<tr><td class="font-weight-bold text-center" colspan="6">Error!</td></tr>'); - } - }); -} diff --git a/public/js/reservations.js b/public/js/reservations.js deleted file mode 100644 index bc9c6de..0000000 --- a/public/js/reservations.js +++ /dev/null @@ -1,119 +0,0 @@ -$.ajaxSetup({ - headers: { - 'X-CSRF-TOKEN': $('input[name="csrf-token"]').attr('content') - } -}); - -jQuery('select#call-sign').change(fillTable); -jQuery(document).ready(fillTable); - -function fillTable() { - tableData = jQuery('table#ajax-table>tbody').first(); - tableData.html('<tr><td class="font-weight-bold text-center" colspan="13">Loading...</td></tr>'); - - sign = jQuery('select#call-sign').first().val(); - - jQuery.post('/special-calls/reservations', {'call-sign': sign}, function (data, status) { - if (status === 'success') { - if (data.data.length > 0) { - tableData.empty(); - for (i = 0, len = data.data.length; i < len; i++) { - tr = '<tr>'; - tr += '<td>' + data.data[i].id + '</td>'; - if (data.data[i].approved === 1) - tr += '<td class="text-center"><input type="checkbox" checked></td>'; - else - tr += '<td class="text-center"><input type="checkbox"></td>'; - - tr += - '<td contenteditable="true">' + data.data[i].operatorCall + '</td>' + - '<td contenteditable="true">' + data.data[i].qso + '</td>' + - '<td contenteditable="true">' + data.data[i].fromTime + '</td>' + - '<td contenteditable="true">' + data.data[i].toTime + '</td>' + - '<td contenteditable="true">' + data.data[i].specialCall + '</td>' + - '<td contenteditable="true">' + data.data[i].frequencies + '</td>' + - '<td contenteditable="true">' + data.data[i].modes + '</td>' + - '<td contenteditable="true">' + data.data[i].operatorName + '</td>' + - '<td contenteditable="true">' + data.data[i].operatorEmail + '</td>' + - '<td contenteditable="true">' + data.data[i].operatorPhone + '</td>'; - tr += '<td>'; - tr += "<button class=\"btn btn-primary mr-2\" onclick=\"btnAction('update', this)\">Update</button>"; - tr += "<button class=\"btn btn-warning mr-2\" onclick=\"btnAction('restore', this)\">Restore</button>"; - tr += "<button class=\"btn btn-danger\" onclick=\"btnAction('delete', this)\">Delete</button>"; - tr += '</td>'; - tr += '</tr>'; - tableData.append(tr); - } - } - else { - tableData.html('<tr><td class="font-weight-bold text-center" colspan="13">No data...</td></tr>'); - } - } - else { - tableData.html('<tr><td class="font-weight-bold text-center" colspan="13">Error!</td></tr>'); - } - }); -} - -function btnAction(action, btn) { - trDom = btn.parentElement.parentElement; - trData = trDom.children; - - actionData = { - action: action, - id: trData[0].innerText, - approved: trData[1].firstElementChild.checked, - operatorCall: trData[2].innerText, - qso: trData[3].innerText, - fromTime: trData[4].innerText, - toTime: trData[5].innerText, - specialCall: trData[6].innerText, - frequencies: trData[7].innerText, - modes: trData[8].innerText, - operatorName: trData[9].innerText, - operatorEmail: trData[10].innerText, - operatorPhone: trData[11].innerText - }; - - if (actionData.action == 'delete') { - if (confirm("Are you sure you want to delete reservation #" + actionData.id + " made by " + actionData.operatorCall + "?") === true) - trDom.remove(); - else return; - } - - jQuery.post('/api/reservations', actionData, function (response, status) { - if (status === 'success') { - try { - // Handle various actions - if (response.action == "update") { - jQuery('#notice').html("Record #" + actionData.id + " updated."); - } else if (response.action == "restore") { - trData[1].firstElementChild.checked = response.approved == 1; - trData[2].innerText = response.operatorCall; - trData[3].innerText = response.qso; - trData[4].innerText = response.fromTime; - trData[5].innerText = response.toTime; - trData[6].innerText = response.specialCall; - trData[7].innerText = response.frequencies; - trData[8].innerText = response.modes; - trData[9].innerText = response.operatorName; - trData[10].innerText = response.operatorEmail; - trData[11].innerText = response.operatorPhone; - jQuery('#notice').html("Record's #" + actionData.id + " data restored."); - } else if (response.action == "delete") { - jQuery('#notice').html("Record #" + actionData.id + " deleted."); - } else { - console.log("No action?"); - //console.log(data); - } - } catch { - //console.log(data); - alert("Bad input data!"); - } - } - else { - console.log('AJAX error'); - alert("Bad input data!"); - } - }); -} diff --git a/public/js/reserve.js b/public/js/reserve.js deleted file mode 100644 index 7a20c37..0000000 --- a/public/js/reserve.js +++ /dev/null @@ -1,9 +0,0 @@ -jQuery('select#special-call').change(setCallDesc); -jQuery(document).ready(setCallDesc); - -function setCallDesc() { - sign = jQuery('select#special-call').first().val(); - jQuery.get('/special-calls/show/' + sign, function (data, status) { - jQuery('div#call-desc').html(data); - }); -} diff --git a/resources/js/app.js b/resources/js/app.js index 40c55f6..b5fbbbf 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -1 +1,20 @@ require('./bootstrap'); + +import Vue from 'vue'; + +import store from './store.js'; + +import callSignDescription from './components/call-sign-description.vue'; + +import activitiesView from './components/activities.vue'; +import reservationsView from './components/reservations.vue'; + +new Vue({ + el: '#vue', + store, + components: { + callSignDescription, + activitiesView, + reservationsView + } +}); diff --git a/resources/js/bootstrap.js b/resources/js/bootstrap.js index ffc0a2b..e5e0ab6 100644 --- a/resources/js/bootstrap.js +++ b/resources/js/bootstrap.js @@ -3,7 +3,13 @@ window._ = require('lodash'); window.axios = require('axios'); window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; -window.Vue = require('vue'); +let token = document.querySelector('meta[name="csrf-token"]'); + +if (token) { + window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content; +} else { + console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token'); +} window.$ = window.jQuery = require('jquery'); window.Popper = require('popper.js'); diff --git a/resources/js/components/activities.vue b/resources/js/components/activities.vue new file mode 100644 index 0000000..1b1057c --- /dev/null +++ b/resources/js/components/activities.vue @@ -0,0 +1,53 @@ +<template> + <div> + <call-sign-filter :showDescriptions="true" @sign-changed="filterChanged()"></call-sign-filter> + + <div class="table-responsive mt-2"> + <table class="table table-striped table-bordered" style="white-space:nowrap;"> + <thead class="thead-dark"> + <tr> + <th>Operator</th> + <th>From</th> + <th>To</th> + <th>Special Callsign</th> + <th>Frequencies</th> + <th>Modes</th> + <th>QSO</th> + </tr> + </thead> + <tbody> + <tr v-for="(activity, index) in activities" :key="index"> + <td>{{ activity.operatorCall }}</td> + <td>{{ activity.fromTime }}</td> + <td>{{ activity.toTime }}</td> + <td>{{ activity.specialCall }}</td> + <td>{{ activity.frequencies }}</td> + <td>{{ activity.modes }}</td> + <td>{{ activity.qso }}</td> + </tr> + </tbody> + </table> + </div> + </div> +</template> + +<script> +import callSignFilter from './call-sign-filter.vue'; + +export default { + components: { callSignFilter }, + mounted() { + this.$store.dispatch('pullActivities'); + }, + computed: { + activities() { + return this.$store.getters.getData; + } + }, + methods: { + filterChanged() { + this.$store.dispatch('pullActivities'); + } + } +} +</script> diff --git a/resources/js/components/call-sign-description.vue b/resources/js/components/call-sign-description.vue new file mode 100644 index 0000000..7ab8b64 --- /dev/null +++ b/resources/js/components/call-sign-description.vue @@ -0,0 +1,56 @@ +<template> + <div> + <div class="form-group"> + <label for="special-call">Special Callsign:</label> + <select class="form-control" :class="{ 'is-invalid': isInvalid }" id="special-call" v-model="selected" :name="name" required> + <option v-for="option in options" :key="option.id" :value="option.sign" v-text="option.sign"></option> + </select> + </div> + + <div class="card mb-3"> + <div class="card-body pb-1"> + <div class="card-text" v-html="description"></div> + </div> + </div> + </div> +</template> + +<script> +export default { + props: [ 'name', 'old', 'isInvalid' ], + mounted() { + this.$store.dispatch('pullSigns').then(() => { + try { + if (this.old) { + this.$store.dispatch('setSelectedSign', this.old); + } + else { + this.$store.dispatch('setSelectedSign', this.$store.getters.getSigns[0].sign); + } + } + catch { + console.log('No call signs!'); + } + }); + }, + computed: { + options() { + return this.$store.getters.getSigns; + }, + selected: { + get() { + return this.$store.getters.getSelectedSign; + }, + set(value) { + this.$store.dispatch('setSelectedSign', value); + } + }, + description() { + for (let i = 0; i < this.options.length; i++) + if (this.options[i].sign === this.selected) + return this.options[i].description; + return ''; + } + } +} +</script> diff --git a/resources/js/components/call-sign-filter.vue b/resources/js/components/call-sign-filter.vue new file mode 100644 index 0000000..0ba8b4d --- /dev/null +++ b/resources/js/components/call-sign-filter.vue @@ -0,0 +1,44 @@ +<template> + <div> + <label for="call-sign">Filter by special callsign: </label> + <select id="call-sign" v-model="selected"> + <option value="all">All</option> + <option v-for="option in options" :key="option.id" :value="option.sign" v-text="option.sign"></option> + </select> + + <div class="card mb-3" v-if="showDescriptions && (selected !== 'all')"> + <div class="card-body pb-1"> + <div class="card-text" v-html="description"></div> + </div> + </div> + </div> +</template> + +<script> +export default { + props: ['showDescriptions'], + mounted() { + this.$store.dispatch('pullSigns'); + }, + computed: { + selected: { + get() { + return this.$store.getters.getSelectedSign; + }, + set(value) { + this.$store.dispatch('setSelectedSign', value); + this.$emit('sign-changed'); + } + }, + options() { + return this.$store.getters.getSigns; + }, + description() { + for (let i = 0; i < this.options.length; i++) + if (this.options[i].sign === this.selected) + return this.options[i].description; + return ''; + } + } +} +</script> diff --git a/resources/js/components/reservation.vue b/resources/js/components/reservation.vue new file mode 100644 index 0000000..ec430ab --- /dev/null +++ b/resources/js/components/reservation.vue @@ -0,0 +1,59 @@ +<template> + <tr> + <td v-text="reservation.id"></td> + <td><input type="checkbox" v-model="reservation.approved"/></td> + <td><input type="text" v-model="reservation.operatorCall"></td> + <td><input type="text" v-model="reservation.qso"></td> + <td><input type="text" v-model="reservation.fromTime"></td> + <td><input type="text" v-model="reservation.toTime"></td> + <td><input type="text" v-model="reservation.specialCall"></td> + <td><input type="text" v-model="reservation.frequencies"></td> + <td><input type="text" v-model="reservation.modes"></td> + <td><input type="text" v-model="reservation.operatorName"></td> + <td><input type="text" v-model="reservation.operatorEmail"></td> + <td><input type="text" v-model="reservation.operatorPhone"></td> + <td> + <button class="btn btn-primary mr-2" @click="updateRow">Update</button> + <button class="btn btn-warning mr-2" @click="restoreRow">Restore</button> + <button class="btn btn-danger" @click="deleteRow">Delete</button> + </td> + </tr> +</template> + +<script> +export default { + props: [ 'reservationIndex' ], + data() { + return { + reservation: this.$store.getters.getDataRow(this.reservationIndex) + } + }, + methods: { + updateRow() { + this.$store.dispatch('pushReservation', { + index: this.reservationIndex, + reservation: this.reservation + }); + }, + restoreRow() { + this.reservation = this.$store.getters.getDataRow(this.reservationIndex); + }, + deleteRow() { + this.$store.dispatch('removeReservation', this.reservationIndex); + } + } +} +</script> + +<style scoped> +td { + text-align: center; + vertical-align: middle; +} +input { + background-color: white; + border: 1px solid lightgray; + border-radius: 3px; + padding: 0.2em; +} +</style> diff --git a/resources/js/components/reservations.vue b/resources/js/components/reservations.vue new file mode 100644 index 0000000..0c420b8 --- /dev/null +++ b/resources/js/components/reservations.vue @@ -0,0 +1,62 @@ +<template> + <div> + <call-sign-filter @sign-changed="filterChanged()"></call-sign-filter> + + <div class="table-responsive mt-2"> + <table id="ajax-table" class="table table-striped table-bordered" style="white-space:nowrap;"><!-- table-hover --> + <thead class="thead-dark"> + <tr> + <th>ID</th> + <th>Approved</th> + <th>Operator Callsign</th> + <th>QSO</th> + <th>From</th> + <th>To</th> + <th>Special Callsign</th> + <th>Frequencies</th> + <th>Modes</th> + <th>Operator Name</th> + <th>Operator Email</th> + <th>Operator Phone</th> + <th>Actions</th> + </tr> + </thead> + <tbody> + <reservation-view v-for="(reservation, index) in reservations" + :key="reservation.id" :reservation-index="index"> + </reservation-view> + </tbody> + </table> + </div> + </div> +</template> + +<script> +import callSignFilter from './call-sign-filter.vue'; +import reservationView from './reservation.vue'; + +export default { + components: { callSignFilter, reservationView }, + mounted() { + this.$store.dispatch('pullReservations'); + }, + computed: { + reservations() { + return this.$store.getters.getData; + } + }, + methods: { + filterChanged() { + this.$store.dispatch('pullReservations'); + } + } +} +</script> + +<style scoped> +@media only screen and (min-width:961px) { + .table-responsive { + max-height: 80vh; + } +} +</style> diff --git a/resources/js/store.js b/resources/js/store.js new file mode 100644 index 0000000..117db93 --- /dev/null +++ b/resources/js/store.js @@ -0,0 +1,96 @@ +import Vue from 'vue'; +import Vuex from 'vuex'; +Vue.use(Vuex); + +export default new Vuex.Store({ + state: { + selectedSign: "all", + callSigns: [], + data: [] + }, + getters: { + getSelectedSign(state) { + return state.selectedSign; + }, + getSigns(state) { + return state.callSigns; + }, + getData(state) { + return state.data; + }, + getDataRow(state) { + return (index) => _.cloneDeep(state.data[index]); + } + }, + mutations: { + setSelectedSign(state, sign) { + state.selectedSign = sign; + }, + setSigns(state, signs) { + state.callSigns = signs; + }, + setData(state, data) { + state.data = data; + }, + setDataRow(state, row) { + state.data[row.index] = _.cloneDeep(row.data); + }, + removeDataRow(state, index) { + state.data.splice(index, 1); + } + }, + actions: { + setSelectedSign(context, sign) { + context.commit('setSelectedSign', sign); + }, + async pullSigns(context) { + await axios.get('/special-calls/show').then(response => { + context.commit('setSigns', response.data); + }).catch(error => { + console.log(error); + }); + }, + async pullActivities(context) { + await axios.post('/api/activities', {'call-sign': this.state.selectedSign}).then(response => { + context.commit('setData', response.data.data); + }).catch(error => { + console.log(error); + }); + }, + async pullReservations(context) { + await axios.post('/special-calls/reservations', {'call-sign': this.state.selectedSign}).then(response => { + context.commit('setData', response.data.data); + }).catch(error => { + console.log(error); + }); + }, + async pushReservation(context, data) { + await axios.post('/api/reservations', { + action: 'update', + ...data.reservation + }).then(() => { + context.commit('setDataRow', { + index: data.index, + data: data.reservation + }); + }).catch(error => { + console.log(error); + alert("Couldn't update reservation! Bad data!"); + }); + }, + async removeReservation(context, index) { + let data = { + action: 'delete', + ...this.state.data[index] + }; + if (confirm(`Are you sure you want to delete reservation #${data.id} made by ${data.operatorCall}?`) === true) { + await axios.post('/api/reservations', data).then(() => { + context.commit('removeDataRow', index); + }).catch(error => { + console.log(error); + alert('Unable to remove reservation!'); + }); + } + } + } +}); diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index 79235ac..0a8bbda 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -3,6 +3,7 @@ <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="csrf-token" content="{{ csrf_token() }}"> <link rel="stylesheet" href="{{ mix('css/app.css') }}" type="text/css"> @yield('styles') <title>YOTA - @yield('title')</title> @@ -10,7 +11,7 @@ <body> @yield('navbar', View::make('inc.navbar')) @yield('jumbotron') - <div class="container pt-3"> + <div id="vue" class="container pt-3"> @yield('content') </div> <script src="{{ mix('js/manifest.js') }}"></script> diff --git a/resources/views/pages/activities.blade.php b/resources/views/pages/activities.blade.php index 7e7a0ed..275431a 100644 --- a/resources/views/pages/activities.blade.php +++ b/resources/views/pages/activities.blade.php @@ -5,30 +5,7 @@ @section('navbar', View::make('inc.navbar')) @section('content') -<input type="hidden" name="csrf-token" content="{{ csrf_token() }}"> -<label for="call-sign">Filter by special callsign: </label> -<select id="call-sign"> - <option value="all">All</option> - @if (count($signs) > 0) - @foreach ($signs as $sign) - <option value="{{ $sign->sign }}">{{ $sign->sign }}</option> - @endforeach - @endif -</select> -<div id="sign-desc-div"></div> +<activities-view></activities-view> -<div class="table-responsive mt-2"> - <table id="ajax-table" class="table table-striped table-bordered" style="white-space:nowrap;"> - <thead class="thead-dark"> - <tr><th>Operator</th><th>From</th><th>To</th><th>Special Callsign</th><th>Frequencies</th><th>Modes</th><th>QSO</th></tr> - </thead> - <tbody> - </tbody> - </table> -</div> @endsection() - -@section('scripts') - <script src="{{ asset('js/activities.js') }}"></script> -@endsection diff --git a/resources/views/pages/reservations.blade.php b/resources/views/pages/reservations.blade.php index addc9a9..475e5d7 100644 --- a/resources/views/pages/reservations.blade.php +++ b/resources/views/pages/reservations.blade.php @@ -4,98 +4,6 @@ @section('navbar', View::make('inc.navbar')) -@section('scripts') - <script src="{{ asset('js/reservations.js') }}"></script> -@endsection - @section('content') -<input type="hidden" name="csrf-token" content="{{ csrf_token() }}"> -<label for="call-sign">Filter by special callsign: </label> -<select id="call-sign"> - <option value="all">All</option> - @if (count($signs) > 0) - @foreach ($signs as $sign) - <option value="{{ $sign->sign }}">{{ $sign->sign }}</option> - @endforeach - @endif -</select> -<div id="notice" class="float-right font-weight-bold"></div> - -<div class="table-responsive mt-2"> - <table id="ajax-table" class="table table-striped table-bordered" style="white-space:nowrap;"><!-- table-hover --> - <thead class="thead-dark"> - <tr> - <th>ID</th> - <th>Approved</th> - <th>Operator Callsign</th> - <th>QSO</th> - <th>From</th> - <th>To</th> - <th>Special Callsign</th> - <th>Frequencies</th> - <th>Modes</th> - <th>Operator Name</th> - <th>Operator Email</th> - <th>Operator Phone</th> - <th>Actions</th> - </tr> - </thead> - <tbody> - </tbody> - </table> -</div> -{{--@if (count($data) > 0)--}} -{{--<div class="table-responsive">--}} - {{--<table class="table table-striped table-bordered" style="white-space:nowrap;"><!-- table-hover -->--}} - {{--<thead class="thead-dark">--}} - {{--<tr>--}} - {{--<th>ID</th>--}} - {{--<th>Approved</th>--}} - {{--<th>Operator Callsign</th>--}} - {{--<th>QSO</th>--}} - {{--<th>From</th>--}} - {{--<th>To</th>--}} - {{--<th>Frequencies</th>--}} - {{--<th>Modes</th>--}} - {{--<th>Special Callsign</th>--}} - {{--<th>Operator Name</th>--}} - {{--<th>Operator Email</th>--}} - {{--<th>Operator Phone</th>--}} - {{--<th>Actions</th>--}} - {{--</tr>--}} - {{--</thead>--}} - {{--<tbody>--}} - {{--@foreach ($data as $row)--}} - {{--<tr>--}} - {{--<td class="align-middle">{{ $row->id }}</td>--}} - {{--@if ($row->approved)--}} - {{--<td class="align-middle"><input type="checkbox" checked></td> --}} - {{--@else--}} - {{--<td class="align-middle"><input type="checkbox"></td> --}} - {{--@endif--}} - {{--<td class="align-middle">{{ $row->operatorCall }}</td>--}} - {{--<td class="align-middle">{{ $row->qso }}</td>--}} - {{--<td class="align-middle">{{ $row->fromTime }}</td>--}} - {{--<td class="align-middle">{{ $row->toTime }}</td>--}} - {{--<td class="align-middle">{{ $row->frequencies }}</td>--}} - {{--<td class="align-middle">{{ $row->modes }}</td>--}} - {{--<td class="align-middle">{{ $row->specialCall }}</td>--}} - {{--<td class="align-middle">{{ $row->operatorName }}</td>--}} - {{--<td class="align-middle">{{ $row->operatorEmail }}</td>--}} - {{--<td class="align-middle">{{ $row->operatorPhone }}</td>--}} - {{--<td>--}} - {{--<button class="btn btn-primary">Update</button>--}} - {{--<button class="btn btn-warning">Restore</button>--}} - {{--<button class="btn btn-danger">Delete</button>--}} - {{--</td>--}} - {{--</tr>--}} - {{--@endforeach--}} - {{--</tbody>--}} - {{--</table>--}} -{{--</div>--}} -{{--@else--}} -{{--<div class="text-center">--}} - {{--<strong>There are currently no reservations.</strong>--}} -{{--</div>--}} -{{--@endif--}} + <reservations-view></reservations-view> @endsection() diff --git a/resources/views/pages/reserve.blade.php b/resources/views/pages/reserve.blade.php index 823e8b9..17cab9a 100644 --- a/resources/views/pages/reserve.blade.php +++ b/resources/views/pages/reserve.blade.php @@ -17,24 +17,14 @@ @endif <form action="{{ route('reserve') }}" method="POST"> @csrf + <!-- SPECIAL CALL --> -<div class="form-group"> - <label for="special-call">Special Callsign:</label> - <select class="form-control @error('scall') is-invalid @enderror" id="special-call" name="scall" required> - @foreach ($signs as $sign) - <option value="{{ $sign->sign }}" {{ old('scall') == $sign->sign ? 'selected' : '' }}>{{ $sign->sign }}</option> - @endforeach - </select> + <call-sign-description name="scall" old="{{ old('scall') }}" @error('scall') is-invalid="true" @enderror > + </call-sign-description> @error('scall') <div class="alert alert-danger mt-2">{{ $message }}</div> @enderror -</div> -<div class="card mb-3"> - <div class="card-body pb-1"> - <div class="card-text" id="call-desc"></div> - </div> -</div> @error('time') <div class="alert alert-danger mt-2">{{ $message }}</div> @@ -163,5 +153,4 @@ format: 'H:i' }); </script> - <script src="{{ asset('js/reserve.js') }}"></script> @endsection diff --git a/routes/web.php b/routes/web.php index 20437b1..4616364 100644 --- a/routes/web.php +++ b/routes/web.php @@ -48,6 +48,7 @@ Route::post('/api/reservations', [ReservationsController::class, 'update'])->mid Route::get('/special-calls/add', [SpecialCallsController::class, 'create'])->name('addSign')->middleware(['auth']); Route::post('/special-calls/add', [SpecialCallsController::class, 'store'])->name('addSignForm')->middleware(['auth']); +Route::get('/special-calls/show', [SpecialCallsController::class, 'index']); Route::get('/special-calls/show/{name}', [SpecialCallsController::class, 'show']); Route::get('/special-calls/edit/{id}', [SpecialCallsController::class, 'edit'])->name('editSign')->middleware(['auth']); Route::post('/special-calls/edit/{id}', [SpecialCallsController::class, 'update'])->name('editSignForm')->middleware(['auth']); |