Это приложение, созданное на языке Laravel, над которым мы работаем, предназначено для бронирования мест в салоне красоты.
Устоявшаяся система бронирования осуществляется по временным интервалам. Каждому временному интервалу соответствует определенная квота услуг.
Услуги: стрижка, маникюр, мытье волос и т.д. и т.п.
Например:
-от С 10:00 до 11:00 разрешено 3 разные службы,
- с 11:00 до 12:00 разрешено 2 разных службы.
Проблема в том, что я не могу отключить кнопки резервирования, когда квота услуги заполнена в течение определенного интервала времени.
Код: Выделить всё
(For example, from 10:00 to 11:00 A.M. 3 service reservations were made)"This time slot is full."
But this is a bad user experience, since if all the time slots are full in the next 3 days, the user must go through the booking steps hour by hour, and receiving the alert: "This time slot is full" .
As I say, this is bad user experience.
When the time slot has passed, the corresponding buttons are automatically disabled.
I need to do the same when the service quotas have been filled in the time slots
Код: Выделить всё
(For example, from 10:00 to 11:00 A.M. Было сделано 3 бронирования услуг)Это полные файлы:
ПЕРВЫЙ ФАЙЛ:
Код: Выделить всё
@php
$app_local = get_default_language_code();
@endphp
@extends('frontend.layouts.master')
@push("css")
.danger {
background-color: #cca87643;
border-radius: 10px;
}
.text--disable {
color: #ffffffcf
}
@endpush
@section('content')
@csrf
[i]slug }}">
[/i] {{ __("Make Appointment") }}
[i]image, 'site-section') }}" alt="img">
[/i] {{ __("Parlour Details") }}
{{ __("Parlour Name") }} :
{{ @$parlour->name ?? '' }}
{{ __("Parlour Address") }} :
{{ @$parlour->address ?? '' }}
{{ __("Experience") }} :
{{ @$parlour->experience ?? '' }}
[i][/i] {{ __("Service Select") }}
@foreach (@$parlour->services as $item)
[i]service_name }}" class="hide-input service" id="service_{{ $item->id }}" data-item="{{ json_encode($item) }}">
{{ $item->service_name }} {{ get_default_currency_symbol() }}{{ get_amount($item->price) }}
@endforeach
[/i] {{ __("Расписание") }}
@ php
$currentDate = \Carbon\Carbon::now();
$todaysDate = \Carbon\Carbon::now()->format('d F, Y');
$schedule_date = $parlour->number_of_dates;
@endphp
[i]
{{ __("Выбрать дату") }} *
@for ($i = 0; $i < $schedule_date; $i++) {{ $currentDate->format ('d F, Y') }}
@php
$currentDate->addDay();
@endphp
@endfor
@php
$current_time = now() ->setTimeZone($basic_settings->часовой пояс)->format('H:i');
@endphp
{{ __("Выбрать время") }} *
< div class="col-xl-12 col-lg-12 form-group">
@include('admin.comComponents.form.textarea',[
'label' => __("Message ").''.'('.__("Необязательно").')'.'',
'name' => 'message ',
'placeholder' => __("Напишите здесь")"....",
'value' => old("message")
])
{{ __("Оформить заказ") }} ( {{ get_default_currency_symbol() }} )[/i]
< br />@endsection
@push("script")
$('.service').on('change', function() {
var servicePrice = [];
$('.service:checked').each(function() {
varcheckedPrice = $(this).data('item');
var цена = parseFloat(checkedPrice.price)
servicePrice.push(price);
});
var totalPrice = servicePrice.reduce(function(a, b) {
return a + b;
}, 0);
$('.price').text('{{ get_default_currency_symbol() }}' + totalPrice.toFixed(2))
$('# цена').val(totalPrice.toFixed(2));
});
$(document).ready(function() {
var selectedDate = $('.date') .val();
var TodaysDate = $('.todays-date').val();
var currentTime = $('.current_time').val();
var data = JSON.parse($('.shedule-option').attr("data-item"));
run(selectedDate, TodaysDate, CurrentTime, data);
});
$('.date').on('change', function() {
var selectedDate = $(this).val();
var TodaysDate = $('.todays-date ').val();
var currentTime = $('.current_time').val();
var data = JSON.parse($('.shedule-option').attr(" data-item"));
$('.schedule-option').html('');
run(selectedDate, TodaysDate, CurrentTime, data);
});< br />
function run(selectedDate, TodaysDate, currentTime, data) {
$.each(data, function(index, item) {
var fromTime = item.from_time;
var отключен = currentTime > fromTime? 'отключено': '';
var DisableClassName = отключено === 'отключено' ? 'опасность': '';
var textClass = отключено === 'отключено' ? 'text--disable' : '';
var itemData = '';
if (todaysDate == selectedDate) {
itemData += `
[b]${item.from_time} - [/b]
[b]${item.to_time
`;
$('.shedule-option').append(itemData) ;
} else {
itemData += `
${item.from_time} - [/b]
[b]${item.to_time[/b]
`;
$('.shedule-option').append(itemData);
});
@endpush
Код: Выделить всё
namespace App\Http\Controllers\Frontend;
use Exception;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
use App\Models\TemporaryData;
use App\Models\ParlourBooking;
use App\Models\UserNotification;
use App\Models\Admin\ParlourList;
use App\Models\Admin\UsefullLink;
use App\Models\Admin\SiteSections;
use App\Constants\SiteSectionConst;
use App\Models\Admin\BasicSettings;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
use App\Constants\PaymentGatewayConst;
use App\Models\Admin\TransactionSetting;
use App\Notifications\emailNotification;
use Illuminate\Support\Facades\Validator;
use App\Models\Admin\ParlourListHasSchedule;
use App\Models\Admin\PaymentGatewayCurrency;
use Illuminate\Support\Facades\Notification;
use KingFlamez\Rave\Facades\Rave as Flutterwave;
use App\Http\Helpers\PaymentGateway as PaymentGatewayHelper;
use Illuminate\Support\Facades\Session;
class ParlourBookingController extends Controller
{
/**
* Method for show parlour booking page
* @param $slug
* @param \Illuminate\Http\Request $request
*/
public function getService(Request $request, $slug)
{
$page_title = "| Parlour Booking";
$parlour = ParlourList::with(['schedules', 'services'])->where('slug', $slug)->first();
$validated_user = auth()->user();
$footer_slug = Str::slug(SiteSectionConst::FOOTER_SECTION);
$footer = SiteSections::getData($footer_slug)->first();
$usefull_links = UsefullLink::where('status', true)->get();
$contact_slug = Str::slug(SiteSectionConst::CONTACT_SECTION);
$contact = SiteSections::getData($contact_slug)->first();
return view('frontend.pages.parlour-booking.index', compact(
'page_title',
'parlour',
'validated_user',
'footer',
'usefull_links',
'contact'
));
}
/**
* Method for store appointment booking information and passed it to preview page
* @param \Illuminate\Http\Request $request
*/
public function store(Request $request)
{
if (auth()->check() == false) return back()->with(['error' => ['Please Login First.']]);
$charge_data = TransactionSetting::where('slug', 'parlour')->where('status', 1)->first();
$validator = Validator::make($request->all(), [
'parlour' => 'required',
'price' => 'required',
'service' => "required|array",
'service.*' => "required|string|max:255",
'date' => "required",
'schedule' => 'required',
'message' => "nullable"
]);
if ($validator->fails()) {
return back()->withErrors($validator)->withInput($request->all());
}
$validated = $validator->validate();
$validated['slug'] = Str::uuid();
$slug = $validated['parlour'];
$parlour = ParlourList::where('slug', $slug)->first();
if (!$parlour) return back()->with(['error' => ['Parlour Not Found!']]);
$validated['user_id'] = auth()->user()->id;
$validated['parlour_id'] = $parlour->id;
$schedule = ParlourListHasSchedule::where('id', $validated['schedule'])->whereHas('parlour', function ($q) use ($parlour) {
$q->where('id', $parlour->id);
})->first();
if (!$schedule) {
return back()->with(['error' => ['Расписание не найдено!']]);
$validated['schedule_id'] = $validated['schedule'];
$price = floatval($ validated['price']);
$fixed_charge = floatval($charge_data->fixed_charge);
$percent_charge = floatval($charge_data->percent_charge);
$total_percent_charge = ($percent_charge) / 100) * $price;
$total_charge = $fixed_charge + $total_percent_charge;
$total_price = $price + $total_charge;
$validated['total_charge'] = $total_charge;
$validated['price'] = $price;
$validated['payable_price'] = $total_price;
$validated['status'] = global_const()::PARLOUR_BOOKING_STATUS_REVIEW_PAYMENT;
$alrady_appointed = ParlourBooking::where('parlour_id', $parlour->id)
->where('schedule_id', $validated['schedule_id'])
->count ();
if ($alrady_appointed >= $schedule->max_client) {
return back()->with(['error' => ['¡Esta hora esta completa! ']]);
$next_appointment_no = $alrady_appointed + 1;
$validated['serial_number'] = $next_appointment_no;
попробуйте {
$booking = ParlourBooking::create($validated);
} catch (Exception $e) {
return back()->with(['error' => ['Something пошло не так! Пожалуйста, попробуйте еще раз.']]);
return redirect()->route('parlour.booking.preview', $booking->slug);
/**
* Метод для показа страницы предварительного просмотра
* @param $slug
* @param \Illuminate\Http\Request $request
*/
публичная функция view(Request $request, $slug)
{
$page_title = "| Предварительный просмотр встречи";
$booking = ParlourBooking::with(['parlour', 'schedule', 'pay_gateway '])->where('slug', $slug)->first();
if (!$booking) {
return redirect()->route('find.parlour')- >with(['error' => ['Бронирование не найдено']]);
$pay_gateway = PaymentGatewayCurrency::whereHas('gateway', function ($gateway) {
$gateway->where('slug', PaymentGatewayConst::paid_method_slug());
$gateway->where('status', 1);
})->get();
$footer_slug = Str::slug(SiteSectionConst::FOOTER_SECTION);
$footer = SiteSections::getData($footer_slug)->first();
$usefull_links = UsefullLink::where('status ', true)->get();
$contact_slug = Str::slug(SiteSectionConst::CONTACT_SECTION);
$contact = SiteSections::getData($contact_slug)->first();< br />
return view('frontend.pages.parlour-booking.preview', Compact(
'page_title',
'booking',
'pay_gateway',
'footer',
'usefull_links',
'contact'
));
public function deleteBooking(Request $request)
{
$booking = ParlourBooking::where('slug', $request->bookingSlug)->first();
if ($booking) {
$booking-> delete();
Session::flash('message', ['success' => ['Booking Time Out!']]);
return response()->json(['success' => true]);
return response()->json(['success' => false]);
/**
* Метод подтверждения бронирования
* @param $slug
* @param \Illuminate\Http\Request $request
*/
публичная функция подтверждения (Запрос $request, $slug)
{
$data = ParlourBooking::with(['pay_gateway', 'parlour', 'schedule', 'user'])->where('slug' , $slug)->first();
$otp_exp_sec = $data->booking_exp_секунды ?? global_const()::BOOKING_EXP_SEC;
if ($data->created_at->addSeconds($otp_exp_sec) < now()) {
$data->delete();
return redirect()->route('find.parlour')->with(['error' => ['Booking Time Out!']]);
$ validator = Validator::make($request->all(), [
'pay_method' => 'required',
]);
$validated = $validator->validate() ;
$from_time = $data->schedule->from_time ?? '';
$to_time = $data->schedule->to_time ?? '';
$user = auth()->user();
$basic_setting = BasicSettings::first();
if ($validated['pay_method'] == global_const( ::CASH_PAYMENT) {
try {
$trx_id =generateTrxString('parlour_bookings', 'trx_id', 'PB', 8);
$data->update([
'trx_id' => $trx_id,
'pay_method' => $validated['pay_method'],
'remark' => 'CASH',
'status' => global_const()::PARLOUR_BOOKING_STATUS_PENDING,
]);
UserNotification::create([
'user_id' => auth()->user()->id,
' message' => [
'title' => "Ваше бронирование",
'салон' => $data->зал->имя,
'date' => $data-> date,
'from_time' => $from_time,
'to_time' => $to_time,
'serial_number' => $data->serial_number,
'success' => «Забронировано успешно».
],
]);
if ($basic_setting->email_notification == true) {
Notification::route("mail", $user- >email)->notify(new emailNotification($user, $data, $trx_id));
} catch (Exception $e) {
return back()->with( ['error' => ['Что-то пошло не так! Повторите попытку.']]);
return redirect()->route('user.my.booking.index')-> with(['success' => ['¡Felicidades! Подтверждение резерва с выходом.']]);
} else {
$requested_data = [
'data' => $data,
'Payment_method' => $validated['Payment_method']
];
try {
$instance = PaymentGatewayHelper::init($requested_data)->шлюз ()->render();
} catch (Exception $e) {
return back()->with(['error' => [$e->getMessage()]]);
return $instance;
/**
* Метод успешного платежа PayPal
*/
успех публичной функции (Запрос $request, $gateway)
{
$requestData = $request->all();
$token = $requestData['token'] ?? "";
$checkTempData = TemporaryData::where("type", $gateway)->where("identifier", $token)->first();
if (!$checkTempData) return redirect()->route('find.parlour')->with(['error' => ['Транзакция не удалась. Запись не сохранена должным образом. Повторите попытку.']]);
$ checkTempData = $checkTempData->toArray();
try {
PaymentGatewayHelper::init($checkTempData)->responseReceive();
} catch (Exception $e) {
return back()->with(['error' => [$e->getMessage()]]);
return redirect()->route ("user.my.booking.index")->with(['success' => ['¡Счастья! Подтверждение резерва с выходом.']]);
/**
* Этот метод для отмены оповещения PayPal
* @method POST
* @param Illuminate\Http\Request $request
* @return Illuminate\Http\Request
*/
публичная функция cancel(Request $request, $gateway)
{
$requestData = $request->all();
$token = $requestData['token'] ? ? "";
if ($token) {
TemporaryData::where("identifier", $token)->delete();
return redirect()-> маршрут('find.parlour');
/**
* Метод для успешного платежа по полосе
*/
публичная функция StripePaymentSuccess($trx)
{
$token = $trx;
$checkTempData = TemporaryData::where("type", PaymentGatewayConst::STRIPE)->where("identifier", $token)- >first();
if (!$checkTempData) return redirect()->route('find.parlour')->with(['error' => ['Транзакция не удалась. Запись не сохранена правильно. Повторите попытку.']]);
$checkTempData = $checkTempData->toArray();
попробуйте {
PaymentGatewayHelper::init($checkTempData)-> responseReceive();
} catch (Exception $e) {
return back()->with(['error' => ["Что-то не так..."]]) ;
return redirect()->route("user.my.booking.index")->with(['success' => ['¡Счастья! Подтверждение резерва с выходом.'] ]);
}
//обратный вызов flutterwave
public function flutterwaveCallback()
{
$status = request()-> status;
//если платеж успешен
if ($status == 'successful') {
$transactionID = Flutterwave::getTransactionIDFromCallback();
$ data = Flutterwave::verifyTransaction($transactionID);
$requestData = request()->tx_ref;
$token = $requestData;
$checkTempData = TemporaryData::where("type", 'flutterwave')->where("identifier", $token)->first();
if (!$checkTempData) return redirect()-> маршрут('find.parlour')->with(['error' => ['Транзакция не удалась. Запись не сохранилась должным образом. Пожалуйста, попробуйте еще раз.']]);
$checkTempData = $checkTempData->toArray();
попробуйте {
PaymentGatewayHelper::init ($checkTempData)->responseReceive();
} catch (Exception $e) {
return back()->with(['error' => [$e->getMessage()]] );
return redirect()->route("user.my.booking.index")->with(['success' => ['¡Счастья! Подтверждение резерва с выходом.' ]]);
} elseif ($status == 'cancelled') {
return redirect()->route('find.parlour')->with(['error' => [' Бронирование салона отменено.']]);
} else {
return redirect()->route('find.parlour')->with(['error' => ['Транзакция не удалась'] ]);
/**
* Успешный платеж SSL Commerz
*/
public function sllCommerzSuccess(Request $request)
{
$data = $request->all();
$token = $data['tran_id'];
$checkTempData = TemporaryData::where("type ", PaymentGatewayConst::SSLCOMMERZ)->where("identifier", $token)->first();
if (!$checkTempData) return redirect()->route('find.parlour ')->with(['error' => ['Транзакция не удалась. Запись не сохранилась должным образом. Пожалуйста, попробуйте еще раз.']]);
$checkTempData = $checkTempData->toArray();
$creator_id = $checkTempData['data']->creator_id ?? null;
$creator_guard = $checkTempData['data']->creator_guard ?? null;
$user = Auth::guard($creator_guard)->loginUsingId($creator_id);
if ($data['status'] != "VALID") {< br /> return redirect()->route("find.parlour")->with(['error' => ['Failed!']]);
try {
PaymentGatewayHelper::init($checkTempData)->responseReceive();
} catch (Exception $e) {
return back()->with(['error' => [ "Что-то не так..."]]);
return redirect()->route("user.my.booking.index")->with(['success' => [ '¡Felicidades! Резервное подтверждение с выходом.']]);
/**
* обратный вызов платежного шлюза razor pay
*/
public function razorCallback( )
{
$request_data = request()->all();
//если платеж прошел успешно
if ($request_data['razorpay_pay_link_status'] == 'оплачено' ) {
$token = $request_data['razorpay_pay_link_reference_id'];
$checkTempData = TemporaryData::where("type", PaymentGatewayConst::RAZORPAY)->where("идентификатор", $token)->first();
if (!$checkTempData) return redirect()->route('find.parlour')->with(['error' => ['Ошибка транзакции. Запись не сохранилась должным образом. Пожалуйста, попробуйте еще раз.']]);
$checkTempData = $checkTempData->toArray();
попробуйте {
PaymentGatewayHelper::init($checkTempData)->responseReceive();
} catch (Exception $e) {
return back()->with(['error' => [$e->getMessage()]]);
return redirect()->route("user.my.booking.index")->with(['success' => ['¡Счастья! Подтверждение резерва с выходом.']]);
} else {< br /> return redirect()->route('find.parlour')->with(['error' => ['Failed']]);
}
Источник: https://stackoverflow.com/questions/781 ... on-buttons
Мобильная версия