migrate to gtea from bistbucket
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Authentication\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Modules\Authentication\Models\User;
|
||||
use Modules\HRM\Models\Attendance;
|
||||
use Modules\HRM\Models\AttendanceLog;
|
||||
|
||||
class AttendanceSyncController extends Controller
|
||||
{
|
||||
public function allUsersGet(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'restaurant_id' => 'required|integer|exists:restaurants,id',
|
||||
]);
|
||||
|
||||
$users = User::select('id', 'first_name', 'last_name', 'user_type', 'role_id')
|
||||
->where('restaurant_id', (int) $request->restaurant_id)
|
||||
->get();
|
||||
|
||||
return $this->responseSuccess(
|
||||
$users,
|
||||
_lang('Users have been fetched successfully.')
|
||||
);
|
||||
}
|
||||
|
||||
public function sync(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'logs' => ['required', 'array'],
|
||||
'logs.*.id' => ['required', 'integer'],
|
||||
'logs.*.timestamp' => ['required', 'date'],
|
||||
'logs.*.type' => ['required', 'integer'],
|
||||
]);
|
||||
|
||||
$restaurantId = 1; // or from device
|
||||
$inserted = 0;
|
||||
$skipped = 0;
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
foreach ($request->logs as $log) {
|
||||
|
||||
$employeeId = $log['id'];
|
||||
$dateTime = Carbon::parse($log['timestamp']);
|
||||
$date = $dateTime->toDateString();
|
||||
$type = $this->mapPunchType($log['type']);
|
||||
|
||||
/**
|
||||
* 1️⃣ Avoid duplicate punch
|
||||
*/
|
||||
$exists = AttendanceLog::where([
|
||||
'employee_id' => $employeeId,
|
||||
'restaurant_id' => $restaurantId,
|
||||
'punch_time' => $dateTime,
|
||||
'type' => $type,
|
||||
])->exists();
|
||||
|
||||
if ($exists) {
|
||||
$skipped++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 2️⃣ Get or create daily attendance
|
||||
*/
|
||||
$attendance = Attendance::firstOrCreate(
|
||||
[
|
||||
'employee_id' => $employeeId,
|
||||
'date' => $date,
|
||||
],
|
||||
[
|
||||
'restaurant_id' => $restaurantId,
|
||||
'status' => 'present',
|
||||
]
|
||||
);
|
||||
|
||||
/**
|
||||
* 3️⃣ Insert punch log
|
||||
*/
|
||||
AttendanceLog::create([
|
||||
'attendance_id' => $attendance->id,
|
||||
'employee_id' => $employeeId,
|
||||
'restaurant_id' => $restaurantId,
|
||||
'type' => $type,
|
||||
'punch_time' => $dateTime,
|
||||
]);
|
||||
|
||||
/**
|
||||
* 4️⃣ Recalculate attendance summary
|
||||
*/
|
||||
$this->recalculateWorkedHours($attendance);
|
||||
|
||||
$inserted++;
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
|
||||
return $this->responseSuccess([
|
||||
'inserted' => $inserted,
|
||||
'skipped' => $skipped,
|
||||
'message' => 'Attendance synced successfully',
|
||||
], 'Profile fetched successfully.');
|
||||
} catch (\Throwable $e) {
|
||||
DB::rollBack();
|
||||
|
||||
return $this->responseError([
|
||||
'message' => 'Attendance sync failed',
|
||||
'error' => $e->getMessage(),
|
||||
], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private function mapPunchType(int $type): string
|
||||
{
|
||||
return in_array($type, [0, 4]) ? 'in' : 'out';
|
||||
}
|
||||
|
||||
/**
|
||||
* RECALCULATE TOTAL WORKED HOURS BASED ON ALL LOGS
|
||||
*/
|
||||
private function recalculateWorkedHours(Attendance $attendance)
|
||||
{
|
||||
$logs = AttendanceLog::where('attendance_id', $attendance->id)
|
||||
->orderBy('punch_time')
|
||||
->get();
|
||||
|
||||
if ($logs->count() < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
$first = Carbon::parse($logs->first()->punch_time);
|
||||
$last = Carbon::parse($logs->last()->punch_time);
|
||||
|
||||
$hours = round($first->diffInMinutes($last) / 60, 2);
|
||||
|
||||
$attendance->update([
|
||||
'first_clock_in' => $first,
|
||||
'last_clock_out' => $last,
|
||||
'hours_worked' => $hours,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Authentication\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\ResponseTrait;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Modules\Authentication\Models\User;
|
||||
|
||||
class AuthController extends Controller
|
||||
{
|
||||
use ResponseTrait;
|
||||
|
||||
public function user(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$user = User::where('id', Auth::id())->first();
|
||||
|
||||
if ($user) {
|
||||
$permissions = $user->role?->permissions->pluck('name')->toArray() ?? []; // Convert permissions to an array
|
||||
|
||||
$user = [
|
||||
'id' => $user->id,
|
||||
'name' => $user->first_name.' '.$user->last_name,
|
||||
'email' => $user->email,
|
||||
'phone' => $user->phone,
|
||||
'image' => $user->avatar,
|
||||
'role' => $user->role?->name ?? 'no-role-assign',
|
||||
'status' => $user->status,
|
||||
'restaurant_info' => $user?->restaurant,
|
||||
'rider_info' => $user?->rider,
|
||||
'permissions' => $permissions,
|
||||
];
|
||||
|
||||
return $this->responseSuccess($user, 'Profile fetched successfully.');
|
||||
} else {
|
||||
return $this->responseError([], 'Profile not found.');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], 'An error occurred while fetching the profile.');
|
||||
}
|
||||
}
|
||||
|
||||
public function login(Request $request): JsonResponse
|
||||
{
|
||||
$user = User::where('phone', $request->phone)->with('rider')->first();
|
||||
|
||||
if (! $user || ! Hash::check($request->password, $user->password)) {
|
||||
return $this->responseError([], 'The provided credentials are incorrect..', 403);
|
||||
}
|
||||
|
||||
$accessToken = $user->createToken('app')->accessToken;
|
||||
|
||||
return $this->responseSuccess([
|
||||
'user' => $user,
|
||||
'access_token' => $accessToken,
|
||||
'check' => 'Droplet Update',
|
||||
]);
|
||||
}
|
||||
|
||||
public function profileUpdate(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
// ✅ Validate input
|
||||
$request->validate([
|
||||
'name' => 'nullable|string|max:255',
|
||||
'email' => 'nullable|email|max:255',
|
||||
'avatar' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg,webp|max:2048',
|
||||
]);
|
||||
|
||||
$user = Auth::user();
|
||||
$updateData = [];
|
||||
|
||||
// 🧠 Update name if provided
|
||||
if ($request->filled('name')) {
|
||||
$updateData['first_name'] = $request->input('name');
|
||||
$updateData['last_name'] = null;
|
||||
}
|
||||
|
||||
// 🧠 Update email if provided
|
||||
if ($request->filled('email')) {
|
||||
$updateData['email'] = $request->input('email');
|
||||
}
|
||||
|
||||
// 🧹 Handle avatar upload using fileUploader()
|
||||
if ($request->hasFile('avatar')) {
|
||||
// 🔁 Delete old avatar if exists
|
||||
if (
|
||||
$user->avatar &&
|
||||
Storage::disk('public')->exists('users/'.$user->avatar)
|
||||
) {
|
||||
Storage::disk('public')->delete('users/'.$user->avatar);
|
||||
}
|
||||
|
||||
// 💾 Use custom uploader and store only the filename
|
||||
$filename = fileUploader('users/', 'png', $request->file('avatar')); // should return filename only
|
||||
$updateData['avatar'] = $filename;
|
||||
}
|
||||
|
||||
// 💾 Perform update
|
||||
if (! empty($updateData)) {
|
||||
$user->update($updateData);
|
||||
}
|
||||
|
||||
return $this->responseSuccess([
|
||||
'name' => $user->first_name.$user->last_name,
|
||||
'email' => $user->email,
|
||||
'avatar' => $user->avatar, // ✅ manual full URL from filename
|
||||
], 'Profile updated successfully.');
|
||||
} catch (ValidationException $e) {
|
||||
return $this->responseError($e->errors(), 'Validation failed.', 422);
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], 'Something went wrong.', 500);
|
||||
}
|
||||
}
|
||||
|
||||
public function changePassword(Request $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validateWithBag('updatePassword', [
|
||||
'current_password' => ['required', 'current_password'],
|
||||
'password' => ['required', 'confirmed'],
|
||||
]);
|
||||
|
||||
$request->user()->update([
|
||||
'password' => Hash::make($validated['password']),
|
||||
]);
|
||||
|
||||
return $this->responseSuccess([], 'Password Changes Successfully Done.');
|
||||
}
|
||||
|
||||
public function logout(Request $request)
|
||||
{
|
||||
try {
|
||||
$result = $request->user()->token()->revoke();
|
||||
if ($result) {
|
||||
return $this->responseSuccess([], 'Logout Success');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return $this->responseSuccess([], 'Logout Failed');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Authentication\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Mail\BillingInvoiceMail;
|
||||
use App\Traits\Authenticatable;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Modules\Authentication\Models\Restaurant;
|
||||
use Modules\Authentication\Models\Subscription;
|
||||
use Modules\Authentication\Models\SubscriptionItem;
|
||||
|
||||
class BillingController extends Controller
|
||||
{
|
||||
use Authenticatable;
|
||||
|
||||
public function expiringSoon(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$days = $request->get('days', 7); // default next 7 days
|
||||
|
||||
$query = Subscription::with([
|
||||
'package:id,name',
|
||||
'restaurant:id,name', // include restaurant info
|
||||
'user:id,name,email',
|
||||
])
|
||||
->where('status', 1)
|
||||
->whereNotNull('end_date')
|
||||
->whereBetween('end_date', [
|
||||
now()->startOfDay(),
|
||||
now()->addDays($days)->endOfDay(),
|
||||
]);
|
||||
|
||||
// Optional date range filter
|
||||
if ($request->filled(['from_date', 'to_date'])) {
|
||||
$query->whereBetween('end_date', [
|
||||
Carbon::parse($request->from_date)->startOfDay(),
|
||||
Carbon::parse($request->to_date)->endOfDay(),
|
||||
]);
|
||||
}
|
||||
|
||||
// Optional filtering by restaurant_id
|
||||
if ($request->filled('restaurant_id')) {
|
||||
$query->where('restaurant_id', $request->restaurant_id);
|
||||
}
|
||||
|
||||
// Sorting
|
||||
$sortBy = $request->get('sort_by', 'end_date');
|
||||
$sortOrder = $request->get('sort_order', 'asc');
|
||||
|
||||
$subscriptions = $query->orderBy($sortBy, $sortOrder)
|
||||
->paginate($request->get('per_page', 20));
|
||||
|
||||
return $this->responseSuccess($subscriptions, 'Expiring subscriptions fetched successfully.');
|
||||
} catch (\Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function transactions(Request $request): JsonResponse
|
||||
{
|
||||
$query = SubscriptionItem::with(['subscription', 'package', 'user']);
|
||||
|
||||
// Optional Restaurant Filter
|
||||
if ($request->filled('restaurant_id')) {
|
||||
$query->where('restaurant_id', $request->restaurant_id);
|
||||
}
|
||||
|
||||
// Status filter
|
||||
if ($request->filled('status')) {
|
||||
$query->where('status', $request->status);
|
||||
}
|
||||
|
||||
// Date range
|
||||
if ($request->filled(['from_date', 'to_date'])) {
|
||||
$query->whereBetween('created_at', [
|
||||
Carbon::parse($request->from_date)->startOfDay(),
|
||||
Carbon::parse($request->to_date)->endOfDay(),
|
||||
]);
|
||||
}
|
||||
|
||||
// Sorting
|
||||
$sortBy = $request->get('sort_by', 'created_at');
|
||||
$sortOrder = $request->get('sort_order', 'desc');
|
||||
|
||||
$transactions = $query->orderBy($sortBy, $sortOrder)
|
||||
->paginate($request->get('per_page', 20));
|
||||
|
||||
return $this->responseSuccess($transactions, 'Transactions list fetched successfully.');
|
||||
}
|
||||
|
||||
public function invoices(Request $request): JsonResponse
|
||||
{
|
||||
$query = SubscriptionItem::with(['subscription.restaurant', 'package', 'user'])
|
||||
->where('status', 1);
|
||||
|
||||
// Optional Restaurant Filter
|
||||
if ($request->filled('restaurant_id')) {
|
||||
$query->where('restaurant_id', $request->restaurant_id);
|
||||
}
|
||||
|
||||
// Date range
|
||||
if ($request->filled(['from_date', 'to_date'])) {
|
||||
$query->whereBetween('created_at', [
|
||||
Carbon::parse($request->from_date)->startOfDay(),
|
||||
Carbon::parse($request->to_date)->endOfDay(),
|
||||
]);
|
||||
}
|
||||
|
||||
$invoices = $query
|
||||
->orderBy($request->get('sort_by', 'created_at'), $request->get('sort_order', 'desc'))
|
||||
->paginate($request->get('per_page', 20));
|
||||
|
||||
// Append Invoice Number
|
||||
$invoices->getCollection()->transform(function ($item) {
|
||||
$item->invoice_no = 'INV-'.str_pad($item->id, 6, '0', STR_PAD_LEFT);
|
||||
|
||||
return $item;
|
||||
});
|
||||
|
||||
return $this->responseSuccess($invoices, 'Invoices list fetched successfully.');
|
||||
}
|
||||
|
||||
public function revenueAnalytics(Request $request): JsonResponse
|
||||
{
|
||||
$baseQuery = SubscriptionItem::where('status', 1);
|
||||
|
||||
// Optional Restaurant Filter
|
||||
if ($request->filled('restaurant_id')) {
|
||||
$baseQuery->where('restaurant_id', $request->restaurant_id);
|
||||
}
|
||||
|
||||
// Date range
|
||||
if ($request->filled(['from_date', 'to_date'])) {
|
||||
$baseQuery->whereBetween('created_at', [
|
||||
Carbon::parse($request->from_date)->startOfDay(),
|
||||
Carbon::parse($request->to_date)->endOfDay(),
|
||||
]);
|
||||
}
|
||||
|
||||
$totalRevenue = (clone $baseQuery)->sum('amount');
|
||||
$totalTransactions = (clone $baseQuery)->count();
|
||||
|
||||
$monthlyRevenue = (clone $baseQuery)
|
||||
->selectRaw('YEAR(created_at) year, MONTH(created_at) month, SUM(amount) total')
|
||||
->groupBy('year', 'month')
|
||||
->orderBy('year', 'desc')
|
||||
->get();
|
||||
|
||||
$packageWise = (clone $baseQuery)
|
||||
->selectRaw('package_id, SUM(amount) total')
|
||||
->groupBy('package_id')
|
||||
->with('package:id,name')
|
||||
->get();
|
||||
|
||||
return $this->responseSuccess([
|
||||
'total_revenue' => $totalRevenue,
|
||||
'total_transactions' => $totalTransactions,
|
||||
'monthly_revenue' => $monthlyRevenue,
|
||||
'package_wise' => $packageWise,
|
||||
], 'Revenue analytics fetched successfully.');
|
||||
}
|
||||
|
||||
public function sendBillingNotification(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'restaurant_ids' => 'required|array',
|
||||
'type' => 'required|in:email,sms,both',
|
||||
'notification_for' => 'required|string',
|
||||
]);
|
||||
|
||||
$restaurants = Restaurant::with(['subscriptions.package'])
|
||||
->whereIn('id', $request->restaurant_ids)
|
||||
->get();
|
||||
|
||||
foreach ($restaurants as $restaurant) {
|
||||
|
||||
$subscription = $restaurant->subscriptions()
|
||||
->where('status', 1)
|
||||
->latest()
|
||||
->first();
|
||||
|
||||
if (! $subscription) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$invoiceData = $this->buildInvoiceData($restaurant, $subscription);
|
||||
$paymentUrl = $this->generatePaymentUrl($restaurant, $subscription);
|
||||
$domainInfo = $this->getDomainInfo($restaurant);
|
||||
|
||||
/** EMAIL **/
|
||||
if (in_array($request->type, ['email', 'both'])) {
|
||||
Mail::to($restaurant->email)
|
||||
->queue(new BillingInvoiceMail(
|
||||
$restaurant,
|
||||
$subscription,
|
||||
$invoiceData,
|
||||
$paymentUrl,
|
||||
$domainInfo
|
||||
));
|
||||
}
|
||||
|
||||
/** SMS **/
|
||||
if (in_array($request->type, ['sms', 'both'])) {
|
||||
// TODO: Implement SMS sending logic here
|
||||
// $this->sendSms(
|
||||
// $restaurant->phone,
|
||||
// $this->buildSmsMessage($restaurant, $subscription, $paymentUrl)
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
||||
return $this->responseSuccess([], 'Billing notification sent successfully');
|
||||
}
|
||||
|
||||
private function buildInvoiceData($restaurant, $subscription)
|
||||
{
|
||||
return (object) [
|
||||
'invoice_no' => 'INV-'.str_pad($subscription->id, 6, '0', STR_PAD_LEFT),
|
||||
'amount' => $subscription->package->price,
|
||||
'package_name' => $subscription->package->name,
|
||||
'start_date' => $subscription->start_date,
|
||||
'end_date' => $subscription->end_date,
|
||||
'status' => 'Unpaid',
|
||||
'month' => now()->format('Y-m'),
|
||||
];
|
||||
}
|
||||
|
||||
private function getDomainInfo($restaurant)
|
||||
{
|
||||
return [
|
||||
'domain' => $restaurant->domain ?? null,
|
||||
'domain_expiry' => $restaurant->domain_expiry ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
private function generatePaymentUrl($restaurant, $subscription)
|
||||
{
|
||||
return config('app.frontend_url')
|
||||
.'/billing/upgrade?restaurant_id='
|
||||
.encrypt($restaurant->id);
|
||||
}
|
||||
|
||||
private function buildSmsMessage($restaurant, $subscription, $paymentUrl)
|
||||
{
|
||||
return "Hello {$restaurant->name}, your subscription expires on "
|
||||
.Carbon::parse($subscription->end_date)->format('d M Y')
|
||||
.". Upgrade now: {$paymentUrl}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Authentication\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Modules\Authentication\Http\Requests\Settings\MailConfigUpdateRequest;
|
||||
use Modules\Authentication\Http\Requests\Settings\SAASSettingUpdateRequest;
|
||||
use Modules\Authentication\Models\Package;
|
||||
use Modules\Authentication\Models\Restaurant;
|
||||
use Modules\Authentication\Models\SAASSetting;
|
||||
|
||||
class DashboardController extends Controller
|
||||
{
|
||||
public function saasDashboardData(Request $request): JsonResponse
|
||||
{
|
||||
$year = (int) $request->year ?? now()->year;
|
||||
|
||||
$totalRestaurantsCount = Restaurant::count();
|
||||
$totalPackages = Package::count();
|
||||
$totalActiveRestaurant = Restaurant::where('status', 1)->count();
|
||||
$totalInactiveRestaurant = Restaurant::where('status', 2)->count();
|
||||
|
||||
// Monthly transaction collection for current year
|
||||
$monthlyData = DB::table('subscription_items')
|
||||
->selectRaw('MONTH(created_at) as month, SUM(amount) as total_paid')
|
||||
->whereYear('created_at', $year)
|
||||
->whereNull('deleted_at')
|
||||
->groupBy(DB::raw('MONTH(created_at)'))
|
||||
->pluck('total_paid', 'month');
|
||||
|
||||
$monthlyReport = collect(range(1, 12))->map(function ($month) use ($monthlyData) {
|
||||
return [
|
||||
'month' => Carbon::create()->month($month)->format('F'),
|
||||
'total_paid' => $monthlyData->get($month, 0),
|
||||
];
|
||||
});
|
||||
|
||||
// Total unique restaurants with active subscriptions
|
||||
$totalRestaurants = DB::table('subscriptions')
|
||||
->where('status', 'active')
|
||||
->whereNull('deleted_at')
|
||||
->distinct('restaurant_id')
|
||||
->count('restaurant_id');
|
||||
|
||||
// Plan usage with percentages
|
||||
$packageUsage = DB::table('subscriptions')
|
||||
->join('packages', 'subscriptions.package_id', '=', 'packages.id')
|
||||
->where('subscriptions.status', 'active')
|
||||
->whereNull('subscriptions.deleted_at')
|
||||
->select('packages.id as package_id', 'packages.name as plan_name', DB::raw('COUNT(DISTINCT subscriptions.restaurant_id) as restaurant_count'))
|
||||
->groupBy('packages.id', 'packages.name')
|
||||
->get()
|
||||
->map(function ($item) use ($totalRestaurants) {
|
||||
return [
|
||||
'package_id' => $item->package_id,
|
||||
'plan_name' => $item->plan_name,
|
||||
'restaurant_count' => $item->restaurant_count,
|
||||
'usage_percent' => $totalRestaurants > 0
|
||||
? round(($item->restaurant_count / $totalRestaurants) * 100, 2)
|
||||
: 0,
|
||||
];
|
||||
});
|
||||
|
||||
return $this->responseSuccess([
|
||||
'total_restaurants' => $totalRestaurantsCount,
|
||||
'total_packages' => $totalPackages,
|
||||
'total_active_restaurant' => $totalActiveRestaurant,
|
||||
'total_inactive_restaurant' => $totalInactiveRestaurant,
|
||||
'transactions' => $monthlyReport,
|
||||
'package_report' => $packageUsage,
|
||||
], _lang('SaaS Dashboard data has been fetched successfully.'));
|
||||
}
|
||||
|
||||
public function getSAASSettings(Request $request)
|
||||
{
|
||||
$saas_settings = DB::table('s_a_a_s_settings')->pluck('value', 'name')->toArray();
|
||||
|
||||
return $this->responseSuccess($saas_settings, _lang('SaaS settings fetched successfully.'));
|
||||
}
|
||||
|
||||
public function saasSettingsUpdate(SAASSettingUpdateRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
// If POST request, update or create settings
|
||||
DB::beginTransaction();
|
||||
foreach ($request->validated() as $key => $value) {
|
||||
if ($key === '_token') {
|
||||
continue;
|
||||
}
|
||||
|
||||
SAASSetting::updateOrInsert(
|
||||
[
|
||||
'name' => $key,
|
||||
],
|
||||
[
|
||||
'value' => $value,
|
||||
'updated_at' => now(),
|
||||
'created_at' => now(),
|
||||
]
|
||||
);
|
||||
}
|
||||
DB::commit();
|
||||
|
||||
return $this->responseSuccess([], _lang('SAAS Settings have been successfully updated.'));
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
|
||||
return $this->responseError(['error' => $e->getMessage()], _lang('Failed to store records unsuccessfully.'));
|
||||
}
|
||||
}
|
||||
|
||||
public function getMailConfig()
|
||||
{
|
||||
$mailConfig = [
|
||||
'MAIL_MAILER' => env('MAIL_MAILER'),
|
||||
'MAIL_HOST' => env('MAIL_HOST'),
|
||||
'MAIL_PORT' => env('MAIL_PORT'),
|
||||
'MAIL_USERNAME' => env('MAIL_USERNAME'),
|
||||
'MAIL_PASSWORD' => env('MAIL_PASSWORD') ? '********' : null, // mask password
|
||||
'MAIL_FROM_ADDRESS' => env('MAIL_FROM_ADDRESS'),
|
||||
'MAIL_FROM_NAME' => env('MAIL_FROM_NAME'),
|
||||
];
|
||||
|
||||
return $this->responseSuccess(
|
||||
$mailConfig,
|
||||
_lang('Mail configuration fetched successfully.')
|
||||
);
|
||||
}
|
||||
|
||||
public function updateMailConfig(MailConfigUpdateRequest $request)
|
||||
{
|
||||
foreach ($request->validated() as $key => $value) {
|
||||
setEnvValue($key, $value);
|
||||
}
|
||||
|
||||
// Clear config cache so changes apply immediately
|
||||
Artisan::call('config:clear');
|
||||
Artisan::call('cache:clear');
|
||||
|
||||
return $this->responseSuccess(
|
||||
[],
|
||||
_lang('Mail configuration updated successfully.')
|
||||
);
|
||||
}
|
||||
|
||||
public function uploadLogo(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'logo' => 'required|image|mimes:jpeg,png,jpg|max:8192',
|
||||
]);
|
||||
|
||||
$imageUrl = null;
|
||||
if (isset($request['logo'])) {
|
||||
if (isset($request['logo']) && $request['logo']->isValid()) {
|
||||
$imageUrl = fileUploader('logos/', 'png', $request['logo']);
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare data
|
||||
$data = [
|
||||
'name' => 'logo',
|
||||
'value' => $imageUrl,
|
||||
'updated_at' => Carbon::now(),
|
||||
];
|
||||
|
||||
// Update or create logo setting
|
||||
$saasSetting = SAASSetting::updateOrCreate(
|
||||
[
|
||||
'name' => 'logo',
|
||||
],
|
||||
$data
|
||||
);
|
||||
|
||||
return $this->responseSuccess(
|
||||
$saasSetting,
|
||||
_lang('SAAS settings logo have been successfully updated')
|
||||
);
|
||||
}
|
||||
|
||||
public function getPackages(Request $request): JsonResponse
|
||||
{
|
||||
$perPage = $request->get('per_page', 10);
|
||||
$getPackages = Package::paginate($perPage);
|
||||
|
||||
return $this->responseSuccess($getPackages, 'Packages fetched successfully.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Authentication\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\RequestSanitizerTrait;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\Authentication\Models\Feedback;
|
||||
use Modules\Authentication\Repositories\FeedbackRepository;
|
||||
|
||||
class FeedbackController extends Controller
|
||||
{
|
||||
use RequestSanitizerTrait;
|
||||
|
||||
public function __construct(private FeedbackRepository $feedback) {}
|
||||
|
||||
public function getFeedbacks(Request $request): JsonResponse
|
||||
{
|
||||
$perPage = $request->get('per_page', 10);
|
||||
|
||||
$getFeedbacks = Feedback::with('user')->paginate($perPage);
|
||||
|
||||
return $this->responseSuccess($getFeedbacks, 'Feedbacks fetched successfully.');
|
||||
}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess(
|
||||
$this->feedback->getAll(request()->all()),
|
||||
'Feedback has been fetched successfully.'
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess(
|
||||
$this->feedback->create($request->all()),
|
||||
'Feedback has been created successfully.'
|
||||
);
|
||||
} catch (Exception $exception) {
|
||||
return $this->responseError([], $exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess(
|
||||
$this->feedback->getById($id),
|
||||
'Feedback has been fetched successfully.'
|
||||
);
|
||||
} catch (Exception $exception) {
|
||||
return $this->responseError([], $exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(Request $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess(
|
||||
$this->feedback->update($id, $this->getUpdateRequest($request)),
|
||||
'Feedback has been updated successfully.'
|
||||
);
|
||||
} catch (Exception $exception) {
|
||||
return $this->responseError([], $exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess(
|
||||
$this->feedback->delete($id),
|
||||
'Feedback has been deleted successfully.'
|
||||
);
|
||||
} catch (Exception $exception) {
|
||||
return $this->responseError([], $exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Authentication\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Modules\Authentication\Models\Gallery;
|
||||
|
||||
class GalleryController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$galleries = Gallery::all();
|
||||
|
||||
return $this->responseSuccess(
|
||||
$galleries,
|
||||
'Galleries has been fetched successfully.'
|
||||
);
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'title' => 'nullable|string|max:255',
|
||||
'image' => 'required',
|
||||
]);
|
||||
|
||||
$image = $request->file('image');
|
||||
$path = $image->store('gallery', 'public');
|
||||
|
||||
$gallery = Gallery::create([
|
||||
'title' => $request->title,
|
||||
'type' => $request->type,
|
||||
'image' => asset('storage/'.$path),
|
||||
]);
|
||||
|
||||
return response()->json($gallery, 201);
|
||||
}
|
||||
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$gallery = Gallery::where('id', $id)->first();
|
||||
if (! $gallery) {
|
||||
return $this->responseError([], 'Gallery Image Not Found');
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'title' => 'nullable|string|max:255',
|
||||
'image' => 'nullable',
|
||||
]);
|
||||
|
||||
if ($request->hasFile('image')) {
|
||||
// Delete old image
|
||||
$oldPath = str_replace(asset('storage/'), '', $gallery->image);
|
||||
Storage::disk('public')->delete($oldPath);
|
||||
|
||||
// Store new image
|
||||
$image = $request->file('image');
|
||||
$path = $image->store('gallery', 'public');
|
||||
$gallery->image = asset('storage/'.$path);
|
||||
}
|
||||
|
||||
if ($request->has('title')) {
|
||||
$gallery->title = $request->title;
|
||||
$gallery->type = $request->type;
|
||||
}
|
||||
|
||||
$gallery->save();
|
||||
|
||||
return $this->responseSuccess(
|
||||
$gallery,
|
||||
'Gallery image update successfully.'
|
||||
);
|
||||
}
|
||||
|
||||
public function destroy(int $id)
|
||||
{
|
||||
$gallery = Gallery::where('id', $id)->first();
|
||||
if (! $gallery) {
|
||||
return $this->responseError([], 'Gallery Image Not Found');
|
||||
}
|
||||
|
||||
// Delete file from storage
|
||||
$path = str_replace(asset('storage/'), '', $gallery->image);
|
||||
Storage::disk('public')->delete($path);
|
||||
|
||||
// Delete DB record
|
||||
$gallery->delete();
|
||||
|
||||
return $this->responseSuccess(
|
||||
$gallery,
|
||||
'Gallery image delete successfully.'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Authentication\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Modules\Authentication\Models\Language;
|
||||
|
||||
class LanguageController extends Controller
|
||||
{
|
||||
/**
|
||||
* List all languages or get a specific language by name
|
||||
*/
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$name = $request->query('name');
|
||||
|
||||
if ($name) {
|
||||
$lang = Language::where('name', $name)->first();
|
||||
|
||||
if (! $lang) {
|
||||
return $this->responseError([], 'Language not found', 404);
|
||||
}
|
||||
|
||||
$filePath = storage_path("app/public/{$lang->file_path}");
|
||||
if (! file_exists($filePath)) {
|
||||
return $this->responseError([], 'Language file not found', 404);
|
||||
}
|
||||
|
||||
$json = json_decode(file_get_contents($filePath), true);
|
||||
|
||||
return $this->responseSuccess([
|
||||
'name' => $lang->name,
|
||||
'code' => $lang->code,
|
||||
'flag_url' => $lang->flag ? asset("storage/flags/{$lang->flag}") : null,
|
||||
'file_url' => $lang->file_path ? asset("storage/{$lang->file_path}") : null,
|
||||
'data' => $json,
|
||||
'is_default' => $lang->is_default,
|
||||
'status' => $lang->status,
|
||||
], 'Language fetched successfully.');
|
||||
}
|
||||
|
||||
// Fetch all languages, default first
|
||||
$languages = Language::orderByDesc('is_default')->get()->map(function ($lang) {
|
||||
return [
|
||||
'id' => $lang->id,
|
||||
'name' => $lang->name,
|
||||
'code' => $lang->code,
|
||||
'flag_url' => $lang->flag ? asset("storage/flags/{$lang->flag}") : null,
|
||||
'file_url' => $lang->file_path ? asset("storage/{$lang->file_path}") : null,
|
||||
'is_default' => $lang->is_default,
|
||||
'status' => $lang->status,
|
||||
];
|
||||
});
|
||||
|
||||
return $this->responseSuccess($languages, 'Languages fetched successfully.');
|
||||
} catch (\Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a new language
|
||||
*/
|
||||
public function store(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'name' => 'required|string',
|
||||
'code' => 'required|string|max:10|unique:languages,code',
|
||||
'file' => 'required|file|mimes:json',
|
||||
'flag' => 'nullable|image|mimes:png,jpg,jpeg,svg',
|
||||
'is_default' => 'nullable|boolean',
|
||||
]);
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$fileName = $request->name.'.json';
|
||||
$filePath = $request->file('file')->storeAs('languages', $fileName, 'public');
|
||||
|
||||
$flagPath = null;
|
||||
if ($request->hasFile('flag')) {
|
||||
$flagPath = fileUploader('flags/', 'png', $request->file('flag'));
|
||||
}
|
||||
|
||||
$lang = Language::create([
|
||||
'name' => $request->name,
|
||||
'code' => $request->code,
|
||||
'file_path' => $filePath,
|
||||
'flag' => $flagPath,
|
||||
'is_default' => $request->input('is_default', false),
|
||||
'status' => 'active',
|
||||
]);
|
||||
|
||||
DB::commit();
|
||||
|
||||
return $this->responseSuccess($lang, 'Language created successfully.');
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update language (name, code, file, flag, default)
|
||||
*/
|
||||
public function update(Request $request, int $id): JsonResponse
|
||||
{
|
||||
$lang = Language::findOrFail($id);
|
||||
$request->validate([
|
||||
'name' => 'sometimes|required|string',
|
||||
'code' => 'sometimes|required|string|max:10|unique:languages,code,'.$lang->id,
|
||||
'file' => 'sometimes|file|mimes:json',
|
||||
'flag' => 'nullable|image|mimes:png,jpg,jpeg,svg|max:2048',
|
||||
'is_default' => 'nullable|boolean',
|
||||
]);
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
if ($request->has('name')) {
|
||||
$lang->name = $request->name;
|
||||
}
|
||||
if ($request->has('code')) {
|
||||
$lang->code = $request->code;
|
||||
}
|
||||
if ($request->hasFile('file')) {
|
||||
$fileName = $lang->name.'.json';
|
||||
$lang->file_path = $request->file('file')->storeAs('languages', $fileName, 'public');
|
||||
}
|
||||
if ($request->hasFile('flag')) {
|
||||
$lang->flag = fileUploader('flags/', 'png', $request->file('flag'), $lang->flag);
|
||||
}
|
||||
if ($request->has('is_default') && $request->is_default) {
|
||||
Language::where('is_default', true)->update(['is_default' => false]);
|
||||
$lang->is_default = true;
|
||||
}
|
||||
|
||||
$lang->save();
|
||||
DB::commit();
|
||||
|
||||
return $this->responseSuccess($lang, 'Language updated successfully.');
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Export JSON file
|
||||
*/
|
||||
public function export($id)
|
||||
{
|
||||
try {
|
||||
$lang = Language::findOrFail($id);
|
||||
$filePath = storage_path("app/public/{$lang->file_path}");
|
||||
|
||||
if (! file_exists($filePath)) {
|
||||
return $this->responseError([], 'File not found', 404);
|
||||
}
|
||||
|
||||
return response()->download($filePath, $lang->name.'.json');
|
||||
} catch (\Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default language
|
||||
*/
|
||||
public function setDefault($id): JsonResponse
|
||||
{
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
// Get the language to set as default
|
||||
$lang = Language::findOrFail($id);
|
||||
|
||||
// Set all languages is_default = false
|
||||
Language::where('is_default', true)->update(['is_default' => false]);
|
||||
|
||||
// Set selected language as default
|
||||
$lang->is_default = true;
|
||||
$lang->save();
|
||||
|
||||
DB::commit();
|
||||
|
||||
return $this->responseSuccess([], "$lang->name has been set as default.");
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete language
|
||||
*/
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
$lang = Language::findOrFail($id);
|
||||
|
||||
if ($lang->file_path && file_exists(storage_path("app/public/{$lang->file_path}"))) {
|
||||
unlink(storage_path("app/public/{$lang->file_path}"));
|
||||
}
|
||||
if ($lang->flag && file_exists(storage_path("app/public/{$lang->flag}"))) {
|
||||
unlink(storage_path("app/public/{$lang->flag}"));
|
||||
}
|
||||
|
||||
$lang->delete();
|
||||
|
||||
return $this->responseSuccess([], 'Language deleted successfully.');
|
||||
} catch (\Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function updateKey(Request $request, $name): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'key' => 'required|string',
|
||||
'value' => 'required|string',
|
||||
]);
|
||||
|
||||
$lang = Language::where('name', $name)->firstOrFail();
|
||||
$path = storage_path("app/public/{$lang->file_path}");
|
||||
|
||||
if (! file_exists($path)) {
|
||||
return response()->json(['error' => 'Language file not found'], 404);
|
||||
}
|
||||
|
||||
$json = json_decode(file_get_contents($path), true) ?? [];
|
||||
$json[$request->key] = $request->value;
|
||||
|
||||
file_put_contents($path, json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
||||
|
||||
return response()->json(['success' => true, 'message' => 'Key updated']);
|
||||
}
|
||||
|
||||
public function import(Request $request, $name): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'file' => 'required|file|mimes:json',
|
||||
]);
|
||||
|
||||
$lang = Language::where('code', $name)->firstOrFail();
|
||||
$fileName = $name.'.json';
|
||||
$path = $request->file('file')->storeAs('languages', $fileName, 'public');
|
||||
|
||||
$lang->update(['file_path' => $path]);
|
||||
|
||||
return response()->json(['success' => true, 'message' => 'Language updated']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,365 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Authentication\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\Authenticatable;
|
||||
use App\Traits\ResponseTrait;
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
use Modules\Authentication\Models\Package;
|
||||
use Modules\Authentication\Models\Restaurant;
|
||||
use Modules\Authentication\Models\Setting;
|
||||
use Modules\Authentication\Models\Subscription;
|
||||
use Modules\Authentication\Models\SubscriptionItem;
|
||||
use Modules\Authentication\Models\User;
|
||||
use Modules\Frontend\Models\Onboarding;
|
||||
use Spatie\Permission\Models\Role;
|
||||
|
||||
class OnboardingsController extends Controller
|
||||
{
|
||||
use Authenticatable, ResponseTrait;
|
||||
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
$query = Onboarding::query()
|
||||
->whereNull('approved_by') // Only unapproved list
|
||||
->orderBy('id', 'desc');
|
||||
|
||||
// 🔍 Search filter (restaurant name, email, phone)
|
||||
if ($request->filled('search')) {
|
||||
$search = $request->search;
|
||||
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('restaurant_name', 'LIKE', "%{$search}%")
|
||||
->orWhere('restaurant_email', 'LIKE', "%{$search}%")
|
||||
->orWhere('restaurant_phone', 'LIKE', "%{$search}%")
|
||||
->orWhere('name', 'LIKE', "%{$search}%") // Owner name
|
||||
->orWhere('email', 'LIKE', "%{$search}%"); // Owner email
|
||||
});
|
||||
}
|
||||
|
||||
// 🎯 Status filter
|
||||
if ($request->filled('status')) {
|
||||
$query->where('status', $request->status);
|
||||
}
|
||||
|
||||
$onboardings = $query->get();
|
||||
|
||||
return $this->responseSuccess($onboardings);
|
||||
}
|
||||
|
||||
public function approve(int $onboardingId): JsonResponse
|
||||
{
|
||||
// Start a transaction to ensure atomicity
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
// Fetch the data to approve
|
||||
$onboarding = Onboarding::where('id', $onboardingId)->first();
|
||||
if (! $onboarding) {
|
||||
return $this->responseError([], 'No Data Found!', 404);
|
||||
}
|
||||
|
||||
// Update the approval status and details
|
||||
$onboarding->status = 'approved';
|
||||
$onboarding->approved_by = $this->getCurrentUserId();
|
||||
$onboarding->approved_at = Carbon::now();
|
||||
$onboarding->save();
|
||||
|
||||
// Create the new restaurant
|
||||
$restaurant = Restaurant::create([
|
||||
'name' => $onboarding->restaurant_name,
|
||||
'type' => $onboarding->restaurant_type,
|
||||
'email' => $onboarding->restaurant_email,
|
||||
'phone' => $onboarding->restaurant_phone,
|
||||
'domain' => $onboarding->restaurant_domain,
|
||||
'address' => $onboarding->restaurant_address,
|
||||
'logo' => $onboarding->restaurant_logo,
|
||||
]);
|
||||
|
||||
$defaultSettings = [
|
||||
// 🏠 General Information
|
||||
['type' => 'general', 'option_key' => 'restaurant_name', 'option_value' => 'Restaurant'],
|
||||
['type' => 'general', 'option_key' => 'site_title', 'option_value' => 'restaurant'],
|
||||
['type' => 'general', 'option_key' => 'phone', 'option_value' => '01XXXXXXXXX'],
|
||||
['type' => 'general', 'option_key' => 'email', 'option_value' => 'restaurant@gmail.com'],
|
||||
['type' => 'general', 'option_key' => 'language', 'option_value' => 'en'],
|
||||
['type' => 'general', 'option_key' => 'google_map', 'option_value' => ''],
|
||||
['type' => 'general', 'option_key' => 'address', 'option_value' => 'Asia,Dhaka-1219'],
|
||||
['type' => 'general', 'option_key' => 'on_google_map', 'option_value' => ''],
|
||||
['type' => 'general', 'option_key' => 'restaurant_code', 'option_value' => '987654'],
|
||||
['type' => 'general', 'option_key' => 'currency_symbol', 'option_value' => '$'],
|
||||
['type' => 'general', 'option_key' => 'logo', 'option_value' => 'logo.png'],
|
||||
['type' => 'general', 'option_key' => 'mail_type', 'option_value' => 'mail'],
|
||||
['type' => 'general', 'option_key' => 'disabled_website', 'option_value' => 'no'],
|
||||
['type' => 'general', 'option_key' => 'copyright_text', 'option_value' => '© Copyright 2025. All Rights Reserved by FueDevs LTD'],
|
||||
['type' => 'general', 'option_key' => 'facebook_link', 'option_value' => 'https://www.facebook.com/'],
|
||||
['type' => 'general', 'option_key' => 'google_plus_link', 'option_value' => 'https://www.google.com/'],
|
||||
['type' => 'general', 'option_key' => 'youtube_link', 'option_value' => 'https://www.youtube.com/'],
|
||||
['type' => 'general', 'option_key' => 'whats_app_link', 'option_value' => ''],
|
||||
['type' => 'general', 'option_key' => 'twitter_link', 'option_value' => 'https://www.twitter.com'],
|
||||
['type' => 'general', 'option_key' => 'eiin_code', 'option_value' => ''],
|
||||
['type' => 'general', 'option_key' => 'sms_gateway', 'option_value' => 'twilio'],
|
||||
['type' => 'general', 'option_key' => 'bulk_sms_api_key', 'option_value' => ''],
|
||||
['type' => 'general', 'option_key' => 'bulk_sms_sender_id', 'option_value' => ''],
|
||||
['type' => 'general', 'option_key' => 'twilio_sid', 'option_value' => ''],
|
||||
['type' => 'general', 'option_key' => 'twilio_token', 'option_value' => ''],
|
||||
['type' => 'general', 'option_key' => 'twilio_from_number', 'option_value' => ''],
|
||||
['type' => 'general', 'option_key' => 'header_notice', 'option_value' => ''],
|
||||
['type' => 'general', 'option_key' => 'app_version', 'option_value' => '1.0.0'],
|
||||
['type' => 'general', 'option_key' => 'app_url', 'option_value' => 'drive-link'],
|
||||
|
||||
// 🎨 Restaurant Identity & Display
|
||||
['type' => 'general', 'option_key' => 'tagline', 'option_value' => 'Delicious Food, Fresh Taste'],
|
||||
['type' => 'general', 'option_key' => 'favicon', 'option_value' => 'favicon.png'],
|
||||
['type' => 'general', 'option_key' => 'theme_color', 'option_value' => '#ff6b00'],
|
||||
['type' => 'general', 'option_key' => 'background_image', 'option_value' => 'bg.jpg'],
|
||||
|
||||
// 💰 Finance / POS / Invoice
|
||||
['type' => 'pos', 'option_key' => 'tax_type', 'option_value' => 'exclusive'],
|
||||
['type' => 'pos', 'option_key' => 'tax_percentage', 'option_value' => '10'],
|
||||
['type' => 'pos', 'option_key' => 'service_charge', 'option_value' => '5'],
|
||||
['type' => 'pos', 'option_key' => 'default_currency', 'option_value' => 'USD'],
|
||||
['type' => 'pos', 'option_key' => 'billing_prefix', 'option_value' => 'INV-'],
|
||||
['type' => 'pos', 'option_key' => 'invoice_footer', 'option_value' => 'Thank you! Visit again.'],
|
||||
['type' => 'pos', 'option_key' => 'enable_kitchen_print', 'option_value' => 'yes'],
|
||||
['type' => 'pos', 'option_key' => 'enable_customer_copy', 'option_value' => 'yes'],
|
||||
|
||||
// 🚚 Online Ordering & Delivery
|
||||
['type' => 'order', 'option_key' => 'enable_online_order', 'option_value' => 'yes'],
|
||||
['type' => 'order', 'option_key' => 'delivery_charge', 'option_value' => '50'],
|
||||
['type' => 'order', 'option_key' => 'minimum_order_amount', 'option_value' => '100'],
|
||||
['type' => 'order', 'option_key' => 'auto_accept_order', 'option_value' => 'no'],
|
||||
['type' => 'order', 'option_key' => 'estimated_preparation_time', 'option_value' => '30'],
|
||||
|
||||
// 🔔 Notifications / Integrations
|
||||
['type' => 'integration', 'option_key' => 'slack_webhook_url', 'option_value' => ''],
|
||||
['type' => 'integration', 'option_key' => 'telegram_bot_token', 'option_value' => ''],
|
||||
['type' => 'integration', 'option_key' => 'telegram_chat_id', 'option_value' => ''],
|
||||
['type' => 'integration', 'option_key' => 'twilio_sms_enabled', 'option_value' => 'yes'],
|
||||
['type' => 'integration', 'option_key' => 'email_notifications', 'option_value' => 'yes'],
|
||||
['type' => 'integration', 'option_key' => 'whatsapp_notifications', 'option_value' => 'no'],
|
||||
|
||||
// 🧾 Reports & Logs
|
||||
['type' => 'system', 'option_key' => 'auto_backup', 'option_value' => 'daily'],
|
||||
['type' => 'system', 'option_key' => 'report_timezone', 'option_value' => 'Asia/Dhaka'],
|
||||
['type' => 'system', 'option_key' => 'data_retention_days', 'option_value' => '365'],
|
||||
|
||||
// 💻 UI/UX Preferences
|
||||
['type' => 'ui', 'option_key' => 'sidebar_collapsed', 'option_value' => 'no'],
|
||||
['type' => 'ui', 'option_key' => 'dark_mode', 'option_value' => 'no'],
|
||||
['type' => 'ui', 'option_key' => 'default_dashboard', 'option_value' => 'sales'],
|
||||
|
||||
// 💳 Payment Gateways
|
||||
['type' => 'payment', 'option_key' => 'razorpay_key', 'option_value' => ''],
|
||||
['type' => 'payment', 'option_key' => 'razorpay_secret', 'option_value' => ''],
|
||||
['type' => 'payment', 'option_key' => 'stripe_key', 'option_value' => ''],
|
||||
['type' => 'payment', 'option_key' => 'stripe_secret', 'option_value' => ''],
|
||||
['type' => 'payment', 'option_key' => 'cash_on_delivery', 'option_value' => 'yes'],
|
||||
|
||||
// 👨🍳 Kitchen & Staff
|
||||
['type' => 'staff', 'option_key' => 'max_table_capacity', 'option_value' => '10'],
|
||||
['type' => 'staff', 'option_key' => 'default_shift_start', 'option_value' => '09:00'],
|
||||
['type' => 'staff', 'option_key' => 'default_shift_end', 'option_value' => '23:00'],
|
||||
['type' => 'staff', 'option_key' => 'auto_logout_idle_minutes', 'option_value' => '60'],
|
||||
|
||||
// 🎨 Color Combination
|
||||
['type' => 'system_color', 'option_key' => 'primary_color', 'option_value' => null],
|
||||
['type' => 'system_color', 'option_key' => 'secondary_color', 'option_value' => null],
|
||||
['type' => 'system_color', 'option_key' => 'primary_container_color', 'option_value' => null],
|
||||
['type' => 'system_color', 'option_key' => 'dark_primary_color', 'option_value' => null],
|
||||
['type' => 'system_color', 'option_key' => 'dark_secondary_color', 'option_value' => null],
|
||||
['type' => 'system_color', 'option_key' => 'dark_container_color', 'option_value' => null],
|
||||
['type' => 'system_color', 'option_key' => 'text_color', 'option_value' => null],
|
||||
['type' => 'system_color', 'option_key' => 'dark_text_color', 'option_value' => null],
|
||||
['type' => 'system_color', 'option_key' => 'sidebar_selected_bg_color', 'option_value' => null],
|
||||
['type' => 'system_color', 'option_key' => 'sidebar_selected_text_color', 'option_value' => null],
|
||||
|
||||
// 🚚 Delivery / Online features
|
||||
['type' => 'delivery', 'option_key' => 'is_online', 'option_value' => false],
|
||||
['type' => 'delivery', 'option_key' => 'latitude', 'option_value' => '23.8103'],
|
||||
['type' => 'delivery', 'option_key' => 'longitude', 'option_value' => '90.4125'],
|
||||
['type' => 'delivery', 'option_key' => 'delivery_radius_km', 'option_value' => 5.00],
|
||||
['type' => 'delivery', 'option_key' => 'delivery_fee', 'option_value' => 0],
|
||||
['type' => 'delivery', 'option_key' => 'delivery_partner_count', 'option_value' => 0],
|
||||
['type' => 'delivery', 'option_key' => 'delivery_time_avg', 'option_value' => 30],
|
||||
['type' => 'delivery', 'option_key' => 'pickup_enabled', 'option_value' => true],
|
||||
|
||||
// 🕒 Operational hours & capacity
|
||||
['type' => 'operational', 'option_key' => 'opening_time', 'option_value' => '09:00:00'],
|
||||
['type' => 'operational', 'option_key' => 'closing_time', 'option_value' => '22:00:00'],
|
||||
['type' => 'operational', 'option_key' => 'auto_accept_orders', 'option_value' => false],
|
||||
['type' => 'operational', 'option_key' => 'pre_order_enabled', 'option_value' => false],
|
||||
['type' => 'operational', 'option_key' => 'max_order_capacity', 'option_value' => 50],
|
||||
|
||||
// 📊 Analytics / reviews
|
||||
['type' => 'analytics', 'option_key' => 'avg_rating', 'option_value' => 0.00],
|
||||
['type' => 'analytics', 'option_key' => 'review_count', 'option_value' => 0],
|
||||
['type' => 'analytics', 'option_key' => 'total_orders', 'option_value' => 0],
|
||||
['type' => 'analytics', 'option_key' => 'last_order_time', 'option_value' => null],
|
||||
['type' => 'analytics', 'option_key' => 'last_active_time', 'option_value' => null],
|
||||
|
||||
// 🎯 Marketing & social
|
||||
['type' => 'marketing', 'option_key' => 'loyalty_points_enabled', 'option_value' => false],
|
||||
['type' => 'marketing', 'option_key' => 'offers_enabled', 'option_value' => false],
|
||||
['type' => 'marketing', 'option_key' => 'social_media_links', 'option_value' => json_encode([
|
||||
'facebook' => '',
|
||||
'instagram' => '',
|
||||
'twitter' => '',
|
||||
'linkedin' => '',
|
||||
])],
|
||||
|
||||
// 💻 Future-proof / extra
|
||||
['type' => 'system', 'option_key' => 'settings', 'option_value' => json_encode([])],
|
||||
['type' => 'system', 'option_key' => 'uuid', 'option_value' => uniqid('rest_')],
|
||||
|
||||
// Email Config
|
||||
['type' => 'email_config', 'option_key' => 'email_smtp_host', 'option_value' => null],
|
||||
['type' => 'email_config', 'option_key' => 'email_smtp_port', 'option_value' => null],
|
||||
['type' => 'email_config', 'option_key' => 'email_smtp_username', 'option_value' => null],
|
||||
['type' => 'email_config', 'option_key' => 'email_smtp_password', 'option_value' => null],
|
||||
['type' => 'email_config', 'option_key' => 'email_smtp_encryption', 'option_value' => null],
|
||||
|
||||
// SMS Config
|
||||
['type' => 'sms_config', 'option_key' => 'twilio_api_key', 'option_value' => null],
|
||||
['type' => 'sms_config', 'option_key' => 'twilio_api_secret', 'option_value' => null],
|
||||
['type' => 'sms_config', 'option_key' => 'twilio_sender_id', 'option_value' => null],
|
||||
['type' => 'sms_config', 'option_key' => 'twilio_api_url', 'option_value' => null],
|
||||
['type' => 'sms_config', 'option_key' => 'twilio_is_default', 'option_value' => null],
|
||||
['type' => 'sms_config', 'option_key' => 'nexmo_api_key', 'option_value' => null],
|
||||
['type' => 'sms_config', 'option_key' => 'nexmo_api_secret', 'option_value' => null],
|
||||
['type' => 'sms_config', 'option_key' => 'nexmo_sender_id', 'option_value' => null],
|
||||
['type' => 'sms_config', 'option_key' => 'nexmo_api_url', 'option_value' => null],
|
||||
['type' => 'sms_config', 'option_key' => 'nexmo_is_default', 'option_value' => null],
|
||||
['type' => 'sms_config', 'option_key' => 'muthofun_api_key', 'option_value' => null],
|
||||
['type' => 'sms_config', 'option_key' => 'smsglobal_api_key', 'option_value' => null],
|
||||
['type' => 'sms_config', 'option_key' => 'smsglobal_api_secret', 'option_value' => null],
|
||||
['type' => 'sms_config', 'option_key' => 'smsglobal_sender_id', 'option_value' => null],
|
||||
['type' => 'sms_config', 'option_key' => 'smsglobal_api_url', 'option_value' => null],
|
||||
['type' => 'sms_config', 'option_key' => 'smsglobal_extra_key', 'option_value' => null],
|
||||
['type' => 'sms_config', 'option_key' => 'smsglobal_is_default', 'option_value' => null],
|
||||
|
||||
];
|
||||
foreach ($defaultSettings as &$setting) {
|
||||
$setting['setting_type'] = 'Restaurant';
|
||||
$setting['restaurant_id'] = $restaurant->id;
|
||||
$setting['created_at'] = now();
|
||||
$setting['updated_at'] = now();
|
||||
}
|
||||
|
||||
Setting::insert($defaultSettings);
|
||||
|
||||
// Create the associated user
|
||||
$user = User::create([
|
||||
'first_name' => $onboarding->name,
|
||||
'email' => $onboarding->email,
|
||||
'phone' => $onboarding->phone,
|
||||
'password' => $onboarding->password,
|
||||
'restaurant_id' => $restaurant->id,
|
||||
'avatar' => $onboarding->avatar,
|
||||
'role_id' => 2,
|
||||
]);
|
||||
|
||||
$role = Role::where('id', 2)->first();
|
||||
$user->assignRole($role);
|
||||
|
||||
// Commit the transaction
|
||||
DB::commit();
|
||||
|
||||
// Respond with a success message
|
||||
return response()->json([
|
||||
'status' => true,
|
||||
'message' => 'Restaurant approved and created successfully.',
|
||||
'data' => [
|
||||
'restaurant' => $restaurant,
|
||||
'user' => $user,
|
||||
],
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
// Rollback in case of any failure
|
||||
DB::rollBack();
|
||||
|
||||
return response()->json([
|
||||
'status' => false,
|
||||
'message' => 'An error occurred: '.$e->getMessage(),
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
public function upgrade(Request $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'restaurant_id' => 'required|exists:restaurants,id',
|
||||
'package_id' => 'required|exists:packages,id',
|
||||
'extra_days' => 'nullable|integer|min:0',
|
||||
'payment_method' => 'required|string',
|
||||
'amount_paid' => 'required|numeric|min:0',
|
||||
'notes' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$restaurantId = $validated['restaurant_id'];
|
||||
$package = Package::findOrFail($validated['package_id']);
|
||||
$extraDays = $validated['extra_days'] ?? 0;
|
||||
|
||||
$startDate = Carbon::now();
|
||||
$endDate = $startDate->copy()->addDays($package->duration_days + $extraDays);
|
||||
|
||||
// Fetch or create subscription for the restaurant
|
||||
$subscription = Subscription::where('restaurant_id', $restaurantId)->first();
|
||||
if ($subscription) {
|
||||
// Update existing subscription
|
||||
$subscription->update([
|
||||
'package_id' => $package->id,
|
||||
'start_date' => $startDate,
|
||||
'end_date' => $endDate,
|
||||
'status' => 1,
|
||||
'transactions_details' => json_encode([
|
||||
'invoice_no' => 'INV-'.strtoupper(Str::random(6)),
|
||||
'issued_at' => now()->toDateString(),
|
||||
'notes' => $validated['notes'] ?? 'Upgraded Package',
|
||||
'amount' => $validated['amount_paid'],
|
||||
]),
|
||||
]);
|
||||
} else {
|
||||
// Fallback (shouldn’t happen unless subscription was deleted)
|
||||
$subscription = Subscription::create([
|
||||
'restaurant_id' => $restaurantId,
|
||||
'package_id' => $package->id,
|
||||
'start_date' => $startDate,
|
||||
'end_date' => $endDate,
|
||||
'status' => 1,
|
||||
'user_id' => Auth::id(),
|
||||
'transactions_details' => json_encode([
|
||||
'invoice_no' => 'INV-'.strtoupper(Str::random(6)),
|
||||
'issued_at' => now()->toDateString(),
|
||||
'notes' => $validated['notes'] ?? 'Initial Subscription',
|
||||
'amount' => $validated['amount_paid'],
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
// Create new subscription item
|
||||
SubscriptionItem::create([
|
||||
'user_id' => Auth::id(),
|
||||
'restaurant_id' => $subscription->restaurant_id,
|
||||
'subscription_id' => $subscription->id,
|
||||
'reference_no' => 'PAY-'.strtoupper(Str::random(8)),
|
||||
'amount_paid' => $validated['amount_paid'],
|
||||
'payment_method' => $validated['payment_method'],
|
||||
]);
|
||||
|
||||
// Respond with a success message
|
||||
return $this->responseSuccess([
|
||||
'restaurantId' => $restaurantId,
|
||||
'package' => $package,
|
||||
'extraDays' => $extraDays,
|
||||
'subscription' => $subscription,
|
||||
], 'Subscription updated successfully.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Authentication\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Package\PackageStoreRequest;
|
||||
use App\Http\Requests\Package\PackageUpdateRequest;
|
||||
use App\Services\Package\PackageService;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class PackageController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private PackageService $service
|
||||
) {}
|
||||
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
$packages = $this->service->getAll($request);
|
||||
|
||||
return $this->responseSuccess($packages);
|
||||
}
|
||||
|
||||
public function store(PackageStoreRequest $request)
|
||||
{
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$package = $this->service->create($request->validated());
|
||||
DB::commit();
|
||||
|
||||
return $this->responseSuccess($package, 'Package has been create successfully.');
|
||||
} catch (Exception $e) {
|
||||
DB::rollBack();
|
||||
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id)
|
||||
{
|
||||
$package = $this->service->getById($id);
|
||||
|
||||
return $this->responseSuccess($package, 'Package has been fetch successfully.');
|
||||
}
|
||||
|
||||
public function update(PackageUpdateRequest $request, int $id)
|
||||
{
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
// Get validated data
|
||||
$data = $request->validated();
|
||||
|
||||
// List of all checkbox/boolean fields
|
||||
$booleanFields = [
|
||||
'sms_management_enabled',
|
||||
'chat_enable',
|
||||
'landing_page_enable',
|
||||
'blog_enable',
|
||||
'auto_renew',
|
||||
];
|
||||
|
||||
// Set missing checkboxes to 0
|
||||
foreach ($booleanFields as $field) {
|
||||
if (! isset($data[$field])) {
|
||||
$data[$field] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
$package = $this->service->update($id, $data);
|
||||
|
||||
DB::commit();
|
||||
|
||||
return $this->responseSuccess($package, 'Package has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
DB::rollBack();
|
||||
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id)
|
||||
{
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$this->service->delete($id);
|
||||
|
||||
DB::commit();
|
||||
|
||||
return $this->responseSuccess([], 'Package has been delete successfully.');
|
||||
} catch (Exception $e) {
|
||||
DB::rollBack();
|
||||
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Authentication\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Modules\Authentication\Services\PermissionService;
|
||||
|
||||
class PermissionController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private PermissionService $service
|
||||
) {}
|
||||
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
$perPage = request('per_page') ?? 10;
|
||||
$permissions = $this->service->index($request, $perPage);
|
||||
|
||||
if (! $permissions) {
|
||||
return $this->responseError([], 'No Data Found!', 404);
|
||||
}
|
||||
|
||||
return $this->responseSuccess($permissions);
|
||||
}
|
||||
|
||||
public function store(Request $request): JsonResponse
|
||||
{
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$this->service->create($request->validated());
|
||||
DB::commit();
|
||||
|
||||
return $this->responseSuccess([]);
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
|
||||
return $this->responseError([], 'Data Process Error!');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Authentication\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\RequestSanitizerTrait;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\Authentication\Http\Requests\Restaurant\RestaurantStoreRequest;
|
||||
use Modules\Authentication\Http\Requests\Restaurant\RestaurantUpdateRequest;
|
||||
use Modules\Authentication\Models\Restaurant;
|
||||
use Modules\Authentication\Repositories\RestaurantRepository;
|
||||
|
||||
class RestaurantController extends Controller
|
||||
{
|
||||
use RequestSanitizerTrait;
|
||||
|
||||
public function __construct(private RestaurantRepository $restaurant) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess(
|
||||
$this->restaurant->getAll(request()->all()),
|
||||
'Restaurant has been fetched successfully.'
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(RestaurantStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess(
|
||||
$this->restaurant->create($request->all()),
|
||||
'Restaurant has been created successfully.'
|
||||
);
|
||||
} catch (Exception $exception) {
|
||||
return $this->responseError([], $exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess(
|
||||
$this->restaurant->getById($id),
|
||||
'Restaurant has been fetched successfully.'
|
||||
);
|
||||
} catch (Exception $exception) {
|
||||
return $this->responseError([], $exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(RestaurantUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess(
|
||||
$this->restaurant->update($id, $this->getUpdateRequest($request)),
|
||||
'Restaurant has been updated successfully.'
|
||||
);
|
||||
} catch (Exception $exception) {
|
||||
return $this->responseError([], $exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
$restaurant = $this->restaurant->getById($id);
|
||||
if (! $restaurant) {
|
||||
return $this->responseError([], 'Restaurant Not Found');
|
||||
}
|
||||
if ($restaurant->logo) {
|
||||
fileUploader('restaurant/', 'png', null, $restaurant->logo);
|
||||
}
|
||||
|
||||
return $this->responseSuccess(
|
||||
$this->restaurant->delete($id),
|
||||
'Restaurant has been deleted successfully.'
|
||||
);
|
||||
} catch (Exception $exception) {
|
||||
return $this->responseError([], $exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
public function statusUpdate(int $restaurantId): JsonResponse
|
||||
{
|
||||
try {
|
||||
$restaurant = Restaurant::find($restaurantId);
|
||||
|
||||
if ($restaurant) {
|
||||
$restaurant->status = $restaurant->status == 1 ? 0 : 1;
|
||||
$restaurant->save();
|
||||
|
||||
return $this->responseSuccess(
|
||||
['status' => $restaurant->status],
|
||||
'Restaurant status updated successfully.'
|
||||
);
|
||||
}
|
||||
|
||||
return $this->responseError([], 'Restaurant not found.', 404);
|
||||
} catch (Exception $exception) {
|
||||
return $this->responseError([], $exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
public function restaurantTheme(Request $request)
|
||||
{
|
||||
$restaurant = Restaurant::where('id', $request->restaurant_id)->first();
|
||||
$restaurant->update([
|
||||
'theme_id' => $request->theme_id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Authentication\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Authentication\Http\Requests\RestaurantImageSAASSetting\RestaurantImageSAASSettingStoreRequest;
|
||||
use Modules\Authentication\Http\Requests\RestaurantImageSAASSetting\RestaurantImageSAASSettingUpdateRequest;
|
||||
use Modules\Authentication\Models\RestaurantImageSAASSetting;
|
||||
use Modules\Authentication\Repositories\RestaurantImageSAASSettingRepository;
|
||||
|
||||
class RestaurantImageSAASSettingController extends Controller
|
||||
{
|
||||
public function __construct(private RestaurantImageSAASSettingRepository $repo) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
$restaurantImageSAASSetting = RestaurantImageSAASSetting::first();
|
||||
|
||||
return $this->responseSuccess($restaurantImageSAASSetting, 'RestaurantImageSAASSetting has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(RestaurantImageSAASSettingStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->create($request->all()), 'RestaurantImageSAASSetting has been created successfully.');
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
return $this->responseError([], 'Database error: '.$exception->getMessage());
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getById($id), 'RestaurantImageSAASSetting has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(RestaurantImageSAASSettingUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->update($id, $request->all()), 'RestaurantImageSAASSetting has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->delete($id), 'RestaurantImageSAASSetting has been deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Authentication\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Authentication\Http\Requests\RestaurantImageSetting\RestaurantImageSettingStoreRequest;
|
||||
use Modules\Authentication\Http\Requests\RestaurantImageSetting\RestaurantImageSettingUpdateRequest;
|
||||
use Modules\Authentication\Models\RestaurantImageSetting;
|
||||
use Modules\Authentication\Repositories\RestaurantImageSettingRepository;
|
||||
|
||||
class RestaurantImageSettingController extends Controller
|
||||
{
|
||||
public function __construct(private RestaurantImageSettingRepository $repo) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
$restaurantImageSetting = RestaurantImageSetting::where('restaurant_id', getUserRestaurantId())->first();
|
||||
|
||||
return $this->responseSuccess($restaurantImageSetting, 'RestaurantImageSetting has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(RestaurantImageSettingStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->create($request->all()), 'RestaurantImageSetting has been created successfully.');
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
return $this->responseError([], 'Database error: '.$exception->getMessage());
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getById($id), 'RestaurantImageSetting has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(RestaurantImageSettingUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->update($id, $request->all()), 'RestaurantImageSetting has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->delete($id), 'RestaurantImageSetting has been deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Authentication\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\HasPermissionTrait;
|
||||
use Exception;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Modules\Authentication\Http\Requests\Role\RoleStoreRequest;
|
||||
use Modules\Authentication\Http\Requests\Role\RoleUpdateRequest;
|
||||
use Modules\Authentication\Models\Restaurant;
|
||||
use Modules\Authentication\Services\PermissionService;
|
||||
use Modules\Authentication\Services\RoleService;
|
||||
|
||||
class RoleController extends Controller
|
||||
{
|
||||
use HasPermissionTrait;
|
||||
|
||||
public function __construct(
|
||||
private RoleService $service,
|
||||
private PermissionService $permissionService
|
||||
) {}
|
||||
|
||||
public function index(Request $request)
|
||||
{
|
||||
$perPage = request('perPage') ?? env('PER_PAGE');
|
||||
$roles = $this->service->index($request, (int) $perPage);
|
||||
|
||||
if (! $roles) {
|
||||
return $this->responseSuccess([], 'No Data Found!');
|
||||
}
|
||||
|
||||
return $this->responseSuccess($roles);
|
||||
}
|
||||
|
||||
public function store(RoleStoreRequest $request)
|
||||
{
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$this->service->create($request->validated());
|
||||
DB::commit();
|
||||
|
||||
return $this->responseSuccess([]);
|
||||
} catch (Exception $e) {
|
||||
DB::rollBack();
|
||||
|
||||
return $this->responseError([], $e->getMessage().' in '.$e->getFile().' on line '.$e->getLine());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id)
|
||||
{
|
||||
$role = $this->service->getById($id);
|
||||
|
||||
if (! $role) {
|
||||
return $this->responseSuccess([], 'No Data Found!');
|
||||
}
|
||||
|
||||
return $this->responseSuccess($role);
|
||||
}
|
||||
|
||||
public function edit(Request $request, int $id)
|
||||
{
|
||||
$permissions = $this->permissionService->getAll($request);
|
||||
$groupedPermissions = [];
|
||||
foreach ($permissions as $permission) {
|
||||
$group = explode('-', $permission->name)[0];
|
||||
if (! isset($groupedPermissions[$group])) {
|
||||
$groupedPermissions[$group] = [];
|
||||
}
|
||||
$groupedPermissions[$group][] = $permission;
|
||||
}
|
||||
|
||||
return $this->responseSuccess([]);
|
||||
}
|
||||
|
||||
public function update(RoleUpdateRequest $request, int $id)
|
||||
{
|
||||
// Block system generated roles (ID: 1–7)
|
||||
if ($id >= 1 && $id <= 7) {
|
||||
return $this->responseError([], 'System-generated roles cannot be updated.', 422);
|
||||
}
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$this->service->update($id, $request->validated());
|
||||
DB::commit();
|
||||
|
||||
return $this->responseSuccess([]);
|
||||
} catch (Exception $e) {
|
||||
DB::rollBack();
|
||||
|
||||
return $this->responseError([], $e->getMessage().' in '.$e->getFile().' on line '.$e->getLine());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id)
|
||||
{
|
||||
// Block system generated roles (ID: 1–7)
|
||||
if ($id >= 1 && $id <= 7) {
|
||||
return $this->responseError([], 'System-generated roles cannot be deleted.', 422);
|
||||
}
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$data = $this->service->delete($id);
|
||||
if (! $data) {
|
||||
return $this->responseError([], 'Data Not Found!');
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
|
||||
return $this->responseSuccess([], 'Data successfully delete!');
|
||||
} catch (Exception $e) {
|
||||
DB::rollBack();
|
||||
|
||||
return $this->responseError([], $e->getMessage().' in '.$e->getFile().' on line '.$e->getLine());
|
||||
}
|
||||
}
|
||||
|
||||
public function restaurantTheme(Request $request)
|
||||
{
|
||||
// Validate request
|
||||
$validated = $request->validate([
|
||||
'restaurant_id' => 'required|integer|exists:restaurants,id',
|
||||
'theme_id' => 'required|integer|exists:themes,id',
|
||||
]);
|
||||
|
||||
// Fetch restaurant safely
|
||||
$restaurant = Restaurant::findOrFail($validated['restaurant_id']);
|
||||
// Update theme
|
||||
$restaurant->update([
|
||||
'theme_id' => $validated['theme_id'],
|
||||
]);
|
||||
|
||||
// Return formatted API response
|
||||
return response()->json([
|
||||
'status' => true,
|
||||
'message' => 'Restaurant theme updated successfully.',
|
||||
'data' => [
|
||||
'restaurant_id' => $restaurant->id,
|
||||
'theme_id' => $restaurant->theme_id,
|
||||
],
|
||||
], 200);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Authentication\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\RequestSanitizerTrait;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\Authentication\Models\SAASFaq;
|
||||
use Modules\Authentication\Repositories\SAASFaqRepository;
|
||||
use Modules\Frontend\Http\Requests\Faq\FaqStoreRequest;
|
||||
use Modules\Frontend\Http\Requests\Faq\FaqUpdateRequest;
|
||||
|
||||
class SAASFaqController extends Controller
|
||||
{
|
||||
use RequestSanitizerTrait;
|
||||
|
||||
public function __construct(private SAASFaqRepository $faqQuestion) {}
|
||||
|
||||
public function getSaasFaqs(Request $request): JsonResponse
|
||||
{
|
||||
$perPage = $request->get('per_page', 10);
|
||||
|
||||
$getSaasFaqs = SAASFaq::paginate($perPage);
|
||||
|
||||
return $this->responseSuccess($getSaasFaqs, 'Saas Faqs fetched successfully.');
|
||||
}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess(
|
||||
$this->faqQuestion->getAll(request()->all()),
|
||||
'Faq has been fetched successfully.'
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(FaqStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess(
|
||||
$this->faqQuestion->create($request->all()),
|
||||
'Faq has been created successfully.'
|
||||
);
|
||||
} catch (Exception $exception) {
|
||||
return $this->responseError([], $exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess(
|
||||
$this->faqQuestion->getById($id),
|
||||
'Faq has been fetched successfully.'
|
||||
);
|
||||
} catch (Exception $exception) {
|
||||
return $this->responseError([], $exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(FaqUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess(
|
||||
$this->faqQuestion->update($id, $this->getUpdateRequest($request)),
|
||||
'Faq has been updated successfully.'
|
||||
);
|
||||
} catch (Exception $exception) {
|
||||
return $this->responseError([], $exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess(
|
||||
$this->faqQuestion->delete($id),
|
||||
'Faq has been deleted successfully.'
|
||||
);
|
||||
} catch (Exception $exception) {
|
||||
return $this->responseError([], $exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Authentication\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\RequestSanitizerTrait;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\Authentication\Http\Requests\SAASSubscription\SAASSubscriptionStoreRequest;
|
||||
use Modules\Authentication\Http\Requests\SAASSubscription\SAASSubscriptionUpdateRequest;
|
||||
use Modules\Authentication\Models\SAASSubscription;
|
||||
use Modules\Authentication\Repositories\SAASSubscriptionRepository;
|
||||
|
||||
class SAASSubscriptionController extends Controller
|
||||
{
|
||||
use RequestSanitizerTrait;
|
||||
|
||||
public function __construct(private SAASSubscriptionRepository $sAASSubscriptionRepository) {}
|
||||
|
||||
public function getSAASSubscriptions(Request $request): JsonResponse
|
||||
{
|
||||
$perPage = $request->get('per_page', 10);
|
||||
|
||||
$getSAASSubscriptions = SAASSubscription::paginate($perPage);
|
||||
|
||||
return $this->responseSuccess($getSAASSubscriptions, 'Saas Subscriptions fetched successfully.');
|
||||
}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess(
|
||||
$this->sAASSubscriptionRepository->getAll(request()->all()),
|
||||
'SAAS Subscription has been fetched successfully.'
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(SAASSubscriptionStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess(
|
||||
$this->sAASSubscriptionRepository->create($request->all()),
|
||||
'SAAS Subscription has been created successfully.'
|
||||
);
|
||||
} catch (Exception $exception) {
|
||||
return $this->responseError([], $exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess(
|
||||
$this->sAASSubscriptionRepository->getById($id),
|
||||
'SAAS Subscription has been fetched successfully.'
|
||||
);
|
||||
} catch (Exception $exception) {
|
||||
return $this->responseError([], $exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(SAASSubscriptionUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess(
|
||||
$this->sAASSubscriptionRepository->update($id, $this->getUpdateRequest($request)),
|
||||
'SAAS Subscription has been updated successfully.'
|
||||
);
|
||||
} catch (Exception $exception) {
|
||||
return $this->responseError([], $exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess(
|
||||
$this->sAASSubscriptionRepository->delete($id),
|
||||
'SAAS Subscription has been deleted successfully.'
|
||||
);
|
||||
} catch (Exception $exception) {
|
||||
return $this->responseError([], $exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
public function publicStore(SAASSubscriptionStoreRequest $request): JsonResponse
|
||||
{
|
||||
$subscriptions = SAASSubscription::create([
|
||||
'email' => $request->email,
|
||||
]);
|
||||
|
||||
return $this->responseSuccess(
|
||||
$subscriptions,
|
||||
'SAAS Subscription has been created successfully.'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Authentication\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\Authenticatable;
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
use Modules\Authentication\Models\Package;
|
||||
use Modules\Authentication\Models\Subscription;
|
||||
use Modules\Authentication\Models\SubscriptionItem;
|
||||
use Modules\Authentication\Models\SubscriptionUpgradeRequest;
|
||||
|
||||
class SAASSubscriptionUpgradeController extends Controller
|
||||
{
|
||||
use Authenticatable;
|
||||
|
||||
public function requestUpgradeList(Request $request): JsonResponse
|
||||
{
|
||||
$perPage = $request->input('per_page', 15);
|
||||
$search = $request->input('search');
|
||||
|
||||
$query = SubscriptionUpgradeRequest::where('status', 'pending')
|
||||
->with(['restaurant', 'package'])
|
||||
->when($search, function ($q) use ($search) {
|
||||
$q->whereHas('restaurant', function ($q2) use ($search) {
|
||||
$q2->where('name', 'like', "%{$search}%")
|
||||
->orWhere('email', 'like', "%{$search}%")
|
||||
->orWhere('phone', 'like', "%{$search}%");
|
||||
});
|
||||
})
|
||||
->orderBy('id', 'desc');
|
||||
|
||||
$upgradeRequests = $query->paginate($perPage);
|
||||
|
||||
return $this->responseSuccess($upgradeRequests, 'Upgrade requests fetched successfully.');
|
||||
}
|
||||
|
||||
public function requestUpgrade(Request $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'package_id' => 'required|exists:packages,id',
|
||||
'notes' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$restaurantId = $this->getCurrentRestaurantId(); // assume this gets the current user's restaurant ID
|
||||
$package = Package::findOrFail($validated['package_id']);
|
||||
|
||||
// Check for existing pending upgrade request for the same restaurant & package
|
||||
$existingPending = SubscriptionUpgradeRequest::where('restaurant_id', $restaurantId)
|
||||
// ->where('package_id', $package->id)
|
||||
->where('status', 'pending')
|
||||
->first();
|
||||
|
||||
if ($existingPending) {
|
||||
// Update the existing pending request
|
||||
$existingPending->update([
|
||||
'extra_days' => (int) $package->duration_days,
|
||||
'payment_method' => 'online', // or from config
|
||||
'amount_paid' => $package->price,
|
||||
'notes' => $validated['notes'] ?? null,
|
||||
]);
|
||||
|
||||
$upgradeRequest = $existingPending;
|
||||
} else {
|
||||
// Create a new upgrade request
|
||||
$upgradeRequest = SubscriptionUpgradeRequest::create([
|
||||
'restaurant_id' => $restaurantId,
|
||||
'package_id' => $package->id,
|
||||
'extra_days' => (int) $package->duration_days,
|
||||
'payment_method' => 'online',
|
||||
'amount_paid' => $package->price,
|
||||
'notes' => $validated['notes'] ?? null,
|
||||
'status' => 'pending',
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->responseSuccess($upgradeRequest, 'Upgrade request submitted successfully.');
|
||||
}
|
||||
|
||||
public function approveRequest(int $requestId): JsonResponse
|
||||
{
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$subscriptionRequest = SubscriptionUpgradeRequest::where('id', $requestId)->where('status', 'pending')->first();
|
||||
|
||||
if (! $subscriptionRequest) {
|
||||
return $this->responseError([], 'No pending request found.', 404);
|
||||
}
|
||||
|
||||
$package = Package::findOrFail($subscriptionRequest->package_id);
|
||||
$extraDays = $subscriptionRequest->extra_days ?? 0;
|
||||
|
||||
$startDate = Carbon::now();
|
||||
$endDate = $startDate->copy()->addDays($package->duration_days + $extraDays);
|
||||
|
||||
// Update or create subscription
|
||||
$subscription = Subscription::where('restaurant_id', $subscriptionRequest->restaurant_id)->first();
|
||||
|
||||
if ($subscription) {
|
||||
$subscription->update([
|
||||
'package_id' => $package->id,
|
||||
'start_date' => $startDate,
|
||||
'end_date' => $endDate,
|
||||
'status' => 1,
|
||||
'transactions_details' => json_encode([
|
||||
'invoice_no' => 'INV-'.strtoupper(Str::random(6)),
|
||||
'issued_at' => now()->toDateString(),
|
||||
'notes' => $subscriptionRequest->notes,
|
||||
'amount' => $subscriptionRequest->amount_paid,
|
||||
]),
|
||||
]);
|
||||
} else {
|
||||
$subscription = Subscription::create([
|
||||
'restaurant_id' => $subscriptionRequest->restaurant_id,
|
||||
'package_id' => $package->id,
|
||||
'user_id' => Auth::id(),
|
||||
'start_date' => $startDate,
|
||||
'end_date' => $endDate,
|
||||
'status' => 1,
|
||||
'transactions_details' => json_encode([
|
||||
'invoice_no' => 'INV-'.strtoupper(Str::random(6)),
|
||||
'issued_at' => now()->toDateString(),
|
||||
'notes' => $subscriptionRequest->notes,
|
||||
'amount' => $subscriptionRequest->amount_paid,
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
SubscriptionItem::create([
|
||||
'user_id' => Auth::id(),
|
||||
'restaurant_id' => $subscriptionRequest->restaurant_id,
|
||||
'subscription_id' => $subscription->id,
|
||||
'reference_no' => 'PAY-'.strtoupper(Str::random(8)),
|
||||
'amount_paid' => $subscriptionRequest->amount_paid,
|
||||
'payment_method' => $subscriptionRequest->payment_method,
|
||||
]);
|
||||
|
||||
$subscriptionRequest->update([
|
||||
'status' => 'approved',
|
||||
'approved_by' => auth()->id(),
|
||||
'approved_at' => now(),
|
||||
]);
|
||||
|
||||
DB::commit();
|
||||
|
||||
return $this->responseSuccess($subscription, 'Subscription request approved successfully.');
|
||||
} catch (Exception $e) {
|
||||
DB::rollBack();
|
||||
|
||||
return $this->responseError([], 'Error: '.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function paymentSubscriptions(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'restaurant_id' => 'required|exists:restaurants,id',
|
||||
'package_id' => 'required|exists:packages,id',
|
||||
'upgrade_request_id' => 'nullable|exists:subscription_upgrade_requests,id',
|
||||
'payment_method' => 'nullable|string',
|
||||
'notes' => 'nullable|string',
|
||||
'amount_paid' => 'nullable|numeric',
|
||||
'extra_days' => 'nullable|integer',
|
||||
]);
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
$package = Package::findOrFail((int) $request->package_id);
|
||||
$startDate = Carbon::now();
|
||||
$extraDays = $request->input('extra_days', 0);
|
||||
$endDate = $startDate->copy()->addDays($package->duration_days + $extraDays);
|
||||
|
||||
$subscription = Subscription::where('restaurant_id', (int) $request->restaurant_id)->first();
|
||||
$invoiceData = [
|
||||
'invoice_no' => 'INV-'.strtoupper(Str::random(6)),
|
||||
'issued_at' => now()->toDateString(),
|
||||
'notes' => $request->input('notes', 'Payment for upgrade'),
|
||||
'amount' => $request->input('amount_paid', $package->price),
|
||||
];
|
||||
|
||||
if ($subscription) {
|
||||
$subscription->update([
|
||||
'package_id' => $package->id,
|
||||
'start_date' => $startDate,
|
||||
'end_date' => $endDate,
|
||||
'status' => 1,
|
||||
'transactions_details' => $invoiceData,
|
||||
]);
|
||||
} else {
|
||||
$subscription = Subscription::create([
|
||||
'restaurant_id' => (int) $request->restaurant_id,
|
||||
'package_id' => $package->id,
|
||||
'start_date' => $startDate,
|
||||
'end_date' => $endDate,
|
||||
'status' => 1,
|
||||
'transactions_details' => $invoiceData,
|
||||
]);
|
||||
}
|
||||
|
||||
SubscriptionItem::create([
|
||||
'subscription_id' => $subscription->id,
|
||||
'reference_no' => 'PAY-'.strtoupper(Str::random(8)),
|
||||
'amount_paid' => $invoiceData['amount'],
|
||||
'payment_method' => $request->input('payment_method', 'online'),
|
||||
]);
|
||||
|
||||
// If upgrading through a pending request, mark it as approved
|
||||
if ($request->filled('upgrade_request_id')) {
|
||||
$upgradeRequest = SubscriptionUpgradeRequest::where('id', $request->upgrade_request_id)
|
||||
->where('status', 'pending')
|
||||
->first();
|
||||
|
||||
if (! $upgradeRequest) {
|
||||
return $this->responseError([], 'No pending upgrade request found.', 404);
|
||||
}
|
||||
|
||||
$upgradeRequest->update([
|
||||
'status' => 'approved',
|
||||
'approved_by' => auth()->id(),
|
||||
'approved_at' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
|
||||
return $this->responseSuccess($subscription, 'Subscription payment processed successfully.');
|
||||
} catch (Exception $e) {
|
||||
DB::rollBack();
|
||||
|
||||
return $this->responseError([], 'Error: '.$e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Authentication\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\RequestSanitizerTrait;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Authentication\Http\Requests\Auth\UserCreateRequest;
|
||||
use Modules\Authentication\Http\Requests\Auth\UserUpdateRequest;
|
||||
use Modules\Authentication\Repositories\UserRepository;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
use RequestSanitizerTrait;
|
||||
|
||||
public function __construct(private UserRepository $user) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess(
|
||||
$this->user->getAll(request()->all()),
|
||||
'User has been fetched successfully.'
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(UserCreateRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess(
|
||||
$this->user->create($request->all()),
|
||||
'User has been created successfully.'
|
||||
);
|
||||
} catch (Exception $exception) {
|
||||
return $this->responseError([], $exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess(
|
||||
$this->user->getById($id),
|
||||
'User has been fetched successfully.'
|
||||
);
|
||||
} catch (Exception $exception) {
|
||||
return $this->responseError([], $exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(UserUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess(
|
||||
$this->user->update($id, $this->getUpdateRequest($request)),
|
||||
'User has been updated successfully.'
|
||||
);
|
||||
} catch (Exception $exception) {
|
||||
return $this->responseError([], $exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
$user = $this->user->getById($id);
|
||||
if (! $user) {
|
||||
return $this->responseError([], 'User Not Found');
|
||||
}
|
||||
if ($user->image) {
|
||||
fileRemover('users/', $user->image);
|
||||
}
|
||||
|
||||
return $this->responseSuccess(
|
||||
$this->user->delete($id),
|
||||
'User has been deleted successfully.'
|
||||
);
|
||||
} catch (Exception $exception) {
|
||||
return $this->responseError([], $exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,447 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Authentication\Http\Controllers\API;
|
||||
|
||||
use App\Helper\DomainHelper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\Authenticatable;
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Modules\Authentication\Http\Requests\Settings\SettingUpdateRequest;
|
||||
use Modules\Authentication\Models\Setting;
|
||||
use Modules\Authentication\Models\User;
|
||||
use Modules\HRM\Models\Attendance;
|
||||
use Modules\HRM\Models\AttendanceLog;
|
||||
|
||||
class UtilityController extends Controller
|
||||
{
|
||||
use Authenticatable;
|
||||
|
||||
public function restaurantSettingsUpdate(SettingUpdateRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
if ($request->isMethod('get')) {
|
||||
$settings = Setting::where('restaurant_id', $this->getCurrentRestaurantId())
|
||||
->pluck('option_value', 'option_key'); // Return settings as key-value pairs
|
||||
|
||||
return $this->responseSuccess($settings, 'System Settings fetched successfully.');
|
||||
}
|
||||
|
||||
// If POST request, update or create settings
|
||||
DB::beginTransaction();
|
||||
foreach ($request->validated() as $key => $value) {
|
||||
if ($key === '_token') {
|
||||
continue;
|
||||
}
|
||||
|
||||
Setting::updateOrInsert(
|
||||
[
|
||||
'restaurant_id' => $this->getCurrentRestaurantId(),
|
||||
'option_key' => $key,
|
||||
],
|
||||
[
|
||||
'option_value' => $value,
|
||||
'updated_at' => now(),
|
||||
'created_at' => now(),
|
||||
]
|
||||
);
|
||||
}
|
||||
DB::commit();
|
||||
|
||||
return $this->responseSuccess([], 'System Settings have been successfully updated.');
|
||||
} catch (Exception $e) {
|
||||
DB::rollBack();
|
||||
|
||||
return $this->responseError(['error' => $e->getMessage()], 'Failed to store records unsuccessfully.');
|
||||
}
|
||||
}
|
||||
|
||||
public function restaurantLogoUpdate(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'logo' => 'required|image|mimes:jpeg,png,jpg|max:8192',
|
||||
]);
|
||||
|
||||
$image = $request->file('logo');
|
||||
$name = 'logo.'.$image->getClientOriginalExtension();
|
||||
$destinationPath = public_path('/uploads/logos');
|
||||
$image->move($destinationPath, $name);
|
||||
|
||||
$data = [];
|
||||
$data['option_value'] = $name;
|
||||
$data['updated_at'] = Carbon::now();
|
||||
|
||||
if (Setting::where('restaurant_id', $this->getCurrentRestaurantId())->where('option_key', 'app_logo')->exists()) {
|
||||
Setting::where('restaurant_id', $this->getCurrentRestaurantId())->where('option_key', '=', 'app_logo')->update($data);
|
||||
} else {
|
||||
$data['option_value'] = 'app_logo';
|
||||
$data['created_at'] = Carbon::now();
|
||||
Setting::insert($data);
|
||||
}
|
||||
|
||||
return $this->responseSuccess(
|
||||
[],
|
||||
'Restaurant logo have been successfully updated'
|
||||
);
|
||||
}
|
||||
|
||||
public function getAllImageFolderUrls(): JsonResponse
|
||||
{
|
||||
$uploadBasePath = public_path('uploads');
|
||||
$storageBasePath = public_path('storage');
|
||||
|
||||
$uploadBaseUrl = url('uploads');
|
||||
$storageBaseUrl = url('storage');
|
||||
|
||||
$data = [];
|
||||
|
||||
// Helper to recursively get folders and create URLs
|
||||
$getFolders = function ($basePath, $baseUrl, $prefix = '') use (&$data) {
|
||||
if (! File::exists($basePath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$folders = File::directories($basePath);
|
||||
foreach ($folders as $folderPath) {
|
||||
$relativePath = str_replace($basePath.DIRECTORY_SEPARATOR, '', $folderPath);
|
||||
$key = str_replace(['/', '\\'], '_', ($prefix.$relativePath)).'_url';
|
||||
$data[$key] = str_replace('\\', '/', $baseUrl.'/'.$relativePath.'/');
|
||||
|
||||
// Recursive scan subfolders
|
||||
$thisFolder = $basePath.DIRECTORY_SEPARATOR.$relativePath;
|
||||
$getSub = File::directories($thisFolder);
|
||||
foreach ($getSub as $subFolder) {
|
||||
$subRelative = str_replace($basePath.DIRECTORY_SEPARATOR, '', $subFolder);
|
||||
$key = str_replace(['/', '\\'], '_', ($prefix.$subRelative)).'_url';
|
||||
$data[$key] = str_replace('\\', '/', $baseUrl.'/'.$subRelative.'/');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Scan and build URLs from public/uploads
|
||||
$getFolders($uploadBasePath, $uploadBaseUrl);
|
||||
|
||||
// Scan and build URLs from public/storage
|
||||
$getFolders($storageBasePath, $storageBaseUrl, 'storage_');
|
||||
|
||||
// Add any manually defined static folders (optional)
|
||||
$data['system_logo_url'] = url('/uploads/logos/');
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'message' => 'All image folder URLs fetched successfully.',
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
public function linkStorage(): JsonResponse
|
||||
{
|
||||
$publicStorage = public_path('storage');
|
||||
$target = storage_path('app/public');
|
||||
|
||||
try {
|
||||
if (! File::exists($publicStorage)) {
|
||||
File::link($target, $publicStorage);
|
||||
|
||||
return response()->json(['status' => 'success', 'message' => 'Storage link created.']);
|
||||
} else {
|
||||
return response()->json(['status' => 'info', 'message' => 'Storage link already exists.']);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return response()->json(['status' => 'error', 'message' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
public function unlinkStorage(): JsonResponse
|
||||
{
|
||||
$publicStorage = public_path('storage');
|
||||
|
||||
try {
|
||||
if (is_link($publicStorage)) {
|
||||
unlink($publicStorage);
|
||||
} elseif (is_dir($publicStorage)) {
|
||||
// Attempt to delete via Laravel
|
||||
if (! File::deleteDirectory($publicStorage)) {
|
||||
// Fallback for stubborn directories
|
||||
exec('rm -rf '.escapeshellarg($publicStorage));
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'message' => 'The storage link or directory has been removed.',
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
return response()->json([
|
||||
'status' => 'error',
|
||||
'message' => 'Failed: '.$e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function clearAllCache(): JsonResponse
|
||||
{
|
||||
try {
|
||||
Artisan::call('config:clear');
|
||||
Artisan::call('cache:clear');
|
||||
Artisan::call('route:clear');
|
||||
Artisan::call('view:clear');
|
||||
|
||||
return response()->json(['status' => 'success', 'message' => 'All caches cleared.']);
|
||||
} catch (Exception $e) {
|
||||
return response()->json(['status' => 'error', 'message' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
// Predefined models map (from your dump)
|
||||
protected $models = [
|
||||
'gallery' => 'Modules\\Authentication\\Models\\Gallery',
|
||||
'restaurant' => 'Modules\\Authentication\\Models\\Restaurant',
|
||||
'setting' => 'Modules\\Authentication\\Models\\Setting',
|
||||
'user' => 'Modules\\Authentication\\Models\\User',
|
||||
'aboutus' => 'Modules\\Frontend\\Models\\AboutUs',
|
||||
'banner' => 'Modules\\Frontend\\Models\\Banner',
|
||||
'contact' => 'Modules\\Frontend\\Models\\Contact',
|
||||
'faqquestion' => 'Modules\\Frontend\\Models\\FaqQuestion',
|
||||
'mobileappsection' => 'Modules\\Frontend\\Models\\MobileAppSection',
|
||||
'onboarding' => 'Modules\\Frontend\\Models\\Onboarding',
|
||||
'ourhistory' => 'Modules\\Frontend\\Models\\OurHistory',
|
||||
'page' => 'Modules\\Frontend\\Models\\Page',
|
||||
'policy' => 'Modules\\Frontend\\Models\\Policy',
|
||||
'readytojoinus' => 'Modules\\Frontend\\Models\\ReadyToJoinUs',
|
||||
'testimonial' => 'Modules\\Frontend\\Models\\Testimonial',
|
||||
'theme' => 'Modules\\Frontend\\Models\\Theme',
|
||||
'whychooseus' => 'Modules\\Frontend\\Models\\WhyChooseUs',
|
||||
'paymentrequest' => 'Modules\\Gateways\\Models\\PaymentRequest',
|
||||
'addon' => 'Modules\\Restaurant\\Models\\Addon',
|
||||
'addonfood' => 'Modules\\Restaurant\\Models\\AddonFood',
|
||||
'customer' => 'Modules\\Restaurant\\Models\\Customer',
|
||||
'foodavailability' => 'Modules\\Restaurant\\Models\\FoodAvailability',
|
||||
'foodcategory' => 'Modules\\Restaurant\\Models\\FoodCategory',
|
||||
'fooditem' => 'Modules\\Restaurant\\Models\\FoodItem',
|
||||
'foodvariant' => 'Modules\\Restaurant\\Models\\FoodVariant',
|
||||
'foodvariantingredient' => 'Modules\\Restaurant\\Models\\FoodVariantIngredient',
|
||||
'ingredient' => 'Modules\\Restaurant\\Models\\Ingredient',
|
||||
'ingredientdamage' => 'Modules\\Restaurant\\Models\\IngredientDamage',
|
||||
'menutype' => 'Modules\\Restaurant\\Models\\MenuType',
|
||||
'menusection' => 'Modules\\Restaurant\\Models\\MenuSection',
|
||||
'menucategory' => 'Modules\\Restaurant\\Models\\MenuCategory',
|
||||
'order' => 'Modules\\Restaurant\\Models\\Order',
|
||||
'orderitem' => 'Modules\\Restaurant\\Models\\OrderItem',
|
||||
'paymentmethod' => 'Modules\\Restaurant\\Models\\PaymentMethod',
|
||||
'purchase' => 'Modules\\Restaurant\\Models\\Purchase',
|
||||
'purchaseitem' => 'Modules\\Restaurant\\Models\\PurchaseItem',
|
||||
'purchasereturn' => 'Modules\\Restaurant\\Models\\PurchaseReturn',
|
||||
'reservation' => 'Modules\\Restaurant\\Models\\Reservation',
|
||||
'reservationunavailable' => 'Modules\\Restaurant\\Models\\ReservationUnavailable',
|
||||
'stock' => 'Modules\\Restaurant\\Models\\Stock',
|
||||
'supplier' => 'Modules\\Restaurant\\Models\\Supplier',
|
||||
'table' => 'Modules\\Restaurant\\Models\\Table',
|
||||
'unit' => 'Modules\\Restaurant\\Models\\Unit',
|
||||
'restaurantschedules' => 'Modules\\Restaurant\\Models\\RestaurantSchedule',
|
||||
'kitchen' => 'Modules\\Restaurant\\Models\\Kitchen',
|
||||
'unitmeasurements' => 'Modules\\Restaurant\\Models\\Kitchen',
|
||||
'hotel' => 'Modules\\Booking\\Models\\Hotel',
|
||||
'floor' => 'Modules\\Booking\\Models\\Floor',
|
||||
'roomtype' => 'Modules\\Booking\\Models\\RoomType',
|
||||
'room' => 'Modules\\Booking\\Models\\Room',
|
||||
'booking' => 'Modules\\Booking\\Models\\Booking',
|
||||
];
|
||||
|
||||
public function globalStatusUpdate(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'required|integer',
|
||||
'type' => 'required|string',
|
||||
'status' => 'required|string',
|
||||
]);
|
||||
|
||||
$type = strtolower($request->type);
|
||||
$id = $request->id;
|
||||
$status = $request->status;
|
||||
|
||||
if (! isset($this->models[$type])) {
|
||||
return $this->responseError([], 'Invalid type', 400);
|
||||
}
|
||||
|
||||
$modelClass = $this->models[$type];
|
||||
$item = $modelClass::find($id);
|
||||
|
||||
if (! $item) {
|
||||
return $this->responseError([], 'Invalid type', 400);
|
||||
}
|
||||
|
||||
$item->status = $status;
|
||||
$item->save();
|
||||
|
||||
return $this->responseSuccess(
|
||||
$item,
|
||||
'Status updated successfully.'
|
||||
);
|
||||
}
|
||||
|
||||
private function getClassFromFile($file, $modulePath)
|
||||
{
|
||||
$relative = str_replace(base_path().'/', '', $file->getPathname());
|
||||
$class = str_replace('/', '\\', substr($relative, 0, -4)); // remove .php
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
public function domainCheck(Request $request): JsonResponse
|
||||
{
|
||||
$domain = $request->query('domain');
|
||||
$info = DomainHelper::getDomainInfo($domain);
|
||||
|
||||
// Add expiration warning
|
||||
$warning = DomainHelper::expirationWarning($info['expiration'] ?? null);
|
||||
$info['expiration_warning'] = $warning;
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $info,
|
||||
]);
|
||||
}
|
||||
|
||||
public function allUsersGet(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'restaurant_id' => 'required|integer|exists:restaurants,id',
|
||||
]);
|
||||
|
||||
$users = User::select('id', 'first_name', 'last_name', 'user_type', 'role_id')
|
||||
->where('restaurant_id', (int) $request->restaurant_id)
|
||||
->get();
|
||||
|
||||
return $this->responseSuccess(
|
||||
$users,
|
||||
_lang('Users have been fetched successfully.')
|
||||
);
|
||||
}
|
||||
|
||||
public function sync(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'logs' => ['required', 'array'],
|
||||
'logs.*.id' => ['required', 'integer'],
|
||||
'logs.*.timestamp' => ['required', 'date'],
|
||||
'logs.*.type' => ['required', 'integer'],
|
||||
]);
|
||||
|
||||
$restaurantId = $this->getCurrentRestaurantId(); // or from device
|
||||
$inserted = 0;
|
||||
$skipped = 0;
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
foreach ($request->logs as $log) {
|
||||
|
||||
$employeeId = $log['id'];
|
||||
$dateTime = Carbon::parse($log['timestamp']);
|
||||
$date = $dateTime->toDateString();
|
||||
$type = $this->mapPunchType($log['type']);
|
||||
|
||||
/**
|
||||
* 1️⃣ Avoid duplicate punch
|
||||
*/
|
||||
$exists = AttendanceLog::where([
|
||||
'employee_id' => $employeeId,
|
||||
'restaurant_id' => $restaurantId,
|
||||
'punch_time' => $dateTime,
|
||||
'type' => $type,
|
||||
])->exists();
|
||||
|
||||
if ($exists) {
|
||||
$skipped++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 2️⃣ Get or create daily attendance
|
||||
*/
|
||||
$attendance = Attendance::firstOrCreate(
|
||||
[
|
||||
'employee_id' => $employeeId,
|
||||
'date' => $date,
|
||||
],
|
||||
[
|
||||
'restaurant_id' => $restaurantId,
|
||||
'status' => 'present',
|
||||
]
|
||||
);
|
||||
|
||||
/**
|
||||
* 3️⃣ Insert punch log
|
||||
*/
|
||||
AttendanceLog::create([
|
||||
'attendance_id' => $attendance->id,
|
||||
'employee_id' => $employeeId,
|
||||
'restaurant_id' => $restaurantId,
|
||||
'type' => $type,
|
||||
'punch_time' => $dateTime,
|
||||
]);
|
||||
|
||||
/**
|
||||
* 4️⃣ Recalculate attendance summary
|
||||
*/
|
||||
$this->recalculateWorkedHours($attendance);
|
||||
|
||||
$inserted++;
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
|
||||
return response()->json([
|
||||
'status' => true,
|
||||
'inserted' => $inserted,
|
||||
'skipped' => $skipped,
|
||||
'message' => 'Attendance synced successfully',
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
DB::rollBack();
|
||||
|
||||
return response()->json([
|
||||
'status' => false,
|
||||
'message' => 'Attendance sync failed',
|
||||
'error' => $e->getMessage(),
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
private function mapPunchType(int $type): string
|
||||
{
|
||||
return in_array($type, [0, 4]) ? 'in' : 'out';
|
||||
}
|
||||
|
||||
/**
|
||||
* RECALCULATE TOTAL WORKED HOURS BASED ON ALL LOGS
|
||||
*/
|
||||
private function recalculateWorkedHours(Attendance $attendance)
|
||||
{
|
||||
$logs = AttendanceLog::where('attendance_id', $attendance->id)
|
||||
->orderBy('punch_time')
|
||||
->get();
|
||||
|
||||
if ($logs->count() < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
$first = Carbon::parse($logs->first()->punch_time);
|
||||
$last = Carbon::parse($logs->last()->punch_time);
|
||||
|
||||
$hours = round($first->diffInMinutes($last) / 60, 2);
|
||||
|
||||
$attendance->update([
|
||||
'first_clock_in' => $first,
|
||||
'last_clock_out' => $last,
|
||||
'hours_worked' => $hours,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Authentication\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\Authenticatable;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Modules\Authentication\Models\ZoomMeeting;
|
||||
|
||||
class ZoomController extends Controller
|
||||
{
|
||||
use Authenticatable;
|
||||
|
||||
private $zoomToken;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->zoomToken = $this->generateZoomToken();
|
||||
}
|
||||
|
||||
private function generateZoomToken()
|
||||
{
|
||||
$response = Http::asForm()->withHeaders([
|
||||
'Authorization' => 'Basic '.base64_encode(config('zoom.client_id').':'.config('zoom.client_secret')),
|
||||
])->post('https://zoom.us/oauth/token', [
|
||||
'grant_type' => 'account_credentials',
|
||||
'account_id' => config('zoom.account_id'),
|
||||
]);
|
||||
|
||||
return $response->json()['access_token'] ?? null;
|
||||
}
|
||||
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
$perPage = request('per_page', 10);
|
||||
$zoomMeetings = ZoomMeeting::where('start_time', '>=', Carbon::now('Asia/Dhaka')->toDateTimeString())
|
||||
->latest()
|
||||
->paginate($perPage);
|
||||
|
||||
return $this->responseSuccess(
|
||||
$zoomMeetings,
|
||||
'Zoom Meetings list fetch successfully.'
|
||||
);
|
||||
}
|
||||
|
||||
public function store(Request $request): JsonResponse
|
||||
{
|
||||
// Validate the request data
|
||||
$request->validate([
|
||||
'topic' => 'required|string|max:255',
|
||||
'start_time' => 'required|date_format:Y-m-d\TH:i:s\Z', // ISO 8601 format (Asia/Dhaka)
|
||||
'duration' => 'required|integer|min:1', // Duration in minutes
|
||||
'password' => 'nullable|string|min:6|max:10', // Optional password, min 6 chars
|
||||
'agenda' => 'nullable|string|max:5000',
|
||||
]);
|
||||
|
||||
$startTimeUtc = Carbon::parse($request->start_time, 'Asia/Dhaka')->setTimezone('Asia/Dhaka')->toIso8601String();
|
||||
$response = Http::withToken($this->zoomToken)->post(config('zoom.base_url').'users/me/meetings', [
|
||||
'topic' => $request->topic,
|
||||
'type' => 2, // Scheduled Meeting
|
||||
'start_time' => $startTimeUtc,
|
||||
'duration' => $request->duration,
|
||||
'password' => '123456',
|
||||
'agenda' => 'Live Class',
|
||||
'timezone' => 'Asia/Dhaka',
|
||||
'settings' => [
|
||||
'host_video' => true,
|
||||
'participant_video' => true,
|
||||
'waiting_room' => false,
|
||||
],
|
||||
]);
|
||||
|
||||
if ($response->successful()) {
|
||||
$data = $response->json();
|
||||
$meeting = ZoomMeeting::create([
|
||||
'restaurant_id' => $this->getCurrentRestaurantId(),
|
||||
'meeting_id' => $data['id'],
|
||||
'topic' => $data['topic'],
|
||||
'agenda' => 'Live Class',
|
||||
'start_time' => $data['start_time'],
|
||||
'duration' => $data['duration'],
|
||||
'password' => '123456',
|
||||
'join_url' => $data['join_url'],
|
||||
'start_url' => $data['start_url'],
|
||||
'created_by' => Auth::id(),
|
||||
]);
|
||||
|
||||
return $this->responseSuccess(
|
||||
$meeting,
|
||||
'Zoom Meeting create successfully.'
|
||||
);
|
||||
}
|
||||
|
||||
return $this->responseError([], 'Zoom API Error');
|
||||
}
|
||||
|
||||
public function show(string $id): JsonResponse
|
||||
{
|
||||
$zoomMeeting = ZoomMeeting::where('meeting_id', $id)->first();
|
||||
if (! $zoomMeeting) {
|
||||
return $this->responseError($zoomMeeting, 'Zoom Meeting not found');
|
||||
}
|
||||
|
||||
return $this->responseSuccess(
|
||||
$zoomMeeting,
|
||||
'Zoom Meeting fetch successfully.'
|
||||
);
|
||||
}
|
||||
|
||||
public function update(Request $request, int $id): JsonResponse
|
||||
{
|
||||
$zoomMeeting = ZoomMeeting::where('meeting_id', $id)->first();
|
||||
if (! $zoomMeeting) {
|
||||
return $this->responseError($zoomMeeting, 'Zoom Meeting not found');
|
||||
}
|
||||
$response = Http::withToken($this->zoomToken)->patch(config('zoom.base_url')."meetings/{$id}", [
|
||||
'topic' => $request->topic ?? $zoomMeeting->topic,
|
||||
'start_time' => $request->start_time ?? $zoomMeeting->start_time,
|
||||
'duration' => $request->duration ?? $zoomMeeting->duration,
|
||||
'password' => $request->password ?? $zoomMeeting->password,
|
||||
]);
|
||||
|
||||
if ($response->successful()) {
|
||||
$zoomMeeting->update($request->only(['topic', 'start_time', 'duration', 'password']));
|
||||
|
||||
return $this->responseSuccess(
|
||||
$zoomMeeting,
|
||||
'Zoom Meeting update successfully.'
|
||||
);
|
||||
}
|
||||
|
||||
return $this->responseError([], 'Failed to update meeting');
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
$zoomMeeting = ZoomMeeting::where('meeting_id', $id)->first();
|
||||
if (! $zoomMeeting) {
|
||||
return $this->responseError($zoomMeeting, 'Zoom Meeting not found');
|
||||
}
|
||||
|
||||
$response = Http::withToken($this->zoomToken)->delete(config('zoom.base_url')."meetings/{$id}");
|
||||
if ($response->successful()) {
|
||||
$zoomMeeting->delete();
|
||||
|
||||
return $this->responseSuccess(
|
||||
[],
|
||||
'Zoom Meeting deleted successfully.'
|
||||
);
|
||||
}
|
||||
|
||||
return $this->responseError([], 'Failed to delete meeting');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Authentication\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\ProfileUpdateRequest;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Redirect;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class ProfileController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the user's profile form.
|
||||
*/
|
||||
public function edit(Request $request): View
|
||||
{
|
||||
return view('profile.edit', [
|
||||
'user' => $request->user(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the user's profile information.
|
||||
*/
|
||||
public function update(ProfileUpdateRequest $request): RedirectResponse
|
||||
{
|
||||
$request->user()->fill($request->validated());
|
||||
|
||||
if ($request->user()->isDirty('email')) {
|
||||
$request->user()->email_verified_at = null;
|
||||
}
|
||||
|
||||
$request->user()->save();
|
||||
|
||||
return Redirect::route('profile.edit')->with('status', 'profile-updated');
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the user's account.
|
||||
*/
|
||||
public function destroy(Request $request): RedirectResponse
|
||||
{
|
||||
$request->validateWithBag('userDeletion', [
|
||||
'password' => ['required', 'current_password'],
|
||||
]);
|
||||
|
||||
$user = $request->user();
|
||||
|
||||
Auth::logout();
|
||||
|
||||
$user->delete();
|
||||
|
||||
$request->session()->invalidate();
|
||||
$request->session()->regenerateToken();
|
||||
|
||||
return Redirect::to('/');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user