init
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
vendor
|
||||
config.php
|
||||
jobs/node_modules
|
||||
jobs/locations/*
|
||||
jobs/*.png
|
||||
jobs
|
||||
@@ -0,0 +1,97 @@
|
||||
|
||||
|
||||
# Module 3 - TFU
|
||||
|
||||
A PHP-based web application for managing team campaigns and followups.
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- PHP 7.4 or higher
|
||||
- MySQL/MariaDB
|
||||
|
||||
### Installation
|
||||
|
||||
1. Clone this repository to your local machine
|
||||
2. Configure your database connection in `config.php`
|
||||
3. Import the database schema using the provided `db.sql` file
|
||||
|
||||
### Running the Application
|
||||
|
||||
Start the development server using:
|
||||
|
||||
```bash
|
||||
|
||||
php -S localhost:9000
|
||||
|
||||
```
|
||||
|
||||
|
||||
Then visit `http://localhost:9000` in your web browser.
|
||||
|
||||
Admin Login `http://localhost:9000/admin/login`
|
||||
|
||||
admin@manaknight.com
|
||||
a123456
|
||||
|
||||
Client Login `http://localhost:9000/client/login`
|
||||
|
||||
emmy@manaknight.com
|
||||
a123456
|
||||
|
||||
## Features
|
||||
|
||||
- Campaign management
|
||||
- Team followup tracking
|
||||
- Add and edit campaign details
|
||||
- Database-driven application
|
||||
|
||||
## Core Project Files
|
||||
|
||||
├── README.md
|
||||
|
||||
├── config.php # Database configuration
|
||||
|
||||
├── db.sql # Database schema
|
||||
|
||||
├── migration.sql # Database seed data
|
||||
|
||||
├── index.php # Main entry point
|
||||
|
||||
## Tasks
|
||||
|
||||
### Miscs
|
||||
|
||||
1. On admin login redirect to Access Log
|
||||
|
||||
2. Client login is broken
|
||||
|
||||
3. Add bulk delete functionality to the Admin License list
|
||||
|
||||
4. Change the pagination type from offset to cursor pagination on Admin License list (https://www.merge.dev/blog/cursor-pagination)
|
||||
|
||||
### Filter functionality
|
||||
|
||||
5. Add a fuzzy email search to the Admin license list
|
||||
|
||||
6. Add a fuzzy search for the project & webhook fields to the Admin Availability Checker list
|
||||
|
||||
7. Add a fuzzy search filter for the project field in Admin Reports
|
||||
|
||||
8. Add a date range filter for the date field in Admin Reports
|
||||
|
||||
### Integrations
|
||||
|
||||
9. User should be able to Add Campign without connecting drive. On the Add Campign screen when the user selects the Select Google sheet button, display the google picker modal to select spreadsheets only.
|
||||
|
||||
https://developers.google.com/drive/picker/guides/overview
|
||||
|
||||
Google console creds:
|
||||
Client id: 356934742115-c707dqbhct9b7gj64eo9rqfdfi47rb8o.apps.googleusercontent.com
|
||||
API key: AIzaSyB7JhbYloABBC-jZebyjHoiXUiM-s_7sBA
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
<style>
|
||||
.dark-header thead th {
|
||||
background-color: #343a40; /* Dark gray background */
|
||||
color: white; /* White text color */
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="container">
|
||||
<h2 class="text-left ">Access Log</h2>
|
||||
<div class="row mt-2 mb-2">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<form action="?" method="GET">
|
||||
<div class="input-group">
|
||||
<input type="text" name="relationship_num" class="form-control mr-2" placeholder="Enter Relationship #" value="<?php echo $data['relationship_num'];?>">
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-primary" type="submit">Search</button>
|
||||
</span>
|
||||
</div><!-- /input-group -->
|
||||
</form>
|
||||
</div><!-- /.col-md-6 -->
|
||||
</div><!-- /.row -->
|
||||
|
||||
<!-- Table Responsive Wrapper -->
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover dark-header">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Relationship #</th>
|
||||
<th>IP</th>
|
||||
<th>Date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($data['data'] as $key => $value) {
|
||||
echo ' <tr>';
|
||||
echo ' <td>' . $value->id . ' <br/> <a class="text-danger" href="/admin/accesslog/delete/' . $value->id . '">delete</a></td>';
|
||||
echo ' <td>' . $value->relationship_num . ' </td>';
|
||||
echo ' <td>' . $value->ip . ' </td>';
|
||||
echo ' <td>' . $value->created_at . ' </td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
// Retrieve parameters
|
||||
$total = $data['total'];
|
||||
$currentPage = $data['page'];
|
||||
$perPage = 10;
|
||||
|
||||
// Calculate the number of pages
|
||||
$totalPages = ceil($total / $perPage);
|
||||
|
||||
// Define a range of pages to show at any given time
|
||||
$range = 2; // This can be adjusted as needed
|
||||
|
||||
$startPage = ($currentPage - $range) > 0 ? ($currentPage - $range) : 1;
|
||||
$endPage = ($currentPage + $range) < $totalPages ? ($currentPage + $range) : $totalPages;
|
||||
|
||||
if ($total > 0) {
|
||||
?>
|
||||
<!-- Pagination -->
|
||||
<nav aria-label="Page navigation">
|
||||
<ul class="pagination">
|
||||
<li class="ml-2">
|
||||
<a href="?page=1" aria-label="Previous">
|
||||
<span aria-hidden="true">««</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="ml-2">
|
||||
<a href="?page=<?= ($currentPage - 1) > 0 ? $currentPage - 1 : 1 ?>" aria-label="Previous">
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
<?php
|
||||
for ($i = $startPage; $i <= $endPage; $i++) {
|
||||
echo '<li class="ml-2' . ($currentPage == $i ? ' active' : '') . '"><a href="?page=' . $i . '">' . $i . '</a></li>';
|
||||
}
|
||||
?>
|
||||
<li class="ml-2">
|
||||
<a href="?page=<?= ($currentPage + 1) < $totalPages ? $currentPage + 1 : $totalPages ?>" aria-label="Next">
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="ml-2">
|
||||
<a href="?page=<?= $totalPages ?>" aria-label="Next">
|
||||
<span aria-hidden="true">»»</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<?php } ?>
|
||||
</div>
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
include_once __DIR__ . "/mysql-database-service.php";
|
||||
|
||||
class AccesslogModel extends MySqlDatabaseService
|
||||
{
|
||||
protected $_table = 'accesslog';
|
||||
protected $_primary_key = 'id';
|
||||
protected $_return_type = 'array';
|
||||
protected $_allowed_fields = [
|
||||
'id', 'relationship_num', 'ip'
|
||||
];
|
||||
protected $_label_fields = [
|
||||
'ID', 'Relationship #', 'IP',
|
||||
];
|
||||
protected $_use_timestamps = true;
|
||||
protected $_created_field = 'created_at';
|
||||
protected $_updated_field = 'updated_at';
|
||||
protected $_validation_rules = [
|
||||
['relationship_num', 'Relationship #', 'required'],
|
||||
];
|
||||
protected $_validation_edit_rules = [
|
||||
['relationship_num', 'Relationship #', 'required'],
|
||||
];
|
||||
protected $_validation_messages = [
|
||||
['relationship_num', 'Relationship #', 'required'],
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function get_mapping()
|
||||
{
|
||||
return [
|
||||
// TODO: ADD MAPPING
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
include_once 'core.php';
|
||||
include_once 'config.php';
|
||||
include_once 'project-model.php';
|
||||
include_once 'calendar_functions.php';
|
||||
|
||||
Route::add('/check-calendar', function () {
|
||||
// check_login();
|
||||
$error = false;
|
||||
|
||||
$data = [
|
||||
'page_title' => 'Project'
|
||||
];
|
||||
// $config = MkdConfig::get_instance()->get_config();
|
||||
// $apikey = $config['gohighlevel_key'];
|
||||
// $cid = $_POST['calendar_id'];
|
||||
$pid = (int)$_POST['project_id'];
|
||||
$projectModel = new ProjectModel();
|
||||
|
||||
try {
|
||||
$model = $projectModel->get((int)$pid);
|
||||
$apikey = $model->location;
|
||||
$curl = curl_init();
|
||||
|
||||
curl_setopt_array($curl, [
|
||||
CURLOPT_URL => "https://rest.gohighlevel.com/v1/calendars/services",
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_ENCODING => "",
|
||||
CURLOPT_MAXREDIRS => 10,
|
||||
CURLOPT_TIMEOUT => 30,
|
||||
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||
CURLOPT_CUSTOMREQUEST => "GET",
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"Accept: application/json",
|
||||
"Authorization: Bearer " . $apikey,
|
||||
"Version: 2021-04-15"
|
||||
],
|
||||
]);
|
||||
|
||||
$response = curl_exec($curl);
|
||||
$err = curl_error($curl);
|
||||
$status_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
||||
|
||||
curl_close($curl);
|
||||
|
||||
// print_r($response);
|
||||
|
||||
if ($status_code != 200) {
|
||||
echo "Something went wrong.";
|
||||
exit;
|
||||
}
|
||||
|
||||
$cid = $model->calendar;
|
||||
$data = json_decode($response, true);
|
||||
|
||||
// Search for the service with the specified ID
|
||||
$searchedService = null;
|
||||
if (!empty($data['services']) && count($data['services'] ) > 0) {
|
||||
foreach ($data['services'] as $service) {
|
||||
if ($service['id'] === $cid) {
|
||||
$searchedService = $service;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($searchedService == null) {
|
||||
echo "Calendar Service with ID '" . $cid . "' not found.";
|
||||
exit;
|
||||
}
|
||||
|
||||
$slots = json_decode($model->slot);
|
||||
|
||||
echo checkServiceInSlot($searchedService["availability"]["officeHours"],
|
||||
$slots,
|
||||
$model,
|
||||
$searchedService["availability"]["eventTiming"],
|
||||
$searchedService["availability"]["schedule"],
|
||||
$searchedService["id"]);
|
||||
} catch (\Throwable $th) {
|
||||
echo "Something went wrong.";
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
exit;
|
||||
|
||||
|
||||
}, 'post');
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
include_once __DIR__ . "/mysql-database-service.php";
|
||||
|
||||
class CalendarModel extends MySqlDatabaseService
|
||||
{
|
||||
protected $_table = 'calendar';
|
||||
protected $_primary_key = 'id';
|
||||
protected $_return_type = 'array';
|
||||
protected $_allowed_fields = [
|
||||
'id', 'slot', 'days', 'project_id', 'calendar'
|
||||
];
|
||||
protected $_label_fields = [
|
||||
'ID', 'Slot', 'Days', 'Project ID', 'Calendar'
|
||||
];
|
||||
protected $_use_timestamps = true;
|
||||
protected $_created_field = 'created_at';
|
||||
protected $_updated_field = 'updated_at';
|
||||
protected $_validation_rules = [
|
||||
['slot', 'Slot', 'required'],
|
||||
['days', 'Days', 'required'],
|
||||
['project_id', 'Project ID', 'required'],
|
||||
['calendar', 'Calendar', 'required']
|
||||
];
|
||||
|
||||
protected $_validation_edit_rules = [
|
||||
['slot', 'Slot', 'required'],
|
||||
['days', 'Days', 'required'],
|
||||
['project_id', 'Project ID', 'required'],
|
||||
['calendar', 'Calendar', 'required']
|
||||
];
|
||||
protected $_validation_messages = [
|
||||
['slot', 'Slot', 'required'],
|
||||
['days', 'Days', 'required'],
|
||||
['project_id', 'Project ID', 'required'],
|
||||
['calendar', 'Calendar', 'required']
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function get_mapping()
|
||||
{
|
||||
return [
|
||||
// TODO: ADD MAPPING
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
Route::add('/check-calendar', function () {
|
||||
|
||||
|
||||
}, 'post');
|
||||
+271
@@ -0,0 +1,271 @@
|
||||
<!-- <div id='calendar'></div>
|
||||
<div id="timeSlots">
|
||||
<h3>Available Time Slots</h3>
|
||||
<ul></ul>
|
||||
</div> -->
|
||||
|
||||
<!-- <div id='calendar-container'> -->
|
||||
<div id='calendar'></div>
|
||||
<!-- </div> -->
|
||||
<!-- <script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var calendarEl = document.getElementById('calendar');
|
||||
var timeSlotsList = document.querySelector('#timeSlots ul');
|
||||
|
||||
var calendar = new FullCalendar.Calendar(calendarEl, {
|
||||
// plugins: ['dayGrid'],
|
||||
slotMinTime: '08:00',
|
||||
slotMaxTime: '20:00',
|
||||
initialView: 'dayGridMonth',
|
||||
header: {
|
||||
left: 'prev,next today',
|
||||
center: 'title',
|
||||
right: 'dayGridMonth,dayGridWeek,dayGridDay'
|
||||
},
|
||||
// events: [
|
||||
// // Your events data will go here
|
||||
// ],
|
||||
dateClick: function(info) {
|
||||
var selectedDate = info.dateStr;
|
||||
|
||||
// Generate time slots for the selected date
|
||||
var timeSlots = generateTimeSlots(selectedDate);
|
||||
|
||||
// Display time slots
|
||||
displayTimeSlots(timeSlots);
|
||||
}
|
||||
});
|
||||
|
||||
calendar.render();
|
||||
|
||||
|
||||
|
||||
function generateTimeSlots(date) {
|
||||
// Replace this with your logic to determine available time slots based on the date
|
||||
var availableTimeSlots = ['10:00 AM', '11:00 AM', '2:00 PM', '4:00 PM'];
|
||||
|
||||
return availableTimeSlots;
|
||||
}
|
||||
|
||||
function displayTimeSlots(timeSlots) {
|
||||
// Clear existing time slots
|
||||
timeSlotsList.innerHTML = '';
|
||||
|
||||
// Display time slots in the list
|
||||
timeSlots.forEach(function(timeSlot) {
|
||||
var listItem = document.createElement('li');
|
||||
listItem.textContent = timeSlot;
|
||||
timeSlotsList.appendChild(listItem);
|
||||
});
|
||||
}
|
||||
});
|
||||
</script> -->
|
||||
|
||||
<!-- <script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var calendarEl = document.getElementById('calendar');
|
||||
var calendar = new FullCalendar.Calendar(calendarEl, {
|
||||
initialView: 'dayGridMonth'
|
||||
});
|
||||
calendar.render();
|
||||
});
|
||||
</script> -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var calendarEl = document.getElementById('calendar');
|
||||
var slots = <?php echo json_encode($data['calendar']); ?>;
|
||||
console.log(slots)
|
||||
var calendar = new FullCalendar.Calendar(calendarEl, {
|
||||
// height: '100%',
|
||||
// expandRows: true,
|
||||
// slotMinTime: '08:00',
|
||||
// slotMaxTime: '20:00',
|
||||
headerToolbar: {
|
||||
left: 'prev,next today',
|
||||
center: 'title',
|
||||
right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek'
|
||||
},
|
||||
initialView: 'dayGridMonth',
|
||||
// initialDate: '2023-01-12',
|
||||
navLinks: true, // can click day/week names to navigate views
|
||||
editable: true,
|
||||
selectable: true,
|
||||
nowIndicator: true,
|
||||
dayMaxEvents: true, // allow "more" link when too many events
|
||||
events: []
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
function addEvents(startDateString, selectedDays, numberOfDays, response) {
|
||||
console.log('startDateString:' + startDateString)
|
||||
var startDate = moment(startDateString, 'YYYY-MM-DD');
|
||||
console.log('startDate:' + startDate)
|
||||
|
||||
var currentDay = startDate.clone();
|
||||
console.log('currentDay:' + currentDay)
|
||||
|
||||
var addedEvents = 0;
|
||||
console.log('addedEvents:' + addedEvents)
|
||||
console.log('numberOfDays:' + numberOfDays)
|
||||
|
||||
while (addedEvents < numberOfDays) {
|
||||
console.log('addedEvents:' + addedEvents)
|
||||
|
||||
// if (currentDay.isAfter(startDate, 'day')) {
|
||||
var dayOfWeek = currentDay.format('dddd');
|
||||
|
||||
// var test = currentDay.isAfter(startDate, 'day');
|
||||
|
||||
// Check if the current day is in the selectedDays array
|
||||
if (selectedDays.includes(dayOfWeek)) {
|
||||
console.log('dayOfWeek:' + dayOfWeek)
|
||||
console.log('isAfter:' + currentDay.isAfter(startDate, 'day'))
|
||||
|
||||
// console.log(startDate)
|
||||
// console.log(currentDay)
|
||||
// console.log(test)
|
||||
// console.log("yes")
|
||||
// calendar.addEvent({
|
||||
// title: 'Event Title',
|
||||
// start: currentDay.format('YYYY-MM-DD'),
|
||||
// allDay: true,
|
||||
// // You can add more properties to the event as needed
|
||||
// });
|
||||
|
||||
|
||||
var week_day = dayOfWeek.charAt(0).toLowerCase() + dayOfWeek.slice(1);
|
||||
|
||||
var events = response[week_day];
|
||||
console.log(events)
|
||||
// var targetDay = moment().day(dayOfWeek);
|
||||
// console.log(targetDay)
|
||||
|
||||
events.forEach(function(event) {
|
||||
// var startDateTime = moment(`${dayOfWeek} ${event.from}`, 'dddd HH:mm');
|
||||
// var endDateTime = moment(`${dayOfWeek} ${event.to}`, 'dddd HH:mm');
|
||||
// Adjust startDateTime calculation
|
||||
// If the target day is today or in the past, get the next occurrence
|
||||
|
||||
// console.log(targetDay)
|
||||
// if (targetDay.isBefore(currentDay, 'day')) {
|
||||
// targetDay.add(1, 'week');
|
||||
// // var newTarget = targetDay.clone().add(1, 'week');
|
||||
// console.log(targetDay)
|
||||
// // console.log(newTarget)
|
||||
|
||||
// }
|
||||
// var daysToAdd = targetDay.diff(currentDay, 'days');
|
||||
// console.log(daysToAdd)
|
||||
// var startDateTime = currentDay.clone().add(daysToAdd, 'days');
|
||||
var startDateTime = currentDay.clone();
|
||||
|
||||
// Use the event time as usual
|
||||
startDateTime.set({
|
||||
hour: event.from.split(':')[0],
|
||||
minute: event.from.split(':')[1]
|
||||
});
|
||||
|
||||
var endDateTime = moment(startDateTime).set({
|
||||
hour: event.to.split(':')[0],
|
||||
minute: event.to.split(':')[1]
|
||||
});
|
||||
calendar.addEvent({
|
||||
title: event.point + "points",
|
||||
start: startDateTime.format(),
|
||||
end: endDateTime.format(),
|
||||
// You can add more properties to the event as needed
|
||||
});
|
||||
// if (targetDay.isAfter(currentDay, 'day')) {
|
||||
// console.log('yes')
|
||||
// targetDay.add(1, 'week');
|
||||
// }
|
||||
// targetDay = newTarget
|
||||
// targetDay.add(1, 'week');
|
||||
// var newTarget = targetDay.clone().add(1, 'week');
|
||||
// daysToAdd = newTarget.diff(currentDay, 'days');
|
||||
// console.log(daysToAdd)
|
||||
|
||||
// console.log(newTarget)
|
||||
|
||||
});
|
||||
addedEvents++;
|
||||
}
|
||||
// }
|
||||
currentDay.add(1, 'days'); // Move to the next day
|
||||
}
|
||||
}
|
||||
|
||||
// function addEvents(response) {
|
||||
// for (var dayOfWeek in response) {
|
||||
// if (response.hasOwnProperty(dayOfWeek)) {
|
||||
// var events = response[dayOfWeek];
|
||||
|
||||
// events.forEach(function(event) {
|
||||
// var startDateTime = moment(`${dayOfWeek} ${event.from}`, 'dddd HH:mm');
|
||||
// var endDateTime = moment(`${dayOfWeek} ${event.to}`, 'dddd HH:mm');
|
||||
|
||||
// calendar.addEvent({
|
||||
// title: 'Event Title',
|
||||
// start: startDateTime.format(),
|
||||
// end: endDateTime.format(),
|
||||
// // You can add more properties to the event as needed
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
$.ajax({
|
||||
url: '/admin/calendar',
|
||||
method: 'POST',
|
||||
data: {
|
||||
calendar: slots
|
||||
},
|
||||
success: function(response) {
|
||||
// console.log(response.slots_available);
|
||||
response = JSON.parse(response)
|
||||
console.log(response);
|
||||
var slot = JSON.parse(response.slot)
|
||||
var days = parseInt(JSON.parse(response.days))
|
||||
var keysArray = Object.keys(slot);
|
||||
|
||||
console.log(keysArray);
|
||||
|
||||
var currentDate = new Date();
|
||||
|
||||
// Extract year, month, and day
|
||||
var year = currentDate.getFullYear();
|
||||
var month = (currentDate.getMonth() + 1).toString().padStart(2, '0'); // Months are zero-based
|
||||
var day = currentDate.getDate().toString().padStart(2, '0');
|
||||
|
||||
// Form the desired date string
|
||||
var formattedDate = `${year}-${month}-${day}`;
|
||||
console.log(keysArray.length)
|
||||
|
||||
var capitalizedArray = keysArray.map(function(str) {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||
});
|
||||
console.log(formattedDate + "ajax")
|
||||
addEvents(formattedDate, capitalizedArray, days, slot);
|
||||
|
||||
// alert(JSON.stringify(response));
|
||||
|
||||
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.log(error)
|
||||
// console.log("error oooo")
|
||||
}
|
||||
});
|
||||
// Example: Start date is '2023-12-12', selected days are ['Monday', 'Thursday'], and number of days is 2
|
||||
|
||||
calendar.render();
|
||||
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,545 @@
|
||||
<?php
|
||||
|
||||
include_once 'lib/ghl/calendar.php';
|
||||
|
||||
function filterTimeSlots($timeSlots, $startTime, $endTime) {
|
||||
$filteredSlots = array_filter($timeSlots, function($slot) use ($startTime, $endTime) {
|
||||
$slotTime = strtotime($slot);
|
||||
$start = strtotime($startTime);
|
||||
$end = strtotime($endTime);
|
||||
|
||||
return $slotTime >= $start && $slotTime <= $end;
|
||||
});
|
||||
|
||||
return array_values($filteredSlots); // Reset array keys
|
||||
}
|
||||
|
||||
|
||||
function extractRanges($slotStart, $slotEnd, $serviceStart, $serviceEnd, $interval, $date)
|
||||
{
|
||||
//echo function args
|
||||
echo "\n Function Args \n ";
|
||||
print_r(func_get_args());
|
||||
// Convert time strings to DateTime objects for easier comparison
|
||||
$slotStartTime = new DateTime($date . ' ' . $slotStart);
|
||||
$slotEndTime = new DateTime($date . ' ' . $slotEnd);
|
||||
$serviceStartTime = new DateTime($date . ' ' . $serviceStart);
|
||||
$serviceEndTime = new DateTime($date . ' ' . $serviceEnd);
|
||||
|
||||
if ($slotEndTime < $slotStartTime) {
|
||||
$slotEndTime->modify('+1 day');
|
||||
}
|
||||
if ($serviceEndTime < $serviceStartTime) {
|
||||
$serviceEndTime->modify('+1 day');
|
||||
}
|
||||
|
||||
// Check if service time falls within the slot range
|
||||
if ($serviceStartTime >= $slotStartTime && $serviceStartTime < $slotEndTime) {
|
||||
// if ($serviceStartTime >= $slotStartTime && $serviceEndTime <= $slotEndTime) {
|
||||
$ranges = array();
|
||||
|
||||
// Calculate ranges based on the interval
|
||||
$currentRangeStart = $serviceStartTime;
|
||||
if ($currentRangeStart < $slotEndTime) {
|
||||
array_push($ranges, $currentRangeStart->format('H:i:s'));
|
||||
}
|
||||
while ($currentRangeStart < $serviceEndTime) {
|
||||
$currentRangeEnd = clone $currentRangeStart;
|
||||
$currentRangeEnd->add(new DateInterval("PT" . $interval . "M"));
|
||||
|
||||
// Check if the calculated range end is within the slot range
|
||||
if ($currentRangeEnd < $slotEndTime) {
|
||||
// array_push(
|
||||
// $ranges,
|
||||
// $currentRangeStart->format('H:i:s')
|
||||
// );
|
||||
array_push(
|
||||
$ranges,
|
||||
$currentRangeEnd->format('H:i:s')
|
||||
);
|
||||
} else {
|
||||
// Add the end time within the slot range
|
||||
// array_push($ranges, $slotEndTime->format('H:i:s'));
|
||||
// Break the loop if the range end exceeds the slot end time
|
||||
break;
|
||||
}
|
||||
|
||||
$currentRangeStart = $currentRangeEnd;
|
||||
}
|
||||
|
||||
return $ranges;
|
||||
} elseif ($serviceStartTime < $slotStartTime && $serviceEndTime <= $slotEndTime) {
|
||||
// if ($serviceStartTime >= $slotStartTime && $serviceEndTime <= $slotEndTime) {
|
||||
$ranges = array();
|
||||
|
||||
// Calculate ranges based on the interval
|
||||
$currentRangeStart = $slotStartTime;
|
||||
if ($currentRangeStart < $serviceEndTime) {
|
||||
array_push($ranges, $currentRangeStart->format('H:i:s'));
|
||||
}
|
||||
// array_push($ranges, $currentRangeStart->format('H:i:s'));
|
||||
while ($currentRangeStart < $serviceEndTime) {
|
||||
$currentRangeEnd = clone $currentRangeStart;
|
||||
$currentRangeEnd->add(new DateInterval("PT" . $interval . "M"));
|
||||
|
||||
// Check if the calculated range end is within the slot range
|
||||
if ($currentRangeEnd < $slotEndTime) {
|
||||
// array_push(
|
||||
// $ranges,
|
||||
// $currentRangeStart->format('H:i:s')
|
||||
// );
|
||||
array_push(
|
||||
$ranges,
|
||||
$currentRangeEnd->format('H:i:s')
|
||||
);
|
||||
} else {
|
||||
// Add the end time within the slot range
|
||||
// array_push($ranges, $slotEndTime->format('H:i:s'));
|
||||
// Break the loop if the range end exceeds the slot end time
|
||||
break;
|
||||
}
|
||||
|
||||
$currentRangeStart = $currentRangeEnd;
|
||||
}
|
||||
|
||||
return $ranges;
|
||||
} elseif ($serviceStartTime < $slotStartTime && $serviceEndTime > $slotEndTime) {
|
||||
// if ($serviceStartTime >= $slotStartTime && $serviceEndTime <= $slotEndTime) {
|
||||
$ranges = array();
|
||||
|
||||
// Calculate ranges based on the interval
|
||||
$currentRangeStart = $slotStartTime;
|
||||
if ($currentRangeStart < $serviceEndTime) {
|
||||
array_push($ranges, $currentRangeStart->format('H:i:s'));
|
||||
}
|
||||
// array_push($ranges, $currentRangeStart->format('H:i:s'));
|
||||
while ($currentRangeStart < $serviceEndTime) {
|
||||
$currentRangeEnd = clone $currentRangeStart;
|
||||
$currentRangeEnd->add(new DateInterval("PT" . $interval . "M"));
|
||||
|
||||
// Check if the calculated range end is within the slot range
|
||||
if ($currentRangeEnd < $slotEndTime) {
|
||||
// array_push(
|
||||
// $ranges,
|
||||
// $currentRangeStart->format('H:i:s')
|
||||
// );
|
||||
array_push(
|
||||
$ranges,
|
||||
$currentRangeEnd->format('H:i:s')
|
||||
);
|
||||
} else {
|
||||
// Add the end time within the slot range
|
||||
// array_push($ranges, $slotEndTime->format('H:i:s'));
|
||||
// Break the loop if the range end exceeds the slot end time
|
||||
break;
|
||||
}
|
||||
|
||||
$currentRangeStart = $currentRangeEnd;
|
||||
}
|
||||
|
||||
return $ranges;
|
||||
} elseif ($serviceStartTime >= $slotStartTime && $serviceEndTime <= $slotEndTime) {
|
||||
$ranges = array();
|
||||
|
||||
// Calculate ranges based on the interval
|
||||
$currentRangeStart = $serviceStartTime;
|
||||
if ($currentRangeStart < $serviceEndTime) {
|
||||
array_push($ranges, $currentRangeStart->format('H:i:s'));
|
||||
}
|
||||
// array_push($ranges, $currentRangeStart->format('H:i:s'));
|
||||
while ($currentRangeStart <= $serviceEndTime) {
|
||||
$currentRangeEnd = clone $currentRangeStart;
|
||||
$currentRangeEnd->add(new DateInterval("PT" . $interval . "M"));
|
||||
|
||||
// Check if the calculated range end is within the slot range
|
||||
if ($currentRangeEnd <= $slotEndTime) {
|
||||
// array_push(
|
||||
// $ranges,
|
||||
// $currentRangeStart->format('H:i:s')
|
||||
// );
|
||||
array_push(
|
||||
$ranges,
|
||||
$currentRangeEnd->format('H:i:s')
|
||||
);
|
||||
} else {
|
||||
// Add the end time within the slot range
|
||||
// array_push($ranges, $slotEndTime->format('H:i:s'));
|
||||
// Break the loop if the range end exceeds the slot end time
|
||||
break;
|
||||
}
|
||||
|
||||
$currentRangeStart = $currentRangeEnd;
|
||||
}
|
||||
|
||||
return $ranges;
|
||||
} else {
|
||||
// Service time is outside the slot range
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function convertToDays($value, $unit)
|
||||
{
|
||||
switch ($unit) {
|
||||
case 'weeks':
|
||||
return $value * 7;
|
||||
case 'months':
|
||||
// Assuming a month is considered as 30 days for simplicity
|
||||
return $value * 30;
|
||||
case 'hours':
|
||||
return 0; // Set to 0 days if the unit is hours
|
||||
default:
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
function checkServiceInSlot($service, $slot, $mod, $eventTiming, $schedule, $ser_id)
|
||||
{
|
||||
|
||||
$slotpoint = 0;
|
||||
// Get the current date
|
||||
$currentDate = new DateTime('now');
|
||||
|
||||
|
||||
// return $slotpoint;
|
||||
|
||||
// if ($mod->alert == 'On') {
|
||||
$slotpoint = calculateTotalPoints((int)$mod->days, $slot, $service, $eventTiming, $schedule, $mod, $ser_id);
|
||||
if (is_array($slotpoint)) {
|
||||
return $slotpoint['message'];
|
||||
}
|
||||
if ($slotpoint < (int)$mod->score_threshold && $slotpoint != 0) {
|
||||
|
||||
$data = [
|
||||
'actual_score' => $slotpoint,
|
||||
];
|
||||
// Update points
|
||||
$projectModel = new ProjectModel();
|
||||
$projectModel->edit($data, $mod->id);
|
||||
|
||||
if ($mod->alert != 'On') {
|
||||
return 'No alert set for project ' . $mod->project_name . ' with ID ' . $mod->id;
|
||||
}
|
||||
|
||||
// Retrieve webhook URL and payload
|
||||
$webhookUrl = $mod->webhook;
|
||||
if (!filter_var($webhookUrl, FILTER_VALIDATE_URL)) {
|
||||
// error_log('Webhook for project ' . $mod->project_name . ' with ID ' . $mod->id . ' is not a URL ');
|
||||
return 'Invalid Webhook URL';
|
||||
}
|
||||
|
||||
|
||||
$jsonData = json_decode($mod->payload);
|
||||
|
||||
|
||||
// Set up cURL options
|
||||
$curlOptions = [
|
||||
CURLOPT_URL => $webhookUrl,
|
||||
CURLOPT_POST => 1,
|
||||
CURLOPT_POSTFIELDS => json_encode($jsonData), // Send encoded JSON
|
||||
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
];
|
||||
|
||||
// Initialize cURL session
|
||||
$ch = curl_init();
|
||||
|
||||
// Set cURL options
|
||||
curl_setopt_array($ch, $curlOptions);
|
||||
|
||||
// Execute cURL session and get the result
|
||||
$response = curl_exec($ch);
|
||||
$status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
|
||||
if ($status_code != 200) {
|
||||
// error_log('Curl error for project ' . $mod->project_name . ' with ID ' . $mod->id . ': ' . curl_error($ch));
|
||||
return 'Something went wrong while sending webhook payload';
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Log the webhook response
|
||||
curl_close($ch);
|
||||
// error_log('Webhook response for project ' . $mod->project_name . ' with ID ' . $mod->id . ': ' . $response);
|
||||
return 'Accepted';
|
||||
|
||||
|
||||
// Close cURL session
|
||||
|
||||
} else {
|
||||
|
||||
$data = [
|
||||
'actual_score' => $slotpoint,
|
||||
];
|
||||
|
||||
|
||||
// Insert data into the database using LicenseModel
|
||||
$projectModel = new ProjectModel();
|
||||
$projectModel->edit($data, $mod->id);
|
||||
$result = "Point is higher than mininum point set"; // Initialize result variable
|
||||
|
||||
return $result; // Return the accumulated result
|
||||
|
||||
}
|
||||
// } else {
|
||||
// // Log that no alert is set for the project
|
||||
// // error_log('No alert set for project ' . $mod->project_name . ' with ID ' . $mod->id);
|
||||
// return 'No alert set for project ' . $mod->project_name . ' with ID ' . $mod->id;
|
||||
// }
|
||||
}
|
||||
|
||||
function generateTimeIntervals($startTime, $endTime, $intervalMinutes, $date)
|
||||
{
|
||||
|
||||
$start = new DateTime($date . ' ' . $startTime);
|
||||
$end = new DateTime($date . ' ' . $endTime);
|
||||
|
||||
if ($end < $start) {
|
||||
$end->modify('+1 day');
|
||||
}
|
||||
$intervalObj = new DateInterval("PT{$intervalMinutes}M"); // PT stands for 'Period Time'
|
||||
|
||||
$period = new DatePeriod($start, $intervalObj, $end);
|
||||
|
||||
$intervals = array();
|
||||
foreach ($period as $dt) {
|
||||
$intervals[] = $dt->format('H:i:s');
|
||||
}
|
||||
|
||||
return $intervals;
|
||||
}
|
||||
|
||||
function calculateTotalPoints($daysToCheck, $slot, $service, $eventTiming, $schedule, $project, $ser_id)
|
||||
{
|
||||
// return json_encode($schedule);
|
||||
|
||||
|
||||
// Convert allowBookingFor to days if the unit is months or weeks
|
||||
if ($schedule['allowBookingForUnit'] === 'months' || $schedule['allowBookingForUnit'] === 'weeks') {
|
||||
$schedule['allowBookingFor'] = convertToDays($schedule['allowBookingFor'], $schedule['allowBookingForUnit']);
|
||||
$schedule['allowBookingForUnit'] = 'days';
|
||||
}
|
||||
if ($schedule['allowBookingAfterUnit'] === 'months' || $schedule['allowBookingAfterUnit'] === 'weeks' || $schedule['allowBookingAfterUnit'] === 'hours') {
|
||||
$schedule['allowBookingAfter'] = convertToDays($schedule['allowBookingAfter'], $schedule['allowBookingAfterUnit']);
|
||||
$schedule['allowBookingAfterUnit'] = 'days';
|
||||
}
|
||||
// return "well";
|
||||
|
||||
$currentDate = new DateTime();
|
||||
$currentDayIndex = $currentDate->format('w'); // 0 for Sunday, 1 for Monday, ..., 6 for Saturday
|
||||
|
||||
// Get the next two days to check
|
||||
// $daysToCheckIndices = [];
|
||||
// for ($i = 0; $i < $daysToCheck; $i++) {
|
||||
// $nextDayIndex = ($currentDayIndex + $i + 1) % 7;
|
||||
// $daysToCheckIndices[] = $nextDayIndex;
|
||||
// }
|
||||
|
||||
|
||||
|
||||
$daysToCheckData = [];
|
||||
if (empty($schedule['allowBookingFor'])) {
|
||||
$counter = $daysToCheck;
|
||||
} else {
|
||||
$counter = (int)$schedule['allowBookingFor'];
|
||||
}
|
||||
if (empty($schedule['allowBookingAfter'])) {
|
||||
$skip = 0;
|
||||
} else {
|
||||
$skip = (int)$schedule['allowBookingAfter'];
|
||||
}
|
||||
// Don't check beyond 30 days
|
||||
if ($counter > 30) {
|
||||
$counter = 30;
|
||||
}
|
||||
for ($i = $skip; $i < $counter; $i++) {
|
||||
$nextDayIndex = ($currentDayIndex + $i) % 7;
|
||||
$nextDate = clone $currentDate;
|
||||
$nextDate->modify("+$i day");
|
||||
|
||||
|
||||
$daysToCheckData[] = [
|
||||
'index' => $nextDayIndex,
|
||||
'date' => $nextDate->format('Y-m-d'),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
// return json_encode($daysToCheckData);
|
||||
$totalPoints = 0;
|
||||
|
||||
// Loop through the selected days and calculate total points
|
||||
$dayNames = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
|
||||
|
||||
// GHL V2 token refresh
|
||||
// access token expires in a day
|
||||
// refresh token last for a year if you don't use it.
|
||||
// after refreshing you get a new refresh token and access token but previous refresh token expires.
|
||||
$canRefresh = false;
|
||||
if ($project->refresh_token != "") {
|
||||
$canRefresh = true;
|
||||
}
|
||||
|
||||
if ($canRefresh) {
|
||||
$calendar = new GHLCalendar($project->id, $project->access_token, $project->refresh_token);
|
||||
$result = $calendar->refreshToken();
|
||||
|
||||
if ($result['code'] != 200) {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
// foreach ($daysToCheckIndices as $dayIndex) {
|
||||
foreach ($daysToCheckData as $dayData) {
|
||||
// $dayName = $dayNames[$dayIndex];
|
||||
// // error_log("day" . $dayName);
|
||||
|
||||
|
||||
|
||||
$dayIndex = $dayData['index'];
|
||||
$dayName = $dayNames[$dayIndex];
|
||||
$date = $dayData['date'];
|
||||
|
||||
|
||||
|
||||
// print_r($service);
|
||||
if (isset($slot->$dayName)) {
|
||||
if (!array_key_exists($dayName, $service)) {
|
||||
continue; // Day not found in the service schedule
|
||||
}
|
||||
$der = [];
|
||||
|
||||
|
||||
foreach ($service[$dayName] as $ser) {
|
||||
$serviceStart = strtotime(sprintf("%02d:%02d", $ser["openHour"], $ser["openMinute"]));
|
||||
$serviceEnd = strtotime(sprintf("%02d:%02d", $ser["closeHour"], $ser["closeMinute"]));
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// $start = strtotime($dayData['date'] . ' ' . sprintf("%02d:%02d", $ser["openHour"], $ser["openMinute"]));
|
||||
// $end = strtotime($dayData['date'] . ' ' . sprintf("%02d:%02d", $ser["closeHour"], $ser["closeMinute"]));
|
||||
// $start = $start * 1000;
|
||||
// $end = $end * 1000;
|
||||
|
||||
// Set start time to beginning of day (00:00:00)
|
||||
$start = strtotime($dayData['date'] . ' 00:00:00');
|
||||
// Set end time to end of day (23:59:59)
|
||||
$end = strtotime($dayData['date'] . ' 23:59:59');
|
||||
|
||||
$start = $start * 1000;
|
||||
$end = $end * 1000;
|
||||
$apikey2 = $project->location;
|
||||
$curl = curl_init();
|
||||
|
||||
|
||||
|
||||
curl_setopt_array($curl, [
|
||||
CURLOPT_URL => "https://rest.gohighlevel.com/v1/appointments/slots?calendarId=$ser_id&startDate=$start&endDate=$end",
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_ENCODING => "",
|
||||
CURLOPT_MAXREDIRS => 10,
|
||||
CURLOPT_TIMEOUT => 30,
|
||||
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||
CURLOPT_CUSTOMREQUEST => "GET",
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"Accept: application/json",
|
||||
"Authorization: Bearer " . $apikey2,
|
||||
"Version: 2021-04-15"
|
||||
],
|
||||
]);
|
||||
|
||||
$response = curl_exec($curl);
|
||||
$err = curl_error($curl);
|
||||
$status_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
||||
|
||||
|
||||
curl_close($curl);
|
||||
|
||||
|
||||
if ($status_code != 200 && !$canRefresh) {
|
||||
return ["code" => 422, "message" => "Please authorize the app to fetch free slots"];
|
||||
}
|
||||
|
||||
if ($status_code != 200) {
|
||||
|
||||
$calendar = new GHLCalendar($project->id, $project->access_token, $project->refresh_token);
|
||||
$result = $calendar->getFreeSlots( $ser_id, $start, $end);
|
||||
sleep(3); // consider rate limit
|
||||
|
||||
if ($result['code'] != 200) {
|
||||
// $result['message'] = "Something went wrong while fetching free slots";
|
||||
return $result;
|
||||
continue;
|
||||
}
|
||||
$data = $result['data'];
|
||||
// continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
$data = empty($data) ? json_decode($response, true) : $data;
|
||||
|
||||
|
||||
// return json_encode($data);
|
||||
|
||||
|
||||
$stepSize = $eventTiming["slotInterval"];
|
||||
|
||||
$rslt = [];
|
||||
|
||||
if (!empty($data[$dayData['date']]["slots"])) {
|
||||
|
||||
foreach ($data[$dayData['date']]["slots"] as $time) {
|
||||
$dateTime = new DateTime($time);
|
||||
$dateTime->modify('+5 hours'); // Convert from UTC-5 to UTC/GMT
|
||||
|
||||
$rslt[] = $dateTime->format('H:i:s');
|
||||
}
|
||||
|
||||
|
||||
|
||||
foreach ($slot->$dayName as $timeSlot) {
|
||||
|
||||
|
||||
$slotStart = strtotime($timeSlot->from);
|
||||
$slotEnd = strtotime($timeSlot->to);
|
||||
|
||||
$filteredSlots = filterTimeSlots($rslt, date("H:i:s", $slotStart), date("H:i:s", $slotEnd));
|
||||
|
||||
|
||||
$totalPoints += (int)$timeSlot->point * count($filteredSlots);
|
||||
|
||||
/*
|
||||
$range = extractRanges(date("H:i:s", $slotStart), date("H:i:s", $slotEnd), date("H:i:s", $serviceStart), date("H:i:s", $serviceEnd), $stepSize, $dayData['date']);
|
||||
|
||||
|
||||
if (!$range) {
|
||||
continue;
|
||||
}
|
||||
$intersection = array_intersect($rslt, $range);
|
||||
if (empty($intersection)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($intersection as $intersect) {
|
||||
$totalPoints += (int)$timeSlot->point;
|
||||
// return $totalPoints;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// $totalPoints += intval($timeSlot->point) ?: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// return json_encode(["derrr" => $der]);
|
||||
}
|
||||
|
||||
return $totalPoints;
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
include_once __DIR__ . "/mysql-database-service.php";
|
||||
|
||||
class CampaignModel extends MySqlDatabaseService
|
||||
{
|
||||
protected $_table = 'campaign';
|
||||
protected $_primary_key = 'id';
|
||||
protected $_return_type = 'array';
|
||||
protected $_allowed_fields = [
|
||||
'id', 'name', 'file_id', 'data', 'user_id', 'created_at', 'updated_at'
|
||||
];
|
||||
protected $_label_fields = [
|
||||
'ID', 'Name', 'File ID', 'Data', 'User ID', 'Created At', 'Updated At'
|
||||
];
|
||||
protected $_use_timestamps = true;
|
||||
protected $_created_field = 'created_at';
|
||||
protected $_updated_field = 'updated_at';
|
||||
protected $_validation_rules = [
|
||||
['name', 'Name', 'required'],
|
||||
['file_id', 'File ID', 'required'],
|
||||
['data', 'Data', ''],
|
||||
['user_id', 'User ID', 'required'],
|
||||
['created_at', 'Created At', 'required'],
|
||||
['updated_at', 'Updated At', 'required']
|
||||
];
|
||||
|
||||
protected $_validation_edit_rules = [
|
||||
['name', 'Name', 'required'],
|
||||
['file_id', 'File ID', 'required'],
|
||||
['data', 'Data', ''],
|
||||
['user_id', 'User ID', 'required'],
|
||||
['updated_at', 'Updated At', 'required']
|
||||
];
|
||||
|
||||
protected $_validation_messages = [
|
||||
['name', 'Name ', 'required'],
|
||||
['file_id', 'File ID', 'required'],
|
||||
['data', 'Data', ''],
|
||||
['user_id', 'User ID', 'required'],
|
||||
['updated_at', 'Updated At', 'required']
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function get_mapping()
|
||||
{
|
||||
return [
|
||||
// TODO: ADD MAPPING
|
||||
];
|
||||
}
|
||||
|
||||
public function csvToObject($csvString) {
|
||||
$lines = explode("\n", $csvString);
|
||||
$headers = array_map(function($header) {
|
||||
return str_replace(' ', '_', trim(strtolower($header)));
|
||||
}, explode(',', $lines[0])); // Convert headers to snake case
|
||||
|
||||
$object = [];
|
||||
|
||||
foreach ($headers as $header) {
|
||||
$object[$header] = [];
|
||||
}
|
||||
|
||||
for ($i = 1; $i < count($lines) - 1; $i++) {
|
||||
$values = explode(',', $lines[$i]);
|
||||
for ($j = 0; $j < count($headers) && $j < count($values); $j++) {
|
||||
$value = trim($values[$j]);
|
||||
if ($headers[$j] == 'duration' && $value === "-") {
|
||||
$object[$headers[$j]][] = '00:00';
|
||||
continue;
|
||||
}
|
||||
$object[$headers[$j]][] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
public function getFilteredData($campaign, $filters) {
|
||||
$config = MkdConfig::get_instance()->get_config();
|
||||
$userModel = new UserModel();
|
||||
$user = $userModel->get($_SESSION['user']);
|
||||
|
||||
$oauth = new \Lib\Google\GoogleOAuth2(
|
||||
$config['google_client_id'],
|
||||
$config['google_client_secret'],
|
||||
$config['google_redirect_uri']
|
||||
);
|
||||
|
||||
$oauth->setRefreshToken($user->drive_refresh_token);
|
||||
$oauth->refreshAccessToken();
|
||||
|
||||
$drive = new \Lib\Google\GoogleDrive($oauth);
|
||||
|
||||
// Download and parse the file
|
||||
$content = $drive->downloadFile($campaign->file_id, 'text/csv');
|
||||
$data = $this->csvToObject($content);
|
||||
|
||||
// Apply filters
|
||||
$validRows = range(0, count($data['date']) - 1);
|
||||
|
||||
if ($filters['campaign_name']) {
|
||||
$validRows = array_filter($validRows, function($i) use ($data, $filters) {
|
||||
return $data['campaign_name'][$i] === $filters['campaign_name'];
|
||||
});
|
||||
}
|
||||
|
||||
if ($filters['ad_set_name']) {
|
||||
$validRows = array_filter($validRows, function($i) use ($data, $filters) {
|
||||
return $data['ad_set_name'][$i] === $filters['ad_set_name'];
|
||||
});
|
||||
}
|
||||
|
||||
if ($filters['ad_name']) {
|
||||
$validRows = array_filter($validRows, function($i) use ($data, $filters) {
|
||||
return $data['ad_name'][$i] === $filters['ad_name'];
|
||||
});
|
||||
}
|
||||
|
||||
// Filter the data
|
||||
$filteredData = [];
|
||||
foreach ($data as $column => $values) {
|
||||
$filteredData[$column] = array_intersect_key($values, array_flip($validRows));
|
||||
}
|
||||
|
||||
return $filteredData;
|
||||
}
|
||||
|
||||
}
|
||||
+260
@@ -0,0 +1,260 @@
|
||||
<div class="container mt-5">
|
||||
<h2 class="text-center">Add Campaign Data</h2>
|
||||
<?php if (isset($error) && $error) : ?>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
Please fill in all required fields!
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="/<?php echo $_SESSION['role']; ?>/campaign/add" method="POST" class="mt-4">
|
||||
<div class="form-group">
|
||||
<label for="name">Name</label>
|
||||
<input type="name" name="name" id="name" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="file_id">Google Sheet</label>
|
||||
<div class="card">
|
||||
<div class="card-body text-center p-4">
|
||||
<div class="mb-3">
|
||||
<input type="text" name="file_id" id="file_id" class="form-control bg-white text-center"
|
||||
placeholder="No file selected" readonly required>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary" onclick="openDrivePicker()">
|
||||
<i class="fas fa-file-excel mr-2"></i>Select Google Sheet
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.drive-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.drive-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 0.25rem;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.drive-item:hover {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.drive-item svg {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.drive-item-name {
|
||||
font-size: 0.9rem;
|
||||
word-break: break-word;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.drive-breadcrumb {
|
||||
padding: 0.5rem 1rem;
|
||||
background-color: #f8f9fa;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.drive-breadcrumb a {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.drive-breadcrumb a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.modal-body-scroll {
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.drive-loader {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.drive-loader-spinner {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border: 5px solid #f3f3f3;
|
||||
border-top: 5px solid #3498db;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.drive-grid {
|
||||
min-height: 300px; /* Prevent layout shift */
|
||||
}
|
||||
|
||||
.drive-content {
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.drive-content.loading {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
let currentFolderId = 'root';
|
||||
let folderPath = [{id: 'root', name: 'My Drive'}];
|
||||
let currentModal = null;
|
||||
|
||||
async function createModal() {
|
||||
const modal = document.createElement('div');
|
||||
modal.className = 'modal fade';
|
||||
modal.innerHTML = `
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Select Google Sheet</h5>
|
||||
<button type="button" class="close" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="drive-breadcrumb">
|
||||
${renderBreadcrumb()}
|
||||
</div>
|
||||
<div class="modal-body p-0 modal-body-scroll">
|
||||
<div class="drive-content">
|
||||
<div class="drive-loader">
|
||||
<div class="drive-loader-spinner"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(modal);
|
||||
currentModal = modal;
|
||||
|
||||
// Handle modal cleanup on close
|
||||
$(modal).on('hidden.bs.modal', function () {
|
||||
modal.remove();
|
||||
currentModal = null;
|
||||
});
|
||||
|
||||
$(modal).modal('show');
|
||||
return modal;
|
||||
}
|
||||
|
||||
async function updateModalContent(modal, files) {
|
||||
const content = modal.querySelector('.drive-content');
|
||||
content.innerHTML = `
|
||||
<div class="drive-grid">
|
||||
${files.length === 0 ?
|
||||
'<div class="text-center p-4 w-100">No files found in this folder</div>' :
|
||||
files.map(file => `
|
||||
<div class="drive-item ${file.mimeType === 'application/vnd.google-apps.folder' ? 'folder' : 'sheet'}"
|
||||
onclick="${file.mimeType === 'application/vnd.google-apps.folder' ?
|
||||
`openFolder('${file.id}', '${file.name}')` :
|
||||
`selectFile('${file.id}', '${file.name}')`}">
|
||||
${file.mimeType === 'application/vnd.google-apps.folder' ?
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50">
|
||||
<path d="M 5 4 C 3.3544268 4 2 5.3555411 2 7 L 2 16 L 2 26 L 2 43 C 2 44.644459 3.3544268 46 5 46 L 45 46 C 46.645063 46 48 44.645063 48 43 L 48 26 L 48 16 L 48 11 C 48 9.3549372 46.645063 8 45 8 L 18 8 C 18.08657 8 17.96899 8.000364 17.724609 7.71875 C 17.480227 7.437136 17.179419 6.9699412 16.865234 6.46875 C 16.55105 5.9675588 16.221777 5.4327899 15.806641 4.9628906 C 15.391504 4.4929914 14.818754 4 14 4 L 5 4 z M 5 6 L 14 6 C 13.93925 6 14.06114 6.00701 14.308594 6.2871094 C 14.556051 6.5672101 14.857231 7.0324412 15.169922 7.53125 C 15.482613 8.0300588 15.806429 8.562864 16.212891 9.03125 C 16.619352 9.499636 17.178927 10 18 10 L 45 10 C 45.562937 10 46 10.437063 46 11 L 46 13.1875 C 45.685108 13.07394 45.351843 13 45 13 L 5 13 C 4.6481575 13 4.3148915 13.07394 4 13.1875 L 4 7 C 4 6.4364589 4.4355732 6 5 6 z M 5 15 L 45 15 C 45.56503 15 46 15.43497 46 16 L 46 26 L 46 43 C 46 43.562937 45.562937 44 45 44 L 5 44 C 4.4355732 44 4 43.563541 4 43 L 4 26 L 4 16 C 4 15.43497 4.4349698 15 5 15 z"></path>
|
||||
</svg>` :
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
|
||||
<path fill="#43a047" d="M37,45H11c-1.657,0-3-1.343-3-3V6c0-1.657,1.343-3,3-3h19l10,10v29C40,43.657,38.657,45,37,45z"></path>
|
||||
<path fill="#c8e6c9" d="M40 13L30 13 30 3z"></path>
|
||||
<path fill="#2e7d32" d="M30 13L40 23 40 13z"></path>
|
||||
<path fill="#e8f5e9" d="M31,23H17h-2v2v2v2v2v2v2v2h18v-2v-2v-2v-2v-2v-2v-2H31z M17,25h4v2h-4V25z M17,29h4v2h-4V29z M17,33h4v2h-4V33z M31,35h-8v-2h8V35z M31,31h-8v-2h8V31z M31,27h-8v-2h8V27z"></path>
|
||||
</svg>`
|
||||
}
|
||||
<div class="drive-item-name">${file.name}</div>
|
||||
</div>
|
||||
`).join('')
|
||||
}
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Update breadcrumb
|
||||
modal.querySelector('.drive-breadcrumb').innerHTML = renderBreadcrumb();
|
||||
}
|
||||
|
||||
async function loadFiles(modal) {
|
||||
const content = modal.querySelector('.drive-content');
|
||||
content.classList.add('loading');
|
||||
|
||||
try {
|
||||
const response = await fetch(`/drive/files?folderId=${currentFolderId}`);
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch files');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!data.files || !Array.isArray(data.files)) {
|
||||
throw new Error('Invalid response format');
|
||||
}
|
||||
|
||||
await updateModalContent(modal, data.files);
|
||||
} catch (error) {
|
||||
alert('Error loading files: ' + error.message);
|
||||
// Reset to root if there's an error
|
||||
currentFolderId = 'root';
|
||||
folderPath = [{id: 'root', name: 'My Drive'}];
|
||||
} finally {
|
||||
content.classList.remove('loading');
|
||||
}
|
||||
}
|
||||
|
||||
async function openDrivePicker() {
|
||||
if (!currentModal) {
|
||||
const modal = await createModal();
|
||||
await loadFiles(modal);
|
||||
}
|
||||
}
|
||||
|
||||
async function openFolder(folderId, folderName) {
|
||||
currentFolderId = folderId;
|
||||
folderPath.push({id: folderId, name: folderName});
|
||||
await loadFiles(currentModal);
|
||||
}
|
||||
|
||||
async function navigateToFolder(index) {
|
||||
currentFolderId = folderPath[index].id;
|
||||
folderPath = folderPath.slice(0, index + 1);
|
||||
await loadFiles(currentModal);
|
||||
}
|
||||
|
||||
function selectFile(fileId, fileName) {
|
||||
document.getElementById('file_id').value = fileId;
|
||||
if (currentModal) {
|
||||
$(currentModal).modal('hide');
|
||||
}
|
||||
}
|
||||
|
||||
function renderBreadcrumb() {
|
||||
return folderPath.map((folder, index) => `
|
||||
<a href="#" onclick="event.preventDefault(); navigateToFolder(${index})">${folder.name}</a>
|
||||
${index < folderPath.length - 1 ? ' / ' : ''}
|
||||
`).join('');
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,254 @@
|
||||
<div class="container">
|
||||
<h2>Edit Campaign</h2>
|
||||
|
||||
<form method="post" action="/<?php echo $_SESSION['role']; ?>/campaign/edit/<?php echo $data['campaign']->id; ?>" class="needs-validation" novalidate>
|
||||
<div class="form-group">
|
||||
<label for="name">Campaign Name</label>
|
||||
<input type="text" class="form-control" id="name" name="name"
|
||||
value="<?php echo htmlspecialchars($data['campaign']->name); ?>" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="file_id">Google Sheet</label>
|
||||
<div class="card">
|
||||
<div class="card-body text-center p-4">
|
||||
<div class="mb-3">
|
||||
<input type="text" name="file_id" id="file_id" class="form-control bg-white text-center"
|
||||
value="<?php echo $data['campaign']->file_id; ?>" placeholder="No file selected" readonly required>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary" onclick="openDrivePicker()">
|
||||
<i class="fas fa-file-excel mr-2"></i>Select Google Sheet
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Update Campaign</button>
|
||||
<a href="/<?php echo $_SESSION['role']; ?>/campaign" class="btn btn-secondary">Cancel</a>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
/* Copy all styles from campaignAdd.php */
|
||||
.drive-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.drive-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 0.25rem;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.drive-item:hover {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.drive-item svg {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.drive-item-name {
|
||||
font-size: 0.9rem;
|
||||
word-break: break-word;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.drive-breadcrumb {
|
||||
padding: 0.5rem 1rem;
|
||||
background-color: #f8f9fa;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.drive-breadcrumb a {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.drive-breadcrumb a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.modal-body-scroll {
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.drive-loader {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.drive-loader-spinner {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border: 5px solid #f3f3f3;
|
||||
border-top: 5px solid #3498db;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.drive-grid {
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.drive-content {
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.drive-content.loading {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// Copy all JavaScript from campaignAdd.php
|
||||
let currentFolderId = 'root';
|
||||
let folderPath = [{id: 'root', name: 'My Drive'}];
|
||||
let currentModal = null;
|
||||
|
||||
async function createModal() {
|
||||
const modal = document.createElement('div');
|
||||
modal.className = 'modal fade';
|
||||
modal.innerHTML = `
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Select Google Sheet</h5>
|
||||
<button type="button" class="close" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="drive-breadcrumb">
|
||||
${renderBreadcrumb()}
|
||||
</div>
|
||||
<div class="modal-body p-0 modal-body-scroll">
|
||||
<div class="drive-content">
|
||||
<div class="drive-loader">
|
||||
<div class="drive-loader-spinner"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(modal);
|
||||
currentModal = modal;
|
||||
|
||||
$(modal).on('hidden.bs.modal', function () {
|
||||
modal.remove();
|
||||
currentModal = null;
|
||||
});
|
||||
|
||||
$(modal).modal('show');
|
||||
return modal;
|
||||
}
|
||||
|
||||
async function updateModalContent(modal, files) {
|
||||
const content = modal.querySelector('.drive-content');
|
||||
content.innerHTML = `
|
||||
<div class="drive-grid">
|
||||
${files.length === 0 ?
|
||||
'<div class="text-center p-4 w-100">No files found in this folder</div>' :
|
||||
files.map(file => `
|
||||
<div class="drive-item ${file.mimeType === 'application/vnd.google-apps.folder' ? 'folder' : 'sheet'}"
|
||||
onclick="${file.mimeType === 'application/vnd.google-apps.folder' ?
|
||||
`openFolder('${file.id}', '${file.name}')` :
|
||||
`selectFile('${file.id}', '${file.name}')`}">
|
||||
${file.mimeType === 'application/vnd.google-apps.folder' ?
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50">
|
||||
<path d="M 5 4 C 3.3544268 4 2 5.3555411 2 7 L 2 16 L 2 26 L 2 43 C 2 44.644459 3.3544268 46 5 46 L 45 46 C 46.645063 46 48 44.645063 48 43 L 48 26 L 48 16 L 48 11 C 48 9.3549372 46.645063 8 45 8 L 18 8 C 18.08657 8 17.96899 8.000364 17.724609 7.71875 C 17.480227 7.437136 17.179419 6.9699412 16.865234 6.46875 C 16.55105 5.9675588 16.221777 5.4327899 15.806641 4.9628906 C 15.391504 4.4929914 14.818754 4 14 4 L 5 4 z M 5 6 L 14 6 C 13.93925 6 14.06114 6.00701 14.308594 6.2871094 C 14.556051 6.5672101 14.857231 7.0324412 15.169922 7.53125 C 15.482613 8.0300588 15.806429 8.562864 16.212891 9.03125 C 16.619352 9.499636 17.178927 10 18 10 L 45 10 C 45.562937 10 46 10.437063 46 11 L 46 13.1875 C 45.685108 13.07394 45.351843 13 45 13 L 5 13 C 4.6481575 13 4.3148915 13.07394 4 13.1875 L 4 7 C 4 6.4364589 4.4355732 6 5 6 z M 5 15 L 45 15 C 45.56503 15 46 15.43497 46 16 L 46 26 L 46 43 C 46 43.562937 45.562937 44 45 44 L 5 44 C 4.4355732 44 4 43.563541 4 43 L 4 26 L 4 16 C 4 15.43497 4.4349698 15 5 15 z"></path>
|
||||
</svg>` :
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
|
||||
<path fill="#43a047" d="M37,45H11c-1.657,0-3-1.343-3-3V6c0-1.657,1.343-3,3-3h19l10,10v29C40,43.657,38.657,45,37,45z"></path>
|
||||
<path fill="#c8e6c9" d="M40 13L30 13 30 3z"></path>
|
||||
<path fill="#2e7d32" d="M30 13L40 23 40 13z"></path>
|
||||
<path fill="#e8f5e9" d="M31,23H17h-2v2v2v2v2v2v2v2h18v-2v-2v-2v-2v-2v-2v-2H31z M17,25h4v2h-4V25z M17,29h4v2h-4V29z M17,33h4v2h-4V33z M31,35h-8v-2h8V35z M31,31h-8v-2h8V31z M31,27h-8v-2h8V27z"></path>
|
||||
</svg>`
|
||||
}
|
||||
<div class="drive-item-name">${file.name}</div>
|
||||
</div>
|
||||
`).join('')
|
||||
}
|
||||
</div>
|
||||
`;
|
||||
|
||||
modal.querySelector('.drive-breadcrumb').innerHTML = renderBreadcrumb();
|
||||
}
|
||||
|
||||
async function loadFiles(modal) {
|
||||
const content = modal.querySelector('.drive-content');
|
||||
content.classList.add('loading');
|
||||
|
||||
try {
|
||||
const response = await fetch(`/drive/files?folderId=${currentFolderId}`);
|
||||
if (!response.ok) throw new Error('Failed to fetch files');
|
||||
|
||||
const data = await response.json();
|
||||
if (!data.files || !Array.isArray(data.files)) {
|
||||
throw new Error('Invalid response format');
|
||||
}
|
||||
|
||||
await updateModalContent(modal, data.files);
|
||||
} catch (error) {
|
||||
alert('Error loading files: ' + error.message);
|
||||
currentFolderId = 'root';
|
||||
folderPath = [{id: 'root', name: 'My Drive'}];
|
||||
} finally {
|
||||
content.classList.remove('loading');
|
||||
}
|
||||
}
|
||||
|
||||
async function openDrivePicker() {
|
||||
if (!currentModal) {
|
||||
const modal = await createModal();
|
||||
await loadFiles(modal);
|
||||
}
|
||||
}
|
||||
|
||||
async function openFolder(folderId, folderName) {
|
||||
currentFolderId = folderId;
|
||||
folderPath.push({id: folderId, name: folderName});
|
||||
await loadFiles(currentModal);
|
||||
}
|
||||
|
||||
async function navigateToFolder(index) {
|
||||
currentFolderId = folderPath[index].id;
|
||||
folderPath = folderPath.slice(0, index + 1);
|
||||
await loadFiles(currentModal);
|
||||
}
|
||||
|
||||
function selectFile(fileId, fileName) {
|
||||
document.getElementById('file_id').value = fileId;
|
||||
if (currentModal) {
|
||||
$(currentModal).modal('hide');
|
||||
}
|
||||
}
|
||||
|
||||
function renderBreadcrumb() {
|
||||
return folderPath.map((folder, index) => `
|
||||
<a href="#" onclick="event.preventDefault(); navigateToFolder(${index})">${folder.name}</a>
|
||||
${index < folderPath.length - 1 ? ' / ' : ''}
|
||||
`).join('');
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,166 @@
|
||||
<style>
|
||||
.dark-header thead th {
|
||||
background-color: #343a40;
|
||||
/* Dark gray background */
|
||||
color: white;
|
||||
/* White text color */
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="container">
|
||||
<h2 class="text-left">Campaigns</h2>
|
||||
|
||||
<?php
|
||||
$userModel = new UserModel();
|
||||
$user = $userModel->get($_SESSION['user']);
|
||||
|
||||
if (!$user->drive_refresh_token): ?>
|
||||
<div class="alert alert-warning">
|
||||
Connect your Google Drive to create campaigns
|
||||
<a href="/drive/authorize" class="btn btn-primary ml-3">
|
||||
Connect Drive
|
||||
</a>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="mb-3 d-flex justify-content-between">
|
||||
<a href="/<?php echo $_SESSION['role']; ?>/campaign/add" class="btn btn-success">
|
||||
Add Campaign
|
||||
</a>
|
||||
|
||||
<form action="/drive/disconnect" method="post" class="d-inline">
|
||||
<button type="submit" class="btn btn-warning">
|
||||
Disconnect Drive
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="row mt-2 mb-2 d-none">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<form action="?" method="GET">
|
||||
<div class="input-group">
|
||||
<input type="date" name="date" class="form-control" placeholder="" value="<?php echo $data['date']; ?>">
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-primary" type="submit">Search</button>
|
||||
</span>
|
||||
</div><!-- /input-group -->
|
||||
</form>
|
||||
</div><!-- /.col-md-6 -->
|
||||
</div><!-- /.row -->
|
||||
<!-- Table Responsive Wrapper -->
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover dark-header">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Date</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($data['data'] as $campaign): ?>
|
||||
<tr>
|
||||
<td><?php echo $campaign->id; ?></td>
|
||||
<td><?php echo htmlspecialchars($campaign->name); ?></td>
|
||||
<td><?php echo $campaign->created_at; ?></td>
|
||||
<td>
|
||||
<a href="/<?php echo $_SESSION['role']; ?>/campaign/view/<?php echo $campaign->id; ?>"
|
||||
class="btn btn-sm btn-primary">View</a>
|
||||
<a href="/<?php echo $_SESSION['role']; ?>/campaign/edit/<?php echo $campaign->id; ?>"
|
||||
class="btn btn-sm btn-info">Edit</a>
|
||||
<a href="/<?php echo $_SESSION['role']; ?>/campaign/delete/<?php echo $campaign->id; ?>"
|
||||
class="btn btn-sm btn-danger"
|
||||
onclick="return confirm('Are you sure you want to delete this campaign?')">Delete</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<?php
|
||||
// Retrieve parameters
|
||||
$total = $data['total'];
|
||||
$currentPage = $data['page'];
|
||||
$perPage = 10;
|
||||
|
||||
// Calculate the number of pages
|
||||
$totalPages = ceil($total / $perPage);
|
||||
|
||||
// Define a range of pages to show at any given time
|
||||
$range = 2; // This can be adjusted as needed
|
||||
|
||||
$startPage = ($currentPage - $range) > 0 ? ($currentPage - $range) : 1;
|
||||
$endPage = ($currentPage + $range) < $totalPages ? ($currentPage + $range) : $totalPages;
|
||||
|
||||
?>
|
||||
<!-- Pagination -->
|
||||
<nav aria-label="Page navigation">
|
||||
<ul class="pagination">
|
||||
<li class="ml-2">
|
||||
<a href="?page=1" aria-label="Previous">
|
||||
<span aria-hidden="true">««</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="ml-2">
|
||||
<a href="?page=<?= ($currentPage - 1) > 0 ? $currentPage - 1 : 1 ?>" aria-label="Previous">
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
<?php
|
||||
for ($i = $startPage; $i <= $endPage; $i++) {
|
||||
echo '<li class="ml-2' . ($currentPage == $i ? ' active' : '') . '"><a href="?page=' . $i . '">' . $i . '</a></li>';
|
||||
}
|
||||
?>
|
||||
<li class="ml-2">
|
||||
<a href="?page=<?= ($currentPage + 1) < $totalPages ? $currentPage + 1 : $totalPages ?>" aria-label="Next">
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="ml-2">
|
||||
<a href="?page=<?= $totalPages ?>" aria-label="Next">
|
||||
<span aria-hidden="true">»»</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
function send(element, url, data) {
|
||||
|
||||
// Get the selected value (Yes or No)
|
||||
var projectId = $(element).attr("data-id");
|
||||
var selectedValue = $(element).val();
|
||||
console.log(projectId)
|
||||
console.log(selectedValue)
|
||||
if (selectedValue == "On") {
|
||||
var values = "Off"
|
||||
}
|
||||
if (selectedValue == "Off") {
|
||||
var values = "On"
|
||||
}
|
||||
// Make an AJAX request to update the database
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/alert-toggle', // Replace with the actual path to your update script
|
||||
data: {
|
||||
projectId: projectId,
|
||||
selectedValue: values
|
||||
},
|
||||
success: function(response) {
|
||||
// Handle the response from the server (if needed)
|
||||
console.log(response);
|
||||
window.location.reload()
|
||||
},
|
||||
error: function(error) {
|
||||
console.error('Error updating data:', error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
</script>
|
||||
File diff suppressed because it is too large
Load Diff
+1276
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,50 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login Page</title>
|
||||
<!-- Bootstrap 4.3 CSS -->
|
||||
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body class="bg-light">
|
||||
|
||||
<div class="container">
|
||||
<div class="row justify-content-center align-items-center vh-100">
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title text-center">Login</h5>
|
||||
<?php if (isset($error) && $error) : ?>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
Invalid Credentials!
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<form action="/client/login" method="post">
|
||||
<div class="form-group">
|
||||
<label for="email">Email address</label>
|
||||
<input type="email" class="form-control" id="email" name="email" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<input type="password" class="form-control" id="password" name="password" required>
|
||||
</div>
|
||||
<div class="form-group text-center">
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap 4.3 JS and jQuery -->
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,93 @@
|
||||
<div class="container mt-5">
|
||||
<h2 class="text-center">Profile</h2>
|
||||
<?php if (isset($error) && $error): ?>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
Please fill in all required fields!
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<form action="/client/profile/edit/<?php echo $data['model']->id?>" method="POST" class="mt-4" id="profileForm">
|
||||
<div class="form-group">
|
||||
<label for="email">Email</label>
|
||||
<input type="email" name="email" id="email" class="form-control" required
|
||||
value="<?php echo isset($data['model']) ? $data['model']->email : ''?>"
|
||||
pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$">
|
||||
<div class="invalid-feedback">Please enter a valid email address</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<input type="password" name="password" id="password" class="form-control" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="confirm_password">Confirm Password</label>
|
||||
<input type="password" name="confirm_password" id="confirm_password" class="form-control" />
|
||||
<div class="invalid-feedback">Passwords do not match!</div>
|
||||
</div>
|
||||
|
||||
<!-- <div class="form-group">
|
||||
<label for="status">Status</label>
|
||||
<select name="status" class="form-control">
|
||||
<option value="active" <?php echo $data['model']['status'] == 'active' ? 'selected' : ''?>>Active</option>
|
||||
<option value="inactive" <?php echo $data['model']['status'] == 'inactive' ? 'selected' : ''?>>Inactive</option>
|
||||
</select>
|
||||
</div> -->
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Email validation function
|
||||
function isValidEmail(email) {
|
||||
const emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
||||
return emailRegex.test(email);
|
||||
}
|
||||
|
||||
// Form submission validation
|
||||
document.getElementById('profileForm').addEventListener('submit', function(e) {
|
||||
const password = document.getElementById('password').value;
|
||||
const confirmPassword = document.getElementById('confirm_password').value;
|
||||
const confirmPasswordInput = document.getElementById('confirm_password');
|
||||
const emailInput = document.getElementById('email');
|
||||
let isValid = true;
|
||||
|
||||
// Password validation
|
||||
if (password && (password !== confirmPassword)) {
|
||||
confirmPasswordInput.classList.add('is-invalid');
|
||||
isValid = false;
|
||||
} else {
|
||||
confirmPasswordInput.classList.remove('is-invalid');
|
||||
}
|
||||
|
||||
// Email validation
|
||||
if (!isValidEmail(emailInput.value)) {
|
||||
emailInput.classList.add('is-invalid');
|
||||
isValid = false;
|
||||
} else {
|
||||
emailInput.classList.remove('is-invalid');
|
||||
}
|
||||
|
||||
if (!isValid) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
// Real-time password validation
|
||||
document.getElementById('confirm_password').addEventListener('input', function() {
|
||||
const password = document.getElementById('password').value;
|
||||
const confirmPassword = this.value;
|
||||
|
||||
if (password && (password !== confirmPassword)) {
|
||||
this.classList.add('is-invalid');
|
||||
} else {
|
||||
this.classList.remove('is-invalid');
|
||||
}
|
||||
});
|
||||
|
||||
// Real-time email validation
|
||||
document.getElementById('email').addEventListener('input', function() {
|
||||
if (!isValidEmail(this.value)) {
|
||||
this.classList.add('is-invalid');
|
||||
} else {
|
||||
this.classList.remove('is-invalid');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
+1021
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
class Route
|
||||
{
|
||||
|
||||
private static $routes = [];
|
||||
private static $pathNotFound = null;
|
||||
private static $methodNotAllowed = null;
|
||||
|
||||
public static function add($expression, $function, $method = 'get')
|
||||
{
|
||||
array_push(self::$routes, [
|
||||
'expression' => $expression,
|
||||
'function' => $function,
|
||||
'method' => $method
|
||||
]);
|
||||
}
|
||||
|
||||
public static function pathNotFound($function)
|
||||
{
|
||||
self::$pathNotFound = $function;
|
||||
}
|
||||
|
||||
public static function methodNotAllowed($function)
|
||||
{
|
||||
self::$methodNotAllowed = $function;
|
||||
}
|
||||
|
||||
public static function run($basepath = '/')
|
||||
{
|
||||
|
||||
// Parse current url
|
||||
$parsed_url = parse_url($_SERVER['REQUEST_URI']); //Parse Uri
|
||||
if (isset($parsed_url['path']))
|
||||
{
|
||||
$path = $parsed_url['path'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$path = '/';
|
||||
}
|
||||
|
||||
// Get current request method
|
||||
$method = $_SERVER['REQUEST_METHOD'];
|
||||
|
||||
$path_match_found = false;
|
||||
|
||||
$route_match_found = false;
|
||||
|
||||
foreach (self::$routes as $route)
|
||||
{
|
||||
|
||||
// If the method matches check the path
|
||||
// Add basepath to matching string
|
||||
if ($basepath != '' && $basepath != '/')
|
||||
{
|
||||
$route['expression'] = '(' . $basepath . ')' . $route['expression'];
|
||||
}
|
||||
|
||||
// Add 'find string start' automatically
|
||||
$route['expression'] = '^' . $route['expression'];
|
||||
|
||||
// Add 'find string end' automatically
|
||||
$route['expression'] = $route['expression'] . '$';
|
||||
|
||||
// echo $route['expression'].'<br/>';
|
||||
// Check path match
|
||||
if (preg_match('#' . $route['expression'] . '#', $path, $matches))
|
||||
{
|
||||
|
||||
$path_match_found = true;
|
||||
|
||||
// Check method match
|
||||
if (strtolower($method) == strtolower($route['method']))
|
||||
{
|
||||
|
||||
array_shift($matches); // Always remove first element. This contains the whole string
|
||||
if ($basepath != '' && $basepath != '/')
|
||||
{
|
||||
array_shift($matches); // Remove basepath
|
||||
|
||||
}
|
||||
|
||||
call_user_func_array($route['function'], $matches);
|
||||
|
||||
$route_match_found = true;
|
||||
|
||||
// Do not check other routes
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No matching route was found
|
||||
if (!$route_match_found)
|
||||
{
|
||||
|
||||
// But a matching path exists
|
||||
if ($path_match_found)
|
||||
{
|
||||
header("HTTP/1.0 405 Method Not Allowed");
|
||||
if (self::$methodNotAllowed)
|
||||
{
|
||||
call_user_func_array(self::$methodNotAllowed, Array(
|
||||
$path,
|
||||
$method
|
||||
));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
header("HTTP/1.0 404 Not Found");
|
||||
if (self::$pathNotFound)
|
||||
{
|
||||
call_user_func_array(self::$pathNotFound, Array(
|
||||
$path
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+605
@@ -0,0 +1,605 @@
|
||||
|
||||
<?php
|
||||
|
||||
include_once 'core.php';
|
||||
include_once 'config.php';
|
||||
include_once 'license-model.php';
|
||||
include_once 'user-model.php';
|
||||
include_once 'calendar-model.php';
|
||||
include_once 'project-model.php';
|
||||
include_once 'accesslog-model.php';
|
||||
// Instantiate ProjectModel
|
||||
$projectModel = new ProjectModel();
|
||||
|
||||
// Retrieve all projects
|
||||
$projects = $projectModel->get_all();
|
||||
|
||||
// Log the start of the script
|
||||
error_log('Cron job started: ' . date('Y-m-d H:i:s'));
|
||||
// Function to convert weeks or months to days
|
||||
function convertToDays($value, $unit)
|
||||
{
|
||||
switch ($unit) {
|
||||
case 'weeks':
|
||||
return $value * 7;
|
||||
case 'months':
|
||||
// Assuming a month is considered as 30 days for simplicity
|
||||
return $value * 30;
|
||||
case 'hours':
|
||||
return 0; // Set to 0 days if the unit is hours
|
||||
default:
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
function extractRanges($slotStart, $slotEnd, $serviceStart, $serviceEnd, $interval, $date)
|
||||
{
|
||||
// Convert time strings to DateTime objects for easier comparison
|
||||
$slotStartTime = new DateTime($date . ' ' . $slotStart);
|
||||
$slotEndTime = new DateTime($date . ' ' . $slotEnd);
|
||||
$serviceStartTime = new DateTime($date . ' ' . $serviceStart);
|
||||
$serviceEndTime = new DateTime($date . ' ' . $serviceEnd);
|
||||
|
||||
if ($slotEndTime < $slotStartTime) {
|
||||
$slotEndTime->modify('+1 day');
|
||||
}
|
||||
if ($serviceEndTime < $serviceStartTime) {
|
||||
$serviceEndTime->modify('+1 day');
|
||||
}
|
||||
|
||||
// Check if service time falls within the slot range
|
||||
if ($serviceStartTime >= $slotStartTime && $serviceStartTime < $slotEndTime) {
|
||||
// if ($serviceStartTime >= $slotStartTime && $serviceEndTime <= $slotEndTime) {
|
||||
$ranges = array();
|
||||
|
||||
// Calculate ranges based on the interval
|
||||
$currentRangeStart = $serviceStartTime;
|
||||
if ($currentRangeStart < $slotEndTime) {
|
||||
array_push($ranges, $currentRangeStart->format('H:i:s'));
|
||||
}
|
||||
while ($currentRangeStart < $serviceEndTime) {
|
||||
$currentRangeEnd = clone $currentRangeStart;
|
||||
$currentRangeEnd->add(new DateInterval("PT" . $interval . "M"));
|
||||
|
||||
// Check if the calculated range end is within the slot range
|
||||
if ($currentRangeEnd < $slotEndTime) {
|
||||
// array_push(
|
||||
// $ranges,
|
||||
// $currentRangeStart->format('H:i:s')
|
||||
// );
|
||||
array_push(
|
||||
$ranges,
|
||||
$currentRangeEnd->format('H:i:s')
|
||||
);
|
||||
} else {
|
||||
// Add the end time within the slot range
|
||||
// array_push($ranges, $slotEndTime->format('H:i:s'));
|
||||
// Break the loop if the range end exceeds the slot end time
|
||||
break;
|
||||
}
|
||||
|
||||
$currentRangeStart = $currentRangeEnd;
|
||||
}
|
||||
|
||||
return $ranges;
|
||||
} elseif ($serviceStartTime < $slotStartTime && $serviceEndTime <= $slotEndTime) {
|
||||
// if ($serviceStartTime >= $slotStartTime && $serviceEndTime <= $slotEndTime) {
|
||||
$ranges = array();
|
||||
|
||||
// Calculate ranges based on the interval
|
||||
$currentRangeStart = $slotStartTime;
|
||||
if ($currentRangeStart < $serviceEndTime) {
|
||||
array_push($ranges, $currentRangeStart->format('H:i:s'));
|
||||
}
|
||||
// array_push($ranges, $currentRangeStart->format('H:i:s'));
|
||||
while ($currentRangeStart < $serviceEndTime) {
|
||||
$currentRangeEnd = clone $currentRangeStart;
|
||||
$currentRangeEnd->add(new DateInterval("PT" . $interval . "M"));
|
||||
|
||||
// Check if the calculated range end is within the slot range
|
||||
if ($currentRangeEnd < $slotEndTime) {
|
||||
// array_push(
|
||||
// $ranges,
|
||||
// $currentRangeStart->format('H:i:s')
|
||||
// );
|
||||
array_push(
|
||||
$ranges,
|
||||
$currentRangeEnd->format('H:i:s')
|
||||
);
|
||||
} else {
|
||||
// Add the end time within the slot range
|
||||
// array_push($ranges, $slotEndTime->format('H:i:s'));
|
||||
// Break the loop if the range end exceeds the slot end time
|
||||
break;
|
||||
}
|
||||
|
||||
$currentRangeStart = $currentRangeEnd;
|
||||
}
|
||||
|
||||
return $ranges;
|
||||
} elseif ($serviceStartTime < $slotStartTime && $serviceEndTime > $slotEndTime) {
|
||||
// if ($serviceStartTime >= $slotStartTime && $serviceEndTime <= $slotEndTime) {
|
||||
$ranges = array();
|
||||
|
||||
// Calculate ranges based on the interval
|
||||
$currentRangeStart = $slotStartTime;
|
||||
if ($currentRangeStart < $serviceEndTime) {
|
||||
array_push($ranges, $currentRangeStart->format('H:i:s'));
|
||||
}
|
||||
// array_push($ranges, $currentRangeStart->format('H:i:s'));
|
||||
while ($currentRangeStart < $serviceEndTime) {
|
||||
$currentRangeEnd = clone $currentRangeStart;
|
||||
$currentRangeEnd->add(new DateInterval("PT" . $interval . "M"));
|
||||
|
||||
// Check if the calculated range end is within the slot range
|
||||
if ($currentRangeEnd < $slotEndTime) {
|
||||
// array_push(
|
||||
// $ranges,
|
||||
// $currentRangeStart->format('H:i:s')
|
||||
// );
|
||||
array_push(
|
||||
$ranges,
|
||||
$currentRangeEnd->format('H:i:s')
|
||||
);
|
||||
} else {
|
||||
// Add the end time within the slot range
|
||||
// array_push($ranges, $slotEndTime->format('H:i:s'));
|
||||
// Break the loop if the range end exceeds the slot end time
|
||||
break;
|
||||
}
|
||||
|
||||
$currentRangeStart = $currentRangeEnd;
|
||||
}
|
||||
|
||||
return $ranges;
|
||||
} elseif ($serviceStartTime >= $slotStartTime && $serviceEndTime <= $slotEndTime) {
|
||||
$ranges = array();
|
||||
|
||||
// Calculate ranges based on the interval
|
||||
$currentRangeStart = $serviceStartTime;
|
||||
if ($currentRangeStart < $serviceEndTime) {
|
||||
array_push($ranges, $currentRangeStart->format('H:i:s'));
|
||||
}
|
||||
// array_push($ranges, $currentRangeStart->format('H:i:s'));
|
||||
while ($currentRangeStart <= $serviceEndTime) {
|
||||
$currentRangeEnd = clone $currentRangeStart;
|
||||
$currentRangeEnd->add(new DateInterval("PT" . $interval . "M"));
|
||||
|
||||
// Check if the calculated range end is within the slot range
|
||||
if ($currentRangeEnd <= $slotEndTime) {
|
||||
// array_push(
|
||||
// $ranges,
|
||||
// $currentRangeStart->format('H:i:s')
|
||||
// );
|
||||
array_push(
|
||||
$ranges,
|
||||
$currentRangeEnd->format('H:i:s')
|
||||
);
|
||||
} else {
|
||||
// Add the end time within the slot range
|
||||
// array_push($ranges, $slotEndTime->format('H:i:s'));
|
||||
// Break the loop if the range end exceeds the slot end time
|
||||
break;
|
||||
}
|
||||
|
||||
$currentRangeStart = $currentRangeEnd;
|
||||
}
|
||||
|
||||
return $ranges;
|
||||
} else {
|
||||
// Service time is outside the slot range
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function calculateTotalPoints($daysToCheck, $slot, $service, $eventTiming, $schedule, $project, $ser_id)
|
||||
{
|
||||
|
||||
|
||||
|
||||
// Convert allowBookingFor to days if the unit is months or weeks
|
||||
if ($schedule['allowBookingForUnit'] === 'months' || $schedule['allowBookingForUnit'] === 'weeks') {
|
||||
$schedule['allowBookingFor'] = convertToDays($schedule['allowBookingFor'], $schedule['allowBookingForUnit']);
|
||||
$schedule['allowBookingForUnit'] = 'days';
|
||||
}
|
||||
if ($schedule['allowBookingAfterUnit'] === 'months' || $schedule['allowBookingAfterUnit'] === 'weeks' || $schedule['allowBookingAfterUnit'] === 'hours') {
|
||||
$schedule['allowBookingAfter'] = convertToDays($schedule['allowBookingAfter'], $schedule['allowBookingAfterUnit']);
|
||||
$schedule['allowBookingAfterUnit'] = 'days';
|
||||
}
|
||||
$currentDate = new DateTime();
|
||||
$currentDayIndex = $currentDate->format('w'); // 0 for Sunday, 1 for Monday, ..., 6 for Saturday
|
||||
|
||||
// Get the next two days to check
|
||||
// $daysToCheckIndices = [];
|
||||
// for ($i = 0; $i < $daysToCheck; $i++) {
|
||||
// $nextDayIndex = ($currentDayIndex + $i + 1) % 7;
|
||||
// $daysToCheckIndices[] = $nextDayIndex;
|
||||
// }
|
||||
|
||||
$daysToCheckData = [];
|
||||
if (empty($schedule['allowBookingFor'])) {
|
||||
$counter = $daysToCheck;
|
||||
} else {
|
||||
$counter = (int)$schedule['allowBookingFor'];
|
||||
}
|
||||
if (empty($schedule['allowBookingAfter'])) {
|
||||
$skip = 0;
|
||||
} else {
|
||||
$skip = (int)$schedule['allowBookingAfter'];
|
||||
}
|
||||
// error_log("counter" . json_encode($counter));
|
||||
// error_log("skip" . json_encode($skip));
|
||||
|
||||
for ($i = $skip; $i < $counter; $i++) {
|
||||
$nextDayIndex = ($currentDayIndex + $i) % 7;
|
||||
$nextDate = clone $currentDate;
|
||||
$nextDate->modify("+$i day");
|
||||
|
||||
$daysToCheckData[] = [
|
||||
'index' => $nextDayIndex,
|
||||
'date' => $nextDate->format('Y-m-d'),
|
||||
];
|
||||
}
|
||||
// error_log("daysToCheckIndices" . json_encode($daysToCheckIndices));
|
||||
// error_log("daysToCheckIndices" . json_encode($daysToCheckData));
|
||||
|
||||
$totalPoints = 0;
|
||||
|
||||
// Loop through the selected days and calculate total points
|
||||
$dayNames = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
|
||||
// foreach ($daysToCheckIndices as $dayIndex) {
|
||||
foreach ($daysToCheckData as $dayData) {
|
||||
// $dayName = $dayNames[$dayIndex];
|
||||
// error_log("day" . $dayName);
|
||||
|
||||
|
||||
$dayIndex = $dayData['index'];
|
||||
$dayName = $dayNames[$dayIndex];
|
||||
$date = $dayData['date'];
|
||||
|
||||
error_log("day: $dayName, date: $date");
|
||||
|
||||
if (isset($slot->$dayName)) {
|
||||
if (!array_key_exists($dayName, $service)) {
|
||||
continue; // Day not found in the service schedule
|
||||
}
|
||||
|
||||
|
||||
foreach ($service[$dayName] as $ser) {
|
||||
|
||||
$serviceStart = strtotime(sprintf("%02d:%02d", $ser["openHour"], $ser["openMinute"]));
|
||||
$serviceEnd = strtotime(sprintf("%02d:%02d", $ser["closeHour"], $ser["closeMinute"]));
|
||||
|
||||
// Comparing service hours with slot hours
|
||||
// if ($serviceStart >= $slotStart && $serviceEnd <= $slotEnd) {
|
||||
// error_log("id" . $project->id);
|
||||
// error_log("slotStart" . date("H:i:s", $slotStart));
|
||||
|
||||
$start = strtotime($dayData['date'] . ' ' . sprintf("%02d:%02d", $ser["openHour"], $ser["openMinute"]));
|
||||
$end = strtotime($dayData['date'] . ' ' . sprintf("%02d:%02d", $ser["closeHour"], $ser["closeMinute"]));
|
||||
$start = $start * 1000;
|
||||
$end = $end * 1000;
|
||||
$apikey2 = $project->location;
|
||||
$curl = curl_init();
|
||||
|
||||
curl_setopt_array($curl, [
|
||||
CURLOPT_URL => "https://rest.gohighlevel.com/v1/appointments/slots?calendarId=$ser_id&startDate=$start&endDate=$end",
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_ENCODING => "",
|
||||
CURLOPT_MAXREDIRS => 10,
|
||||
CURLOPT_TIMEOUT => 30,
|
||||
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||
CURLOPT_CUSTOMREQUEST => "GET",
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"Accept: application/json",
|
||||
"Authorization: Bearer " . $apikey2,
|
||||
"Version: 2021-04-15"
|
||||
],
|
||||
]);
|
||||
|
||||
$response = curl_exec($curl);
|
||||
$err = curl_error($curl);
|
||||
|
||||
|
||||
curl_close($curl);
|
||||
|
||||
if ($err) {
|
||||
echo "cURL Error #:" . $err;
|
||||
} else {
|
||||
|
||||
// error_log($response);
|
||||
$data = json_decode($response, true);
|
||||
$stepSize = $eventTiming["slotInterval"];
|
||||
error_log("stepSize" . json_encode($stepSize));
|
||||
|
||||
$rslt = [];
|
||||
if (!empty($data[$dayData['date']]["slots"])) {
|
||||
|
||||
foreach ($data[$dayData['date']]["slots"] as $time) {
|
||||
$dateTime = new DateTime($time);
|
||||
// while ($dateTime <= new DateTime($data[$dayData['date']]["slots"][1])) {
|
||||
$rslt[] = $dateTime->format('H:i:s');
|
||||
// $dateTime->modify("+$stepSize minutes");
|
||||
// }
|
||||
}
|
||||
foreach ($slot->$dayName as $timeSlot) {
|
||||
|
||||
// Convert the day to a DateTime object for comparison
|
||||
// $currentDay = new DateTime($dayName);
|
||||
|
||||
// Check if the day is after or equal to today.
|
||||
// if ($currentDay > $currentDate) {
|
||||
|
||||
|
||||
// error_log("currentDay" . $currentDay->format('Y-m-d H:i:s'));
|
||||
|
||||
// foreach ($timeSlots as $timeSlot) {
|
||||
$slotStart = strtotime($timeSlot->from);
|
||||
$slotEnd = strtotime($timeSlot->to);
|
||||
|
||||
$range = extractRanges(date("H:i:s", $slotStart), date("H:i:s", $slotEnd), date("H:i:s", $serviceStart), date("H:i:s", $serviceEnd), $stepSize, $dayData['date']);
|
||||
|
||||
// $intervals = generateTimeIntervals(date("H:i:s", $serviceStart), date("H:i:s", $serviceEnd), $eventTiming["slotInterval"], $dayData['date']);
|
||||
// $intervals2 = generateTimeIntervals(date("H:i:s", $slotStart), date("H:i:s", $slotEnd), $eventTiming["slotInterval"], $dayData['date']);
|
||||
|
||||
error_log("slotStart " . date("H:i:s", $slotStart));
|
||||
|
||||
error_log("slotEnd " . date("H:i:s", $slotEnd));
|
||||
|
||||
error_log("serviceStart " . date("H:i:s", $serviceStart));
|
||||
error_log("serviceEnd " . date("H:i:s", $serviceEnd));
|
||||
// error_log("intervals " . json_encode($intervals));
|
||||
// error_log("intervals2 " . json_encode($intervals2));
|
||||
// error_log("slots " . json_encode($rslt));
|
||||
error_log("range " . json_encode($range));
|
||||
|
||||
// $intersections = array_intersect($intervals, $intervals2);
|
||||
if (!$range) {
|
||||
continue;
|
||||
}
|
||||
$intersection = array_intersect($rslt, $range);
|
||||
if (empty($intersection)) {
|
||||
continue;
|
||||
}
|
||||
// error_log("intersections " . json_encode($intersections));
|
||||
error_log("intersection " . json_encode($intersection));
|
||||
|
||||
foreach ($intersection as $intersect) {
|
||||
$totalPoints += (int)$timeSlot->point;
|
||||
}
|
||||
}
|
||||
error_log($totalPoints);
|
||||
}
|
||||
// error_log("start " . $start * 1000);
|
||||
// error_log("slotEnd" . date("Y-m-d H:i:s", $start));
|
||||
// error_log("end " . $end * 1000);
|
||||
// error_log("slotStart " . $slotStart * 1000);
|
||||
// // error_log("slotEnd" . date("H:i:s", $slotEnd));
|
||||
// error_log("slotEnd " . $slotEnd * 1000);
|
||||
// // error_log("serviceStart" . date("H:i:s", $serviceStart));
|
||||
// error_log("serviceStart " . $serviceStart * 1000);
|
||||
// // error_log("serviceEnd" . date("H:i:s", $serviceEnd));
|
||||
// error_log("serviceEnd " . $serviceEnd * 1000);
|
||||
// foreach ($intersections as $intersection) {
|
||||
// $totalPoints += (int)$timeSlot->point;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
// }
|
||||
// }
|
||||
|
||||
// $totalPoints += intval($timeSlot->point) ?: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $totalPoints;
|
||||
}
|
||||
|
||||
function generateTimeIntervals($startTime, $endTime, $intervalMinutes, $date)
|
||||
{
|
||||
// $start = new DateTime($startTime);
|
||||
// $end = new DateTime($endTime);
|
||||
// $start = $date . ' ' . date("H:i:s", $startTime);
|
||||
// $end = $date . ' ' . date("H:i:s", $endTime);
|
||||
$start = new DateTime($date . ' ' . $startTime);
|
||||
$end = new DateTime($date . ' ' . $endTime);
|
||||
|
||||
// error_log("start " . $start);
|
||||
// error_log("end " . $end);
|
||||
|
||||
if ($end < $start) {
|
||||
$end->modify('+1 day');
|
||||
}
|
||||
$intervalObj = new DateInterval("PT{$intervalMinutes}M"); // PT stands for 'Period Time'
|
||||
// Adjust end time to include it in the intervals
|
||||
// $end->add($intervalObj);
|
||||
$period = new DatePeriod($start, $intervalObj, $end);
|
||||
|
||||
$intervals = array();
|
||||
foreach ($period as $dt) {
|
||||
$intervals[] = $dt->format('H:i:s');
|
||||
}
|
||||
|
||||
return $intervals;
|
||||
}
|
||||
|
||||
function checkServiceInSlot($service, $slot, $project, $eventTiming, $schedule, $ser_id)
|
||||
{
|
||||
$slotpoint = 0;
|
||||
// Get the current date
|
||||
$currentDate = new DateTime('now');
|
||||
error_log("currentDate" . $currentDate->format('Y-m-d H:i:s'));
|
||||
|
||||
|
||||
|
||||
if ($project->alert == 'On') {
|
||||
$slotpoint = calculateTotalPoints((int)$project->days, $slot, $service, $eventTiming, $schedule, $project, $ser_id);
|
||||
|
||||
if ($slotpoint < (int)$project->score_threshold && $slotpoint != 0) {
|
||||
// Retrieve webhook URL and payload
|
||||
$webhookUrl = $project->webhook;
|
||||
// $webhookUrl = "https://hook.eu1.make.com/tcdowbyvjswiw6xnhxu8derehhyczfb4";
|
||||
// $webhookUrl = "https://hook.eu1.make.com/otaauwkih0ojxa3bezs4gdg6dr8w7bpd";
|
||||
if (filter_var($webhookUrl, FILTER_VALIDATE_URL)) {
|
||||
|
||||
// echo json_validate($project->payload);
|
||||
$jsonData = json_decode($project->payload);
|
||||
// echo json_encode($project->payload);
|
||||
// echo gettype($project->payload);
|
||||
// echo gettype($jsonData);
|
||||
// error_log(gettype($jsonData));
|
||||
|
||||
|
||||
// Check if decoding was successful
|
||||
// if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
// echo json_last_error_msg();
|
||||
// error_log('Error decoding JSON for project with ID ' . $project->id);
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// Set up cURL options
|
||||
$curlOptions = [
|
||||
CURLOPT_URL => $webhookUrl,
|
||||
CURLOPT_POST => 1,
|
||||
CURLOPT_POSTFIELDS => json_encode($jsonData), // Send encoded JSON
|
||||
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
];
|
||||
|
||||
// Initialize cURL session
|
||||
$ch = curl_init();
|
||||
|
||||
// Set cURL options
|
||||
curl_setopt_array($ch, $curlOptions);
|
||||
|
||||
// Execute cURL session and get the result
|
||||
$response = curl_exec($ch);
|
||||
|
||||
// Check for cURL errors
|
||||
if (curl_errno($ch)) {
|
||||
error_log('Curl error for project with ID ' . $project->id . ': ' . curl_error($ch));
|
||||
} else {
|
||||
// Log the webhook response
|
||||
$data = [
|
||||
'actual_score' => $slotpoint,
|
||||
];
|
||||
|
||||
|
||||
|
||||
|
||||
// Insert data into the database using LicenseModel
|
||||
$projectModel = new ProjectModel();
|
||||
$projectModel->edit($data, $project->id);
|
||||
error_log('Webhook response for project with ID ' . $project->id . ': ' . $response);
|
||||
}
|
||||
|
||||
// Close cURL session
|
||||
curl_close($ch);
|
||||
} else {
|
||||
error_log('Webhook for project with ID ' . $project->id . ' is not a URL ');
|
||||
}
|
||||
} else {
|
||||
$data = [
|
||||
'actual_score' => $slotpoint,
|
||||
];
|
||||
|
||||
|
||||
|
||||
|
||||
// Insert data into the database using LicenseModel
|
||||
$projectModel = new ProjectModel();
|
||||
$projectModel->edit($data, $project->id);
|
||||
error_log("Point is higher than mininum point set");
|
||||
}
|
||||
} else {
|
||||
// Log that no alert is set for the project
|
||||
error_log('No alert set for project with ID ' . $project->id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// $config = MkdConfig::get_instance()->get_config();
|
||||
// $apikey = $config['gohighlevel_key'];
|
||||
// $cid = $_POST['calendar_id'];
|
||||
// $pid = (int)$_POST['project_id'];
|
||||
|
||||
|
||||
|
||||
// Iterate through projects
|
||||
foreach ($projects as $project) {
|
||||
if (!empty($project->location)) {
|
||||
$apikey = $project->location;
|
||||
|
||||
$curl = curl_init();
|
||||
|
||||
curl_setopt_array($curl, [
|
||||
CURLOPT_URL => "https://rest.gohighlevel.com/v1/calendars/services",
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_ENCODING => "",
|
||||
CURLOPT_MAXREDIRS => 10,
|
||||
CURLOPT_TIMEOUT => 30,
|
||||
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||
CURLOPT_CUSTOMREQUEST => "GET",
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"Accept: application/json",
|
||||
"Authorization: Bearer " . $apikey,
|
||||
"Version: 2021-04-15"
|
||||
],
|
||||
]);
|
||||
|
||||
$response = curl_exec($curl);
|
||||
$err = curl_error($curl);
|
||||
// error_log($response);
|
||||
|
||||
curl_close($curl);
|
||||
|
||||
if ($err) {
|
||||
echo "cURL Error #:" . $err;
|
||||
} else {
|
||||
|
||||
// Check if the project has an alert set to 'Yes'
|
||||
$slots = json_decode($project->slot);
|
||||
|
||||
// error_log($slotpoint);
|
||||
// error_log((int)$project->score_threshold);
|
||||
error_log(' ');
|
||||
// exit;
|
||||
|
||||
|
||||
|
||||
$cid = $project->calendar;
|
||||
// Decode JSON string to PHP array
|
||||
$data = json_decode($response, true);
|
||||
// Search for the service with the specified ID
|
||||
$searchedService = null;
|
||||
foreach ($data['services'] as $service) {
|
||||
if ($service['id'] === $cid) {
|
||||
$searchedService = $service;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Output the result
|
||||
if ($searchedService !== null) {
|
||||
|
||||
// echo json_encode($searchedService["availability"]["officeHours"]);
|
||||
|
||||
|
||||
$slots = json_decode($project->slot);
|
||||
|
||||
// echo json_encode($slots);
|
||||
|
||||
|
||||
|
||||
|
||||
echo checkServiceInSlot($searchedService["availability"]["officeHours"], $slots, $project, $searchedService["availability"]["eventTiming"], $searchedService["availability"]["schedule"], $searchedService["id"]);
|
||||
} else {
|
||||
error_log("Service with ID '" . $cid . "' not found.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error_log('Empty location api key');
|
||||
}
|
||||
// Log the end of the script
|
||||
}
|
||||
error_log('Cron job completed: ' . date('Y-m-d H:i:s'));
|
||||
@@ -0,0 +1,137 @@
|
||||
-- Adminer 4.8.1 MySQL 8.0.39-0ubuntu0.22.04.1 dump
|
||||
|
||||
SET NAMES utf8;
|
||||
SET time_zone = '+00:00';
|
||||
SET foreign_key_checks = 0;
|
||||
SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO';
|
||||
|
||||
DROP TABLE IF EXISTS `accesslog`;
|
||||
CREATE TABLE `accesslog` (
|
||||
`id` int NOT NULL AUTO_INCREMENT,
|
||||
`relationship_num` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL,
|
||||
`ip` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL,
|
||||
`created_at` datetime NOT NULL,
|
||||
`updated_at` varchar(191) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
|
||||
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
DROP TABLE IF EXISTS `calendar`;
|
||||
CREATE TABLE `calendar` (
|
||||
`id` int unsigned NOT NULL AUTO_INCREMENT,
|
||||
`slot` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
|
||||
`days` int unsigned DEFAULT NULL,
|
||||
`calendar` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
|
||||
`created_at` datetime DEFAULT NULL,
|
||||
`updated_at` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `campaign`;
|
||||
CREATE TABLE `campaign` (
|
||||
`id` int NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
|
||||
`file_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
|
||||
`data` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci,
|
||||
`user_id` int unsigned NOT NULL,
|
||||
`created_at` date DEFAULT NULL,
|
||||
`updated_at` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `license`;
|
||||
CREATE TABLE `license` (
|
||||
`id` int NOT NULL AUTO_INCREMENT,
|
||||
`relationship_num` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL,
|
||||
`email` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL,
|
||||
`apikey` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL,
|
||||
`ip` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL,
|
||||
`status` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL,
|
||||
`created_at` date NOT NULL,
|
||||
`updated_at` datetime NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `location`;
|
||||
CREATE TABLE `location` (
|
||||
`id` int unsigned NOT NULL AUTO_INCREMENT,
|
||||
`user_id` int unsigned DEFAULT NULL,
|
||||
`name` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`apikey` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
|
||||
`location_id` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
|
||||
`webhook` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
|
||||
`created_at` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`updated_at` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `project`;
|
||||
CREATE TABLE `project` (
|
||||
`id` int NOT NULL AUTO_INCREMENT,
|
||||
`project_name` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
|
||||
`user_id` int DEFAULT NULL,
|
||||
`slot` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci,
|
||||
`days` int DEFAULT NULL,
|
||||
`alert` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
|
||||
`score_threshold` int DEFAULT NULL,
|
||||
`actual_score` int DEFAULT NULL,
|
||||
`webhook` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
|
||||
`calendar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
|
||||
`created_at` datetime DEFAULT NULL,
|
||||
`updated_at` datetime DEFAULT NULL,
|
||||
`payload` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
|
||||
`location` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci,
|
||||
`access_token` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci,
|
||||
`refresh_token` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci,
|
||||
`token_expiry` int DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `report`;
|
||||
CREATE TABLE `report` (
|
||||
`id` int unsigned NOT NULL AUTO_INCREMENT,
|
||||
`project` varchar(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`location_id` int unsigned DEFAULT NULL,
|
||||
`type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
||||
`report` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
|
||||
`new_lead` int unsigned DEFAULT NULL,
|
||||
`outbound_dial` int unsigned DEFAULT NULL,
|
||||
`pickup` int unsigned DEFAULT NULL,
|
||||
`conversation` int unsigned DEFAULT NULL,
|
||||
`booked_appointment` int unsigned DEFAULT NULL,
|
||||
`callback_request` int unsigned DEFAULT NULL,
|
||||
`created_at` datetime DEFAULT NULL,
|
||||
`status` int DEFAULT '0',
|
||||
`updated_at` datetime DEFAULT NULL,
|
||||
`date` date DEFAULT NULL,
|
||||
`webhook_sent` int DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `location_id` (`location_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `user`;
|
||||
CREATE TABLE `user` (
|
||||
`id` int unsigned NOT NULL AUTO_INCREMENT,
|
||||
`email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
|
||||
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
|
||||
`status` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
|
||||
`role` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT 'admin',
|
||||
`company` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT 'Team Follow Up',
|
||||
`drive_access_token` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci,
|
||||
`drive_refresh_token` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci,
|
||||
`created_at` date DEFAULT NULL,
|
||||
`updated_at` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `email` (`email`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
|
||||
-- 2025-02-04 21:37:09
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
Flight::route('GET /v1/api/images', function()
|
||||
{
|
||||
|
||||
$model = new ImagesModel();
|
||||
|
||||
$type_array = [
|
||||
'id' => 'integer',
|
||||
'url' => 'string',
|
||||
'user_id' => 'integer',
|
||||
'caption' => 'string'
|
||||
];
|
||||
|
||||
$allow_fields = ['id', 'url', 'user_id', 'caption'];
|
||||
$allow_column_fields = ['ID', 'URL', 'User ID', 'Caption'];
|
||||
$format = isset($_GET['format']) ? $_GET['format'] : 'json';
|
||||
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
|
||||
$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
|
||||
$per_page = isset($_GET['size']) ? intval($_GET['size']) : 25;
|
||||
$sort = isset($_GET['sort']) ? $_GET['sort'] : 'id';
|
||||
$direction = isset($_GET['direction']) ? $_GET['direction'] : 'ASC';
|
||||
//TODO: default sort/direction from cms builder
|
||||
|
||||
$query_service = new QueryService();
|
||||
$format_service = new FormatService();
|
||||
$custom_where = $query_service->create_where(
|
||||
$type_array,
|
||||
Flight::request()->query['field'] ?? [],
|
||||
Flight::request()->query['operator'] ?? [],
|
||||
Flight::request()->query['value'] ?? []);
|
||||
|
||||
$result = $model->get_paginated($page, $per_page, $custom_where, $sort, $direction);
|
||||
// $result = $model->get_cursor_paginated($page, $per_page, $custom_where, $sort, $direction, $id);
|
||||
|
||||
if ($result)
|
||||
{
|
||||
$result['code'] = 200;
|
||||
$result['error'] = false;
|
||||
$result['query'] = $custom_where;
|
||||
|
||||
if ($format == 'json')
|
||||
{
|
||||
$result['data'] = $format_service->to_json($result['data'], $model->get_mapping(), $allow_column_fields, $allow_fields);
|
||||
echo json_encode($result);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($format == 'csv')
|
||||
{
|
||||
header('Content-Type: text/csv');
|
||||
header('Content-Disposition: attachment; filename="export.csv"');
|
||||
|
||||
echo implode("\n", $format_service->to_csv($result['list'], $model->get_mapping(), $allow_column_fields), $allow_fields);
|
||||
exit();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
echo json_encode([
|
||||
'code' => 409,
|
||||
'error' => false
|
||||
]);
|
||||
http_response_code(409);
|
||||
exit;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
Flight::route('GET /v1/api/image/get/@id', function($id)
|
||||
{
|
||||
|
||||
$model = new ImagesModel();
|
||||
$result = $model->get($id);
|
||||
$output = [];
|
||||
if ($result)
|
||||
{
|
||||
$allow_fields = ['id', 'url', 'user_id', 'caption'];
|
||||
|
||||
foreach ($result as $key => &$value)
|
||||
{
|
||||
if (in_array($key, $allow_fields))
|
||||
{
|
||||
$output[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'code' => 200,
|
||||
'error' => false,
|
||||
'model' => $output
|
||||
]);
|
||||
http_response_code(200);
|
||||
exit;
|
||||
}
|
||||
else
|
||||
{
|
||||
echo json_encode([
|
||||
'code' => 404,
|
||||
'error' => true
|
||||
]);
|
||||
http_response_code(404);
|
||||
exit;
|
||||
}
|
||||
});
|
||||
Vendored
BIN
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/parsley.js/2.9.2/parsley.min.js" integrity="sha512-eyHL1atYNycXNXZMDndxrDhNAegH2BDWt1TmkXJPoGf1WLlNYt08CSjkqF5lnCRmdm3IrkHid8s2jOUY4NIZVQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
</footer>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/parsley.js/2.9.2/parsley.min.js" integrity="sha512-eyHL1atYNycXNXZMDndxrDhNAegH2BDWt1TmkXJPoGf1WLlNYt08CSjkqF5lnCRmdm3IrkHid8s2jOUY4NIZVQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
</footer>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,170 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title></title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" crossorigin="anonymous">
|
||||
<link href='https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css' rel='stylesheet' />
|
||||
<!-- <link href="/public/assets/css/tabulator_semanticui.min.css" rel="stylesheet"> -->
|
||||
<link href="https://parsleyjs.org/src/parsley.css" rel="stylesheet">
|
||||
<script src="https://code.jquery.com/jquery-2.2.4.min.js" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/stapes/1.0.0/stapes.js" crossorigin="anonymous"></script>
|
||||
|
||||
<link href="https://parsleyjs.org/src/parsley.css" rel="stylesheet">
|
||||
<link href="/style.css" rel="stylesheet">
|
||||
|
||||
<!-- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.10/main.min.css" integrity="sha384-Bn538FnJABF+10ld5ha9hT5IqfDz0nN9gZ+RS4yR1JKGgsUKLB2Np3ucx8Wi6pyy" crossorigin="anonymous"> -->
|
||||
<!-- <script src="https://cdn.jsdelivr.net/npm/@fullcalendar/bootstrap@6.1.10/index.global.min.js"></script> -->
|
||||
|
||||
<script src='https://cdn.jsdelivr.net/npm/fullcalendar/index.global.min.js'></script>
|
||||
|
||||
<!-- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.10/main.min.css" integrity="sha384-Bn538FnJABF+10ld5ha9hT5IqfDz0nN9gZ+RS4yR1JKGgsUKLB2Np3ucx8Wi6pyy" crossorigin="anonymous">
|
||||
<script src="https://code.jquery.com/jquery-3.6.4.min.js" integrity="sha384-o9I/XbYzAOi6aAdG8eEIAIQV7wppF5P8EUGdXq0jH8LVMpG4LAV+OxlUqjKE0W0f" crossorigin="anonymous"></script> -->
|
||||
<!-- <script src="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.10/main.min.js" integrity="sha384-PZ19vJ/QGsewiY3DW3uVgtAw9pxo9+UErhlKn+JzKMuhE+6j0UD54d1CIWc0yXHF" crossorigin="anonymous"></script> -->
|
||||
|
||||
<!-- <script src="https://cdn.jsdelivr.net/npm/@fullcalendar/daygrid@6.1.10/index.global.min.js"></script> -->
|
||||
|
||||
|
||||
|
||||
<style>
|
||||
.time-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.flex-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flex-item {
|
||||
flex: 1;
|
||||
/* margin-right: 10px; */
|
||||
/* Adjust margin as needed */
|
||||
}
|
||||
|
||||
#calendar {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* #calendar-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
} */
|
||||
|
||||
/* Toast styling */
|
||||
.toast-container {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.toast {
|
||||
background: #333;
|
||||
color: white;
|
||||
padding: 15px 25px;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 10px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease-in;
|
||||
}
|
||||
|
||||
.toast.show {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.toast.error {
|
||||
background: #dc3545;
|
||||
}
|
||||
|
||||
.toast.success {
|
||||
background: #28a745;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Add toast container right after body tag -->
|
||||
<div class="toast-container"></div>
|
||||
|
||||
<!-- Add script before the wrapper div -->
|
||||
<script>
|
||||
function showToast(message, type = 'default') {
|
||||
const toastContainer = document.querySelector('.toast-container');
|
||||
const toast = document.createElement('div');
|
||||
toast.className = `toast ${type}`;
|
||||
toast.textContent = message;
|
||||
|
||||
toastContainer.appendChild(toast);
|
||||
|
||||
// Trigger reflow to enable animation
|
||||
toast.offsetHeight;
|
||||
|
||||
// Show toast
|
||||
toast.classList.add('show');
|
||||
|
||||
// Remove after 3 seconds
|
||||
setTimeout(() => {
|
||||
toast.classList.remove('show');
|
||||
setTimeout(() => toast.remove(), 300);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// Check for error and success parameters on page load
|
||||
window.addEventListener('load', function() {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const error = urlParams.get('error');
|
||||
const success = urlParams.get('success');
|
||||
|
||||
if (error) {
|
||||
let message = '';
|
||||
switch(error) {
|
||||
case 'drive_not_connected':
|
||||
message = 'Google Drive is not connected';
|
||||
break;
|
||||
default:
|
||||
message = error.replace(/_/g, ' ');
|
||||
}
|
||||
showToast(message, 'error');
|
||||
}
|
||||
|
||||
if (success) {
|
||||
let message = '';
|
||||
switch(success) {
|
||||
case 'completed':
|
||||
message = 'Operation completed successfully';
|
||||
break;
|
||||
default:
|
||||
message = success.replace(/_/g, ' ');
|
||||
}
|
||||
showToast(message, 'success');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="wrapper">
|
||||
<nav id="sidebar">
|
||||
<div class="sidebar-header">
|
||||
Team Followup
|
||||
</div>
|
||||
<ul class="list-unstyled components">
|
||||
<li><a href="/admin/license" <?php echo ($data['page_title'] == 'License') ? 'class="active"' : ''; ?>> License </a></li>
|
||||
<li><a href="/admin/accesslog" <?php echo ($data['page_title'] == 'Access Log') ? 'class="active"' : ''; ?>> Access log </a></li>
|
||||
<li><a href="/admin/project" <?php echo ($data['page_title'] == 'Project') ? 'class="active"' : ''; ?>> Availability Checker</a></li>
|
||||
<li><a href="/admin/campaign" <?php echo ($data['page_title'] == 'Campaign') ? 'class="active"' : ''; ?>> Campaigns</a></li>
|
||||
<li><a href="/admin/report" <?php echo ($data['page_title'] == 'Report') ? 'class="active"' : '';
|
||||
?>> Report</a></li>
|
||||
<li><a href="/admin/location" <?php echo ($data['page_title'] == 'Location') ? 'class="active"' : '';
|
||||
?>> Locations</a></li>
|
||||
<li><a href="/admin/users" <?php echo ($data['page_title'] == 'Users') ? 'class="active"' : ''; ?>> Users </a></li>
|
||||
<li><a href="/admin/logout"> Logout </a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<div id="content">
|
||||
@@ -0,0 +1,170 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title></title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" crossorigin="anonymous">
|
||||
<link href='https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css' rel='stylesheet' />
|
||||
<!-- <link href="/public/assets/css/tabulator_semanticui.min.css" rel="stylesheet"> -->
|
||||
<link href="https://parsleyjs.org/src/parsley.css" rel="stylesheet">
|
||||
<script src="https://code.jquery.com/jquery-2.2.4.min.js" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/stapes/1.0.0/stapes.js" crossorigin="anonymous"></script>
|
||||
|
||||
<link href="https://parsleyjs.org/src/parsley.css" rel="stylesheet">
|
||||
<link href="/style.css" rel="stylesheet">
|
||||
|
||||
<!-- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.10/main.min.css" integrity="sha384-Bn538FnJABF+10ld5ha9hT5IqfDz0nN9gZ+RS4yR1JKGgsUKLB2Np3ucx8Wi6pyy" crossorigin="anonymous"> -->
|
||||
<!-- <script src="https://cdn.jsdelivr.net/npm/@fullcalendar/bootstrap@6.1.10/index.global.min.js"></script> -->
|
||||
|
||||
<script src='https://cdn.jsdelivr.net/npm/fullcalendar/index.global.min.js'></script>
|
||||
|
||||
<!-- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.10/main.min.css" integrity="sha384-Bn538FnJABF+10ld5ha9hT5IqfDz0nN9gZ+RS4yR1JKGgsUKLB2Np3ucx8Wi6pyy" crossorigin="anonymous">
|
||||
<script src="https://code.jquery.com/jquery-3.6.4.min.js" integrity="sha384-o9I/XbYzAOi6aAdG8eEIAIQV7wppF5P8EUGdXq0jH8LVMpG4LAV+OxlUqjKE0W0f" crossorigin="anonymous"></script> -->
|
||||
<!-- <script src="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.10/main.min.js" integrity="sha384-PZ19vJ/QGsewiY3DW3uVgtAw9pxo9+UErhlKn+JzKMuhE+6j0UD54d1CIWc0yXHF" crossorigin="anonymous"></script> -->
|
||||
|
||||
<!-- <script src="https://cdn.jsdelivr.net/npm/@fullcalendar/daygrid@6.1.10/index.global.min.js"></script> -->
|
||||
|
||||
|
||||
|
||||
<style>
|
||||
.time-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.flex-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flex-item {
|
||||
flex: 1;
|
||||
/* margin-right: 10px; */
|
||||
/* Adjust margin as needed */
|
||||
}
|
||||
|
||||
#calendar {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* #calendar-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
} */
|
||||
|
||||
/* Toast styling */
|
||||
.toast-container {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.toast {
|
||||
background: #333;
|
||||
color: white;
|
||||
padding: 15px 25px;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 10px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease-in;
|
||||
}
|
||||
|
||||
.toast.show {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.toast.error {
|
||||
background: #dc3545;
|
||||
}
|
||||
|
||||
.toast.success {
|
||||
background: #28a745;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Add toast container right after body tag -->
|
||||
<div class="toast-container"></div>
|
||||
|
||||
<!-- Add script before the wrapper div -->
|
||||
<script>
|
||||
function showToast(message, type = 'default') {
|
||||
const toastContainer = document.querySelector('.toast-container');
|
||||
const toast = document.createElement('div');
|
||||
toast.className = `toast ${type}`;
|
||||
toast.textContent = message;
|
||||
|
||||
toastContainer.appendChild(toast);
|
||||
|
||||
// Trigger reflow to enable animation
|
||||
toast.offsetHeight;
|
||||
|
||||
// Show toast
|
||||
toast.classList.add('show');
|
||||
|
||||
// Remove after 3 seconds
|
||||
setTimeout(() => {
|
||||
toast.classList.remove('show');
|
||||
setTimeout(() => toast.remove(), 300);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// Check for error and success parameters on page load
|
||||
window.addEventListener('load', function() {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const error = urlParams.get('error');
|
||||
const success = urlParams.get('success');
|
||||
|
||||
if (error) {
|
||||
let message = '';
|
||||
switch(error) {
|
||||
case 'drive_not_connected':
|
||||
message = 'Google Drive is not connected';
|
||||
break;
|
||||
default:
|
||||
message = error.replace(/_/g, ' ');
|
||||
}
|
||||
showToast(message, 'error');
|
||||
}
|
||||
|
||||
if (success) {
|
||||
let message = '';
|
||||
switch(success) {
|
||||
case 'completed':
|
||||
message = 'Operation completed successfully';
|
||||
break;
|
||||
default:
|
||||
message = success.replace(/_/g, ' ');
|
||||
}
|
||||
showToast(message, 'success');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="wrapper">
|
||||
<nav id="sidebar">
|
||||
<div class="sidebar-header">
|
||||
Team Followup
|
||||
</div>
|
||||
<ul class="list-unstyled components">
|
||||
<li><a href="/client/project" <?php echo ($data['page_title'] == 'Project') ? 'class="active"' : '';
|
||||
?>> Availability Checker</a></li>
|
||||
<li><a href="/client/campaign" <?php echo ($data['page_title'] == 'Campaign') ? 'class="active"' : '';
|
||||
?>> Campaigns</a></li>
|
||||
<li><a href="/client/report" <?php echo ($data['page_title'] == 'Report') ? 'class="active"' : '';
|
||||
?>> Report</a></li>
|
||||
<li><a href="/client/location" <?php echo ($data['page_title'] == 'Location') ? 'class="active"' : '';
|
||||
?>> Locations</a></li>
|
||||
<li><a href="/client/profile" <?php echo ($data['page_title'] == 'Profile') ? 'class="active"' : ''; ?>> Profile </a></li>
|
||||
<li><a href="/client/logout"> Logout </a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<div id="content">
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
include_once 'lib/ghl/oauth2.php';
|
||||
include_once 'config.php';
|
||||
include_once 'project-model.php';
|
||||
|
||||
|
||||
$config = MkdConfig::get_instance()->get_config();
|
||||
|
||||
$oauth = new GHLOAuth2($config);
|
||||
|
||||
class GHLCalendar {
|
||||
private $access_token;
|
||||
private $refresh_token;
|
||||
private $token_expiry;
|
||||
private $oauth;
|
||||
private $project_id;
|
||||
|
||||
public function __construct($project_id, $access_token, $refresh_token) {
|
||||
$this->project_id = $project_id;
|
||||
$this->access_token = $access_token;
|
||||
$this->refresh_token = $refresh_token;
|
||||
$this->oauth = $GLOBALS['oauth'];
|
||||
}
|
||||
|
||||
public function refreshToken() {
|
||||
$result = $this->oauth->refreshToken($this->refresh_token);
|
||||
|
||||
|
||||
if ($result['success']) {
|
||||
$this->access_token = $result['data']['access_token'];
|
||||
$this->refresh_token = $result['data']['refresh_token'] ?? $this->refresh_token;
|
||||
|
||||
$projectModel = new ProjectModel();
|
||||
$projectModel->edit([
|
||||
'access_token' => $this->access_token,
|
||||
'refresh_token' => $this->refresh_token
|
||||
], $this->project_id );
|
||||
|
||||
return [
|
||||
'code' => 200,
|
||||
'message' => 'Access token refreshed successfully',
|
||||
'data' => $result['data']
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'code' => 400,
|
||||
'message' => 'Error: ' . $result['error'] . '. Please authorize',
|
||||
'data' => null
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
public function getFreeSlots($calendar_id, $start_date, $end_date) {
|
||||
|
||||
|
||||
$url = "https://services.leadconnectorhq.com/calendars/$calendar_id/free-slots?startDate=$start_date&endDate=$end_date";
|
||||
$headers = [
|
||||
"Accept: application/json",
|
||||
"Authorization: Bearer " . $this->access_token,
|
||||
"Version: 2021-04-15"
|
||||
];
|
||||
|
||||
// Initialize cURL session
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
|
||||
// Execute cURL request
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); // Get the HTTP response code
|
||||
curl_close($ch);
|
||||
|
||||
// Decode the response
|
||||
$responseData = json_decode($response, true);
|
||||
|
||||
// Prepare the return array
|
||||
return [
|
||||
'code' => $httpCode,
|
||||
'message' => $responseData['message'] ?? 'Success', // Default message if not present
|
||||
'data' => $responseData
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
|
||||
<?php
|
||||
|
||||
class GHLOAuth2 {
|
||||
private $config;
|
||||
private $baseUrl = 'https://services.leadconnectorhq.com';
|
||||
private $authorizeUrl = 'https://marketplace.gohighlevel.com/oauth/chooselocation';
|
||||
private $scopes = ['calendars.readonly', 'calendars/events.readonly'];
|
||||
private $redirectUri = 'https://ghltool.team-followup.com/ghl/callback';
|
||||
private $clientId = 'xxx';
|
||||
private $clientSecret = 'xxx';
|
||||
|
||||
public function __construct(array $config) {
|
||||
if (!isset($config['gohighlevel_client_id']) || !isset($config['gohighlevel_client_secret'])) {
|
||||
throw new Exception('Client ID and Client Secret are required.');
|
||||
}
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function getAuthorizationUrl() {
|
||||
$params = [
|
||||
'response_type' => 'code',
|
||||
'redirect_uri' => $this->config['gohighlevel_redirect_uri'] ?? $this->redirectUri,
|
||||
'client_id' => $this->config['gohighlevel_client_id'] ?? $this->clientId,
|
||||
'scope' => implode(' ', $this->scopes),
|
||||
'user_type' => 'Location'
|
||||
];
|
||||
|
||||
return $this->authorizeUrl . '?' . http_build_query($params);
|
||||
}
|
||||
|
||||
/*
|
||||
Gohighlevel returns the code in the redirect uri ?code=xxxx
|
||||
*/
|
||||
|
||||
public function getAccessToken($code, $redirectUri = null) {
|
||||
$data = [
|
||||
'client_id' => $this->config['gohighlevel_client_id'] ?? $this->clientId,
|
||||
'client_secret' => $this->config['gohighlevel_client_secret'] ?? $this->clientSecret,
|
||||
'grant_type' => 'authorization_code',
|
||||
'code' => $code,
|
||||
'user_type' => 'Location'
|
||||
];
|
||||
|
||||
if ($redirectUri) {
|
||||
$data['redirect_uri'] = $redirectUri;
|
||||
}
|
||||
|
||||
return $this->makeTokenRequest($data);
|
||||
}
|
||||
|
||||
public function refreshToken($refreshToken) {
|
||||
if (empty($refreshToken)) {
|
||||
throw new Exception('Refresh token is required');
|
||||
}
|
||||
|
||||
|
||||
$data = [
|
||||
'client_id' => $this->config['gohighlevel_client_id'] ?? $this->clientId,
|
||||
'client_secret' => $this->config['gohighlevel_client_secret'] ?? $this->clientSecret,
|
||||
'grant_type' => 'refresh_token',
|
||||
'refresh_token' => $refreshToken,
|
||||
'user_type' => 'Location'
|
||||
];
|
||||
|
||||
return $this->makeTokenRequest($data);
|
||||
}
|
||||
|
||||
private function makeTokenRequest($data) {
|
||||
$curl = curl_init();
|
||||
|
||||
$options = [
|
||||
CURLOPT_URL => $this->baseUrl . '/oauth/token',
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_ENCODING => '',
|
||||
CURLOPT_MAXREDIRS => 10,
|
||||
CURLOPT_TIMEOUT => 30,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||
CURLOPT_CUSTOMREQUEST => 'POST',
|
||||
CURLOPT_POSTFIELDS => http_build_query($data),
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'Accept: application/json',
|
||||
'Content-Type: application/x-www-form-urlencoded'
|
||||
],
|
||||
];
|
||||
|
||||
curl_setopt_array($curl, $options);
|
||||
|
||||
$response = curl_exec($curl);
|
||||
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
||||
$error = curl_error($curl);
|
||||
|
||||
curl_close($curl);
|
||||
|
||||
if ($error) {
|
||||
return [
|
||||
'success' => false,
|
||||
'error' => 'Curl error: ' . $error
|
||||
];
|
||||
}
|
||||
|
||||
$responseData = json_decode($response, true);
|
||||
if ($responseData === null) {
|
||||
return [
|
||||
'success' => false,
|
||||
'error' => 'Error decoding JSON response'
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
if ($httpCode !== 200) {
|
||||
return [
|
||||
'success' => false,
|
||||
'error' => $responseData['message'] ?? 'Unknown error occurred',
|
||||
'code' => $httpCode,
|
||||
'raw_response' => $responseData
|
||||
];
|
||||
}
|
||||
|
||||
// Ensure we always return refresh_token if it exists in the response
|
||||
return [
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'access_token' => $responseData['access_token'] ?? null,
|
||||
'refresh_token' => $responseData['refresh_token'] ?? null,
|
||||
'expires_in' => $responseData['expires_in'] ?? null,
|
||||
'locationId' => $responseData['locationId'] ?? null,
|
||||
'companyId' => $responseData['companyId'] ?? null,
|
||||
'userId' => $responseData['userId'] ?? null,
|
||||
'scope' => $responseData['scope'] ?? null,
|
||||
'token_type' => $responseData['token_type'] ?? null
|
||||
],
|
||||
'raw_response' => $responseData
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
<?php
|
||||
|
||||
namespace Lib\Google;
|
||||
|
||||
include_once 'lib/google/oauth2.php';
|
||||
include_once 'config.php';
|
||||
|
||||
class GoogleDrive {
|
||||
private const DRIVE_API_BASE = 'https://www.googleapis.com/drive/v3';
|
||||
private GoogleOAuth2 $oauth;
|
||||
|
||||
// Required scopes for Drive API
|
||||
private const DRIVE_SCOPES = [
|
||||
'https://www.googleapis.com/auth/drive.file'
|
||||
];
|
||||
|
||||
public function __construct(GoogleOAuth2 $oauth) {
|
||||
$this->oauth = $oauth;
|
||||
$this->oauth->addScopes(self::DRIVE_SCOPES);
|
||||
}
|
||||
|
||||
/**
|
||||
* List files and folders in Google Drive
|
||||
* @param string|null $folderId Parent folder ID (null for root)
|
||||
* @param array $options Additional query parameters
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function listFiles(?string $folderId = null, array $options = []): array {
|
||||
try {
|
||||
$query = [];
|
||||
|
||||
if ($folderId) {
|
||||
$query[] = "'{$folderId}' in parents";
|
||||
} else {
|
||||
$query[] = "'root' in parents";
|
||||
}
|
||||
|
||||
// Add trashed=false to exclude deleted files
|
||||
$query[] = "trashed=false";
|
||||
|
||||
// Handle mime type filtering through options
|
||||
if (isset($options['mimeTypes'])) {
|
||||
$mimeTypes = array_map(function($type) {
|
||||
return "mimeType='" . $type . "'";
|
||||
}, $options['mimeTypes']);
|
||||
$query[] = '(' . implode(' or ', $mimeTypes) . ')';
|
||||
unset($options['mimeTypes']); // Remove from options to avoid duplication in params
|
||||
}
|
||||
|
||||
$params = array_merge([
|
||||
'fields' => 'files(id, name, mimeType, size, modifiedTime, parents)',
|
||||
'orderBy' => 'folder,name', // Sort folders first, then by name
|
||||
'q' => implode(' and ', $query)
|
||||
], $options);
|
||||
|
||||
$response = $this->oauth->makeAuthenticatedRequest(
|
||||
self::DRIVE_API_BASE . '/files?' . http_build_query($params)
|
||||
);
|
||||
|
||||
return $response;
|
||||
} catch (\Exception $e) {
|
||||
if ($this->isAuthenticationError($e)) {
|
||||
throw new \Exception('Authentication required. Use getAuthorizationUrl() to start the auth flow.', 401);
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Download a file by its ID
|
||||
* @param string $fileId
|
||||
* @param string|null $mimeType Optional mime type for export (useful for Google Docs)
|
||||
* @return string File content
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function downloadFile(string $fileId, ?string $mimeType = null): string {
|
||||
try {
|
||||
// First, get file metadata to determine if it's a Google Workspace file
|
||||
$file = $this->getFile($fileId);
|
||||
$isGoogleWorkspaceFile = substr($file['mimeType'], 0, 23) === 'application/vnd.google-apps';
|
||||
|
||||
// For Google Sheets, always use export
|
||||
if ($file['mimeType'] === 'application/vnd.google-apps.spreadsheet') {
|
||||
$endpoint = self::DRIVE_API_BASE . '/files/' . $fileId . '/export';
|
||||
$endpoint .= '?mimeType=' . urlencode('text/csv');
|
||||
|
||||
return $this->oauth->makeAuthenticatedRequest(
|
||||
$endpoint,
|
||||
'GET',
|
||||
['Accept: text/csv'],
|
||||
null,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
// For other files, use regular download
|
||||
$endpoint = self::DRIVE_API_BASE . '/files/' . $fileId;
|
||||
if ($isGoogleWorkspaceFile && $mimeType) {
|
||||
$endpoint .= '/export';
|
||||
$endpoint .= '?mimeType=' . urlencode($mimeType);
|
||||
} else {
|
||||
$endpoint .= '?alt=media';
|
||||
}
|
||||
|
||||
return $this->oauth->makeAuthenticatedRequest(
|
||||
$endpoint,
|
||||
'GET',
|
||||
['Accept: */*'],
|
||||
null,
|
||||
true
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
if ($this->isAuthenticationError($e)) {
|
||||
throw new \Exception('Authentication required. Use getAuthorizationUrl() to start the auth flow.', 401);
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file metadata
|
||||
* @param string $fileId
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getFile(string $fileId): array {
|
||||
try {
|
||||
return $this->oauth->makeAuthenticatedRequest(
|
||||
self::DRIVE_API_BASE . '/files/' . $fileId . '?fields=id,name,mimeType,size,modifiedTime,parents'
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
if ($this->isAuthenticationError($e)) {
|
||||
throw new \Exception('Authentication required. Use getAuthorizationUrl() to start the auth flow.', 401);
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get authorization URL to start OAuth flow
|
||||
* @param array $additionalParams
|
||||
* @return string
|
||||
*/
|
||||
public function getAuthorizationUrl(array $additionalParams = []): string {
|
||||
return $this->oauth->getAuthorizationUrl($additionalParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the error is related to authentication
|
||||
* @param \Exception $e
|
||||
* @return bool
|
||||
*/
|
||||
private function isAuthenticationError(\Exception $e): bool {
|
||||
return $e->getCode() === 401 || $e->getCode() === 403;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get common MIME types for Google Workspace files
|
||||
* @return array
|
||||
*/
|
||||
public static function getCommonExportMimeTypes(): array {
|
||||
return [
|
||||
'application/vnd.google-apps.spreadsheet' => [
|
||||
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
'csv' => 'text/csv',
|
||||
'pdf' => 'application/pdf'
|
||||
],
|
||||
'application/vnd.google-apps.document' => [
|
||||
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'pdf' => 'application/pdf',
|
||||
'txt' => 'text/plain'
|
||||
],
|
||||
'application/vnd.google-apps.presentation' => [
|
||||
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
'pdf' => 'application/pdf'
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the service
|
||||
// $oauth = new GoogleOAuth2($clientId, $clientSecret, $redirectUri);
|
||||
// $drive = new GoogleDrive($oauth);
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
|
||||
namespace Lib\Google;
|
||||
|
||||
class GoogleOAuth2 {
|
||||
private string $clientId;
|
||||
private string $clientSecret;
|
||||
private string $redirectUri;
|
||||
private array $scopes = [];
|
||||
private ?string $accessToken = null;
|
||||
private ?string $refreshToken = null;
|
||||
private ?int $tokenExpires = null;
|
||||
|
||||
private const AUTH_URL = 'https://accounts.google.com/o/oauth2/v2/auth';
|
||||
private const TOKEN_URL = 'https://oauth2.googleapis.com/token';
|
||||
|
||||
public function __construct(string $clientId, string $clientSecret, string $redirectUri) {
|
||||
$this->clientId = $clientId;
|
||||
$this->clientSecret = $clientSecret;
|
||||
$this->redirectUri = $redirectUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add scopes for the OAuth2 request
|
||||
* @param array $scopes Array of Google OAuth2 scopes
|
||||
* @return self
|
||||
*/
|
||||
public function addScopes(array $scopes): self {
|
||||
$this->scopes = array_merge($this->scopes, $scopes);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the authorization URL
|
||||
* @param array $additionalParams Additional URL parameters
|
||||
* @return string
|
||||
*/
|
||||
public function getAuthorizationUrl(array $additionalParams = []): string {
|
||||
$params = array_merge([
|
||||
'client_id' => $this->clientId,
|
||||
'redirect_uri' => $this->redirectUri,
|
||||
'response_type' => 'code',
|
||||
'access_type' => 'offline',
|
||||
'prompt' => 'consent',
|
||||
'scope' => implode(' ', array_unique($this->scopes))
|
||||
], $additionalParams);
|
||||
|
||||
return self::AUTH_URL . '?' . http_build_query($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exchange authorization code for tokens
|
||||
* @param string $code Authorization code
|
||||
* @return array Token response
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function exchangeCode(string $code): array {
|
||||
$response = $this->makeTokenRequest([
|
||||
'grant_type' => 'authorization_code',
|
||||
'code' => $code,
|
||||
'redirect_uri' => $this->redirectUri
|
||||
]);
|
||||
|
||||
$this->setTokens($response);
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the access token
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function refreshAccessToken(): array {
|
||||
if (!$this->refreshToken) {
|
||||
throw new \Exception('No refresh token available');
|
||||
}
|
||||
|
||||
$response = $this->makeTokenRequest([
|
||||
'grant_type' => 'refresh_token',
|
||||
'refresh_token' => $this->refreshToken
|
||||
]);
|
||||
|
||||
$this->setTokens($response);
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an authenticated request to Google APIs
|
||||
* @param string $url
|
||||
* @param string $method
|
||||
* @param array $headers
|
||||
* @param mixed $body
|
||||
* @param bool $rawResponse Whether to return raw response instead of JSON
|
||||
* @return array|string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function makeAuthenticatedRequest(
|
||||
string $url,
|
||||
string $method = 'GET',
|
||||
array $headers = [],
|
||||
$body = null,
|
||||
bool $rawResponse = false
|
||||
){
|
||||
if ($this->isTokenExpired()) {
|
||||
$this->refreshAccessToken();
|
||||
}
|
||||
|
||||
$headers = array_merge([
|
||||
'Authorization: Bearer ' . $this->accessToken,
|
||||
'Accept: application/json'
|
||||
], $headers);
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HTTPHEADER => $headers,
|
||||
CURLOPT_CUSTOMREQUEST => $method
|
||||
]);
|
||||
|
||||
if ($body && in_array($method, ['POST', 'PUT', 'PATCH'])) {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
|
||||
}
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode >= 400) {
|
||||
throw new \Exception('Request failed with status ' . $httpCode . ': ' . $response);
|
||||
}
|
||||
|
||||
return $rawResponse ? $response : json_decode($response, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set tokens from OAuth2 response
|
||||
* @param array $response
|
||||
*/
|
||||
private function setTokens(array $response): void {
|
||||
$this->accessToken = $response['access_token'];
|
||||
if (isset($response['refresh_token'])) {
|
||||
$this->refreshToken = $response['refresh_token'];
|
||||
}
|
||||
$this->tokenExpires = time() + ($response['expires_in'] ?? 3600);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current access token is expired
|
||||
* @return bool
|
||||
*/
|
||||
private function isTokenExpired(): bool {
|
||||
return !$this->accessToken || !$this->tokenExpires || time() >= $this->tokenExpires;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a token request to Google's OAuth2 server
|
||||
* @param array $params
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function makeTokenRequest(array $params): array {
|
||||
$params = array_merge([
|
||||
'client_id' => $this->clientId,
|
||||
'client_secret' => $this->clientSecret
|
||||
], $params);
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_URL => self::TOKEN_URL,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => http_build_query($params),
|
||||
CURLOPT_HTTPHEADER => ['Content-Type: application/x-www-form-urlencoded']
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode !== 200) {
|
||||
throw new \Exception('Token request failed with status ' . $httpCode . ': ' . $response);
|
||||
}
|
||||
|
||||
return json_decode($response, true);
|
||||
}
|
||||
|
||||
// Getters for tokens and expiration
|
||||
public function getAccessToken(): ?string {
|
||||
return $this->accessToken;
|
||||
}
|
||||
|
||||
public function setAccessToken(string $accessToken): void {
|
||||
$this->accessToken = $accessToken;
|
||||
}
|
||||
|
||||
public function getRefreshToken(): ?string {
|
||||
return $this->refreshToken;
|
||||
}
|
||||
|
||||
public function setRefreshToken(string $refreshToken): void {
|
||||
$this->refreshToken = $refreshToken;
|
||||
}
|
||||
|
||||
public function getTokenExpires(): ?int {
|
||||
return $this->tokenExpires;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
include_once __DIR__ . "/mysql-database-service.php";
|
||||
|
||||
class LicenseModel extends MySqlDatabaseService
|
||||
{
|
||||
protected $_table = 'license';
|
||||
protected $_primary_key = 'id';
|
||||
protected $_return_type = 'array';
|
||||
protected $_allowed_fields = [
|
||||
'id', 'relationship_num', 'email', 'apikey', 'ip', 'status'
|
||||
];
|
||||
protected $_label_fields = [
|
||||
'ID', 'Relationship #', 'Email', 'API Key', 'ip', 'status'
|
||||
];
|
||||
protected $_use_timestamps = true;
|
||||
protected $_created_field = 'created_at';
|
||||
protected $_updated_field = 'updated_at';
|
||||
protected $_validation_rules = [
|
||||
['relationship_num', 'Relationship #', 'required'],
|
||||
['email', 'Email', 'required'],
|
||||
];
|
||||
|
||||
protected $_validation_edit_rules = [
|
||||
['relationship_num', 'Relationship #', 'required'],
|
||||
['email', 'Email', 'required'],
|
||||
['apikey', 'API Key', 'required'],
|
||||
['status', 'Status', 'required']
|
||||
];
|
||||
protected $_validation_messages = [
|
||||
['relationship_num', 'Relationship #', 'required'],
|
||||
['email', 'Email', 'required'],
|
||||
['apikey', 'API Key', 'required'],
|
||||
['status', 'Status', 'required']
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function get_mapping()
|
||||
{
|
||||
return [
|
||||
// TODO: ADD MAPPING
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<div class="container mt-5">
|
||||
<h2 class="text-center">Add License</h2>
|
||||
<?php if (isset($error) && $error): ?>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
Please fill in all required fields!
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<form action="/admin/license/add" method="POST" class="mt-4">
|
||||
<div class="form-group">
|
||||
<label for="relationship_num">Relationship Number</label>
|
||||
<input type="text" name="relationship_num" id="relationship_num" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="email">Email</label>
|
||||
<input type="email" name="email" id="email" class="form-control" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
@@ -0,0 +1,34 @@
|
||||
<div class="container mt-5">
|
||||
<h2 class="text-center">Edit License</h2>
|
||||
<?php if (isset($error) && $error): ?>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
Please fill in all required fields!
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<form action="/admin/license/edit/<?php echo $id?>" method="POST" class="mt-4">
|
||||
<div class="form-group">
|
||||
<label for="relationship_num">Relationship Number</label>
|
||||
<input type="text" name="relationship_num" id="relationship_num" class="form-control" required value="<?php echo isset($data['model']) ? $data['model']->relationship_num : ''?>">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="email">Email</label>
|
||||
<input type="email" name="email" id="email" class="form-control" required value="<?php echo isset($data['model']) ? $data['model']->email : ''?>">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="relationship_num">API KEY</label>
|
||||
<input type="text" name="apikey" id="apikey" class="form-control" required value="<?php echo isset($data['model']) ? $data['model']->apikey : ''?>">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="relationship_num">Last Access IP</label>
|
||||
<input type="text" name="ip" id="ip" class="form-control" value="<?php echo isset($data['model']) ? $data['model']->ip : ''?>">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="status">Status</label>
|
||||
<select name="status" class="form-control">
|
||||
<option value="active">Active</option>
|
||||
<option value="inactive">Inactive</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
@@ -0,0 +1,101 @@
|
||||
<style>
|
||||
.dark-header thead th {
|
||||
background-color: #343a40;
|
||||
/* Dark gray background */
|
||||
color: white;
|
||||
/* White text color */
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="container">
|
||||
<h2 class="text-left">Licenses <a class="btn btn-primary" href="/admin/license/add">Add</a></h2>
|
||||
<div class="row mt-2 mb-2">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<form action="?" method="GET">
|
||||
<div class="input-group">
|
||||
<input type="text" name="relationship_num" class="form-control mr-2" placeholder="Enter Relationship #" value="<?php echo $data['relationship_num']; ?>">
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-primary" type="submit">Search</button>
|
||||
</span>
|
||||
</div><!-- /input-group -->
|
||||
</form>
|
||||
</div><!-- /.col-md-6 -->
|
||||
</div><!-- /.row -->
|
||||
<!-- Table Responsive Wrapper -->
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover dark-header">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Relationship #</th>
|
||||
<th>Email</th>
|
||||
<th>API Key</th>
|
||||
<th>IP</th>
|
||||
<th>Status</th>
|
||||
<th>Date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($data['data'] as $key => $value) {
|
||||
echo ' <tr>';
|
||||
echo ' <td>' . $value->id . ' <br/><a class=" text-info" href="/admin/license/edit/' . $value->id . '">edit</a> <a class="text-danger" href="/admin/license/delete/' . $value->id . '">delete</a></td>';
|
||||
echo ' <td>' . $value->relationship_num . ' </td>';
|
||||
echo ' <td>' . $value->email . ' </td>';
|
||||
echo ' <td>' . $value->apikey . ' </td>';
|
||||
echo ' <td>' . $value->ip . ' </td>';
|
||||
echo ' <td>' . $value->status . ' </td>';
|
||||
echo ' <td>' . $value->created_at . ' </td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
// Retrieve parameters
|
||||
$total = $data['total'];
|
||||
$currentPage = $data['page'];
|
||||
$perPage = 10;
|
||||
|
||||
// Calculate the number of pages
|
||||
$totalPages = ceil($total / $perPage);
|
||||
|
||||
// Define a range of pages to show at any given time
|
||||
$range = 2; // This can be adjusted as needed
|
||||
|
||||
$startPage = ($currentPage - $range) > 0 ? ($currentPage - $range) : 1;
|
||||
$endPage = ($currentPage + $range) < $totalPages ? ($currentPage + $range) : $totalPages;
|
||||
|
||||
?>
|
||||
<!-- Pagination -->
|
||||
<nav aria-label="Page navigation">
|
||||
<ul class="pagination">
|
||||
<li class="ml-2">
|
||||
<a href="?page=1" aria-label="Previous">
|
||||
<span aria-hidden="true">««</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="ml-2">
|
||||
<a href="?page=<?= ($currentPage - 1) > 0 ? $currentPage - 1 : 1 ?>" aria-label="Previous">
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
<?php
|
||||
for ($i = $startPage; $i <= $endPage; $i++) {
|
||||
echo '<li class="ml-2' . ($currentPage == $i ? ' active' : '') . '"><a href="?page=' . $i . '">' . $i . '</a></li>';
|
||||
}
|
||||
?>
|
||||
<li class="ml-2">
|
||||
<a href="?page=<?= ($currentPage + 1) < $totalPages ? $currentPage + 1 : $totalPages ?>" aria-label="Next">
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="ml-2">
|
||||
<a href="?page=<?= $totalPages ?>" aria-label="Next">
|
||||
<span aria-hidden="true">»»</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
include_once __DIR__ . "/mysql-database-service.php";
|
||||
|
||||
class LocationModel extends MySqlDatabaseService
|
||||
{
|
||||
protected $_table = 'location';
|
||||
protected $_primary_key = 'id';
|
||||
protected $_return_type = 'array';
|
||||
protected $_allowed_fields = [
|
||||
'id', 'name', 'apikey', 'location_id', 'webhook'
|
||||
];
|
||||
protected $_label_fields = [
|
||||
'ID', 'Name', 'API Key', 'Location ID', 'Webhook'
|
||||
];
|
||||
protected $_use_timestamps = true;
|
||||
protected $_created_field = 'created_at';
|
||||
protected $_updated_field = 'updated_at';
|
||||
protected $_validation_rules = [
|
||||
['name', 'Name', 'required'],
|
||||
['apikey', 'API Key', 'required'],
|
||||
['location_id', 'Location ID', 'required'],
|
||||
['webhook', 'Webhook', 'required'],
|
||||
];
|
||||
|
||||
protected $_validation_edit_rules = [
|
||||
['name', 'Name', 'required'],
|
||||
['apikey', 'API Key', 'required'],
|
||||
['location_id', 'Location ID', 'required'],
|
||||
['webhook', 'Webhook', 'required'],
|
||||
];
|
||||
protected $_validation_messages = [
|
||||
['name', 'Name', 'required'],
|
||||
['apikey', 'API Key', 'required'],
|
||||
['location_id', 'Location ID', 'required'],
|
||||
['webhook', 'Webhook', 'required'],
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function get_mapping()
|
||||
{
|
||||
return [
|
||||
// TODO: ADD MAPPING
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<div class="container mt-5">
|
||||
<h2 class="text-center">Add Location</h2>
|
||||
<?php if (isset($error) && $error) : ?>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
Please fill in all required fields!
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<form action="/<?php echo $_SESSION['role']; ?>/location/add" method="POST" class="mt-4">
|
||||
<div class="form-group">
|
||||
<label for="name">Name</label>
|
||||
<input type="name" name="name" id="name" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="name">Location ID</label>
|
||||
<input type="text" name="location_id" id="location_id" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="name">Webhook</label>
|
||||
<input type="text" name="webhook" id="webhook" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="relationship_num">API KEY</label>
|
||||
<input type="text" name="apikey" id="apikey" class="form-control" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
@@ -0,0 +1,29 @@
|
||||
<div class="container mt-5">
|
||||
<h2 class="text-center">Edit Location</h2>
|
||||
<?php if (isset($error) && $error) : ?>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
Please fill in all required fields!
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<form action="/<?php echo $_SESSION['role'] ?>/location/edit/<?php echo $id ?>" method="POST" class="mt-4">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="name">Name</label>
|
||||
<input type="name" name="name" id="name" class="form-control" required value="<?php echo isset($data['model']) ? $data['model']->name : '' ?>">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="name">Location ID</label>
|
||||
<input type="name" name="location_id" id="location_id" class="form-control" required value="<?php echo isset($data['model']) ? $data['model']->location_id : '' ?>">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="name">Webhook</label>
|
||||
<input type="name" name="webhook" id="webhook" class="form-control" required value="<?php echo isset($data['model']) ? $data['model']->webhook : '' ?>">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="relationship_num">API KEY</label>
|
||||
<input type="text" name="apikey" id="apikey" class="form-control" required value="<?php echo isset($data['model']) ? $data['model']->apikey : '' ?>">
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
@@ -0,0 +1,103 @@
|
||||
<style>
|
||||
.dark-header thead th {
|
||||
background-color: #343a40;
|
||||
/* Dark gray background */
|
||||
color: white;
|
||||
/* White text color */
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="container">
|
||||
<h2 class="text-left">Locations <a class="btn btn-primary" href="/<?php echo $_SESSION['role'] ?>/location/add">Add</a></h2>
|
||||
<div class="row mt-2 mb-2">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<form action="?" method="GET">
|
||||
<div class="input-group">
|
||||
<input type="text" name="name" class="form-control mr-2" placeholder="Enter Name" value="<?php echo $data['name']; ?>">
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-primary" type="submit">Search</button>
|
||||
</span>
|
||||
</div><!-- /input-group -->
|
||||
</form>
|
||||
</div><!-- /.col-md-6 -->
|
||||
</div><!-- /.row -->
|
||||
<!-- Table Responsive Wrapper -->
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover dark-header">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Location/Account ID</th>
|
||||
<th>Webhook URL</th>
|
||||
<th>API Key</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($data['data'] as $key => $value) {
|
||||
echo ' <tr>';
|
||||
echo ' <td>' . $value->id . ' <br/><a class=" text-info" href="/'.
|
||||
$_SESSION['role'] . '/location/edit/' . $value->id .
|
||||
'">edit</a> <a class="text-danger" href="/'. $_SESSION['role']. '/location/delete/' .
|
||||
$value->id . '">delete</a></td>';
|
||||
|
||||
echo ' <td>' . $value->name . ' </td>';
|
||||
echo ' <td>' . $value->location_id . ' </td>';
|
||||
echo ' <td>' . $value->webhook . ' </td>';
|
||||
echo ' <td>' . $value->apikey . ' </td>';
|
||||
|
||||
echo '</tr>';
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
// Retrieve parameters
|
||||
$total = $data['total'];
|
||||
$currentPage = $data['page'];
|
||||
$perPage = 10;
|
||||
|
||||
// Calculate the number of pages
|
||||
$totalPages = ceil($total / $perPage);
|
||||
|
||||
// Define a range of pages to show at any given time
|
||||
$range = 2; // This can be adjusted as needed
|
||||
|
||||
$startPage = ($currentPage - $range) > 0 ? ($currentPage - $range) : 1;
|
||||
$endPage = ($currentPage + $range) < $totalPages ? ($currentPage + $range) : $totalPages;
|
||||
|
||||
?>
|
||||
<!-- Pagination -->
|
||||
<nav aria-label="Page navigation">
|
||||
<ul class="pagination">
|
||||
<li class="ml-2">
|
||||
<a href="?page=1" aria-label="Previous">
|
||||
<span aria-hidden="true">««</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="ml-2">
|
||||
<a href="?page=<?= ($currentPage - 1) > 0 ? $currentPage - 1 : 1 ?>" aria-label="Previous">
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
<?php
|
||||
for ($i = $startPage; $i <= $endPage; $i++) {
|
||||
echo '<li class="ml-2' . ($currentPage == $i ? ' active' : '') . '"><a href="?page=' . $i . '">' . $i . '</a></li>';
|
||||
}
|
||||
?>
|
||||
<li class="ml-2">
|
||||
<a href="?page=<?= ($currentPage + 1) < $totalPages ? $currentPage + 1 : $totalPages ?>" aria-label="Next">
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="ml-2">
|
||||
<a href="?page=<?= $totalPages ?>" aria-label="Next">
|
||||
<span aria-hidden="true">»»</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
@@ -0,0 +1,50 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login Page</title>
|
||||
<!-- Bootstrap 4.3 CSS -->
|
||||
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body class="bg-light">
|
||||
|
||||
<div class="container">
|
||||
<div class="row justify-content-center align-items-center vh-100">
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title text-center">Login</h5>
|
||||
<?php if (isset($error) && $error) : ?>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
Invalid Credentials!
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<form action="/admin/login" method="post">
|
||||
<div class="form-group">
|
||||
<label for="email">Email address</label>
|
||||
<input type="email" class="form-control" id="email" name="email" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<input type="password" class="form-control" id="password" name="password" required>
|
||||
</div>
|
||||
<div class="form-group text-center">
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap 4.3 JS and jQuery -->
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
+233
@@ -0,0 +1,233 @@
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
INSERT INTO `calendar` (`id`, `slot`, `days`, `calendar`, `created_at`, `updated_at`) VALUES
|
||||
(1, '{\"sunday\":[{\"from\":\"17:13\",\"to\":\"19:13\",\"point\":\"5\"},{\"from\":\"20:45\",\"to\":\"23:45\",\"point\":\"4\"}]}', 5, '50cd8253f9b01', '2023-12-13 15:13:23', '2023-12-13 20:47:46'),
|
||||
(2, '{\"sunday\":[{\"from\":\"20:50\",\"to\":\"22:50\",\"point\":\"34\"}],\"monday\":[{\"from\":\"03:46\",\"to\":\"04:46\",\"point\":\"3\"}],\"tuesday\":[{\"from\":\"01:54\",\"to\":\"05:54\",\"point\":\"5\"}]}', 5, 'c8be33b88c7cb', '2023-12-13 20:50:42', '2023-12-14 00:59:12'),
|
||||
(3, '{\"sunday\":[{\"from\":\"03:38\",\"to\":\"05:38\",\"point\":\"20\"}],\"thursday\":[{\"from\":\"04:38\",\"to\":\"06:38\",\"point\":\"34\"}]}', 1, '381593c1994c6', '2023-12-16 02:39:11', '2023-12-16 02:39:11'),
|
||||
(4, '{\"sunday\":[{\"from\":\"20:40\",\"to\":\"21:40\",\"point\":\"4\"}],\"tuesday\":[{\"from\":\"19:40\",\"to\":\"22:40\",\"point\":\"4\"}]}', 5, '78b583337f5cf', '2023-12-18 19:40:21', '2023-12-18 19:59:25'),
|
||||
(5, '{\"tuesday\":[{\"from\":\"20:11\",\"to\":\"23:11\",\"point\":\"30\"}]}', 3, 'da610b4f73fcd', '2023-12-18 20:11:27', '2023-12-18 20:11:27'),
|
||||
(6, '{\"tuesday\":[{\"from\":\"21:10\",\"to\":\"23:10\",\"point\":\"30\"}]}', 3, '3b649bd662201', '2023-12-18 20:10:44', '2023-12-18 20:10:44'),
|
||||
(7, '{\"tuesday\":[{\"from\":\"21:10\",\"to\":\"23:10\",\"point\":\"30\"}]}', 3, 'a63a01e97f9a6', '2023-12-18 20:12:05', '2023-12-18 20:12:05'),
|
||||
(8, '{\"tuesday\":[{\"from\":\"21:13\",\"to\":\"12:13\",\"point\":\"23\"}]}', 3, '5d316e074adde', '2023-12-18 20:13:16', '2023-12-18 20:13:16'),
|
||||
(9, '{\"thursday\":[{\"from\":\"13:54\",\"to\":\"16:54\",\"point\":\"20\"}]}', 3, '29a919949ceb7', '2023-12-20 13:01:20', '2023-12-20 13:01:20'),
|
||||
(10, '{\"thursday\":[{\"from\":\"21:02\",\"to\":\"12:02\",\"point\":\"15\"}],\"friday\":[{\"from\":\"18:02\",\"to\":\"19:02\",\"point\":\"14\"}]}', 3, 'ed2ca9d8b720f', '2023-12-20 15:02:37', '2023-12-20 15:02:37'),
|
||||
(11, '{\"thursday\":[{\"from\":\"21:02\",\"to\":\"12:02\",\"point\":\"15\"}],\"friday\":[{\"from\":\"18:02\",\"to\":\"19:02\",\"point\":\"14\"}]}', 3, '4f20856fb7568', '2023-12-20 15:03:22', '2023-12-20 15:03:22'),
|
||||
(12, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"23:59\",\"point\":\"5\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"23:59\",\"point\":\"10\"}]}', 3, 'cc97105cf395b', '2023-12-22 12:36:38', '2023-12-22 12:49:56'),
|
||||
(13, '{\"monday\":[{\"from\":\"15:00\",\"to\":\"17:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"15:00\",\"to\":\"17:00\",\"point\":\"5\"}],\"wednesday\":[{\"from\":\"13:00\",\"to\":\"14:00\",\"point\":\"2\"}]}', 12, 'f1afb12fd65b1', '2023-12-22 13:46:32', '2023-12-22 13:50:31'),
|
||||
(14, '{\"monday\":[{\"from\":\"18:00\",\"to\":\"20:00\",\"point\":\"3\"},{\"from\":\"10:00\",\"to\":\"12:00\",\"point\":\"1\"}]}', 5, 'b15dc3c0d6d55', '2023-12-22 14:05:34', '2023-12-22 14:05:34');
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
INSERT INTO `license` (`id`, `relationship_num`, `email`, `apikey`, `ip`, `status`, `created_at`, `updated_at`) VALUES
|
||||
(3, '1001', 'arin@team-followup.com', '3df120851f52158682ee9de50e4261b3', '', 'active', '2023-09-07', '2023-09-07 11:10:24'),
|
||||
(4, '1002', 'olu@locationflowsolutions.com', '44fdd885a36335af76c04b166e9a2b45', '', 'active', '2023-09-07', '2023-09-07 11:19:13'),
|
||||
(5, '1003', 'brent.oliver@gymlaunch.com', 'a63ddca64598f4840c27e213891a5100', '', 'active', '2023-09-07', '2023-09-07 13:38:53'),
|
||||
(6, '1004', 'bentleyvisualmedia@gmail.com', 'b067d63361f2116d545d3935bfb0fcc0', '', 'active', '2023-09-08', '2023-09-08 16:08:32'),
|
||||
(7, '1007', 'joe@accelerra.co.uk', '36b3f289b0abe2875e488d5d14aafbf9', '', 'inactive', '2023-10-10', '2023-12-27 21:38:29'),
|
||||
(8, '1008', 'ricardo@unknownemail.com', 'bbff746e31c314f27471b0c3693a90e5', '', 'inactive', '2023-11-28', '2024-02-02 19:20:04'),
|
||||
(9, '1009', 'john@dokimarketing.com', '851b23d65b74f45b33c88d99dbf87aba', '', 'active', '2023-12-22', '2023-12-22 15:58:53'),
|
||||
(10, '1010', 'logan@roofreach-ai.com', '9ec1130b249332d4ececd66886a34e6e', '', 'active', '2024-01-14', '2024-01-14 10:26:17'),
|
||||
(11, '1011', 'bailey@roofingaisystems.com', 'ecb483afbf57d16a24fec51485651cd7', '', 'active', '2024-01-28', '2024-01-28 21:41:08'),
|
||||
(12, '1012', 'cam@pinpointscaling.co.uk', 'ee9fbecd7d0aee6ceb928be32a28dd42', '', 'active', '2024-02-02', '2024-02-13 22:17:36'),
|
||||
(14, '1013', 'gr@hoffmarketing.com', 'f13a2cc62a328023e901e32f6ee8a5e4', '', 'active', '2024-02-13', '2024-02-13 17:33:00'),
|
||||
(15, '1014', 'Ahmed@denflow.ai', 'c51d7dd1a1bc12ae8149dd5b8134c6aa', '', 'active', '2024-02-13', '2024-02-13 22:21:37'),
|
||||
(16, '1015', 'sebastian.maldonado.braven@gmail.com', '26a6a3be58c088fdcd2994f76df9a2b9', '', 'active', '2024-02-27', '2024-02-27 14:42:44'),
|
||||
(17, '1016', 'dino@fullcontrolexpansion.com', '8eb65157239a27d7f53746fd26fe32ce', '', 'inactive', '2024-02-27', '2024-06-14 14:26:35'),
|
||||
(18, '1017', 'travis@gocrowndigital.com', 'c01f9bcc9391e562a4b60057077ecdfb', '', 'inactive', '2024-03-06', '2024-06-12 15:00:23'),
|
||||
(19, '1018', 'ej07kim@gmail.com', 'b6ec627194408a4782a797e359afa7e6', '', 'active', '2024-03-06', '2024-03-06 21:00:35'),
|
||||
(20, '1019', 'diegoespi317@gmail.com', 'f2ad2df703612b0b93f7dfcf92b42270', '', 'inactive', '2024-03-12', '2024-04-02 10:44:03'),
|
||||
(21, '1020', 'alex@tcmstrategy.com', '38625d777ae9ab4b61e520a1866e223d', '', 'active', '2024-03-18', '2024-03-18 12:56:00'),
|
||||
(22, '1021', 'bobby@constantconnectionsgroup.com', '94fef52043e2f581d8cc505c32cc5e36', '', 'active', '2024-03-22', '2024-03-22 17:27:00'),
|
||||
(23, '1022', 'justin@evermetric.com', '195acb3ce6b03058b608ea83e0f926aa', '', 'active', '2024-03-25', '2024-04-30 17:15:40'),
|
||||
(24, '1023', 'luke@elitescaledigital.com', 'edd25eb25e9f32cbb9afb549101a3bad', '', 'inactive', '2024-03-26', '2024-06-18 21:24:15'),
|
||||
(25, '1024', 'rotem.kraizberg@gmail.com', '19db4f255cbafe0e16f624e758101043', '', 'inactive', '2024-03-29', '2024-04-30 17:15:51'),
|
||||
(26, '1025', 'gavin@roofreach-ai.com', '7ff75b1bc97bf532b725ac57484b4e4c', '', 'active', '2024-04-02', '2024-04-02 18:04:23'),
|
||||
(27, '1026', 'edgarbuelna20@gmail.com', '3b56a954926b67831d2961b3e3524c85', '', 'inactive', '2024-04-11', '2024-08-09 14:40:52'),
|
||||
(28, '1027', 'vishal@acepointmedia.com', '1bfa00df499460da603ebce35380ab60', '', 'active', '2024-04-11', '2024-04-11 15:29:15'),
|
||||
(29, '1028', 'pasha@dentmarketing.ca', '748feee3de92902d6064783ffde851a5', '', 'active', '2024-04-12', '2024-04-12 13:31:34'),
|
||||
(30, '1029', 'ian@ael-media.com', '0f4f568eaa02263e4a7da24651b98ad1', '', 'inactive', '2024-04-12', '2024-07-16 21:12:53'),
|
||||
(31, '1030', 'solarmarketingpro@gmail.com', 'e0e078df81f92ff4f9ecd55105b4ae77', '', 'inactive', '2024-04-23', '2024-07-15 16:42:20'),
|
||||
(32, '1031', 'raidenwburr@gmail.com', '0d3a7fbfbbf1a7a9ef954985715efdf6', '', 'active', '2024-04-25', '2024-04-25 20:46:03'),
|
||||
(33, '1032', 'Evan@pingmarketingteam.com', '3c972e47103545176f0b46c1ecc7e541', '', 'active', '2024-04-26', '2024-04-26 18:35:36'),
|
||||
(34, '1033', 'eli@fuzesystems.co', '0e10c71a42af8c218966018396e969c9', '', 'active', '2024-04-29', '2024-04-29 15:31:26'),
|
||||
(35, '1034', 'austinjireh8@gmail.com', 'f3b93fe5ae5b15cd2890b16c0887cef0', '', 'active', '2024-04-30', '2024-04-30 13:10:23'),
|
||||
(36, '1035', 'Warriorpipeline@gmail.com', '514fedc8d1dc327d75a537758cf13dfd', '', 'active', '2024-05-03', '2024-05-03 13:24:18'),
|
||||
(37, '1036', 'damien@maestromedia.fr', 'c693f2788fedde782021e4bfa22e9d7f', '', 'active', '2024-05-03', '2024-05-03 14:26:16'),
|
||||
(39, '1037', 'jonas@oakstellar.com', '2eb5f3fe065c0f95ef88c8d38aeb969a', '', 'inactive', '2024-05-10', '2024-09-04 17:26:13'),
|
||||
(40, '1038', 'clutchdigitalio@gmail.com', '741cb0489adb1801e873d9d4a9ce6cac', '', 'active', '2024-05-13', '2024-05-13 13:12:26'),
|
||||
(41, '1039', 'faisal@inflow-agency.com', '03dbb535f5410e368dd6272156963355', '', 'inactive', '2024-05-14', '2024-08-15 13:45:14'),
|
||||
(42, '1040', 'arcasmail@gmail.com', '048ca52fa58b0616f9c388449c0b475c', '', 'inactive', '2024-05-20', '2024-09-18 13:53:13'),
|
||||
(43, '1041', 'orien@daly-media.co.uk', 'cda0978fc74d531ccdef1710a9fb801d', '', 'active', '2024-05-22', '2024-05-22 13:37:28'),
|
||||
(44, '1042', 'Luke@hw-media.co.uk', '34b7377406ddebf03f0ee6a4ab575a5d', '', 'active', '2024-05-23', '2024-05-23 13:31:20'),
|
||||
(45, '1043', 'joe@casesondemand.net', '7a37dfcd31de894cbc7c63e39a1e917b', '', 'inactive', '2024-05-29', '2024-09-16 16:00:24'),
|
||||
(46, '1044', 'andrey@ivsgroupuk.com', '24930fb7f7f1471316ffcc6868bca0f8', '', 'active', '2024-06-04', '2024-06-04 13:45:14'),
|
||||
(47, '1045', 'laith@plaidmedia.org', 'ed2f0c063dbe9d7096dea2184b27fbce', '', 'active', '2024-06-12', '2024-06-12 19:44:52'),
|
||||
(48, '1046', 'Caleb@rapidmediaco.com', '5e493cbc08cffd207baa426db25db65d', '', 'inactive', '2024-06-19', '2024-08-19 13:08:34'),
|
||||
(49, '1047', 'devin@daylight-digital.com', 'eddd3a40749d64f55463892215996753', '', 'active', '2024-06-28', '2024-06-28 13:30:13'),
|
||||
(50, '1048', 'oliver@mymedigrowth.com', '16732fdf5e0d540e9774af352ea7ee14', '', 'active', '2024-07-01', '2024-07-01 14:23:42'),
|
||||
(51, '1049', 'Jacob@stackedstrategies.com', '430abf9083d104f6250020c203cea57a', '', 'active', '2024-07-08', '2024-07-08 16:22:25'),
|
||||
(52, '1050', 'louis@theborregobrothers.com', 'd65d4fe08736d80ad561b8328e772271', '', 'inactive', '2024-07-08', '2024-10-28 14:24:04'),
|
||||
(53, '1051', 'cam@pinpointscaling.co.uk', 'f7348039c4d9ee3da6a6b94de46eb2fc', '', 'active', '2024-07-15', '2024-07-15 13:10:42'),
|
||||
(54, '1052', 'ivan@newlybooked.com', '91d3123c47b736ee4bd4a045681a3671', '', 'active', '2024-07-15', '2024-07-15 19:15:20'),
|
||||
(55, '1053', 'robertprice2000@hotmail.co.uk', '9df794545846e2df71412347d7a702eb', '', 'active', '2024-07-19', '2024-07-19 13:06:29'),
|
||||
(56, '1054', 'lucas@monadispatch.com', '5737f8403bb6555664bbb3337104ba42', '', 'active', '2024-07-22', '2024-07-22 18:42:56'),
|
||||
(57, '1055', 'cate@sanesocialmedia.com', '7befec1cdbecc0dfa5392d20c969defd', '', 'active', '2024-07-26', '2024-07-26 13:59:49'),
|
||||
(58, '1056', 'gabegoertzen@gmail.com', '4689420a66b7d22c945b01bee353b78e', '', 'active', '2024-07-29', '2024-07-29 13:46:33'),
|
||||
(59, '1057', 'sohaib@spamgt.com', '6eb931d900001c12bd70a0d853a924f1', '', 'inactive', '2024-07-29', '2024-08-30 14:43:16'),
|
||||
(61, '1058', 'todd@redlightsystems.com', 'd1256ec6baf78668a54b8e80afbff254', '', 'active', '2024-08-06', '2024-08-06 16:20:54'),
|
||||
(62, '1059', 'cj@roistrategic.com', '2720b8bd88a027f4582de663adf401a9', '', 'active', '2024-08-07', '2024-08-07 13:57:15'),
|
||||
(63, '1060', 'vincent@medspaadvertising.com', '586c2ffd19d4728c5cd0634fdc940369', '', 'active', '2024-08-07', '2024-08-07 20:56:49'),
|
||||
(64, '1061', 'Stasiu@sandcleads.com', 'e3cf0557fb94a69c4669a3f07eb233f0', '', 'active', '2024-08-08', '2024-08-08 21:01:53'),
|
||||
(65, '1062', 'sherman@digitaldoorknockers.com', 'cbb8f77941d15db44ece38cddd1854ef', '', 'active', '2024-08-13', '2024-08-13 15:37:53'),
|
||||
(67, '1063', 'callum.mills@servedia.co', 'd0699797b55c85e4c55e9c15548d1491', '', 'active', '2024-08-16', '2024-08-16 15:23:11'),
|
||||
(68, '1064', 'michael@llmedia.info', 'a3f4e47ae5411ca88dc648e756ac0193', '', 'active', '2024-08-19', '2024-08-19 13:35:21'),
|
||||
(69, '1065', 'david@ll.media', '97fa155d1f5d1e92e867363572532c64', '', 'active', '2024-08-19', '2024-08-19 13:37:37'),
|
||||
(70, '1066', 'millerepalmer@gmail.com', 'e10027ff27561f7b9d4929039eede97e', '', 'active', '2024-08-21', '2024-08-21 13:18:13'),
|
||||
(71, '1067', 'sean@theperfectlook.co', '6ff86fc9de3a60e53757aeb39031965a', '', 'active', '2024-08-23', '2024-08-23 13:27:24'),
|
||||
(72, '1068', 'mark@chirojump.com', 'cff66714d74dd6302bbf32c076804512', '', 'active', '2024-08-27', '2024-08-27 16:10:50'),
|
||||
(73, '1069', 'bartvandaatselaar@gmail.com', '19f46574d8ca0ea61f94611d7fc2c912', '', 'active', '2024-09-02', '2024-09-02 14:00:52'),
|
||||
(74, '1070', 'basnagel2000@gmail.com', 'f6326961ac7d010dcb13972feb5445dc', '', 'active', '2024-09-02', '2024-09-02 14:01:04'),
|
||||
(75, '1071', 'support@jcatmediallc.com', 'd98d77bd15f2f6c33e85092ff9c838ee', '', 'active', '2024-09-04', '2024-09-04 16:39:09'),
|
||||
(76, '1072', 'kontakt.ascendmarketing@gmail.com', '6a1f683233195879e70b59e250df2f67', '', 'active', '2024-09-12', '2024-09-12 13:14:49'),
|
||||
(77, '1073', 'Asa@ablazeai.com', 'cb11fd7e3ff0d2f0f2ffaa5aca3a202f', '', 'active', '2024-09-12', '2024-09-12 13:15:50'),
|
||||
(78, '1074', 'bart@fitnessproductions.nl', '2679df978497627563be18e6a057f0fb', '', 'active', '2024-09-12', '2024-09-12 13:49:26'),
|
||||
(79, '1075', 'asa@ablazeai.com', 'dc2f862305a569e0333e2ef6485f780f', '', 'active', '2024-09-12', '2024-09-12 17:35:52'),
|
||||
(80, '1076', 'info@azureonmarketing.com', 'b8684a0f4f7dfc85686a59043f9dbe8f', '', 'active', '2024-09-16', '2024-09-16 13:22:47'),
|
||||
(81, '1077', 'gemma@elevatedental.agency', '3feecdbf743dd95ee344d4fb539516bd', '', 'active', '2024-09-23', '2024-09-23 14:41:58'),
|
||||
(82, '1078', 'andyceo@neomarkmedia.com', '41a7f2bd3fbff7747f9842bc8b075355', '', 'active', '2024-09-26', '2024-10-31 15:12:28'),
|
||||
(83, '1079', 'yuvraj@badhaodigital.com', 'fb301067b8076c590d78c9ed2a83c64e', '', 'active', '2024-09-26', '2024-09-26 21:01:08'),
|
||||
(84, '1080', 'joe@socialforgesolutions.com', '315aeef54d1d1a186cc184646ce69a76', '', 'active', '2024-10-02', '2024-10-02 20:26:46'),
|
||||
(85, '1081', 'gabriel@midtouchmedia.com', '3d0f591837f689f84ed2a75d6ee5d2e5', '', 'active', '2024-10-03', '2024-10-03 13:16:52'),
|
||||
(86, '1082', 'Cristiano@ruchedigital.com', 'ae8d4b1b47f2c0ddfe5d2ecb7d1a6adf', '', 'active', '2024-10-04', '2024-10-04 14:10:21'),
|
||||
(87, '1083', 'benmacmahon16@gmail.com', '88eebc2c9a945280b295553083cf085e', '', 'active', '2024-10-07', '2024-10-07 14:34:03'),
|
||||
(88, '1084', 'certifiedappointments@gmail.com', '2442c537428edc1f733146a22dcfe11b', '', 'active', '2024-10-07', '2024-10-07 14:36:02'),
|
||||
(89, '1085', 'michaelerichoward@googlemail.com', '87db6dc7c1096c0913af001aa2222fbb', '', 'active', '2024-10-08', '2024-10-08 14:00:59'),
|
||||
(91, '1086', 'hardikdudeja7@gmail.com', 'ce3ad59a2ff80c17ff0329f499597a95', '', 'active', '2024-10-09', '2024-10-09 17:41:41'),
|
||||
(92, '1087', 'lukeborges0@gmail.com', '0f62f93196b0dc1a4a395c759ccfb929', '', 'active', '2024-10-17', '2024-10-17 14:22:20'),
|
||||
(93, '1088', 'Kristian@stargoop.com', '91465e7fbf8c92470ac726072e92a1d6', '', 'active', '2024-10-18', '2024-10-18 13:27:45'),
|
||||
(94, '1089', 'joshua@redlinedigital.ca', '73252554730488a226cfc3114c3330ed', '', 'active', '2024-10-21', '2024-10-21 14:15:46');
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
INSERT INTO `project` (`id`, `project_name`, `user_id`, `slot`, `days`, `alert`, `score_threshold`, `actual_score`, `webhook`, `calendar`, `created_at`, `updated_at`, `payload`, `location`, `access_token`, `refresh_token`, `token_expiry`) VALUES
|
||||
(78, 'No Bad Days Fitness', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 1, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'OTTXZjW9AHLmO9ttljjL', '2024-01-12 11:17:46', '2024-10-31 05:00:07', '{\"Project\":\"No Bad Days Fitness\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/OTTXZjW9AHLmO9ttljjL\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6IktsUTRsZDVCM0RKbThZNlFvaHY4IiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjY4MTA4NTQxNjQxLCJzdWIiOiJ1c2VyX2lkIn0.Afu51evYMgav5Ma1niAw6nZ7VJd8R_413oEGhMeucOw', NULL, NULL, NULL),
|
||||
(79, 'BFS Fitness Club', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 3, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'JL51L2IzywQYBYzeSnz9', '2024-01-12 11:18:41', '2024-10-31 05:00:16', '{\"Project\":\"BFS Fitness Club\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/JL51L2IzywQYBYzeSnz9\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6IlVrYUdISjl1N1lWdGFLNG41MVo2IiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjc4NzQxNDE1OTQ4LCJzdWIiOiJ1c2VyX2lkIn0.Nq_03tly5m1vnkHbDnqblJzIwj0wvHJN39NVLgPazyA', NULL, NULL, NULL),
|
||||
(80, 'DriveRx Fitness', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 37, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', '3MDc2tKsm5PW2zZtAT2n', '2024-01-12 14:25:44', '2024-10-31 05:00:45', '{\"Project\":\"DriveRx Fitness\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/3MDc2tKsm5PW2zZtAT2n\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6InhRZlBOS3pFUGJ4SGhJNHFKanNZIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjcwODc5Njc3ODUzLCJzdWIiOiJ1c2VyX2lkIn0.uJq7Dis9g9EbLLvRnUhtTAkKaInEJb0onWkqRJZ8jrc', NULL, NULL, NULL),
|
||||
(81, 'Balance Sports Performance', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 42, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'toIpOJsRpI5VWKVw948B', '2024-01-12 14:26:46', '2024-04-10 05:00:37', '{\"Project\":\"Balance Sports Performance\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/toIpOJsRpI5VWKVw948B\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6IkFRalpRbnpQSmU0RnpjczZQZ1BYIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjcxMDQ5ODkzNzY4LCJzdWIiOiJ1c2VyX2lkIn0.Jb4m3TAVOXTHs4MIVCfiYyMUgQBxZ8qB9CJ8Jiv6vzQ', NULL, NULL, NULL),
|
||||
(82, 'Transformation Fitness', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 7, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'zzY1nbwt91408shqZQkm', '2024-01-12 14:28:58', '2024-10-31 05:03:04', '{\"Project\":\"Transformation Fitness\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/zzY1nbwt91408shqZQkm\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6IjhMaktUNU9taHBkTDg3azFpMkNqIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjM5NTg2NTAzODcxLCJzdWIiOiJ1c2VyX2lkIn0.HTQB1atxyeeYvIQ6fRMFR3ff3OTeB_hjP-J0bLLd-M8', NULL, NULL, NULL),
|
||||
(83, 'Egley Train Boise', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 4, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'gZJAggtDb0DDFDirLsax', '2024-01-12 14:30:04', '2024-10-31 05:03:13', '{\"Project\":\"Egley Train Boise\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/gZJAggtDb0DDFDirLsax\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6InQwalRmR1dXQ1piTjh0ZE1zbGl1IiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjU1OTI5MzUzNzYyLCJzdWIiOiJ1c2VyX2lkIn0.HWsIwo9IF6TMmKeY1O6hz6sfyC8E6B4OECwyjs66Bcg', NULL, NULL, NULL),
|
||||
(84, 'Bochner\'s Realistic Self-Defense Training and Fitness Center', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 7, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'eK5zoN7kJ8y1L8RGfJ54', '2024-01-12 14:32:26', '2024-10-31 05:03:19', '{\"Project\":\"Bochner\'s Realistic Self-Defense Training and Fitness Center\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/eK5zoN7kJ8y1L8RGfJ54\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6ImxUYVBDekFjb0FkM25qZEVoTDFEIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjYxODAyNzQ0NzA2LCJzdWIiOiJ1c2VyX2lkIn0.IQFSOextPRdBAo0O6MxcOyrxMZs4O0ZmyCko-MZ9PW0', NULL, NULL, NULL),
|
||||
(85, 'The YourLife Gym', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 41, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'ly7WqjsKTuSqkTSo2MWi', '2024-01-12 14:33:11', '2024-03-03 05:01:43', '{\"Project\":\"The YourLife Gym\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/ly7WqjsKTuSqkTSo2MWi\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6IkxjdFUxRzhWekNDakhmdmo5UkVwIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjYxOTcxODcxNDYxLCJzdWIiOiJ1c2VyX2lkIn0.L_uxLg17Trppxi38CYGekAYjWt0UlrKmMR9xJsHYbWY', NULL, NULL, NULL),
|
||||
(86, 'No Doubt Fitness', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 0, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'Gm9esEicp8r0OgHw3pEJ', '2024-01-12 14:34:48', '2024-03-25 05:02:15', '{\"Project\":\"No Doubt Fitness\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/Gm9esEicp8r0OgHw3pEJ\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6IkJINzZxUVZ6WEJHNzlUcHdvQzN2IiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjYyNzM1MjE1MjI1LCJzdWIiOiJ1c2VyX2lkIn0.YHVghMo7zAYLbhmO_YRM_1b-32P7vhllXleOrnfdY1w', NULL, NULL, NULL),
|
||||
(87, 'Recalibrated Performance', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 4, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'c5dM5TQiOGqCpl0k3u0S', '2024-01-12 14:34:49', '2024-10-25 05:01:57', '{\"Project\":\"Recalibrated Performance\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/c5dM5TQiOGqCpl0k3u0S\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6ImNLcGdiNzliWklkWlhjUTFnMDBFIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjYwNjgzMjYyNTA2LCJzdWIiOiJ1c2VyX2lkIn0.ltm8Bozm3jH29CdSu3jZArPydGiepF6vN8KUYulppCw', NULL, NULL, NULL),
|
||||
(88, 'Atom Olson Fitness', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 13, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'uNHrsZxMcSU3AvXzBW1e', '2024-01-12 14:37:37', '2024-10-31 05:03:43', '{\"Project\":\"Atom Olson Fitness\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/uNHrsZxMcSU3AvXzBW1e\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6ImNWZEk2WDdHTmN5dVdvQ3NZTXVUIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjYzMTY4NzI4MzQxLCJzdWIiOiJ1c2VyX2lkIn0.kW37YG13lUdb4UM6MGIFdbK26Pm0GCUAb0HAX6W2kMc', NULL, NULL, NULL),
|
||||
(89, 'Lazaro Health and Fitness', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'Off', 15, 42, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'COe25dXQbKCIHH5EcZiD', '2024-01-12 14:38:49', '2024-02-02 20:42:39', '{\"Project\":\"Lazaro Health and Fitness\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/COe25dXQbKCIHH5EcZiD\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6IjN2TVVFcWNPR21Wa245bGVhdXFDIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjYxMjczNTY4MDgxLCJzdWIiOiJ1c2VyX2lkIn0.v_SaiCCw-B83ZK85JfgG-Yg3SWldqVJ9YS0fjZDisCA', NULL, NULL, NULL),
|
||||
(90, 'Fishers Fit Body Boot Camp', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'Off', 15, 42, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'Y4iKEoos56tOKjHjiHT0', '2024-01-12 14:40:17', '2024-02-02 20:42:31', '{\"Project\":\"Fishers Fit Body Boot Camp\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/Y4iKEoos56tOKjHjiHT0\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6Im5FR2M4ZkI5VmE1b3dKNUJVMWhpIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjY1NzY1NjIxNTA1LCJzdWIiOiJ1c2VyX2lkIn0.bJWnx58j2TKN96TrIHaK1zr1BFmxvuj3ElV_7kzJ300', NULL, NULL, NULL),
|
||||
(91, 'B3 Fitness', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'Off', 15, 42, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 's8BRasW1C7oOBJV7vHbQ', '2024-01-12 14:41:16', '2024-02-02 20:42:23', '{\"Project\":\"B3 Fitness\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/s8BRasW1C7oOBJV7vHbQ\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6Ing1bnZYVTRFR0hMb3Q2bWw3ZTBSIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjYwNjY3Mzk3NTQxLCJzdWIiOiJ1c2VyX2lkIn0.YW0cWlLYLbYVQoVXgce2Sc_LUpUFUxN7zEU8YeQscwM', NULL, NULL, NULL),
|
||||
(92, 'EMF Fitness', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'Off', 15, 42, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', '45Y0BJJylwSqnjGQe4Au', '2024-01-12 14:41:59', '2024-02-02 20:41:17', '{\"Project\":\"EMF Fitness\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/45Y0BJJylwSqnjGQe4Au\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6InNqZkNZV2FLSjljNFNTWXp3VUdNIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjY1NTIxODMzNzU1LCJzdWIiOiJ1c2VyX2lkIn0.PavTQDPFh_tn89Es99mKnKMTOxF3Iw5e06GSMMdbTuU', NULL, NULL, NULL),
|
||||
(93, 'AlphaGainz', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'Off', 15, 42, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'XybaVxFyWSh4y17huWwZ', '2024-01-12 14:42:49', '2024-02-02 20:41:11', '{\"Project\":\"AlphaGainz\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/XybaVxFyWSh4y17huWwZ\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6Ild4VllLWGJXcGM4VXJlSWFIWFhKIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjY0MzgzNjEyNjUyLCJzdWIiOiJ1c2VyX2lkIn0.J8Yqd2kbj167nrJEGbm9sd2RzV6ajUEguEnb6tm4MLI', NULL, NULL, NULL),
|
||||
(94, 'Egans Fitness Aiea', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'Off', 15, 42, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'MSaDdycBZErghHdiRL2N', '2024-01-12 14:43:43', '2024-02-02 20:41:04', '{\"Project\":\"Elite Boxing Fitness (Stockton)\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/MSaDdycBZErghHdiRL2N\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6Inp6UGlEaVBVNHVobHVqd1VsdWtmIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjk3NTY2NjQ4NjA5LCJzdWIiOiJ1c2VyX2lkIn0.Hbc5HesljR1B7LEIUg_5pM3newdrYZeYMwo8i5mEk9g', NULL, NULL, NULL),
|
||||
(95, 'Elite Boxing Fitness (Stockton)', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'Off', 15, 42, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'gKFSZNpiAY4F3DI2kMwj', '2024-01-12 14:46:07', '2024-02-02 20:40:59', '{\"Project\":\"Elite Boxing Fitness (Stockton)\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/gKFSZNpiAY4F3DI2kMwj\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6InhjSEVyeDFxakdiVE1EZDFVYjhCIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjU0NjE2Mjg3NDkyLCJzdWIiOiJ1c2VyX2lkIn0.nV_RFsJSvVhP6GRS3kANpscxy0JTiGYJON8Hj547l-Q', NULL, NULL, NULL),
|
||||
(96, 'Fit Theorem - Novi ', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'Off', 15, 42, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', '5Kg1uoGHVrwFKDfsKQDK', '2024-01-12 14:48:56', '2024-02-02 20:40:48', '{\"Project\":\"Fit Theorem - Novi \", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/5Kg1uoGHVrwFKDfsKQDK\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6Iko2ZlJrVVdPRm50REFMVFgxOEg3IiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjYyMTU0NTI3MzExLCJzdWIiOiJ1c2VyX2lkIn0.mQtozMa-u1m0qAxNTII5YO2p54EoZk9e4htIdc01-pk', NULL, NULL, NULL),
|
||||
(97, 'Gulf Coast Performance', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'Off', 15, 42, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'KsvRnGNXA4gLHuUmHXIz', '2024-01-12 14:50:29', '2024-02-02 20:40:42', '{\"Project\":\"Gulf Coast Performance\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/KsvRnGNXA4gLHuUmHXIz\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6Imlhd3VqdzFtWGpxbzVDb0JCUHg4IiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjYzODY3MDEzMjM5LCJzdWIiOiJ1c2VyX2lkIn0.4ej8T6jTx1g0wXUoxUXVg7iDi6B94rXJEYWPc6NcLuQ', NULL, NULL, NULL),
|
||||
(98, 'G-Five Training and Wellness', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'Off', 15, 42, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'JBtL6Aqa9QDYZGxPpGNb', '2024-01-12 14:56:16', '2024-02-02 20:40:35', '{\"Project\":\"G-Five Training and Wellness\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/JBtL6Aqa9QDYZGxPpGNb\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6IkhoSUFkMTBkREdWYzVKZm5BWlIyIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjg0NDIwMzM1MjQ5LCJzdWIiOiJ1c2VyX2lkIn0.QaBqZNYNeMODkTfaveTn6TwFsQvP0GvXlUFUViz2EeE', NULL, NULL, NULL),
|
||||
(99, 'A1 Health & Fitness', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 20, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'Fx6FwRYgwQBxNRxuLN0Y', '2024-01-12 14:56:57', '2024-10-31 05:04:20', '{\"Project\":\"A1 Health & Fitness\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/Fx6FwRYgwQBxNRxuLN0Y\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6IjBJUzNWVFZQUnFyUjRVMjZMSkIzIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjU2NDQ1NjM5MDMxLCJzdWIiOiJ1c2VyX2lkIn0.Wuvc2UcrErNPXFLFKvshckT3TFl5wkjzyoi_DoxkV6c', NULL, NULL, NULL),
|
||||
(100, 'Southside Knockout Orland Park', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 729, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'dAaKgPMYHSiM7iK5Sp2v', '2024-01-12 14:58:07', '2024-10-31 05:13:54', '{\"Project\":\"Southside Knockout Orland Park\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/dAaKgPMYHSiM7iK5Sp2v\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6IlZkNmo4NVRpSjJZaEQzYVFWM2pSIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjkyMzg1ODk5NjQ0LCJzdWIiOiJ1c2VyX2lkIn0.IrlkkZkT_AeILu7yn1n47gX1U--PXWWYRKLy3eRhi5g', NULL, NULL, NULL),
|
||||
(101, 'CaliUnity', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 2, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', '4tNTFrjLrdpYt5GGmasE', '2024-01-12 14:59:04', '2024-10-01 05:12:18', '{\"Project\":\"CaliUnity\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/4tNTFrjLrdpYt5GGmasE\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6IjBWMlByWXdsT1dqb1hrbkRDV3hWIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjYyMTEwOTI1MTI3LCJzdWIiOiJ1c2VyX2lkIn0.HA3HfYqeuNtGWwjRWSvygiCCTXio2w9k_gHbp5Z7F08', NULL, NULL, NULL),
|
||||
(102, 'Armada Cross Training', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 6, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'D7aBAiNVTpkEK3XiNdZF', '2024-01-12 15:00:05', '2024-10-31 05:14:00', '{\"Project\":\"Armada Cross Training\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/D7aBAiNVTpkEK3XiNdZF\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6ImFjaGhxYTBjUmlpV1dTUmtIN3FZIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjY3ODQ2ODEwNjMxLCJzdWIiOiJ1c2VyX2lkIn0.IbTGi5UrKNr9B5gI7mSScc-m60PVwwCgdEwxHPbxyOY', NULL, NULL, NULL),
|
||||
(103, 'F45 Training Colorado Springs Central', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 0, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'tfKTnUJsMEU4Xb1zBkj6', '2024-01-12 15:07:43', '2024-10-10 05:11:20', '{\"Project\":\"F45 Training Colorado Springs Central\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/tfKTnUJsMEU4Xb1zBkj6\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6Im83WjNYQlR3ZEpiREpiWlNoRTJBIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjc4NDY5MDA0OTE5LCJzdWIiOiJ1c2VyX2lkIn0.J87rwQn_KOK7FFU5coSjOMhdMcJjPTFs4Sui6ybuXuk', NULL, NULL, NULL),
|
||||
(104, 'Empower Fitness', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 20, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', '7DzccNWgbctsPoAKovys', '2024-01-12 15:13:10', '2024-04-12 05:02:20', '{\"Project\":\"Empower Fitness\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/7DzccNWgbctsPoAKovys\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6Imhwdlk3Q2c0ZjZUQjhqQlhZdjJtIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjgzODMzNTc5MTA3LCJzdWIiOiJ1c2VyX2lkIn0.mHc9xn2YYrpZ_pBwdPkSKzCyJfdPu8Z6WqGBl9usrQo', NULL, NULL, NULL),
|
||||
(105, 'Newport LKD', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 18, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'Pe1uNtsPsornOSRLrLM2', '2024-01-12 15:13:56', '2024-10-31 05:14:05', '{\"Project\":\"Newport LKD\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/Pe1uNtsPsornOSRLrLM2\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6IkFQVFRacFBCaVRqQ01PZGlzT2lLIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjczMDI4ODI5MjE5LCJzdWIiOiJ1c2VyX2lkIn0.HAsSC5YKc3QZ1kFw4fjGsVlR7mv39C27PQ9aeXHY8RE', NULL, NULL, NULL),
|
||||
(106, 'Peak 360', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 50, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'V7H3GharFwzTlTasxumj', '2024-01-12 15:15:04', '2024-05-11 05:02:49', '{\"Project\":\"Peak 360\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/V7H3GharFwzTlTasxumj\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6Im1lanRUM3E1bUh2dER1NmhweVNjIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjg0MzU3NzU4MTc3LCJzdWIiOiJ1c2VyX2lkIn0.1R0zZ-6S5m8cq-M4FT3MQGQI0rWC7bHN-wor7HSjPvw', NULL, NULL, NULL),
|
||||
(107, 'Soar Over Obstacles', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 4, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'rrT2vsaMI4ILm1ZhHjym', '2024-01-12 15:16:00', '2024-05-14 05:03:25', '{\"Project\":\"Soar Over Obstacles\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/rrT2vsaMI4ILm1ZhHjym\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6IkduWmwzZmdjSkQ5SVpDUkFCNmxwIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjg0NDM3ODk3NjczLCJzdWIiOiJ1c2VyX2lkIn0.Kie23Tmg4qje7zZYozi8HGHcCFP7lJWkkI5mHUB6GKQ', NULL, NULL, NULL),
|
||||
(108, 'RUF Fitness', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 0, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', '4KDMjSpHsgl6nqjqsYnK', '2024-01-12 15:16:37', '2024-06-19 05:03:12', '{\"Project\":\"RUF Fitness\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/4KDMjSpHsgl6nqjqsYnK\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6IlE0czJuOG1xUGVuWTFWR1FyeTVhIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjUzNjAwNTIwOTQ2LCJzdWIiOiJ1c2VyX2lkIn0.E9liDtPI8ZU6UtKI4dEeMJzAk4NuAXDDQMQFI-FOhHc', NULL, NULL, NULL),
|
||||
(109, 'Wine Country Crossfit', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 19, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'jtIj87jTgIPx1Hk6LVM9', '2024-01-12 15:17:10', '2024-10-31 05:14:26', '{\"Project\":\"Wine Country Crossfit\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/jtIj87jTgIPx1Hk6LVM9\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6IlRDcHlvT0lsdnZNdzE3NnBSWDBWIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjU4MzM3ODgyMzc2LCJzdWIiOiJ1c2VyX2lkIn0.KY_NJuH4KDt9JEu6b9hdGMfOUE6GjLG_BxZi3Gw9WLQ', NULL, NULL, NULL),
|
||||
(110, 'Steel Mill Fleming Island', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 14, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'WaOLlr3W3vOfVX9Ockj5', '2024-01-12 15:19:40', '2024-10-31 05:14:32', '{\"Project\":\"Steel Mill Fleming Island\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/WaOLlr3W3vOfVX9Ockj5\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6IkFkVjBYdFNVNTZGbVMza09HUkI1IiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjYyNTY3MDc4Njk4LCJzdWIiOiJ1c2VyX2lkIn0.wGlYDRn67XEe-v8blBXCIQuPR_MSVh5sGe4BNaPXqbc', NULL, NULL, NULL),
|
||||
(111, 'Empower Fit', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 27, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'xLHuykMQdr5MHKkBrs6j', '2024-01-12 15:20:34', '2024-10-31 05:14:54', '{\"Project\":\"Empower Fit\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/xLHuykMQdr5MHKkBrs6j\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6IlNzNFEwN0tRQllCZFhYNmEzV3dBIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjg0OTU5Mjk4MDY5LCJzdWIiOiJ1c2VyX2lkIn0.cDqYs5DRLydsORmrX1z4LzKB7HsLj5v_ZXa5ztnGb68', NULL, NULL, NULL),
|
||||
(112, 'F45 Training Coeur D\'Alene', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 32, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'GFwKOQXHlB2uepE2pGbW', '2024-01-12 15:21:17', '2024-10-31 05:15:05', '{\"Project\":\"F45 Training Coeur D\'Alene\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/GFwKOQXHlB2uepE2pGbW\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6IkU5YnN2ZmZSMFhZcGN2cVRSVmFlIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjU1ODM5OTQxOTY1LCJzdWIiOiJ1c2VyX2lkIn0.1-suMs0NV7pbNr8ZPMp08kKpeGM3oodJkzXvLK7ysmQ', NULL, NULL, NULL),
|
||||
(113, 'Train PTM', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 18, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'rSb1ljJSzwpsUFSFsjB3', '2024-01-12 15:21:46', '2024-10-29 05:12:25', '{\"Project\":\"Train PTM\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/rSb1ljJSzwpsUFSFsjB3\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6Ijc4eHZoTno1U09oRU1GVW5hWXFIIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjg0MTcxMTIwMjgyLCJzdWIiOiJ1c2VyX2lkIn0.4k3F32U6eaY5uXPeKFtnxL7vZ0g0IXLCVSwP826N9AM', NULL, NULL, NULL),
|
||||
(114, 'Midwest Muscle', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 111, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'Wbk0GmUk81r3eClWCBhz', '2024-01-12 15:22:23', '2024-10-31 05:15:11', '{\"Project\":\"Midwest Muscle\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/Wbk0GmUk81r3eClWCBhz\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6IkQzQUtleXpjQ1pLdFhScXFFSVAzIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjg0ODczNTI4MTUyLCJzdWIiOiJ1c2VyX2lkIn0.6GJr0vmf6At4OCBnaF1NpP0zKU0a7GbQ08h3BIFHghY', NULL, NULL, NULL),
|
||||
(115, 'Fit Results', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 0, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', '69qvBR6v28tCkj6TQcJP', '2024-01-12 15:23:23', '2024-02-15 05:04:05', '{\"Project\":\"Fit Results\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/69qvBR6v28tCkj6TQcJP\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6ImtCM1ZKSElVUTBPYWc2aVlXVk5zIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjgzNzUwNTE3NDIyLCJzdWIiOiJ1c2VyX2lkIn0.5WRSIeDryKn8v1yVOFUj4m1hVTSW-ngqIo6mDft39YA', NULL, NULL, NULL),
|
||||
(116, 'Powerhouse Training Covina', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 60, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'lwWBMoeHARriTFBUOLvV', '2024-01-12 15:24:40', '2024-05-30 05:04:22', '{\"Project\":\"Powerhouse Training Covina\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/lwWBMoeHARriTFBUOLvV\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6IjVyM3FLYVMyUlVBNjlwN3dyUk1iIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjg0MjYxMjkyMzM1LCJzdWIiOiJ1c2VyX2lkIn0.QRhvzhFi_1rX0NCdJiSrQq1hywnKi4NKQTzwaBKr45M', NULL, NULL, NULL),
|
||||
(117, 'CJJF Texas', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 26, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'ksrKihTv60Howjov9ew4', '2024-01-12 15:25:35', '2024-10-31 05:15:16', '{\"Project\":\"Lights Out Boxing\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/ksrKihTv60Howjov9ew4\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6IjFvd1FvenU5Z1FrR0d4d1l4Z0k4IiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjg0MTY1MzY3MzgyLCJzdWIiOiJ1c2VyX2lkIn0.1lbrjX8HwdqgVPsgCDsVDzl9Z0gDeVNcaWtOQ2rBX8I', NULL, NULL, NULL),
|
||||
(118, 'Lights Out Boxing', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 121, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'rEOz0hbfpdhO2zQFxHqd', '2024-01-12 15:26:24', '2024-10-10 05:12:46', '{\"Project\":\"Lights Out Boxing\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/rEOz0hbfpdhO2zQFxHqd\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6Im4zTVdlOTliQk5pZDhPM1ljZ2NLIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjYzMDg5NjgwNTI1LCJzdWIiOiJ1c2VyX2lkIn0.7BIpf_19_eEWpAsOi-xR--cYDxxT7oau8U9Wu2sNXyc', NULL, NULL, NULL),
|
||||
(119, 'Booty Lab', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 0, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'C5tXFKwg8fCNiB9NgR8o', '2024-01-12 15:26:46', '2024-10-31 05:15:22', '{\"Project\":\"Booty Lab\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/C5tXFKwg8fCNiB9NgR8o\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6IjduWHZJdExYcXdlTHNOb0pTU1E5IiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjYwMDY2NDQ1NzE2LCJzdWIiOiJ1c2VyX2lkIn0.f0fPlJc6Bdn9_7DvuGCp8DdluL1Z2HO9b2wOdOOTJ0A', NULL, NULL, NULL),
|
||||
(120, 'Legacy Gym Columbus', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 78, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'FeDCSL9ZjKyNrHA4Z4RP', '2024-01-12 15:26:47', '2024-10-31 05:16:03', '{\"Project\":\"Legacy Gym Columbus\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/FeDCSL9ZjKyNrHA4Z4RP\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6IlZMenQwYUxkVlNPVkpmTVFlQ09QIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjgxNDg3OTQ1MjE3LCJzdWIiOiJ1c2VyX2lkIn0.o8w_heWPJfbvgx3IKylHDXrsbD02Dm6Hvbsb9t4lQOg', NULL, NULL, NULL),
|
||||
(121, 'WEFiT', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 7, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'hOCcfOIxELgCOplzNQKK', '2024-01-12 15:26:48', '2024-02-13 05:05:59', '{\"Project\":\"WEFiT\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/hOCcfOIxELgCOplzNQKK\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6ImVmYkFCN3dzWGFyNTVqZ2U5QTBPIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjYzODc4NTUzMDYzLCJzdWIiOiJ1c2VyX2lkIn0.YqLAMFSCga9B-feY15X5JgKkeS9JTa9ht-F7P76jaFk', NULL, NULL, NULL),
|
||||
(122, 'Clean Cut Elite', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 16, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'j8QMNffOtlrpEsi8iUSk', '2024-01-12 15:26:48', '2024-10-31 05:16:09', '{\"Project\":\"Clean Cut Elite\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/j8QMNffOtlrpEsi8iUSk\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6IndsblFzRUFaY29UN1hLVTBsZkdVIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjU5NzE2NDM2NDQxLCJzdWIiOiJ1c2VyX2lkIn0.x_y5xzi4HuUwV_s4eZzCAx2vLjaVYTbcF8YvsNN1NVM', NULL, NULL, NULL),
|
||||
(123, 'Catalyst Fitness', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 43, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'QrP2X2pWwFGqSCyxHoqP', '2024-01-12 15:26:49', '2024-05-25 05:04:23', '{\"Project\":\"Catalyst Fitness\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/QrP2X2pWwFGqSCyxHoqP\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6IlNxZmZzUUxGYjRNR3VqTjJodnQ1IiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjgzODM3OTU3NTM4LCJzdWIiOiJ1c2VyX2lkIn0.-UhhtuJwW3l-JsrgKULkzCepgBxuSAlPr0vY2Vcm0j0', NULL, NULL, NULL),
|
||||
(124, 'LM Fitness Center', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 9, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'HYmJMZlbp380XtXjy5AC', '2024-01-12 15:26:50', '2024-10-31 05:16:14', '{\"Project\":\"LM Fitness Center\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/HYmJMZlbp380XtXjy5AC\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6IlBVaVJwb2EzY1F0eTRsQ1hOM3NGIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjUyNDY3Mjc5NzU4LCJzdWIiOiJ1c2VyX2lkIn0.cOU2jTZMOe_-sTtLcq4bQaLi73DBPjgKP8ENhNKgLDg', NULL, NULL, NULL),
|
||||
(125, 'Buena Park 1 Fit', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 9, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'yCpyFRGKyp6GJCqvC7aG', '2024-01-12 15:26:51', '2024-10-31 05:16:33', '{\"Project\":\"Buena Park 1 Fit\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/yCpyFRGKyp6GJCqvC7aG\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6IkpUWWhxYzI3TUZYdEtGUVduakwyIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjQwMjE0ODU5NDYyLCJzdWIiOiJ1c2VyX2lkIn0.YEq-jJ5CSlxgiiVov60wIrUpXtcvSfROPEvlR4Wx2rs', NULL, NULL, NULL),
|
||||
(131, 'Performance Arc', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 44, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'Bny3AIXD73nPLEQHyLl9', '2024-01-12 15:41:58', '2024-10-31 05:16:49', '{\"Project\":\"Performance Arc\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/Bny3AIXD73nPLEQHyLl9\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6IjN0cjhaNXdSV3RIWlUySTZ4cDZPIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjgzOTIzMDg1NDg0LCJzdWIiOiJ1c2VyX2lkIn0.iwBC7Lu13UQRQ1uaA2uiua0BWgzNqqD7dPsojdOClcQ', NULL, NULL, NULL),
|
||||
(132, 'F45 Training Brookfield', NULL, '{\"sunday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}],\"monday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"tuesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"wednesday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"thursday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"friday\":[{\"from\":\"00:00\",\"to\":\"16:00\",\"point\":\"1\"},{\"from\":\"16:00\",\"to\":\"19:00\",\"point\":\"2\"},{\"from\":\"19:00\",\"to\":\"00:00\",\"point\":\"1\"}],\"saturday\":[{\"from\":\"00:00\",\"to\":\"00:00\",\"point\":\"2\"}]}', 12, 'On', 15, 40, 'https://hook.eu1.make.com/g2cnu8lsuuqf72ufipq20lwa0abb8xkw', 'fdH5SgeBkv9JYGEuksrD', '2024-01-12 15:43:09', '2024-03-06 05:05:45', '{\"Project\":\"F45 Training Brookfield\", \"Calendar Link\":\"https://link.localbestgyms.com/widget/booking/fdH5SgeBkv9JYGEuksrD\"}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2NhdGlvbl9pZCI6IlJyWEtQVWJHZzFvNVA1dVRwNENjIiwiY29tcGFueV9pZCI6IkVFRHdsY2pnQTlYS29CbVQxNVQwIiwidmVyc2lvbiI6MSwiaWF0IjoxNjYzMDg1ODQyODAwLCJzdWIiOiJ1c2VyX2lkIn0.be_Qn0e9ovGfiXLmIIYquhqQFvqqtzwdpQ5jncUEe1w', NULL, NULL, NULL);
|
||||
|
||||
|
||||
|
||||
|
||||
INSERT INTO `report` (`id`, `project`, `location_id`, `type`, `report`, `new_lead`, `outbound_dial`, `pickup`, `conversation`, `booked_appointment`, `callback_request`, `created_at`, `status`, `updated_at`, `date`, `webhook_sent`) VALUES
|
||||
(1, 'CC - Noah Provencal', 607, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-10-31', 0),
|
||||
(2, 'CC - Tom Roseingrave', 520, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-10-31', 0),
|
||||
(3, 'Noah Provencal - Epique Realty', 515, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-10-31', 0),
|
||||
(4, 'CC - Chyann Wray', 915, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-10-31', 0),
|
||||
(5, 'Synergy Day Spa', 931, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-10-31', 0),
|
||||
(6, 'Sqin Toronto', 821, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\n[object Object],Fritz Neri,83,6s,17\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-10-31', 0),
|
||||
(7, 'Botanica Beauty Studio', 1102, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\n[object Object],Fritz Neri,128,45s,36\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-10-31', 0),
|
||||
(8, 'Reign Medspa', 819, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\n[object Object],Fritz Neri,87,18s,20\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-10-31', 0),
|
||||
(9, 'Collagen Bar', 822, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\n[object Object],Fritz Neri,77,26s,20\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-10-31', 0),
|
||||
(10, 'Active Sports Premium Club', 878, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\nActive Sports Premium Club,Bas Nagel,79,4m 7s,32\nActive Sports Premium Club,Bart van Daatselaar,12,6m 33s,10\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-10-31', 0),
|
||||
(11, 'Feel So Good', 881, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\nFeel So Good,Bas Nagel,29,20s,10\nFeel So Good,Bart van Daatselaar,2,24m 49s,1\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-10-31', 0),
|
||||
(12, 'Circles Waalre', 880, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\nCircles Waalre,Bas Nagel,12,2m 25s,5\nCircles Waalre,Bart van Daatselaar,5,18s,4\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-10-31', 0),
|
||||
(13, 'CCM Snapshot Dutch (MASTER)', 877, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-10-31', 0),
|
||||
(14, 'Bodywork Sportstudio', 879, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\nBodywork Sportstudio,Bas Nagel,40,6m 49s,14\nBodywork Sportstudio,Bart van Daatselaar,2,4s,2\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-10-31', 0),
|
||||
(15, 'Victory insulation', 307, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-10-31', 0),
|
||||
(16, 'Apex Commercial Roofing', 674, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\nApex Commercial Roofing,Jhet Baclaan,2,5s,2\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-10-31', 0),
|
||||
(17, 'Choice Roofing Solutions LLC', 379, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-10-31', 0),
|
||||
(18, 'High Efficiency Roofing Solutions LLC', 574, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-10-31', 0),
|
||||
(19, 'Apex Jiu-Jitsu', 1098, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\nApex Jiu-Jitsu,Sergius Padilla,13,37s,10\nApex Jiu-Jitsu,Mohsin Khan,13,37s,10\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-11-01', 0),
|
||||
(20, 'Loyalty Jiu Jitsu Cranbourne', 975, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-11-01', 0),
|
||||
(21, 'Life BJJ Gold Coast', 950, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-11-01', 0),
|
||||
(22, 'FITNAS Studio', 952, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\nFITNAS Studio,Mohsin Khan,15,13s,15\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-11-01', 0),
|
||||
(23, 'Krownd', 1047, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\nKrownd,Ben MacMahon,18,27s,15\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-11-01', 0),
|
||||
(24, 'Le Blanc Med Spa', 901, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\nLe Blanc Med Spa,Joyce Liscano,7,30s,4\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-11-01', 0),
|
||||
(25, 'Sadhna Art of Wellness', 1028, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-11-01', 0),
|
||||
(26, 'Quakertown Academy of MMA & Fitness', 597, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\nQuakertown Academy of MMA & Fitness,Ranelyn Maneja,14,8s,9\nQuakertown Academy of MMA & Fitness,Janine Dalere,12,10s,8\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-11-01', 0),
|
||||
(27, 'Strength Lab', 598, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\nStrength Lab,Ranelyn Maneja,27,6s,14\nStrength Lab,Ichiro Malibong,5,0s,4\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-11-01', 0),
|
||||
(28, 'Carpenter\'s Garcia LLC', 1070, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-11-01', 0),
|
||||
(29, 'Five Stars Contractors', 1081, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\nFive Stars Contractors,Ruche Digital,2,24s,1\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-11-01', 0),
|
||||
(30, 'Carlino Aesthetics', 997, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\nCarlino Aesthetics,David Stark,15,4m 11s,10\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-11-01', 0),
|
||||
(31, 'Tropicalaser', 994, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-11-01', 0),
|
||||
(32, 'Skin Deep Beauty', 993, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-11-01', 0),
|
||||
(33, 'CellTechMD', 956, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\nCellTechMD,David Stark,68,51s,26\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-11-01', 0),
|
||||
(34, 'Bask on Main (test)', 656, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-11-01', 0),
|
||||
(35, 'LifeGaines', 996, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-11-01', 0),
|
||||
(36, 'Anti Aging Med Spa', 1100, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\nAnti Aging Med Spa,David Stark,17,2m 28s,12\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-11-01', 0),
|
||||
(37, 'Hi Doc Medical & Wellness', 995, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\nHi Doc Medical & Wellness,David Stark,15,14s,9\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-11-01', 0),
|
||||
(38, 'NW Mortgage Hub, Inc.', 336, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-11-01', 0),
|
||||
(39, '🥖 MVMT CrossFit', 1068, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\n🥖 MVMT CrossFit,Nella Mezzasoma,11,3s,9\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-11-01', 0),
|
||||
(40, 'GGA Snapshot ENG', 1054, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-11-01', 0),
|
||||
(41, '🥖 CrossFit Hope', 1065, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-11-01', 0),
|
||||
(42, 'Iron Bird Fit', 1062, 'call', 'Company,Agent,Total Calls,Average Duration,Unique Leads\n', NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, NULL, '2024-11-12', 0);
|
||||
|
||||
|
||||
|
||||
INSERT INTO `campaign` (`id`, `name`, `file_id`, `data`, `user_id`, `created_at`, `updated_at`) VALUES
|
||||
(2, 'Sample sheet', '1bLkw9XNBx2XTymsGTZemqzl88KNKsKoooWGVBPtCAi0', NULL, 10, '2024-12-20', '2024-12-20 20:20:49'),
|
||||
(3, 'Testting', '1bLkw9XNBx2XTymsGTZemqzl88KNKsKoooWGVBPtCAi0', NULL, 10, '2024-12-20', '2024-12-20 21:45:44'),
|
||||
(4, 'New2', '1bLkw9XNBx2XTymsGTZemqzl88KNKsKoooWGVBPtCAi0', NULL, 10, '2024-12-20', '2024-12-20 22:04:57'),
|
||||
(5, 'New Emmy', '1bLkw9XNBx2XTymsGTZemqzl88KNKsKoooWGVBPtCAi0', NULL, 2, '2024-12-20', '2024-12-20 22:05:59'),
|
||||
(6, 'Newwww', '1bLkw9XNBx2XTymsGTZemqzl88KNKsKoooWGVBPtCAi0', NULL, 2, '2024-12-20', '2024-12-31 18:16:28'),
|
||||
(7, 'Tst ag', '1bLkw9XNBx2XTymsGTZemqzl88KNKsKoooWGVBPtCAi0', NULL, 2, '2025-01-29', '2025-01-29 12:42:12');
|
||||
|
||||
|
||||
INSERT INTO `user` (`id`, `email`, `password`, `status`, `role`, `company`, `drive_access_token`, `drive_refresh_token`, `created_at`, `updated_at`) VALUES
|
||||
(2, 'admin@manaknight.com', '$2a$10$LVzS6puBDllaSLt5eGxWCubf6qvLPKtk8taUwnU2tzYfQUKikFlwO', 'active', 'admin', 'Team Follow Up', 'ya29.a0AXeO80QLAusz5s5MA7VNCTvTbjCD4fvMUxbpYi3YZxdWkF_oGjaTzqABwRsR_ZgJwbwsDpKHiYZwWjGuZBQrcCF0MlCCdVLxcYHrcOej53a2u1jwLA4y6QUf6X1BaQIZEO-uVUZ0OD5JKrP098LrdrRlYGsiBid67rYXwKVoaCgYKAcMSARASFQHGX2MiZNcwyHXTbgvh6diBE3VT-w0175', '1//03noSXsyWetHtCgYIARAAGAMSNwF-L9IrbWjCPDsg5f1Xk7BroV5KMMaRR4q8bfE2eNdRwTBfXAOjZSN7r_5XCgWph-wGhZBx6xM', NULL, '2025-01-29 12:31:44'),
|
||||
(3, 'adminteam@manaknight.com', '$2y$10$GpPSVZINjfrlMxm0.pVXhO6oCahQQTy32hWyBNg6gMELih5zLdr.W', 'active', 'admin', 'Team Follow Up', NULL, NULL, '2023-12-13', '2023-12-13 19:40:19'),
|
||||
(5, 'admintest@manaknight.com', '$2y$10$tPLIU5zu0h48T/8i443.3epeaLXpbNMuULbQ7Ju9X7cGGrjqAugkK', 'active', 'admin', 'Team Follow Up', NULL, NULL, '2023-12-18', '2023-12-18 20:03:27'),
|
||||
(9, 'gls@manaknight.com', '$2y$10$0o1TpLTQwVqdVcfXhlH0AO3xT79KK0mcN2bh0iSA5IzKNpE7gEKPa', 'active', 'client', 'GLS', NULL, NULL, NULL, '2024-03-04 20:50:40'),
|
||||
(10, 'emmy@manaknight.com', '$2a$10$LVzS6puBDllaSLt5eGxWCubf6qvLPKtk8taUwnU2tzYfQUKikFlwO', 'active', 'client', 'Team Follow Up', 'ya29.a0ARW5m77GHU7yS5Cfk0zxjL-PcN1oQ4usNwkIfKCChflapA_991O5mVXdZFsHhuiAL8dnHs0CpAJguNrjGwnaMF4YFn7IxVC5Tlddk20kKX2z_L5TQWhykLVXbmCpamcbji6gnwPjaMAnt_S1_3OZp7NDd7zpfoz0uhZIJti5aCgYKAbESARASFQHGX2MibQDtWjAw9btwO5P7urVgEw0175', '1//03nzwtiJYvYkvCgYIARAAGAMSNwF-L9IrZLUjJLpTPSqABB-s_NVAS5stK_h9yZ0tUHED4yTpifxifaaxCOaAJqYnEdq8j_2Rnh8', '2024-02-16', '2025-01-09 18:58:57'),
|
||||
(11, 'teamfollowup@manaknight.com', '$2y$10$e189rlZouySxKwsLLVtqxO2e8cbQzyh8b1b2f4eiKPfFaurUPbjQm', 'active', 'client', 'Team Follow Up', NULL, NULL, '2024-02-16', '2024-03-04 20:31:07'),
|
||||
(14, 'testcomp@mkd.com', '$2y$10$BhVBeBoQobBtqxTydotETOmGHu31sqBxmh5lVeVLl8BBUPQv9yAB2', 'active', 'client', 'Comp', NULL, NULL, '2024-03-04', '2024-03-04 20:35:30');
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
require __DIR__ . '/lib/redbean/rb-mysql.php';
|
||||
/*Powered By: Manaknightdigital Inc. https://manaknightdigital.com/ Year: 2021*/
|
||||
/**
|
||||
* MySqlDatabaseService
|
||||
* @copyright 2021 Manaknightdigital Inc.
|
||||
* @link https://manaknightdigital.com
|
||||
* @license Proprietary Software licensing
|
||||
* @author Ryan Wong
|
||||
*
|
||||
*/
|
||||
class MySqlAdapter
|
||||
{
|
||||
private static $instance = null;
|
||||
private $_con;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$config = MkdConfig::get_instance()->get_config();
|
||||
R::setup("mysql:host={$config['database-hostname']};dbname={$config['database-name']}", "{$config['database-u-ser']}", "{$config['database-password']}");
|
||||
// R::debug(TRUE, 2);
|
||||
$this->_con = R::getToolBox();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Instance
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get_instance()
|
||||
{
|
||||
if (self::$instance == null) {
|
||||
self::$instance = new MySqlAdapter();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Connection
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_connection()
|
||||
{
|
||||
return $this->_con;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,658 @@
|
||||
<?php
|
||||
require 'mysql-adapter.php';
|
||||
/*Powered By: Manaknightdigital Inc. https://manaknightdigital.com/ Year: 2021*/
|
||||
/**
|
||||
* MySqlDatabaseService
|
||||
* @copyright 2021 Manaknightdigital Inc.
|
||||
* @link https://manaknightdigital.com
|
||||
* @license Proprietary Software licensing
|
||||
* @author Ryan Wong
|
||||
*
|
||||
*/
|
||||
class MySqlDatabaseService
|
||||
{
|
||||
protected $_db = NULL;
|
||||
protected $_instance = NULL;
|
||||
protected $_table = '';
|
||||
protected $_primary_key = 'id';
|
||||
protected $_return_type = 'array';
|
||||
protected $_allowed_fields = [];
|
||||
protected $_label_fields = [];
|
||||
protected $_use_timestamps = TRUE;
|
||||
protected $_created_field = 'created_at';
|
||||
protected $_updated_field = 'updated_at';
|
||||
protected $_validation_rules = [];
|
||||
protected $_validation_edit_rules = [];
|
||||
protected $_validation_messages = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->_instance = MySqlAdapter::get_instance();
|
||||
$this->_db = $this->_instance->get_connection();
|
||||
}
|
||||
|
||||
/**
|
||||
* If you need to modify payload before create, overload this function
|
||||
*
|
||||
* @param mixed $data
|
||||
* @return mixed
|
||||
*/
|
||||
protected function _pre_create_processing($data)
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* If you need to modify payload before edit, overload this function
|
||||
*
|
||||
* @param mixed $data
|
||||
* @return mixed
|
||||
*/
|
||||
protected function _post_edit_processing($data)
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow user to add extra counting condition so user don't have to change main function
|
||||
*
|
||||
* @param mixed $parameters
|
||||
* @return $db
|
||||
*/
|
||||
protected function _custom_counting_conditions(&$db)
|
||||
{
|
||||
return $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Raw Mysql query
|
||||
*
|
||||
* @param string $sql
|
||||
* @return mixed
|
||||
*/
|
||||
public function raw_query($sql)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Raw no error query for writes
|
||||
*
|
||||
* @param string $sql
|
||||
* @return mixed
|
||||
*/
|
||||
public function raw_no_error_query($sql)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Raw Mysql query
|
||||
*
|
||||
* @param string $sql
|
||||
* @return mixed
|
||||
*/
|
||||
public function raw_prepare_query($sql, $parameters)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get Model
|
||||
*
|
||||
* @param integer $id
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($id)
|
||||
{
|
||||
return R::load($this->_table, $id);
|
||||
}
|
||||
public function get_like($field, $value)
|
||||
{
|
||||
// Match strings that start with $value
|
||||
return R::find($this->_table, ' ' . $field . ' LIKE ? ORDER BY id DESC LIMIT 1', [$value . '%']);
|
||||
}
|
||||
// public function get_like($field, $value)
|
||||
// {
|
||||
// // Use a custom SQL query with REGEXP for more precise pattern matching
|
||||
// $sql = 'SELECT * FROM ' . $this->_table . ' WHERE ' . $field . ' LIKE ? ORDER BY id DESC';
|
||||
// return R::getAll($sql, [$value . '%']);
|
||||
// }
|
||||
/**
|
||||
* Get Model by field
|
||||
*
|
||||
* @param string $field
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_by_field($field, $value)
|
||||
{
|
||||
return R::findOne($this->_table, " $field = ? ", ["$value"]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get One Model by fields
|
||||
*
|
||||
* @param string $field
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_one_by_fields($where)
|
||||
{
|
||||
$sql = [];
|
||||
foreach ($where as $key => $value) {
|
||||
if (is_string($value) && strlen($value) > 0) {
|
||||
$sql[] = " `$key` = '$value' ";
|
||||
} else {
|
||||
$sql[] = " `$key` = '$value' ";
|
||||
}
|
||||
}
|
||||
|
||||
return R::findOne($this->_table, implode(' AND ', $sql));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Model by fields
|
||||
*
|
||||
* @param string $field
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_by_fields($where)
|
||||
{
|
||||
$sql = [];
|
||||
foreach ($where as $key => $value) {
|
||||
if (is_string($value) && strlen($value) > 0) {
|
||||
// $sql[] = "$key";
|
||||
// echo "1";
|
||||
$sql[] = " `$key` = '$value' ";
|
||||
} else {
|
||||
$sql[] = " `$key` = '$value' ";
|
||||
}
|
||||
}
|
||||
|
||||
return R::find($this->_table, implode(' AND ', $sql));
|
||||
// R::fancyDebug( TRUE );
|
||||
|
||||
// $logs = R::getDatabaseAdapter()
|
||||
// ->getDatabase()
|
||||
// ->getLogger();
|
||||
// echo "<pre>";
|
||||
// print_r( $where );
|
||||
// print_r( $sql );
|
||||
// print_r( $logs->grep( 'SELECT' ) );
|
||||
// die;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all Model
|
||||
* @param array $where
|
||||
* @return array
|
||||
*/
|
||||
public function get_all($where = array())
|
||||
{
|
||||
$sql = [];
|
||||
foreach ($where as $key => $value) {
|
||||
if (is_string($value) && strlen($value) > 0) {
|
||||
$sql[] = "$key";
|
||||
} else {
|
||||
$sql[] = "$key = $value";
|
||||
}
|
||||
}
|
||||
|
||||
return R::findAll($this->_table, implode(' AND ', $sql));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all Model
|
||||
* @param string $status
|
||||
* @return array
|
||||
*/
|
||||
public function get_all_by_status($status)
|
||||
{
|
||||
return R::findAll($this->_table, "status = ", [$status]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all Model key value
|
||||
* @param string $field
|
||||
* @param string $status
|
||||
* @return array
|
||||
*/
|
||||
public function get_all_by_key_value($field, $status)
|
||||
{
|
||||
$results = R::findAll($this->_table, "status = ", [$status]);
|
||||
$key_value = [];
|
||||
|
||||
foreach ($results as $key => $value) {
|
||||
$key_value[$value['id']] = $value[$field];
|
||||
}
|
||||
|
||||
return $key_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create
|
||||
*
|
||||
* @param array $data
|
||||
* @return mixed
|
||||
*/
|
||||
public function create($data)
|
||||
{
|
||||
try {
|
||||
if ($this->_use_timestamps) {
|
||||
if (!isset($data[$this->_created_field])) {
|
||||
$data[$this->_created_field] = date('Y-m-j');
|
||||
}
|
||||
if (!isset($data[$this->_updated_field])) {
|
||||
$data[$this->_updated_field] = date('Y-m-j H:i:s');
|
||||
}
|
||||
}
|
||||
|
||||
$data = $this->_pre_create_processing($data);
|
||||
|
||||
$row = R::dispense($this->_table);
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
$row[$key] = $value;
|
||||
}
|
||||
|
||||
$id = R::store($row);
|
||||
|
||||
if ($id) {
|
||||
return $id;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
} catch (Exception $e) {
|
||||
echo $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk Create
|
||||
*
|
||||
* @param [type] $params
|
||||
* @return void
|
||||
*/
|
||||
public function batch_insert($params)
|
||||
{
|
||||
if ($this->_use_timestamps) {
|
||||
$rows = [];
|
||||
foreach ($params as $key => $value) {
|
||||
$params[$key][$this->_created_field] = date('Y-m-j');
|
||||
$params[$key][$this->_updated_field] = date('Y-m-j H:i:s');
|
||||
$row = R::dispense($this->_table);
|
||||
|
||||
foreach ($value as $field => $val) {
|
||||
$row[$field] = $val;
|
||||
}
|
||||
|
||||
$rows[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
return R::storeAll($rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit Model
|
||||
* @param array $data
|
||||
* @param integer $id
|
||||
* @return bool
|
||||
*/
|
||||
public function edit($data, $id)
|
||||
{
|
||||
if ($this->_use_timestamps) {
|
||||
if (!isset($data[$this->_updated_field])) {
|
||||
$data[$this->_updated_field] = date('Y-m-j H:i:s');
|
||||
}
|
||||
}
|
||||
|
||||
$data = $this->_post_edit_processing($data);
|
||||
|
||||
$row = R::load($this->_table, $id);
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
$row[$key] = $value;
|
||||
}
|
||||
|
||||
return R::store($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit Model
|
||||
* @param array $data
|
||||
* @param integer $id
|
||||
* @return bool
|
||||
*/
|
||||
public function edit_raw($data, $id)
|
||||
{
|
||||
if ($this->_use_timestamps) {
|
||||
$data[$this->_updated_field] = date('Y-m-j H:i:s');
|
||||
}
|
||||
|
||||
$row = R::load($this->_table, $id);
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
$row[$key] = $value;
|
||||
}
|
||||
|
||||
return R::store($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Soft Delete Model
|
||||
* @param array $data
|
||||
* @param integer $id
|
||||
* @return bool
|
||||
*/
|
||||
public function delete($id)
|
||||
{
|
||||
$row = R::load($this->_table, $id);
|
||||
$row['status'] = 0;
|
||||
return R::store($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Real Delete Model
|
||||
* @param integer $id
|
||||
* @return bool
|
||||
*/
|
||||
public function real_delete($id)
|
||||
{
|
||||
$row = R::load($this->_table, $id);
|
||||
R::trash($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Real Delete Model
|
||||
* @param Array
|
||||
* @return bool
|
||||
*/
|
||||
public function real_delete_by_fields($where = [])
|
||||
{
|
||||
$sql = [];
|
||||
foreach ($where as $key => $value) {
|
||||
if (is_string($value) && strlen($value) > 0 && is_int($key)) {
|
||||
$sql[] = "$value";
|
||||
} else {
|
||||
$sql[] = "$key = $value";
|
||||
}
|
||||
}
|
||||
|
||||
$rows = R::find($this->_table, implode(' AND ', $sql));
|
||||
foreach ($rows as $row) {
|
||||
R::trash($row);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Real Delete Model
|
||||
* @return bool
|
||||
*/
|
||||
public function real_delete_all()
|
||||
{
|
||||
return R::wipe($this->_table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get All Validation Rules
|
||||
*
|
||||
* @param string $key
|
||||
* @return array
|
||||
*/
|
||||
public function get_all_validation_rule()
|
||||
{
|
||||
return $this->_validation_rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get All Allowed Rules
|
||||
*
|
||||
* @param string $key
|
||||
* @return array
|
||||
*/
|
||||
public function get_all_allowed_fields()
|
||||
{
|
||||
return $this->_allowed_fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get All Edit Validation Rules
|
||||
*
|
||||
* @param string $key
|
||||
* @return array
|
||||
*/
|
||||
public function get_all_edit_validation_rule()
|
||||
{
|
||||
return $this->_validation_edit_rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill validation rules
|
||||
*
|
||||
* @param mixed $form_validation
|
||||
* @param mixed $validation_rules
|
||||
* @return void
|
||||
*/
|
||||
public function set_form_validation($form_validation, $validation_rules)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Count number of model
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $parameters
|
||||
* @return integer $result
|
||||
*/
|
||||
public function count($parameters)
|
||||
{
|
||||
$sql = [];
|
||||
foreach ($parameters as $key => $value) {
|
||||
if (is_string($value) && strlen($value) > 0) {
|
||||
$sql[] = "$key";
|
||||
} else {
|
||||
$sql[] = "$key = $value";
|
||||
}
|
||||
}
|
||||
|
||||
return R::count($this->_table, implode(' AND ', $sql));
|
||||
}
|
||||
|
||||
/**
|
||||
* Paginated
|
||||
*
|
||||
* @param integer $page
|
||||
* @param integer $limit
|
||||
* @param array $where
|
||||
* @param string $order_by
|
||||
* @param string $direction
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_paginated($page = 0, $limit = 25, $parameters = [], $order_by = '', $direction = 'ASC')
|
||||
{
|
||||
$sql = [];
|
||||
$last_id = 0;
|
||||
foreach ($parameters as $key => $value) {
|
||||
if (is_string($value) && strlen($value) > 0 && is_numeric($key)) {
|
||||
$sql[] = " $value ";
|
||||
} else {
|
||||
$sql[] = " `$key` = $value ";
|
||||
}
|
||||
}
|
||||
|
||||
// echo "<pre>";
|
||||
// print_r( $sql);
|
||||
// print_r( $parameters);
|
||||
// die;
|
||||
|
||||
$total = R::count($this->_table, implode(' AND ', $sql));
|
||||
$offset = ($page - 1) * $limit;
|
||||
$limit_query = " ORDER BY $order_by $direction LIMIT $offset, $limit ";
|
||||
$final_list = R::find($this->_table, implode(' AND ', $sql) . $limit_query);
|
||||
|
||||
//select MODE 2 to see parameters filled in
|
||||
// R::fancyDebug(); //since 4.2
|
||||
|
||||
// $logs = R::getDatabaseAdapter()
|
||||
// ->getDatabase()
|
||||
// ->getLogger();
|
||||
// echo "<pre>";
|
||||
// print_r( $logs);
|
||||
// die;
|
||||
|
||||
// echo "<pre>";
|
||||
// print_r( $sql);
|
||||
// print_r( implode(' AND ', $sql));
|
||||
// $logs = R::getDatabaseAdapter()->getDatabase()->getLogger();
|
||||
// print_r( $final_list);
|
||||
// print_r( $logs);
|
||||
// die;
|
||||
if ($final_list) {
|
||||
$last_id = $final_list[array_key_last($final_list)]['id'];
|
||||
}
|
||||
|
||||
return [
|
||||
'total' => $total,
|
||||
'last_page' => ceil($total / $limit),
|
||||
'page' => $page,
|
||||
'id' => $last_id,
|
||||
'data' => $final_list
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Cursor Pagination
|
||||
*
|
||||
* @param integer $page
|
||||
* @param integer $limit
|
||||
* @param array $where
|
||||
* @param string $order_by
|
||||
* @param string $direction
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_cursor_paginated($page = 1, $limit = 25, $parameters = [], $order_by = '', $direction = 'ASC', $id)
|
||||
{
|
||||
$sql = [];
|
||||
$last_id = 0;
|
||||
|
||||
foreach ($parameters as $key => $value) {
|
||||
if (is_string($value) && strlen($value) > 0) {
|
||||
$sql[] = "$key";
|
||||
} else {
|
||||
$sql[] = "$key = $value";
|
||||
}
|
||||
}
|
||||
|
||||
if (count($sql) < 1) {
|
||||
$total = R::count($this->_table);
|
||||
} else {
|
||||
$total = R::count($this->_table, implode(' AND ', $sql));
|
||||
}
|
||||
|
||||
$limit_query = " ORDER BY $order_by $direction LIMIT $limit";
|
||||
$sql[] = 'id > ' . $id;
|
||||
$final_list = R::find($this->_table, implode(' AND ', $sql) . $limit_query);
|
||||
if ($final_list) {
|
||||
$last_id = $final_list[array_key_last($final_list)]['id'];
|
||||
}
|
||||
$last_page = ceil($total / $limit);
|
||||
return [
|
||||
'total' => $total,
|
||||
'size' => $limit,
|
||||
'page' => $page,
|
||||
'last_page' => $last_page,
|
||||
'data' => $final_list,
|
||||
'id' => $last_id
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Join All
|
||||
*
|
||||
* @param string $table
|
||||
* @param string $field
|
||||
* @param array $where
|
||||
* @param array $custom_duplicate_names
|
||||
* @return void
|
||||
*/
|
||||
public function _join($table, $field, $where, $custom_duplicate_names = [])
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Join Paginate
|
||||
*
|
||||
* @param string $table
|
||||
* @param string $field
|
||||
* @param array $where
|
||||
* @param integer $page
|
||||
* @param integer $limit
|
||||
* @param string $order_by
|
||||
* @param string $direction
|
||||
* @param array $custom_duplicate_names
|
||||
* @return mixed
|
||||
*/
|
||||
public function _join_paginate($table, $field, $where, $page = 0, $limit = 10, $order_by = '', $direction = 'ASC', $custom_duplicate_names = [])
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter all keys before inserting to make sure they are allowed
|
||||
*
|
||||
* @param mixed $data
|
||||
* @return mixed
|
||||
*/
|
||||
protected function _filter_allow_keys($data)
|
||||
{
|
||||
$clean_data = [];
|
||||
$allowed_fields = $this->_allowed_fields;
|
||||
$allowed_fields[] = $this->_primary_key;
|
||||
|
||||
if ($this->_use_timestamps) {
|
||||
$allowed_fields[] = $this->_created_field;
|
||||
$allowed_fields[] = $this->_updated_field;
|
||||
}
|
||||
|
||||
foreach ($data as $key => $val) {
|
||||
if (!in_array($key, $allowed_fields)) {
|
||||
continue;
|
||||
}
|
||||
$clean_data[$key] = $val;
|
||||
}
|
||||
return $clean_data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* escapeLikeString data
|
||||
*
|
||||
* @param mixed $data
|
||||
* @return mixed
|
||||
*/
|
||||
public function escapeLikeString($data)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Last ID in table
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function get_last_id()
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Database Table Schema
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_schema()
|
||||
{
|
||||
return R::inspect($this->_table);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
include_once 'lib/ghl/oauth2.php';
|
||||
include_once 'config.php';
|
||||
include_once 'project-model.php';
|
||||
|
||||
|
||||
$config = MkdConfig::get_instance()->get_config();
|
||||
|
||||
$oauth = new GHLOAuth2($config);
|
||||
|
||||
|
||||
Route::add('/ghl/authorize/([a-zA-Z0-9]+)', function ($id) use ($oauth) {
|
||||
check_login();
|
||||
|
||||
$url = $oauth->getAuthorizationUrl();
|
||||
$redirect_uri = $_GET['redirect_uri'];
|
||||
$project = explode('p', $id)[0];
|
||||
$page = explode('p', $id)[1];
|
||||
$state = $project . '|' . $redirect_uri . '|' . $page;
|
||||
|
||||
header('Location: ' . $url . '&state=' . $state);
|
||||
exit;
|
||||
}, 'get');
|
||||
|
||||
|
||||
Route::add('/ghl/callback', function () use ($oauth) {
|
||||
check_login();
|
||||
|
||||
$code = $_GET['code'];
|
||||
$projectId = explode('|', $_GET['state'])[0] ?? 0;
|
||||
$redirect_uri = explode('|', $_GET['state'])[1] ?? '/admin/project';
|
||||
$page = explode('|', $_GET['state'])[2] ?? 1;
|
||||
|
||||
$token = $oauth->getAccessToken($code);
|
||||
if (!$token['success']) {
|
||||
header('Location: ' . $redirect_uri . '?oauth_error=' . $token['error'] . '&page=' . $page);
|
||||
exit;
|
||||
}
|
||||
|
||||
$projectModel = new ProjectModel();
|
||||
$data = [
|
||||
'access_token' => $token['data']['access_token'],
|
||||
'refresh_token' => $token['data']['refresh_token'],
|
||||
'token_expiry' => $token['data']['expires_in'],
|
||||
];
|
||||
$projectModel->edit($data, $projectId);
|
||||
|
||||
// $page = guessProjectPage($projectId);
|
||||
|
||||
|
||||
|
||||
header('Location: ' . $redirect_uri . '?oauth_success=true&page=' . $page);
|
||||
exit;
|
||||
}, 'get');
|
||||
|
||||
|
||||
|
||||
function guessProjectPage($projectId, $limit = 15) {
|
||||
$projectModel = new ProjectModel();
|
||||
|
||||
// Get the project to find its position
|
||||
$project = $projectModel->get($projectId);
|
||||
|
||||
if ($project) {
|
||||
// Use the same parameters as the listing page would use
|
||||
$parameters = [];
|
||||
|
||||
// Get all projects with higher IDs (which come before in DESC order)
|
||||
$parameters[] = "id > " . $projectId;
|
||||
$projectsBefore = $projectModel->count($parameters);
|
||||
|
||||
// Add 1 to account for current page
|
||||
$pageNumber = floor($projectsBefore / $limit) + 1;
|
||||
|
||||
return $pageNumber;
|
||||
}
|
||||
|
||||
return 1; // Default to first page if project not found
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
Flight::route('POST /v1/api/image/add', function()
|
||||
{
|
||||
|
||||
|
||||
$model = new ImagesModel();
|
||||
$allow_fields = ['url', 'user_id', 'caption'];
|
||||
$validation = new ValidationService();
|
||||
$request = new Auth_common_function();
|
||||
$validation->save_rules($model->get_all_validation_rule());
|
||||
$_POST = Flight::request()->data->getData();
|
||||
if ($validation->validate($_POST))
|
||||
{
|
||||
$result = $model->create([
|
||||
'url' => $request->get_input_post('url'),
|
||||
'user_id' => $request->get_input_post('user_id'),
|
||||
'caption' => $request->get_input_post('caption'),
|
||||
]);
|
||||
|
||||
if ($result)
|
||||
{
|
||||
echo json_encode([
|
||||
'code' => 200,
|
||||
'error' => false,
|
||||
'id' => $result
|
||||
]);
|
||||
http_response_code(200);
|
||||
exit;
|
||||
}
|
||||
else
|
||||
{
|
||||
echo json_encode([
|
||||
'code' => 409,
|
||||
'error' => false
|
||||
]);
|
||||
http_response_code(409);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$message = $validation->get_errors();
|
||||
$result['error'] = true;
|
||||
$result['http_code'] = 403;
|
||||
$result['message'] = $message;
|
||||
|
||||
echo json_encode($result);
|
||||
http_response_code(403);
|
||||
exit;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,103 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
|
||||
<title>Privacy Policy | Team Follow-up </title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container py-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-12 text-center">
|
||||
<h1>Privacy Policy</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<h2>Introduction</h2>
|
||||
|
||||
<p>Darcan LTD trading as Call Center Mastery (herein referred to as Call Center Mastery) understands that your privacy is important to you. We are committed to protecting the privacy of your personally-identifiable information as you use this website. This Privacy Policy tells you how we protect and use information that we gather from you. By using this website, you consent to the terms described in the most recent version of this Privacy Policy. You should also read our Terms of Use to understand the general rules about your use of this website, and any additional terms that may apply when you access particular services or materials on certain areas of this website. “We,” “our” means Call Center Mastery and its affiliates. “You,” “your,” visitor,” or “user” means the individual accessing this site.</p>
|
||||
|
||||
<h2>Personal and non-personal information</h2>
|
||||
|
||||
<p>Our Privacy Policy identifies how we treat your personal and non-personal information.</p>
|
||||
|
||||
<h3>What is non-personal information and how is it collected and used?</h3>
|
||||
|
||||
<p>Non personal information is information that cannot identify you. If you visit this web site to read information, such as information about one of our services, we may collect certain non-personal information about you from your computer’s web browser. Because non-personal information cannot identify you or be tied to you in any way, there are no restrictions on the ways that we can use or share non-personal information.</p>
|
||||
|
||||
<h3>What is personal information and how is it collected?</h3>
|
||||
|
||||
<p>Personal information is information that identifies you as an individual, such as your name, mailing address, e-mail address, telephone number, and fax number. We may collect personal information from you in a variety of ways:</p>
|
||||
|
||||
<ul>
|
||||
<li>When you send us an application or other form</li>
|
||||
<li>When you conduct a transaction with us, our affiliates, or others</li>
|
||||
<li>When we collect information about in you in support of a transaction, such as credit card information</li>
|
||||
</ul>
|
||||
|
||||
<p>In some places on this web site you have the opportunity to send us personal information about yourself, to elect to receive particular information, to purchase access to one of our products or services, or to participate in an activity.</p>
|
||||
|
||||
<h3>Are cookies or other technologies used to collect personal information?</h3>
|
||||
|
||||
<p>Yes, we may use cookies and related technologies, such as web beacons, to collect information on our web site. A cookie is a text file that is placed on your hard disk by a web page server. Cookies cannot be used to run programs or deliver viruses to your computer. Cookies are uniquely assigned to you, and can only be read by a web server in the domain that issued the cookie to you. One of the primary purposes of cookies is to provide a convenience feature to save you time. The purpose of a cookie is to tell the Web server that you have returned to a specific page. For example, if you register with us, a cookie helps Call Center Mastery to recall your specific information on subsequent visits. This simplifies the process of recording your personal information, such as billing addresses, shipping addresses, and so on. When you return to the same Call Center Mastery website, the information you previously provided can be retrieved, so you can easily use the features that you customized.</p>
|
||||
|
||||
<p>A web beacon is a small graphic image that allows the party that set the web beacon to monitor and collect certain information about the viewer of the web page, web-based document or e-mail message, such as the type of browser requesting the web beacon, the IP address of the computer that the web beacon is sent to and the time the web beacon was viewed. Web beacons can be very small and invisible to the user, but, in general, any electronic image viewed as part of a web page or e-mail, including HTML based content, can act as a web beacon. We may use web beacons to count visitors to the web pages on the web site or to monitor how our users navigate the web site, and we may include web beacons in e-mail messages in order to count how many messages sent were actually opened, acted upon or forwarded.</p>
|
||||
|
||||
<p>Third party vendors also may use cookies on our web site. For instance, we may contract with third parties who will use cookies on our web site to track and analyze anonymous usage and volume statistical information from our visitors and members. Such information is shared externally only on an anonymous, aggregated basis. These third parties use persistent cookies to help us to improve the visitor experience, to manage our site content, and to track visitor behaviour. We may also contract with a third party to send e-mail to our registered [users/members].</p>
|
||||
|
||||
<p>To help measure and improve the effectiveness of our e-mail communications, the third party sets cookies. All data collected by this third party on behalf of Call Center Mastery is used solely by or on behalf of Call Center Mastery and is shared externally only on an anonymous, aggregated basis. From time to time we may allow third parties to post advertisements on our web site, and those third-party advertisements may include a cookie or web beacon served by the third party. This Privacy Policy does not cover the use of information collected from you by third party ad servers. We do not control cookies in such third party ads, and you should check the privacy policies of those advertisers and/or ad services to learn about their use of cookies and other technology before linking to an ad. We will not share your personal information with these companies, but these companies may use information about your visits to this and other web sites in order to provide advertisements on this site and other sites about goods and services that may be of interest to you, and they may share your personal information that you provide to them with others.</p>
|
||||
|
||||
<p>You have the ability to accept or decline cookies. Most Web browsers automatically accept cookies, but you can usually modify your browser setting to decline cookies if you prefer. If you choose to decline cookies, you may not be able to fully experience the interactive features of the Call Center Mastery websites you visit.</p>
|
||||
|
||||
<h2>How does Call Center Mastery use personal information?</h2>
|
||||
|
||||
<p>Call Center Mastery may keep and use personal information we collect from or about you to provide you with access to this web site or other products or services, to respond to your requests, to bill you for products/services you purchased, and to provide ongoing service and support, to contact you with information that might be of interest to you, including information about products and services of ours and of others, or ask for your opinion about our products or the products of others, for record keeping and analytical purposes and to research, develop and improve programs, products, services and content.</p>
|
||||
|
||||
<p>Personal information collected online may be combined with information you provide to us through other sources We may also remove your personal identifiers (your name, email address, social security number, etc). In this case, you would no longer be identified as a single unique individual. Once we have de-identified information, it is non-personal information and we may treat it like other non-personal information. Finally, we may use your personal information to protect our rights or property, or to protect someone’s health, safety or welfare, and to comply with a law or regulation, court order or other legal process.</p>
|
||||
|
||||
<h2>Does Call Center Mastery share personal information with others?</h2>
|
||||
|
||||
<p>We will not share your personal information collected from this web site with an unrelated third party without your permission, except as otherwise provided in this Privacy Policy. In the ordinary course of business, we may share some personal information with companies that we hire to perform services or functions on our behalf. In all cases in which we share your personal information with a third party for the purpose of providing a service to us, we will not authorize them to keep, disclose or use your information with others except for the purpose of providing the services we asked them to provide.</p>
|
||||
|
||||
<p>We will not sell, exchange or publish your personal information, except in conjunction with a corporate sale, merger, dissolution, or acquisition. For some sorts of transactions, in addition to our direct collection of information, our third party service vendors (such as credit card companies, clearinghouses and banks) who may provide such services as credit, insurance, and escrow services may collect personal information directly from you to assist you with your transaction. We do not control how these third parties use such information, but we do ask them to disclose how they use your personal information before they collect it. If you submit a review for a third party (person or business) using our Facebook Fan Review Application, during the submission process we ask your permission to gather your basic information (such as name and email address) which we then share with the third party for whom you are submitting the review. We may be legally compelled to release your personal information in response to a court order, subpoena, search warrant, law or regulation.</p>
|
||||
|
||||
<p>We may cooperate with law enforcement authorities in investigating and prosecuting web site visitors who violate our rules or engage in behavior, which is harmful to other visitors (or illegal). We may disclose your personal information to third parties if we feel that the disclosure is necessary to protect our rights or property, protect someone’s health, safety or welfare, or to comply with a law or regulation, court order or other legal process. As discussed in the section on cookies and other technologies, from time to time we may allow a third party to serve advertisements on this web site.</p>
|
||||
|
||||
<p>If you share information with the advertiser, including by clicking on their ads, this Privacy Policy does not control the advertisers use of your personal information, and you should check the privacy policies of those advertisers and/or ad services to learn about their use of cookies and other technology before linking to an ad.</p>
|
||||
|
||||
<h2>How is personal information used for communications?</h2>
|
||||
|
||||
<p>We may contact you periodically by e-mail, mail or telephone to provide information regarding programs, products, services and content that may be of interest to you. In addition, some of the features on this web site allow you to communicate with us using an online form. If your communication requests a response from us, we may send you a response via e-mail. The e-mail response or confirmation may include your personal information. We cannot guarantee that our e-mails to you will be secure from unauthorized interception.</p>
|
||||
|
||||
<h2>How is personal information secured?</h2>
|
||||
|
||||
<p>We have implemented generally accepted standards of technology and operational security in order to protect personally-identifiable information from loss, misuse, alteration, or destruction. Only authorized personnel and third party vendors have access to your personal information, and these employees and vendors are required to treat this information as confidential. Despite these precautions, we cannot guarantee that unauthorized persons will not obtain access to your personal information.</p>
|
||||
|
||||
<h2>Links</h2>
|
||||
|
||||
<p>This site contains links to other sites that provide information that we consider to be interesting. Call Center Mastery is not responsible for the privacy practices or the content of such web sites.</p>
|
||||
|
||||
<h2>Public discussions</h2>
|
||||
|
||||
<p>This site may provide public discussions on various business valuation topics. Please note that any information you post in these discussions will become public, so please do not post sensitive information in the public discussions. Whenever you publicly disclose information online, that information could be collected and used by others. We are not responsible for any action or policies of any third parties who collect information that users disclose in any such forums on the web site. Call Center Mastery does not agree or disagree with anything posted on the discussion board. Also remember that you must comply with our other published policies regarding postings on our public forums.</p>
|
||||
|
||||
<h2>How can a user access, change, and/or delete personal information?</h2>
|
||||
|
||||
<p>You may access, correct, update, and/or delete any personally-identifiable information that you submit to the web site. You may also unsubscribe from mailing lists or any registrations on the web site. To do so, please either follow instructions on the page of the web site on which you have provided such information or subscribed or registered or contact us at [admin@cc-mastery.com]</p>
|
||||
|
||||
<h2>Children’s privacy</h2>
|
||||
|
||||
<p>Call Center Mastery will not intentionally collect any personal information (such as a child’s name or email address) from children under the age of 13. If you think that we have collected personal information from a child under the age of 13, please contact us.</p>
|
||||
|
||||
<h2>Changes</h2>
|
||||
|
||||
<p>Call Center Mastery reserves the right to modify this statement at any time. Any changes to this Privacy Policy will be listed in this section, and if such changes are material, a notice will be included on the homepage of the web site for a period of time. If you have any questions about privacy at any websites operated by Call Center Mastery or about our website practices, please contact us at: admin@cc-mastery.com</p>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
include_once __DIR__ . "/mysql-database-service.php";
|
||||
|
||||
class ProjectModel extends MySqlDatabaseService
|
||||
{
|
||||
protected $_table = 'project';
|
||||
protected $_primary_key = 'id';
|
||||
protected $_return_type = 'array';
|
||||
protected $_allowed_fields = [
|
||||
'id', 'slot', 'days', 'score_threshold', 'actual_score', 'alert', 'webhook', 'payload', 'calendar'
|
||||
];
|
||||
protected $_label_fields = [
|
||||
'ID', 'Slot', 'Days', 'Score Threshold', 'Actual Score', 'Alert', 'Webhook', 'Payload', 'Calendar'
|
||||
];
|
||||
protected $_use_timestamps = true;
|
||||
protected $_created_field = 'created_at';
|
||||
protected $_updated_field = 'updated_at';
|
||||
protected $_validation_rules = [
|
||||
['slot', 'Slot', 'required'],
|
||||
['days', 'Days', 'required'],
|
||||
['score_threshold', 'Score Threshold', 'required'],
|
||||
['actual_score', 'Actual Score', 'required'],
|
||||
['alert', 'Alert', 'required'],
|
||||
['webhook', 'Webhook', 'required'],
|
||||
['payload', 'Payload', 'required'],
|
||||
['calendar', 'Calendar', 'required']
|
||||
];
|
||||
|
||||
protected $_validation_edit_rules = [
|
||||
['slot', 'Slot', 'required'],
|
||||
['days', 'Days', 'required'],
|
||||
['score_threshold', 'Score Threshold', 'required'],
|
||||
['actual_score', 'Actual Score', 'required'],
|
||||
['alert', 'Alert', 'required'],
|
||||
['webhook', 'Webhook', 'required'],
|
||||
['payload', 'Payload', 'required'],
|
||||
['calendar', 'Calendar', 'required']
|
||||
];
|
||||
protected $_validation_messages = [
|
||||
['slot', 'Slot', 'required'],
|
||||
['days', 'Days', 'required'],
|
||||
['score_threshold', 'Score Threshold', 'required'],
|
||||
['actual_score', 'Actual Score', 'required'],
|
||||
['alert', 'Alert', 'required'],
|
||||
['webhook', 'Webhook', 'required'],
|
||||
['payload', 'Payload', 'required'],
|
||||
['calendar', 'Calendar', 'required']
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function get_mapping()
|
||||
{
|
||||
return [
|
||||
// TODO: ADD MAPPING
|
||||
];
|
||||
}
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
var formData = {};
|
||||
|
||||
function showTimeInput(containerId) {
|
||||
var checkbox = document.getElementById(
|
||||
containerId.replace("Time", "Checkbox")
|
||||
);
|
||||
var timeContainer = document.getElementById(containerId);
|
||||
|
||||
if (checkbox.checked) {
|
||||
timeContainer.style.display = "block";
|
||||
} else {
|
||||
timeContainer.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
function addTime(containerId) {
|
||||
var timeContainer = document.getElementById(containerId);
|
||||
var newLabelFrom = document.createElement("label");
|
||||
newLabelFrom.textContent = "From:";
|
||||
var newInputFrom = document.createElement("input");
|
||||
newInputFrom.type = "time";
|
||||
newInputFrom.className = "time-input";
|
||||
|
||||
var newLabelTo = document.createElement("label");
|
||||
newLabelTo.textContent = "To:";
|
||||
var newInputTo = document.createElement("input");
|
||||
newInputTo.type = "time";
|
||||
newInputTo.className = "time-input";
|
||||
|
||||
timeContainer.appendChild(newLabelFrom);
|
||||
timeContainer.appendChild(newInputFrom);
|
||||
timeContainer.appendChild(newLabelTo);
|
||||
timeContainer.appendChild(newInputTo);
|
||||
|
||||
updateFormData(); // Call function to update formData when time is added
|
||||
}
|
||||
|
||||
function updateFormData() {
|
||||
// Reset formData
|
||||
formData = {};
|
||||
|
||||
// Loop through each day
|
||||
["monday", "tuesday"].forEach(function (day) {
|
||||
var fromInputs = document.querySelectorAll(
|
||||
"#" + day + "Time .time-input:nth-child(odd)"
|
||||
);
|
||||
var toInputs = document.querySelectorAll(
|
||||
"#" + day + "Time .time-input:nth-child(even)"
|
||||
);
|
||||
|
||||
var dayData = [];
|
||||
|
||||
// Check if the day is selected and both time inputs have values
|
||||
for (var i = 0; i < fromInputs.length; i++) {
|
||||
if (
|
||||
fromInputs[i].value.trim() !== "" &&
|
||||
toInputs[i].value.trim() !== ""
|
||||
) {
|
||||
// Store the data in dayData array
|
||||
dayData.push({
|
||||
from: fromInputs[i].value,
|
||||
to: toInputs[i].value,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// If there is data for the day, store it in formData
|
||||
if (
|
||||
document.getElementById(day + "Checkbox").checked &&
|
||||
dayData.length > 0
|
||||
) {
|
||||
formData[day] = dayData;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function submitForm(event) {
|
||||
event.preventDefault();
|
||||
|
||||
updateFormData(); // Ensure formData is updated before submission
|
||||
|
||||
// Display the collected data (you can replace this with your own logic)
|
||||
alert(JSON.stringify(formData));
|
||||
}
|
||||
});
|
||||
+514
@@ -0,0 +1,514 @@
|
||||
<div class="container mt-5">
|
||||
<h2 class="text-center">Add Availability</h2>
|
||||
<?php if (isset($error) && $error) : ?>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
Please fill in all required fields!
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<form action="" method="POST" class="mt-4">
|
||||
<div class="form-group">
|
||||
<label for="project_name">Project Name</label>
|
||||
<input type="text" name="project_name" id="project_name" class="form-control" value="" required>
|
||||
</div>
|
||||
<!-- <div class="form-group">
|
||||
<label for="calendar_id">Calendar ID</label>
|
||||
<input type="text" name="calendar_id" id="calendar_id" class="form-control" value="wewe" required>
|
||||
</div> -->
|
||||
<div class="form-group">
|
||||
<label for="webhook">Webhook</label>
|
||||
<input type="text" name="webhook" id="webhook" class="form-control" value="" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="calendar_id">Calendar ID</label>
|
||||
<input type="text" name="calendar_id" id="calendar_id" class="form-control" value="" required>
|
||||
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="location">Location API Key</label>
|
||||
<input type="text" name="location" id="location" class="form-control" value="" required>
|
||||
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="score_threshold">Score Threshold</label>
|
||||
<input type="number" name="score_threshold" id="score_threshold" class="form-control" value="" required>
|
||||
</div>
|
||||
<!-- <div class="form-group">
|
||||
<label for="actual_score">Actual Score</label>
|
||||
<input type="number" name="actual_score" id="actual_score" class="form-control" value="wewe" required>
|
||||
</div> -->
|
||||
<!-- <div class="form-group">
|
||||
<label for="alert">Alert</label>
|
||||
<input type="text" name="alert" id="alert" class="form-control" value="wewe" required>
|
||||
</div> -->
|
||||
<div class="form-group">
|
||||
<label for="actual_score">Slots</label>
|
||||
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="sundayCheckbox" value="sun" onclick="showTimeInput('sundayTime')">
|
||||
<label class="form-check-label" for="sundayCheckbox">Sunday</label>
|
||||
|
||||
<!-- <div id="mondayTime" class="time-container">
|
||||
<label for="mondayFromInput">From:</label>
|
||||
<input type="time" id="mondayFromInput" class="time-input">
|
||||
<label for="mondayToInput">To:</label>
|
||||
<input type="time" id="mondayToInput" class="time-input">
|
||||
<button type="button" onclick="addTime('mondayTime')">Add Time</button>
|
||||
</div> -->
|
||||
|
||||
<div class="form-row time-container" id="sundayTime">
|
||||
<div class="time-set form-row ml-3">
|
||||
<div class="form-group col-md-4">
|
||||
<label for="sundayFromInput">From</label>
|
||||
<input type="time" name="from" id="sundayFromInput" class="form-control mb-2 time-input" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="sundayToInput">To</label>
|
||||
<input type="time" name="to" id="sundayToInput" class="form-control mb-2 time-input" required>
|
||||
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="sundayPoint">Point</label>
|
||||
<input type="number" name="point" id="sundayPoint" class="form-control mb-2 point" required>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<a type="button" class="btn btn-link" onclick="addTime('sundayTime')">Add Time</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- Monday -->
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="mondayCheckbox" value="mon" onclick="showTimeInput('mondayTime')">
|
||||
<label class="form-check-label" for="mondayCheckbox">Monday</label>
|
||||
|
||||
<div class="form-row time-container" id="mondayTime">
|
||||
<div class="time-set form-row ml-3">
|
||||
<div class="form-group col-md-4">
|
||||
<label for="mondayFromInput">From</label>
|
||||
<input type="time" name="from" id="mondayFromInput" class="form-control mb-2 time-input" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="mondayToInput">To</label>
|
||||
<input type="time" name="to" id="mondayToInput" class="form-control mb-2 time-input" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="mondayPoint">Point</label>
|
||||
<input type="number" name="point" id="mondayPoint" class="form-control mb-2 point" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<a type="button" class="btn btn-link" onclick="addTime('mondayTime')">Add Time</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tuesday -->
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="tuesdayCheckbox" value="tue" onclick="showTimeInput('tuesdayTime')">
|
||||
<label class="form-check-label" for="tuesdayCheckbox">Tuesday</label>
|
||||
|
||||
<div class="form-row time-container" id="tuesdayTime">
|
||||
<div class="time-set form-row ml-3">
|
||||
<div class="form-group col-md-4">
|
||||
<label for="tuesdayFromInput">From</label>
|
||||
<input type="time" name="from" id="tuesdayFromInput" class="form-control mb-2 time-input" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="tuesdayToInput">To</label>
|
||||
<input type="time" name="to" id="tuesdayToInput" class="form-control mb-2 time-input" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="tuesdayPoint">Point</label>
|
||||
<input type="number" name="point" id="tuesdayPoint" class="form-control mb-2 point" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<a type="button" class="btn btn-link" onclick="addTime('tuesdayTime')">Add Time</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Wednesday -->
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="wednesdayCheckbox" value="wed" onclick="showTimeInput('wednesdayTime')">
|
||||
<label class="form-check-label" for="wednesdayCheckbox">Wednesday</label>
|
||||
|
||||
<div class="form-row time-container" id="wednesdayTime">
|
||||
<div class="time-set form-row ml-3">
|
||||
<div class="form-group col-md-4">
|
||||
<label for="wednesdayFromInput">From</label>
|
||||
<input type="time" name="from" id="wednesdayFromInput" class="form-control mb-2 time-input" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="wednesdayToInput">To</label>
|
||||
<input type="time" name="to" id="wednesdayToInput" class="form-control mb-2 time-input" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="wednesdayPoint">Point</label>
|
||||
<input type="number" name="point" id="wednesdayPoint" class="form-control mb-2 point" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<a type="button" class="btn btn-link" onclick="addTime('wednesdayTime')">Add Time</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Thursday -->
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="thursdayCheckbox" value="thurs" onclick="showTimeInput('thursdayTime')">
|
||||
<label class="form-check-label" for="thursdayCheckbox">Thursday</label>
|
||||
|
||||
<div class="form-row time-container" id="thursdayTime">
|
||||
<div class="time-set form-row ml-3">
|
||||
<div class="form-group col-md-4">
|
||||
<label for="thursdayFromInput">From</label>
|
||||
<input type="time" name="from" id="thursdayFromInput" class="form-control mb-2 time-input" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="thursdayToInput">To</label>
|
||||
<input type="time" name="to" id="thursdayToInput" class="form-control mb-2 time-input" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="thursdayPoint">Point</label>
|
||||
<input type="number" name="point" id="thursdayPoint" class="form-control mb-2 point" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<a type="button" class="btn btn-link" onclick="addTime('thursdayTime')">Add Time</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Friday -->
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="fridayCheckbox" value="fri" onclick="showTimeInput('fridayTime')">
|
||||
<label class="form-check-label" for="fridayCheckbox">Friday</label>
|
||||
|
||||
<div class="form-row time-container" id="fridayTime">
|
||||
<div class="time-set form-row ml-3">
|
||||
<div class="form-group col-md-4">
|
||||
<label for="fridayFromInput">From</label>
|
||||
<input type="time" name="from" id="fridayFromInput" class="form-control mb-2 time-input" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="fridayToInput">To</label>
|
||||
<input type="time" name="to" id="fridayToInput" class="form-control mb-2 time-input" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="fridayPoint">Point</label>
|
||||
<input type="number" name="point" id="fridayPoint" class="form-control mb-2 point" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<a type="button" class="btn btn-link" onclick="addTime('fridayTime')">Add Time</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Saturday -->
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="saturdayCheckbox" value="sat" onclick="showTimeInput('saturdayTime')">
|
||||
<label class="form-check-label" for="saturdayCheckbox">Saturday</label>
|
||||
|
||||
<div class="form-row time-container" id="saturdayTime">
|
||||
<div class="time-set form-row ml-3">
|
||||
<div class="form-group col-md-4">
|
||||
<label for="saturdayFromInput">From</label>
|
||||
<input type="time" name="from" id="saturdayFromInput" class="form-control mb-2 time-input" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="saturdayToInput">To</label>
|
||||
<input type="time" name="to" id="saturdayToInput" class="form-control mb-2 time-input" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="saturdayPoint">Point</label>
|
||||
<input type="number" name="point" id="saturdayPoint" class="form-control mb-2 point" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<a type="button" class="btn btn-link" onclick="addTime('saturdayTime')">Add Time</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="days">Days</label>
|
||||
<input type="number" name="days" id="days" class="form-control" value="" required>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary" onclick="submitForm(event)">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
var formData = {};
|
||||
|
||||
function showTimeInput(containerId) {
|
||||
var checkbox = document.getElementById(containerId.replace("Time", "Checkbox"));
|
||||
var timeContainer = document.getElementById(containerId);
|
||||
|
||||
if (checkbox.checked) {
|
||||
timeContainer.style.display = "flex";
|
||||
} else {
|
||||
timeContainer.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
function addTime(containerId) {
|
||||
var timeContainer = document.getElementById(containerId);
|
||||
|
||||
var br = document.createElement("br");
|
||||
timeContainer.insertBefore(br, timeContainer.lastElementChild);
|
||||
var newTimeSet = document.createElement("div");
|
||||
newTimeSet.className = "time-set form-row ml-3";
|
||||
|
||||
var newFormGroup1 = document.createElement("div");
|
||||
newFormGroup1.className = "form-group col-md-3";
|
||||
var newLabelFrom = document.createElement("label");
|
||||
newLabelFrom.textContent = "From:";
|
||||
var newInputFrom = document.createElement("input");
|
||||
newInputFrom.type = "time";
|
||||
newInputFrom.name = "from";
|
||||
newInputFrom.className = "form-control mb-2 time-input";
|
||||
newFormGroup1.appendChild(newLabelFrom);
|
||||
newFormGroup1.appendChild(newInputFrom);
|
||||
|
||||
var newFormGroup2 = document.createElement("div");
|
||||
newFormGroup2.className = "form-group col-md-3";
|
||||
var newLabelTo = document.createElement("label");
|
||||
newLabelTo.textContent = "To:";
|
||||
var newInputTo = document.createElement("input");
|
||||
newInputTo.type = "time";
|
||||
newInputTo.name = "to";
|
||||
newInputTo.className = "form-control mb-2 time-input";
|
||||
newFormGroup2.appendChild(newLabelTo);
|
||||
newFormGroup2.appendChild(newInputTo);
|
||||
|
||||
var newFormGroup3 = document.createElement("div");
|
||||
newFormGroup3.className = "form-group col-md-3";
|
||||
var newLabelPoint = document.createElement("label");
|
||||
newLabelPoint.textContent = "Point:";
|
||||
var newInputPoint = document.createElement("input");
|
||||
newInputPoint.type = "number";
|
||||
newInputPoint.name = "point";
|
||||
newInputPoint.className = "form-control mb-2 point";
|
||||
newFormGroup3.appendChild(newLabelPoint);
|
||||
newFormGroup3.appendChild(newInputPoint);
|
||||
|
||||
|
||||
var deleteButtonContainer = document.createElement("div");
|
||||
deleteButtonContainer.className = "form-group col-md-3 mt-auto";
|
||||
var deleteButton = document.createElement("a");
|
||||
deleteButton.type = "button";
|
||||
deleteButton.className = "btn btn-link";
|
||||
deleteButton.textContent = "Delete Time";
|
||||
deleteButton.onclick = function() {
|
||||
deleteTime(newTimeSet);
|
||||
};
|
||||
deleteButtonContainer.appendChild(deleteButton);
|
||||
|
||||
newTimeSet.appendChild(newFormGroup1);
|
||||
newTimeSet.appendChild(newFormGroup2);
|
||||
newTimeSet.appendChild(newFormGroup3);
|
||||
newTimeSet.appendChild(deleteButtonContainer);
|
||||
|
||||
// Insert the new time set before the "Add Time" button
|
||||
// timeContainer.insertBefore(newTimeSet, timeContainer.lastElementChild);
|
||||
|
||||
timeContainer.insertBefore(newTimeSet, br.nextSibling);
|
||||
|
||||
updateFormData(); // Call function to update formData when time is added
|
||||
}
|
||||
|
||||
function deleteTime(timeSet) {
|
||||
var timeContainer = timeSet.parentNode;
|
||||
timeContainer.removeChild(timeSet);
|
||||
|
||||
updateFormData(); // Call function to update formData when time is deleted
|
||||
}
|
||||
|
||||
function calculateTotalPoint() {
|
||||
var totalPoint = 0;
|
||||
|
||||
// Loop through each day in formData
|
||||
for (var day in formData) {
|
||||
if (formData.hasOwnProperty(day)) {
|
||||
// Get the data for the day
|
||||
var dayData = formData[day];
|
||||
|
||||
// Sum up the points for each time set
|
||||
dayData.forEach(function(timeSet) {
|
||||
totalPoint += parseFloat(timeSet.point) || 0; // Convert to float and handle NaN
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Total Point: " + totalPoint);
|
||||
return totalPoint;
|
||||
}
|
||||
|
||||
function calculateTotalPoints(daysToCheck) {
|
||||
var currentDate = new Date();
|
||||
var currentDayIndex = currentDate.getDay(); // 0 for Sunday, 1 for Monday, ..., 6 for Saturday
|
||||
|
||||
// Get the next two days to check
|
||||
var daysToCheckIndices = [];
|
||||
for (var i = 0; i < daysToCheck; i++) {
|
||||
var nextDayIndex = (currentDayIndex + i) % 7;
|
||||
daysToCheckIndices.push(nextDayIndex);
|
||||
}
|
||||
|
||||
var totalPoints = 0;
|
||||
|
||||
// Loop through the selected days and calculate total points
|
||||
daysToCheckIndices.forEach(function(dayIndex) {
|
||||
var dayName = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'][dayIndex];
|
||||
|
||||
if (formData[dayName]) {
|
||||
formData[dayName].forEach(function(timeSlot) {
|
||||
totalPoints += parseFloat(timeSlot.point) || 0;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return totalPoints;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function updateFormData() {
|
||||
// Reset formData
|
||||
formData = {};
|
||||
|
||||
// Loop through each day
|
||||
['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'].forEach(function(day) {
|
||||
var checkbox = document.getElementById(day + 'Checkbox');
|
||||
var timeContainer = document.getElementById(day + 'Time');
|
||||
// var pointContainer = document.getElementById(day + 'Point');
|
||||
var timeSets = timeContainer.querySelectorAll('.time-set');
|
||||
// console.log(checkbox)
|
||||
// console.log(timeContainer)
|
||||
// console.log(timeSets)
|
||||
|
||||
if (checkbox.checked) {
|
||||
var dayData = [];
|
||||
|
||||
// Iterate over each time set
|
||||
timeSets.forEach(function(timeSet) {
|
||||
// console.log(timeSet)
|
||||
|
||||
var fromInput = timeSet.querySelector('input[name="from"]');
|
||||
var toInput = timeSet.querySelector('input[name="to"]');
|
||||
var point = timeSet.querySelector('input[name="point"]');
|
||||
|
||||
// Check if both "From" and "To" inputs have values
|
||||
if (fromInput.value.trim() !== '' && toInput.value.trim() !== '' && point.value.trim() !== '') {
|
||||
// Store the data in dayData array
|
||||
dayData.push({
|
||||
from: fromInput.value,
|
||||
to: toInput.value,
|
||||
point: point.value
|
||||
});
|
||||
}
|
||||
});
|
||||
// console.log(dayData)
|
||||
|
||||
// If there is data for the day, store it in formData
|
||||
if (dayData.length > 0) {
|
||||
formData[day] = dayData;
|
||||
// console.log(formData); // Log formData for debugging
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log(formData); // Log formData for debugging
|
||||
}
|
||||
|
||||
function submitForm(event) {
|
||||
event.preventDefault();
|
||||
|
||||
updateFormData(); // Ensure formData is updated before submission
|
||||
// Get form values using DOM
|
||||
var project_name = document.getElementById("project_name").value;
|
||||
var days = document.getElementById("days").value;
|
||||
var webhook = document.getElementById("webhook").value;
|
||||
var score_threshold = document.getElementById("score_threshold").value;
|
||||
|
||||
var calendar_id = document.getElementById("calendar_id").value;
|
||||
var location = document.getElementById("location").value;
|
||||
|
||||
|
||||
// function isJSON(str) {
|
||||
// try {
|
||||
// JSON.parse(str);
|
||||
// return true;
|
||||
// } catch (e) {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
// console.log(typeof webhook_payload)
|
||||
// var checkJson = JSON.parse(webhook_payload);
|
||||
// console.log(checkJson)
|
||||
|
||||
// var isJsonData = isJSON(webhook_payload);
|
||||
|
||||
// if (isJsonData) {
|
||||
// var json_payload = JSON.parse(webhook_payload);
|
||||
|
||||
// } else {
|
||||
|
||||
// console.log("The variable does not contain valid JSON data.");
|
||||
// }
|
||||
// var actual_score = document.getElementById("actual_score").value;
|
||||
// var alert = document.getElementById("alert").value;
|
||||
console.log(formData)
|
||||
// Get checkbox values (example for Sunday)
|
||||
// var sundayCheckbox = document.getElementById("sundayCheckbox");
|
||||
// var isSundayChecked = sundayCheckbox.checked;
|
||||
|
||||
// var actual_score = calculateTotalPoint()
|
||||
var actual_score = calculateTotalPoints(parseInt(days))
|
||||
console.log("actual score: " + actual_score)
|
||||
// return
|
||||
// Include other form elements as needed...
|
||||
formData = JSON.stringify(formData)
|
||||
// Create an object with the form data
|
||||
var Data = {
|
||||
project_name: project_name,
|
||||
days: days,
|
||||
webhook: webhook,
|
||||
calendar_id: calendar_id,
|
||||
location: location,
|
||||
score_threshold: score_threshold,
|
||||
actual_score: actual_score,
|
||||
slot: formData
|
||||
};
|
||||
console.log(Data)
|
||||
|
||||
$.ajax({
|
||||
url: '/<?php echo $_SESSION['role'] ?>/project/add',
|
||||
method: 'POST',
|
||||
data: Data,
|
||||
success: function(response) {
|
||||
// console.log(response.slots_available);
|
||||
|
||||
console.log(response);
|
||||
if (response == 'Project Added') {
|
||||
alert(response);
|
||||
// alert(JSON.stringify(response));
|
||||
window.location.href = "/<?php echo $_SESSION['role'] ?>/project"
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.log(error)
|
||||
// console.log("error oooo")
|
||||
}
|
||||
});
|
||||
// Display the collected data (you can replace this with your own logic)
|
||||
// alert(JSON.stringify(formData));
|
||||
}
|
||||
</script>
|
||||
+555
@@ -0,0 +1,555 @@
|
||||
<div class="container mt-5">
|
||||
<h2 class="text-center">Edit Availability</h2>
|
||||
<?php if (isset($error) && $error) : ?>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
Please fill in all required fields!
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<form action="" method="POST" class="mt-4">
|
||||
<div class="form-group">
|
||||
<label for="project_name">Project Name</label>
|
||||
<input type="text" name="project_name" id="project_name" class="form-control" value="<?php echo isset($data['model']) ? $data['model']->project_name : '' ?>" required>
|
||||
</div>
|
||||
<!-- <div class="form-group">
|
||||
<label for="calendar_id">Calendar ID</label>
|
||||
<input type="text" name="calendar_id" id="calendar_id" class="form-control" value="wewe" required>
|
||||
</div> -->
|
||||
<div class="form-group">
|
||||
<label for="webhook">Webhook</label>
|
||||
<input type="text" name="webhook" id="webhook" class="form-control" value="<?php echo isset($data['model']) ? $data['model']->webhook : '' ?>" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="webhook_payload">Webhook Payload</label>
|
||||
<textarea name="webhook_payload" id="webhook_payload" class="form-control" placeholder="{'key':'value'}" required><?php echo isset($data['model']) ? $data['model']->payload : '' ?></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="calendar_id">Calendar ID</label>
|
||||
<input type="text" name="calendar_id" id="calendar_id" class="form-control" value="<?php echo isset($data['model']) ? $data['model']->calendar : '' ?>" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="location">Location API Key</label>
|
||||
<input type="text" name="location" id="location" class="form-control" value="<?php echo isset($data['model']) ? $data['model']->location : '' ?>" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="score_threshold">Score Threshold</label>
|
||||
<input type="number" name="score_threshold" id="score_threshold" class="form-control" value="<?php echo isset($data['model']) ? $data['model']->score_threshold : '' ?>" required>
|
||||
</div>
|
||||
<!-- <div class="form-group">
|
||||
<label for="actual_score">Actual Score</label>
|
||||
<input type="number" name="actual_score" id="actual_score" class="form-control" value="wewe" required>
|
||||
</div> -->
|
||||
<!-- <div class="form-group">
|
||||
<label for="alert">Alert</label>
|
||||
<input type="text" name="alert" id="alert" class="form-control" value="wewe" required>
|
||||
</div> -->
|
||||
|
||||
<?php
|
||||
// echo json_encode($data['model']);
|
||||
|
||||
if (isset($data['model'])) {
|
||||
$slots = json_decode($data['model']->slot);
|
||||
// echo json_encode($slots);
|
||||
} ?>
|
||||
<div class="form-group">
|
||||
<label for="actual_score">Slots</label>
|
||||
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="sundayCheckbox" value="sun" onclick="showTimeInput('sundayTime')" <?php echo isset($slots->sunday) ? 'checked' : '' ?>>
|
||||
<label class="form-check-label" for="sundayCheckbox">Sunday</label>
|
||||
|
||||
|
||||
|
||||
<div class="form-row time-container" id="sundayTime" <?php echo isset($slots->sunday) ? 'style="display:flex"' : '' ?>>
|
||||
<?php if (isset($slots->sunday)) {
|
||||
foreach ($slots->sunday as $sun) {
|
||||
?>
|
||||
<div class="time-set form-row ml-3">
|
||||
<div class="form-group col-md-4">
|
||||
<label for="sundayFromInput">From</label>
|
||||
<input type="time" name="from" id="sundayFromInput" class="form-control mb-2 time-input" value="<?php echo $sun->from ?>" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="sundayToInput">To</label>
|
||||
<input type="time" name="to" id="sundayToInput" class="form-control mb-2 time-input" value="<?php echo $sun->to ?>" required>
|
||||
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="sundayPoint">Point</label>
|
||||
<input type="number" name="point" id="sundayPoint" class="form-control mb-2 point" value="<?php echo $sun->point ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
<?php }
|
||||
} ?>
|
||||
<div class="form-group col-md-12">
|
||||
<a type="button" class="btn btn-link" onclick="addTime('sundayTime')">Add Time</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Monday -->
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="mondayCheckbox" value="mon" onclick="showTimeInput('mondayTime')" <?php echo isset($slots->monday) ? 'checked' : '' ?>>
|
||||
<label class="form-check-label" for="mondayCheckbox">Monday</label>
|
||||
|
||||
<div class="form-row time-container" id="mondayTime" <?php echo isset($slots->monday) ? 'style="display:flex"' : '' ?>>
|
||||
<?php if (isset($slots->monday)) {
|
||||
foreach ($slots->monday as $mon) {
|
||||
?>
|
||||
<div class="time-set form-row ml-3">
|
||||
<div class="form-group col-md-4">
|
||||
<label for="mondayFromInput">From</label>
|
||||
<input type="time" name="from" id="mondayFromInput" class="form-control mb-2 time-input" value="<?php echo $mon->from ?>" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="mondayToInput">To</label>
|
||||
<input type="time" name="to" id="mondayToInput" class="form-control mb-2 time-input" value="<?php echo $mon->to ?>" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="mondayPoint">Point</label>
|
||||
<input type="number" name="point" id="mondayPoint" class="form-control mb-2 point" value="<?php echo $mon->point ?>" required>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<?php }
|
||||
} ?>
|
||||
<div class="form-group col-md-12">
|
||||
<a type="button" class="btn btn-link" onclick="addTime('mondayTime')">Add Time</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tuesday -->
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="tuesdayCheckbox" value="tue" onclick="showTimeInput('tuesdayTime')" <?php echo isset($slots->tuesday) ? 'checked' : '' ?>>
|
||||
<label class="form-check-label" for="tuesdayCheckbox">Tuesday</label>
|
||||
|
||||
<div class="form-row time-container" id="tuesdayTime" <?php echo isset($slots->tuesday) ? 'style="display:flex"' : '' ?>>
|
||||
<?php if (isset($slots->tuesday)) {
|
||||
foreach ($slots->tuesday as $tue) {
|
||||
?>
|
||||
<div class="time-set form-row ml-3">
|
||||
<div class="form-group col-md-4">
|
||||
<label for="tuesdayFromInput">From</label>
|
||||
<input type="time" name="from" id="tuesdayFromInput" class="form-control mb-2 time-input" value="<?php echo $tue->from ?>" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="tuesdayToInput">To</label>
|
||||
<input type="time" name="to" id="tuesdayToInput" class="form-control mb-2 time-input" value="<?php echo $tue->to ?>" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="tuesdayPoint">Point</label>
|
||||
<input type="number" name="point" id="tuesdayPoint" class="form-control mb-2 point" value="<?php echo $tue->point ?>" required>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<?php }
|
||||
} ?>
|
||||
<div class="form-group col-md-12">
|
||||
<a type="button" class="btn btn-link" onclick="addTime('tuesdayTime')">Add Time</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Wednesday -->
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="wednesdayCheckbox" value="wed" onclick="showTimeInput('wednesdayTime')" <?php echo isset($slots->wednesday) ? 'checked' : '' ?>>
|
||||
<label class="form-check-label" for="wednesdayCheckbox">Wednesday</label>
|
||||
|
||||
<div class="form-row time-container" id="wednesdayTime" <?php echo isset($slots->wednesday) ? 'style="display:flex"' : '' ?>>
|
||||
<?php if (isset($slots->wednesday)) {
|
||||
foreach ($slots->wednesday as $wed) {
|
||||
?>
|
||||
<div class="time-set form-row ml-3">
|
||||
<div class="form-group col-md-4">
|
||||
<label for="wednesdayFromInput">From</label>
|
||||
<input type="time" name="from" id="wednesdayFromInput" class="form-control mb-2 time-input" value="<?php echo $wed->from ?>" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="wednesdayToInput">To</label>
|
||||
<input type="time" name="to" id="wednesdayToInput" class="form-control mb-2 time-input" value="<?php echo $wed->to ?>" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="wednesdayPoint">Point</label>
|
||||
<input type="number" name="point" id="wednesdayPoint" class="form-control mb-2 point" value="<?php echo $wed->point ?>" required>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<?php }
|
||||
} ?>
|
||||
<div class="form-group col-md-12">
|
||||
<a type="button" class="btn btn-link" onclick="addTime('wednesdayTime')">Add Time</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Thursday -->
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="thursdayCheckbox" value="thurs" onclick="showTimeInput('thursdayTime')" <?php echo isset($slots->thursday) ? 'checked' : '' ?>>
|
||||
<label class="form-check-label" for="thursdayCheckbox">Thursday</label>
|
||||
|
||||
<div class="form-row time-container" id="thursdayTime" <?php echo isset($slots->thursday) ? 'style="display:flex"' : '' ?>>
|
||||
<?php if (isset($slots->thursday)) {
|
||||
foreach ($slots->thursday as $thurs) {
|
||||
?>
|
||||
<div class="time-set form-row ml-3">
|
||||
<div class="form-group col-md-4">
|
||||
<label for="thursdayFromInput">From</label>
|
||||
<input type="time" name="from" id="thursdayFromInput" class="form-control mb-2 time-input" value="<?php echo $thurs->from ?>" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="thursdayToInput">To</label>
|
||||
<input type="time" name="to" id="thursdayToInput" class="form-control mb-2 time-input" value="<?php echo $thurs->to ?>" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="thursdayPoint">Point</label>
|
||||
<input type="number" name="point" id="thursdayPoint" class="form-control mb-2 point" value="<?php echo $thurs->point ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
<?php }
|
||||
} ?>
|
||||
<div class="form-group col-md-12">
|
||||
<a type="button" class="btn btn-link" onclick="addTime('thursdayTime')">Add Time</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Friday -->
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="fridayCheckbox" value="fri" onclick="showTimeInput('fridayTime')" <?php echo isset($slots->friday) ? 'checked' : '' ?>>
|
||||
<label class="form-check-label" for="fridayCheckbox">Friday</label>
|
||||
|
||||
<div class="form-row time-container" id="fridayTime" <?php echo isset($slots->friday) ? 'style="display:flex"' : '' ?>>
|
||||
<?php if (isset($slots->friday)) {
|
||||
foreach ($slots->friday as $fri) {
|
||||
?>
|
||||
<div class="time-set form-row ml-3">
|
||||
<div class="form-group col-md-4">
|
||||
<label for="fridayFromInput">From</label>
|
||||
<input type="time" name="from" id="fridayFromInput" class="form-control mb-2 time-input" value="<?php echo $fri->from ?>" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="fridayToInput">To</label>
|
||||
<input type="time" name="to" id="fridayToInput" class="form-control mb-2 time-input" value="<?php echo $fri->to ?>" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="fridayPoint">Point</label>
|
||||
<input type="number" name="point" id="fridayPoint" class="form-control mb-2 point" value="<?php echo $fri->point ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
<?php }
|
||||
} ?>
|
||||
<div class="form-group col-md-12">
|
||||
<a type="button" class="btn btn-link" onclick="addTime('fridayTime')">Add Time</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Saturday -->
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="saturdayCheckbox" value="sat" onclick="showTimeInput('saturdayTime')" <?php echo isset($slots->saturday) ? 'checked' : '' ?>>
|
||||
<label class="form-check-label" for="saturdayCheckbox">Saturday</label>
|
||||
|
||||
<div class="form-row time-container" id="saturdayTime" <?php echo isset($slots->saturday) ? 'style="display:flex"' : '' ?>>
|
||||
<?php if (isset($slots->saturday)) {
|
||||
foreach ($slots->saturday as $sat) {
|
||||
?>
|
||||
<div class="time-set form-row ml-3">
|
||||
<div class="form-group col-md-4">
|
||||
<label for="saturdayFromInput">From</label>
|
||||
<input type="time" name="from" id="saturdayFromInput" class="form-control mb-2 time-input" value="<?php echo $sat->from ?>" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="saturdayToInput">To</label>
|
||||
<input type="time" name="to" id="saturdayToInput" class="form-control mb-2 time-input" value="<?php echo $sat->to ?>" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="saturdayPoint">Point</label>
|
||||
<input type="number" name="point" id="saturdayPoint" class="form-control mb-2 point" value="<?php echo $sat->point ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
<?php }
|
||||
} ?>
|
||||
<div class="form-group col-md-12">
|
||||
<a type="button" class="btn btn-link" onclick="addTime('saturdayTime')">Add Time</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
// }
|
||||
?>
|
||||
<div class="form-group">
|
||||
<label for="days">Days</label>
|
||||
<input type="number" name="days" id="days" class="form-control" value="<?php echo isset($data['model']) ? $data['model']->days : '' ?>" required>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary" onclick="submitForm(event)">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
var formData = {};
|
||||
|
||||
function showTimeInput(containerId) {
|
||||
var checkbox = document.getElementById(containerId.replace("Time", "Checkbox"));
|
||||
var timeContainer = document.getElementById(containerId);
|
||||
|
||||
if (checkbox.checked) {
|
||||
timeContainer.style.display = "flex";
|
||||
} else {
|
||||
timeContainer.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
function addTime(containerId) {
|
||||
var timeContainer = document.getElementById(containerId);
|
||||
|
||||
var br = document.createElement("br");
|
||||
timeContainer.insertBefore(br, timeContainer.lastElementChild);
|
||||
var newTimeSet = document.createElement("div");
|
||||
newTimeSet.className = "time-set form-row ml-3";
|
||||
|
||||
var newFormGroup1 = document.createElement("div");
|
||||
newFormGroup1.className = "form-group col-md-3";
|
||||
var newLabelFrom = document.createElement("label");
|
||||
newLabelFrom.textContent = "From:";
|
||||
var newInputFrom = document.createElement("input");
|
||||
newInputFrom.type = "time";
|
||||
newInputFrom.name = "from";
|
||||
newInputFrom.className = "form-control mb-2 time-input";
|
||||
newFormGroup1.appendChild(newLabelFrom);
|
||||
newFormGroup1.appendChild(newInputFrom);
|
||||
|
||||
var newFormGroup2 = document.createElement("div");
|
||||
newFormGroup2.className = "form-group col-md-3";
|
||||
var newLabelTo = document.createElement("label");
|
||||
newLabelTo.textContent = "To:";
|
||||
var newInputTo = document.createElement("input");
|
||||
newInputTo.type = "time";
|
||||
newInputTo.name = "to";
|
||||
newInputTo.className = "form-control mb-2 time-input";
|
||||
newFormGroup2.appendChild(newLabelTo);
|
||||
newFormGroup2.appendChild(newInputTo);
|
||||
|
||||
var newFormGroup3 = document.createElement("div");
|
||||
newFormGroup3.className = "form-group col-md-3";
|
||||
var newLabelPoint = document.createElement("label");
|
||||
newLabelPoint.textContent = "Point:";
|
||||
var newInputPoint = document.createElement("input");
|
||||
newInputPoint.type = "number";
|
||||
newInputPoint.name = "point";
|
||||
newInputPoint.className = "form-control mb-2 point";
|
||||
newFormGroup3.appendChild(newLabelPoint);
|
||||
newFormGroup3.appendChild(newInputPoint);
|
||||
|
||||
|
||||
var deleteButtonContainer = document.createElement("div");
|
||||
deleteButtonContainer.className = "form-group col-md-3 mt-auto";
|
||||
var deleteButton = document.createElement("a");
|
||||
deleteButton.type = "button";
|
||||
deleteButton.className = "btn btn-link";
|
||||
deleteButton.textContent = "Delete Time";
|
||||
deleteButton.onclick = function() {
|
||||
deleteTime(newTimeSet);
|
||||
};
|
||||
deleteButtonContainer.appendChild(deleteButton);
|
||||
|
||||
newTimeSet.appendChild(newFormGroup1);
|
||||
newTimeSet.appendChild(newFormGroup2);
|
||||
newTimeSet.appendChild(newFormGroup3);
|
||||
newTimeSet.appendChild(deleteButtonContainer);
|
||||
|
||||
// Insert the new time set before the "Add Time" button
|
||||
// timeContainer.insertBefore(newTimeSet, timeContainer.lastElementChild);
|
||||
|
||||
timeContainer.insertBefore(newTimeSet, br.nextSibling);
|
||||
|
||||
updateFormData(); // Call function to update formData when time is added
|
||||
}
|
||||
|
||||
function deleteTime(timeSet) {
|
||||
var timeContainer = timeSet.parentNode;
|
||||
timeContainer.removeChild(timeSet);
|
||||
|
||||
updateFormData(); // Call function to update formData when time is deleted
|
||||
}
|
||||
|
||||
function calculateTotalPoint() {
|
||||
var totalPoint = 0;
|
||||
|
||||
// Loop through each day in formData
|
||||
for (var day in formData) {
|
||||
if (formData.hasOwnProperty(day)) {
|
||||
// Get the data for the day
|
||||
var dayData = formData[day];
|
||||
|
||||
// Sum up the points for each time set
|
||||
dayData.forEach(function(timeSet) {
|
||||
totalPoint += parseFloat(timeSet.point) || 0; // Convert to float and handle NaN
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Total Point: " + totalPoint);
|
||||
return totalPoint;
|
||||
}
|
||||
|
||||
function calculateTotalPoints(daysToCheck) {
|
||||
var currentDate = new Date();
|
||||
var currentDayIndex = currentDate.getDay(); // 0 for Sunday, 1 for Monday, ..., 6 for Saturday
|
||||
|
||||
// Get the next two days to check
|
||||
var daysToCheckIndices = [];
|
||||
for (var i = 0; i < daysToCheck; i++) {
|
||||
var nextDayIndex = (currentDayIndex + i + 1) % 7;
|
||||
daysToCheckIndices.push(nextDayIndex);
|
||||
}
|
||||
|
||||
var totalPoints = 0;
|
||||
|
||||
// Loop through the selected days and calculate total points
|
||||
daysToCheckIndices.forEach(function(dayIndex) {
|
||||
var dayName = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'][dayIndex];
|
||||
|
||||
if (formData[dayName]) {
|
||||
formData[dayName].forEach(function(timeSlot) {
|
||||
totalPoints += parseFloat(timeSlot.point) || 0;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return totalPoints;
|
||||
}
|
||||
|
||||
function updateFormData() {
|
||||
// Reset formData
|
||||
formData = {};
|
||||
|
||||
// Loop through each day
|
||||
['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'].forEach(function(day) {
|
||||
var checkbox = document.getElementById(day + 'Checkbox');
|
||||
var timeContainer = document.getElementById(day + 'Time');
|
||||
// var pointContainer = document.getElementById(day + 'Point');
|
||||
var timeSets = timeContainer.querySelectorAll('.time-set');
|
||||
// console.log(checkbox)
|
||||
// console.log(timeContainer)
|
||||
// console.log(timeSets)
|
||||
|
||||
if (checkbox.checked) {
|
||||
var dayData = [];
|
||||
|
||||
// Iterate over each time set
|
||||
timeSets.forEach(function(timeSet) {
|
||||
// console.log(timeSet)
|
||||
|
||||
var fromInput = timeSet.querySelector('input[name="from"]');
|
||||
var toInput = timeSet.querySelector('input[name="to"]');
|
||||
var point = timeSet.querySelector('input[name="point"]');
|
||||
|
||||
// Check if both "From" and "To" inputs have values
|
||||
if (fromInput.value.trim() !== '' && toInput.value.trim() !== '' && point.value.trim() !== '') {
|
||||
// Store the data in dayData array
|
||||
dayData.push({
|
||||
from: fromInput.value,
|
||||
to: toInput.value,
|
||||
point: point.value
|
||||
});
|
||||
}
|
||||
});
|
||||
// console.log(dayData)
|
||||
|
||||
// If there is data for the day, store it in formData
|
||||
if (dayData.length > 0) {
|
||||
formData[day] = dayData;
|
||||
// console.log(formData); // Log formData for debugging
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log(formData); // Log formData for debugging
|
||||
}
|
||||
|
||||
function submitForm(event) {
|
||||
event.preventDefault();
|
||||
|
||||
updateFormData(); // Ensure formData is updated before submission
|
||||
// Get form values using DOM
|
||||
var project_name = document.getElementById("project_name").value;
|
||||
var days = document.getElementById("days").value;
|
||||
var webhook = document.getElementById("webhook").value;
|
||||
var score_threshold = document.getElementById("score_threshold").value;
|
||||
|
||||
var webhook_payload = document.getElementById("webhook_payload").value;
|
||||
var calendar_id = document.getElementById("calendar_id").value;
|
||||
var location = document.getElementById("location").value;
|
||||
|
||||
|
||||
function isJSON(str) {
|
||||
try {
|
||||
JSON.parse(str);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// var isJsonData = isJSON(webhook_payload);
|
||||
|
||||
// if (isJsonData) {
|
||||
// var json_payload = JSON.parse(webhook_payload);
|
||||
|
||||
// } else {
|
||||
// console.log("The variable does not contain valid JSON data.");
|
||||
// }
|
||||
// var actual_score = document.getElementById("actual_score").value;
|
||||
// var alert = document.getElementById("alert").value;
|
||||
console.log(formData)
|
||||
// Get checkbox values (example for Sunday)
|
||||
// var sundayCheckbox = document.getElementById("sundayCheckbox");
|
||||
// var isSundayChecked = sundayCheckbox.checked;
|
||||
|
||||
// var actual_score = calculateTotalPoint()
|
||||
var actual_score = calculateTotalPoints(parseInt(days))
|
||||
|
||||
// Include other form elements as needed...
|
||||
formData = JSON.stringify(formData)
|
||||
// Create an object with the form data
|
||||
|
||||
|
||||
var Data = {
|
||||
project_name: project_name,
|
||||
days: days,
|
||||
webhook: webhook,
|
||||
score_threshold: score_threshold,
|
||||
actual_score: actual_score,
|
||||
webhook_payload: webhook_payload,
|
||||
slot: formData,
|
||||
calendar_id: calendar_id,
|
||||
location: location,
|
||||
};
|
||||
var id = <?php if (isset($data['model'])) {
|
||||
echo $data['model']->id;
|
||||
} ?>;
|
||||
$.ajax({
|
||||
url: '/<?php echo $_SESSION['role'] ?>/project/edit/' + id,
|
||||
method: 'POST',
|
||||
data: Data,
|
||||
success: function(response) {
|
||||
// console.log(response.slots_available);
|
||||
|
||||
console.log(response);
|
||||
// alert(JSON.stringify(response));
|
||||
window.location.href = '/<?php echo $_SESSION['role'] ?>/project'
|
||||
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.log(error)
|
||||
// console.log("error oooo")
|
||||
}
|
||||
});
|
||||
// Display the collected data (you can replace this with your own logic)
|
||||
// alert(JSON.stringify(formData));
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,558 @@
|
||||
<div class="container mt-5">
|
||||
<h2 class="text-center">Edit Availability</h2>
|
||||
<?php if (isset($error) && $error) : ?>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
Please fill in all required fields!
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<form action="" method="POST" class="mt-4">
|
||||
<div class="form-group">
|
||||
<label for="project_name">Project Name</label>
|
||||
<input type="text" name="project_name" id="project_name" class="form-control" value="<?php echo isset($data['model']) ? $data['model']->project_name : '' ?>" required>
|
||||
</div>
|
||||
<!-- <div class="form-group">
|
||||
<label for="calendar_id">Calendar ID</label>
|
||||
<input type="text" name="calendar_id" id="calendar_id" class="form-control" value="wewe" required>
|
||||
</div> -->
|
||||
<div class="form-group">
|
||||
<label for="webhook">Webhook</label>
|
||||
<input type="text" name="webhook" id="webhook" class="form-control" value="<?php echo isset($data['model']) ? $data['model']->webhook : '' ?>" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="webhook_payload">Webhook Payload</label>
|
||||
<textarea name="webhook_payload" id="webhook_payload" class="form-control" placeholder="{'key':'value'}" required><?php echo isset($data['model']) ? $data['model']->payload : '' ?></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="calendar_id">Calendar ID</label>
|
||||
<input type="text" name="calendar_id" id="calendar_id" class="form-control" value="<?php echo isset($data['model']) ? $data['model']->calendar : '' ?>" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="location">Location API Key</label>
|
||||
<input type="text" name="location" id="location" class="form-control" value="<?php echo isset($data['model']) ? $data['model']->location : '' ?>" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="score_threshold">Score Threshold</label>
|
||||
<input type="number" name="score_threshold" id="score_threshold" class="form-control" value="<?php echo isset($data['model']) ? $data['model']->score_threshold : '' ?>" required>
|
||||
</div>
|
||||
<!-- <div class="form-group">
|
||||
<label for="actual_score">Actual Score</label>
|
||||
<input type="number" name="actual_score" id="actual_score" class="form-control" value="wewe" required>
|
||||
</div> -->
|
||||
<!-- <div class="form-group">
|
||||
<label for="alert">Alert</label>
|
||||
<input type="text" name="alert" id="alert" class="form-control" value="wewe" required>
|
||||
</div> -->
|
||||
|
||||
<?php
|
||||
// echo json_encode($data['model']);
|
||||
|
||||
if (isset($data['model'])) {
|
||||
$slots = json_decode($data['model']->slot);
|
||||
// echo json_encode($slots);
|
||||
} ?>
|
||||
<div class="form-group">
|
||||
<label for="actual_score">Slots</label>
|
||||
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="sundayCheckbox" value="sun" onclick="showTimeInput('sundayTime')" <?php echo isset($slots->sunday) ? 'checked' : '' ?>>
|
||||
<label class="form-check-label" for="sundayCheckbox">Sunday</label>
|
||||
|
||||
|
||||
|
||||
<div class="form-row time-container" id="sundayTime" <?php echo isset($slots->sunday) ? 'style="display:flex"' : '' ?>>
|
||||
<?php if (isset($slots->sunday)) {
|
||||
foreach ($slots->sunday as $sun) {
|
||||
?>
|
||||
<div class="time-set form-row ml-3">
|
||||
<div class="form-group col-md-4">
|
||||
<label for="sundayFromInput">From</label>
|
||||
<input type="time" name="from" id="sundayFromInput" class="form-control mb-2 time-input" value="<?php echo $sun->from ?>" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="sundayToInput">To</label>
|
||||
<input type="time" name="to" id="sundayToInput" class="form-control mb-2 time-input" value="<?php echo $sun->to ?>" required>
|
||||
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="sundayPoint">Point</label>
|
||||
<input type="number" name="point" id="sundayPoint" class="form-control mb-2 point" value="<?php echo $sun->point ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
<?php }
|
||||
} ?>
|
||||
<div class="form-group col-md-12">
|
||||
<a type="button" class="btn btn-link" onclick="addTime('sundayTime')">Add Time</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Monday -->
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="mondayCheckbox" value="mon" onclick="showTimeInput('mondayTime')" <?php echo isset($slots->monday) ? 'checked' : '' ?>>
|
||||
<label class="form-check-label" for="mondayCheckbox">Monday</label>
|
||||
|
||||
<div class="form-row time-container" id="mondayTime" <?php echo isset($slots->monday) ? 'style="display:flex"' : '' ?>>
|
||||
<?php if (isset($slots->monday)) {
|
||||
foreach ($slots->monday as $mon) {
|
||||
?>
|
||||
<div class="time-set form-row ml-3">
|
||||
<div class="form-group col-md-4">
|
||||
<label for="mondayFromInput">From</label>
|
||||
<input type="time" name="from" id="mondayFromInput" class="form-control mb-2 time-input" value="<?php echo $mon->from ?>" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="mondayToInput">To</label>
|
||||
<input type="time" name="to" id="mondayToInput" class="form-control mb-2 time-input" value="<?php echo $mon->to ?>" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="mondayPoint">Point</label>
|
||||
<input type="number" name="point" id="mondayPoint" class="form-control mb-2 point" value="<?php echo $mon->point ?>" required>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<?php }
|
||||
} ?>
|
||||
<div class="form-group col-md-12">
|
||||
<a type="button" class="btn btn-link" onclick="addTime('mondayTime')">Add Time</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tuesday -->
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="tuesdayCheckbox" value="tue" onclick="showTimeInput('tuesdayTime')" <?php echo isset($slots->tuesday) ? 'checked' : '' ?>>
|
||||
<label class="form-check-label" for="tuesdayCheckbox">Tuesday</label>
|
||||
|
||||
<div class="form-row time-container" id="tuesdayTime" <?php echo isset($slots->tuesday) ? 'style="display:flex"' : '' ?>>
|
||||
<?php if (isset($slots->tuesday)) {
|
||||
foreach ($slots->tuesday as $tue) {
|
||||
?>
|
||||
<div class="time-set form-row ml-3">
|
||||
<div class="form-group col-md-4">
|
||||
<label for="tuesdayFromInput">From</label>
|
||||
<input type="time" name="from" id="tuesdayFromInput" class="form-control mb-2 time-input" value="<?php echo $tue->from ?>" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="tuesdayToInput">To</label>
|
||||
<input type="time" name="to" id="tuesdayToInput" class="form-control mb-2 time-input" value="<?php echo $tue->to ?>" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="tuesdayPoint">Point</label>
|
||||
<input type="number" name="point" id="tuesdayPoint" class="form-control mb-2 point" value="<?php echo $tue->point ?>" required>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<?php }
|
||||
} ?>
|
||||
<div class="form-group col-md-12">
|
||||
<a type="button" class="btn btn-link" onclick="addTime('tuesdayTime')">Add Time</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Wednesday -->
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="wednesdayCheckbox" value="wed" onclick="showTimeInput('wednesdayTime')" <?php echo isset($slots->wednesday) ? 'checked' : '' ?>>
|
||||
<label class="form-check-label" for="wednesdayCheckbox">Wednesday</label>
|
||||
|
||||
<div class="form-row time-container" id="wednesdayTime" <?php echo isset($slots->wednesday) ? 'style="display:flex"' : '' ?>>
|
||||
<?php if (isset($slots->wednesday)) {
|
||||
foreach ($slots->wednesday as $wed) {
|
||||
?>
|
||||
<div class="time-set form-row ml-3">
|
||||
<div class="form-group col-md-4">
|
||||
<label for="wednesdayFromInput">From</label>
|
||||
<input type="time" name="from" id="wednesdayFromInput" class="form-control mb-2 time-input" value="<?php echo $wed->from ?>" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="wednesdayToInput">To</label>
|
||||
<input type="time" name="to" id="wednesdayToInput" class="form-control mb-2 time-input" value="<?php echo $wed->to ?>" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="wednesdayPoint">Point</label>
|
||||
<input type="number" name="point" id="wednesdayPoint" class="form-control mb-2 point" value="<?php echo $wed->point ?>" required>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<?php }
|
||||
} ?>
|
||||
<div class="form-group col-md-12">
|
||||
<a type="button" class="btn btn-link" onclick="addTime('wednesdayTime')">Add Time</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Thursday -->
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="thursdayCheckbox" value="thurs" onclick="showTimeInput('thursdayTime')" <?php echo isset($slots->thursday) ? 'checked' : '' ?>>
|
||||
<label class="form-check-label" for="thursdayCheckbox">Thursday</label>
|
||||
|
||||
<div class="form-row time-container" id="thursdayTime" <?php echo isset($slots->thursday) ? 'style="display:flex"' : '' ?>>
|
||||
<?php if (isset($slots->thursday)) {
|
||||
foreach ($slots->thursday as $thurs) {
|
||||
?>
|
||||
<div class="time-set form-row ml-3">
|
||||
<div class="form-group col-md-4">
|
||||
<label for="thursdayFromInput">From</label>
|
||||
<input type="time" name="from" id="thursdayFromInput" class="form-control mb-2 time-input" value="<?php echo $thurs->from ?>" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="thursdayToInput">To</label>
|
||||
<input type="time" name="to" id="thursdayToInput" class="form-control mb-2 time-input" value="<?php echo $thurs->to ?>" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="thursdayPoint">Point</label>
|
||||
<input type="number" name="point" id="thursdayPoint" class="form-control mb-2 point" value="<?php echo $thurs->point ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
<?php }
|
||||
} ?>
|
||||
<div class="form-group col-md-12">
|
||||
<a type="button" class="btn btn-link" onclick="addTime('thursdayTime')">Add Time</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Friday -->
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="fridayCheckbox" value="fri" onclick="showTimeInput('fridayTime')" <?php echo isset($slots->friday) ? 'checked' : '' ?>>
|
||||
<label class="form-check-label" for="fridayCheckbox">Friday</label>
|
||||
|
||||
<div class="form-row time-container" id="fridayTime" <?php echo isset($slots->friday) ? 'style="display:flex"' : '' ?>>
|
||||
<?php if (isset($slots->friday)) {
|
||||
foreach ($slots->friday as $fri) {
|
||||
?>
|
||||
<div class="time-set form-row ml-3">
|
||||
<div class="form-group col-md-4">
|
||||
<label for="fridayFromInput">From</label>
|
||||
<input type="time" name="from" id="fridayFromInput" class="form-control mb-2 time-input" value="<?php echo $fri->from ?>" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="fridayToInput">To</label>
|
||||
<input type="time" name="to" id="fridayToInput" class="form-control mb-2 time-input" value="<?php echo $fri->to ?>" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="fridayPoint">Point</label>
|
||||
<input type="number" name="point" id="fridayPoint" class="form-control mb-2 point" value="<?php echo $fri->point ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
<?php }
|
||||
} ?>
|
||||
<div class="form-group col-md-12">
|
||||
<a type="button" class="btn btn-link" onclick="addTime('fridayTime')">Add Time</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Saturday -->
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="saturdayCheckbox" value="sat" onclick="showTimeInput('saturdayTime')" <?php echo isset($slots->saturday) ? 'checked' : '' ?>>
|
||||
<label class="form-check-label" for="saturdayCheckbox">Saturday</label>
|
||||
|
||||
<div class="form-row time-container" id="saturdayTime" <?php echo isset($slots->saturday) ? 'style="display:flex"' : '' ?>>
|
||||
<?php if (isset($slots->saturday)) {
|
||||
foreach ($slots->saturday as $sat) {
|
||||
?>
|
||||
<div class="time-set form-row ml-3">
|
||||
<div class="form-group col-md-4">
|
||||
<label for="saturdayFromInput">From</label>
|
||||
<input type="time" name="from" id="saturdayFromInput" class="form-control mb-2 time-input" value="<?php echo $sat->from ?>" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="saturdayToInput">To</label>
|
||||
<input type="time" name="to" id="saturdayToInput" class="form-control mb-2 time-input" value="<?php echo $sat->to ?>" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="saturdayPoint">Point</label>
|
||||
<input type="number" name="point" id="saturdayPoint" class="form-control mb-2 point" value="<?php echo $sat->point ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
<?php }
|
||||
} ?>
|
||||
<div class="form-group col-md-12">
|
||||
<a type="button" class="btn btn-link" onclick="addTime('saturdayTime')">Add Time</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
// }
|
||||
?>
|
||||
<div class="form-group">
|
||||
<label for="days">Days</label>
|
||||
<input type="number" name="days" id="days" class="form-control" value="<?php echo isset($data['model']) ? $data['model']->days : '' ?>" required>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary" onclick="submitForm(event)">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
var formData = {};
|
||||
|
||||
function showTimeInput(containerId) {
|
||||
var checkbox = document.getElementById(containerId.replace("Time", "Checkbox"));
|
||||
var timeContainer = document.getElementById(containerId);
|
||||
|
||||
if (checkbox.checked) {
|
||||
timeContainer.style.display = "flex";
|
||||
} else {
|
||||
timeContainer.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
function addTime(containerId) {
|
||||
var timeContainer = document.getElementById(containerId);
|
||||
|
||||
var br = document.createElement("br");
|
||||
timeContainer.insertBefore(br, timeContainer.lastElementChild);
|
||||
var newTimeSet = document.createElement("div");
|
||||
newTimeSet.className = "time-set form-row ml-3";
|
||||
|
||||
var newFormGroup1 = document.createElement("div");
|
||||
newFormGroup1.className = "form-group col-md-3";
|
||||
var newLabelFrom = document.createElement("label");
|
||||
newLabelFrom.textContent = "From:";
|
||||
var newInputFrom = document.createElement("input");
|
||||
newInputFrom.type = "time";
|
||||
newInputFrom.name = "from";
|
||||
newInputFrom.className = "form-control mb-2 time-input";
|
||||
newFormGroup1.appendChild(newLabelFrom);
|
||||
newFormGroup1.appendChild(newInputFrom);
|
||||
|
||||
var newFormGroup2 = document.createElement("div");
|
||||
newFormGroup2.className = "form-group col-md-3";
|
||||
var newLabelTo = document.createElement("label");
|
||||
newLabelTo.textContent = "To:";
|
||||
var newInputTo = document.createElement("input");
|
||||
newInputTo.type = "time";
|
||||
newInputTo.name = "to";
|
||||
newInputTo.className = "form-control mb-2 time-input";
|
||||
newFormGroup2.appendChild(newLabelTo);
|
||||
newFormGroup2.appendChild(newInputTo);
|
||||
|
||||
var newFormGroup3 = document.createElement("div");
|
||||
newFormGroup3.className = "form-group col-md-3";
|
||||
var newLabelPoint = document.createElement("label");
|
||||
newLabelPoint.textContent = "Point:";
|
||||
var newInputPoint = document.createElement("input");
|
||||
newInputPoint.type = "number";
|
||||
newInputPoint.name = "point";
|
||||
newInputPoint.className = "form-control mb-2 point";
|
||||
newFormGroup3.appendChild(newLabelPoint);
|
||||
newFormGroup3.appendChild(newInputPoint);
|
||||
|
||||
|
||||
var deleteButtonContainer = document.createElement("div");
|
||||
deleteButtonContainer.className = "form-group col-md-3 mt-auto";
|
||||
var deleteButton = document.createElement("a");
|
||||
deleteButton.type = "button";
|
||||
deleteButton.className = "btn btn-link";
|
||||
deleteButton.textContent = "Delete Time";
|
||||
deleteButton.onclick = function() {
|
||||
deleteTime(newTimeSet);
|
||||
};
|
||||
deleteButtonContainer.appendChild(deleteButton);
|
||||
|
||||
newTimeSet.appendChild(newFormGroup1);
|
||||
newTimeSet.appendChild(newFormGroup2);
|
||||
newTimeSet.appendChild(newFormGroup3);
|
||||
newTimeSet.appendChild(deleteButtonContainer);
|
||||
|
||||
// Insert the new time set before the "Add Time" button
|
||||
// timeContainer.insertBefore(newTimeSet, timeContainer.lastElementChild);
|
||||
|
||||
timeContainer.insertBefore(newTimeSet, br.nextSibling);
|
||||
|
||||
updateFormData(); // Call function to update formData when time is added
|
||||
}
|
||||
|
||||
function deleteTime(timeSet) {
|
||||
var timeContainer = timeSet.parentNode;
|
||||
timeContainer.removeChild(timeSet);
|
||||
|
||||
updateFormData(); // Call function to update formData when time is deleted
|
||||
}
|
||||
|
||||
function calculateTotalPoint() {
|
||||
var totalPoint = 0;
|
||||
|
||||
// Loop through each day in formData
|
||||
for (var day in formData) {
|
||||
if (formData.hasOwnProperty(day)) {
|
||||
// Get the data for the day
|
||||
var dayData = formData[day];
|
||||
|
||||
// Sum up the points for each time set
|
||||
dayData.forEach(function(timeSet) {
|
||||
totalPoint += parseFloat(timeSet.point) || 0; // Convert to float and handle NaN
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Total Point: " + totalPoint);
|
||||
return totalPoint;
|
||||
}
|
||||
|
||||
function calculateTotalPoints(daysToCheck) {
|
||||
var currentDate = new Date();
|
||||
var currentDayIndex = currentDate.getDay(); // 0 for Sunday, 1 for Monday, ..., 6 for Saturday
|
||||
|
||||
// Get the next two days to check
|
||||
var daysToCheckIndices = [];
|
||||
for (var i = 0; i < daysToCheck; i++) {
|
||||
var nextDayIndex = (currentDayIndex + i + 1) % 7;
|
||||
daysToCheckIndices.push(nextDayIndex);
|
||||
}
|
||||
|
||||
var totalPoints = 0;
|
||||
|
||||
// Loop through the selected days and calculate total points
|
||||
daysToCheckIndices.forEach(function(dayIndex) {
|
||||
var dayName = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'][dayIndex];
|
||||
|
||||
if (formData[dayName]) {
|
||||
formData[dayName].forEach(function(timeSlot) {
|
||||
totalPoints += parseFloat(timeSlot.point) || 0;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return totalPoints;
|
||||
}
|
||||
|
||||
function updateFormData() {
|
||||
// Reset formData
|
||||
formData = {};
|
||||
|
||||
// Loop through each day
|
||||
['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'].forEach(function(day) {
|
||||
var checkbox = document.getElementById(day + 'Checkbox');
|
||||
var timeContainer = document.getElementById(day + 'Time');
|
||||
// var pointContainer = document.getElementById(day + 'Point');
|
||||
var timeSets = timeContainer.querySelectorAll('.time-set');
|
||||
// console.log(checkbox)
|
||||
// console.log(timeContainer)
|
||||
// console.log(timeSets)
|
||||
|
||||
if (checkbox.checked) {
|
||||
var dayData = [];
|
||||
|
||||
// Iterate over each time set
|
||||
timeSets.forEach(function(timeSet) {
|
||||
// console.log(timeSet)
|
||||
|
||||
var fromInput = timeSet.querySelector('input[name="from"]');
|
||||
var toInput = timeSet.querySelector('input[name="to"]');
|
||||
var point = timeSet.querySelector('input[name="point"]');
|
||||
|
||||
// Check if both "From" and "To" inputs have values
|
||||
if (fromInput.value.trim() !== '' && toInput.value.trim() !== '' && point.value.trim() !== '') {
|
||||
// Store the data in dayData array
|
||||
dayData.push({
|
||||
from: fromInput.value,
|
||||
to: toInput.value,
|
||||
point: point.value
|
||||
});
|
||||
}
|
||||
});
|
||||
// console.log(dayData)
|
||||
|
||||
// If there is data for the day, store it in formData
|
||||
if (dayData.length > 0) {
|
||||
formData[day] = dayData;
|
||||
// console.log(formData); // Log formData for debugging
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log(formData); // Log formData for debugging
|
||||
}
|
||||
|
||||
function submitForm(event) {
|
||||
event.preventDefault();
|
||||
|
||||
updateFormData(); // Ensure formData is updated before submission
|
||||
// Get form values using DOM
|
||||
var project_name = document.getElementById("project_name").value;
|
||||
var days = document.getElementById("days").value;
|
||||
var webhook = document.getElementById("webhook").value;
|
||||
var score_threshold = document.getElementById("score_threshold").value;
|
||||
|
||||
var webhook_payload = document.getElementById("webhook_payload").value;
|
||||
var calendar_id = document.getElementById("calendar_id").value;
|
||||
var location = document.getElementById("location").value;
|
||||
|
||||
|
||||
function isJSON(str) {
|
||||
try {
|
||||
JSON.parse(str);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// var isJsonData = isJSON(webhook_payload);
|
||||
|
||||
// if (isJsonData) {
|
||||
// var json_payload = JSON.parse(webhook_payload);
|
||||
|
||||
// } else {
|
||||
// console.log("The variable does not contain valid JSON data.");
|
||||
// }
|
||||
// var actual_score = document.getElementById("actual_score").value;
|
||||
// var alert = document.getElementById("alert").value;
|
||||
console.log(formData)
|
||||
// Get checkbox values (example for Sunday)
|
||||
// var sundayCheckbox = document.getElementById("sundayCheckbox");
|
||||
// var isSundayChecked = sundayCheckbox.checked;
|
||||
|
||||
// var actual_score = calculateTotalPoint()
|
||||
var actual_score = calculateTotalPoints(parseInt(days))
|
||||
|
||||
// Include other form elements as needed...
|
||||
formData = JSON.stringify(formData)
|
||||
// Create an object with the form data
|
||||
|
||||
|
||||
var ids = '<?php if (isset($data['ids'])) {
|
||||
echo $data['ids'];
|
||||
} ?>';
|
||||
var Data = {
|
||||
project_name: project_name,
|
||||
days: days,
|
||||
webhook: webhook,
|
||||
score_threshold: score_threshold,
|
||||
actual_score: actual_score,
|
||||
webhook_payload: webhook_payload,
|
||||
slot: formData,
|
||||
calendar_id: calendar_id,
|
||||
location: location,
|
||||
ids: ids,
|
||||
multiedit: true
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
url: '/<?php echo $_SESSION['role'] ?>/project/list/multiselect',
|
||||
method: 'POST',
|
||||
data: Data,
|
||||
success: function(response) {
|
||||
// console.log(response.slots_available);
|
||||
|
||||
console.log(response);
|
||||
// alert(JSON.stringify(response));
|
||||
window.location.href = '/<?php echo $_SESSION['role'] ?>/project'
|
||||
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.log(error)
|
||||
// console.log("error oooo")
|
||||
}
|
||||
});
|
||||
// Display the collected data (you can replace this with your own logic)
|
||||
// alert(JSON.stringify(formData));
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,354 @@
|
||||
<style>
|
||||
.dark-header thead th {
|
||||
background-color: #343a40;
|
||||
/* Dark gray background */
|
||||
color: white;
|
||||
/* White text color */
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="container">
|
||||
<h2 class="text-left">Availability Checker <a class="btn btn-primary" href="/<?php echo $_SESSION['role'] ?>/project/add">Add</a></h2>
|
||||
<!-- <div id="loading" style="display:none;">
|
||||
<div class=" spinner-border" role="status">
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- Table Responsive Wrapper -->
|
||||
<div class="table-responsive">
|
||||
<form id="multiselect-form" action="/<?php echo $_SESSION['role'] ?>/project/list/multiselect" method="POST">
|
||||
<button type="submit" id="edit-selected" style="display: none;" name="edit" class="btn btn-primary">Edit Selected</button> <!-- Button for edit action -->
|
||||
<button type="submit" id="delete-selected" style="display: none;" name="delete" class="btn btn-danger">Delete Selected</button> <!-- Button for delete action -->
|
||||
<table class="table table-hover dark-header">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>ID</th>
|
||||
<th>Project Name</th>
|
||||
<!-- <th>Slot</th> -->
|
||||
|
||||
<th>Days</th>
|
||||
<th>Score Threshold</th>
|
||||
<th>Actual Score</th>
|
||||
<th>Alert Check</th>
|
||||
<th>Webhook</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($data['data'] as $key => $value) {
|
||||
$role = $_SESSION['role'] ;
|
||||
echo ' <tr>';
|
||||
echo '<td><input type="checkbox" name="selected[]" value="' . $value->id . '"></td>'; // Checkbox for multiselect
|
||||
echo ' <td>' . $value->id . ' <br/><a class=" text-info" href="/' . $role . '/project/edit/' . $value->id . '">edit</a> <a class="text-danger" href="/'. $role .'/project/delete/' . $value->id . '">delete</a></td>';
|
||||
// echo ' <td>' . $value->id . ' <br/><a class="text-danger" href="/project/delete/' . $value->id . '">delete</a></td>';
|
||||
echo ' <td>' . $value->project_name . ' </td>';
|
||||
// echo ' <td>' . $value->slot . ' </td>';
|
||||
|
||||
echo ' <td>' . $value->days . ' </td>';
|
||||
echo ' <td>' . $value->score_threshold . ' </td>';
|
||||
echo ' <td>' . $value->actual_score . ' </td>';
|
||||
|
||||
echo ' <td><div class="btn-group btn-group-toggle" data-toggle="buttons">
|
||||
<label class="btn btn-primary active">
|
||||
<input type="radio" name="options" value="' . $value->alert . '" data-id="' . $value->id . '" onclick="alert_toggle(this)" class="toggle-option" autocomplete="off" ' . ($value->alert == "On" ? 'checked' : '') . ' > ' . $value->alert . '
|
||||
</label>
|
||||
|
||||
</div></td>';
|
||||
echo ' <td>' . $value->webhook . ' </td>';
|
||||
echo ' <td><a style="cursor: pointer;" class="btn-link link-underline text-underline" target="__blank" data-id="' . $value->id . '" onclick="checkCal(this)">Check</a>
|
||||
<a style="cursor: pointer;" class="btn-link link-underline text-underline" target="__blank" data-id="' . $value->id . '" onclick="dupl(this)">Duplicate</a>
|
||||
<a style="cursor: pointer;" class="btn-link link-underline text-underline" target="__blank" data-id="' . $value->id . '" onclick="authorize(this)">Authorize</a>
|
||||
</td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="calModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div id="makingWishLoading" style="display:none;">
|
||||
<div class=" spinner-border text-light" role="status">
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-header text-center">
|
||||
<h5 class="modal-title w-100" id="wishModalLable">Calendar</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close" id="wishModalCloseBtn">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="calForm">
|
||||
<div class="form-group">
|
||||
<label for="wish-title">Calendar ID</label>
|
||||
<input type="text" class="form-control" id="calendar_id" placeholder="" style="border: 1px solid #ced4da;" required>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button id="submit_cal" class="btn btn-primary btn-block" target="__blank" onclick="duplicate(this)">Submit</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
// Retrieve parameters
|
||||
$total = $data['total'];
|
||||
$currentPage = $data['page'];
|
||||
$perPage = 10;
|
||||
|
||||
// Calculate the number of pages
|
||||
$totalPages = ceil($total / $perPage);
|
||||
|
||||
// Define a range of pages to show at any given time
|
||||
$range = 2; // This can be adjusted as needed
|
||||
|
||||
$startPage = ($currentPage - $range) > 0 ? ($currentPage - $range) : 1;
|
||||
$endPage = ($currentPage + $range) < $totalPages ? ($currentPage + $range) : $totalPages;
|
||||
|
||||
?>
|
||||
<!-- Pagination -->
|
||||
<nav aria-label="Page navigation">
|
||||
<ul class="pagination">
|
||||
<li class="ml-2">
|
||||
<a href="?page=1" aria-label="Previous">
|
||||
<span aria-hidden="true">««</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="ml-2">
|
||||
<a href="?page=<?= ($currentPage - 1) > 0 ? $currentPage - 1 : 1 ?>" aria-label="Previous">
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
<?php
|
||||
for ($i = $startPage; $i <= $endPage; $i++) {
|
||||
echo '<li class="ml-2' . ($currentPage == $i ? ' active' : '') . '"><a href="?page=' . $i . '">' . $i . '</a></li>';
|
||||
}
|
||||
?>
|
||||
<li class="ml-2">
|
||||
<a href="?page=<?= ($currentPage + 1) < $totalPages ? $currentPage + 1 : $totalPages ?>" aria-label="Next">
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="ml-2">
|
||||
<a href="?page=<?= $totalPages ?>" aria-label="Next">
|
||||
<span aria-hidden="true">»»</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
// Attach click event to the toggle buttons
|
||||
function alert_toggle(element) {
|
||||
|
||||
// Get the selected value (Yes or No)
|
||||
var projectId = $(element).attr("data-id");
|
||||
var selectedValue = $(element).val();
|
||||
console.log(projectId)
|
||||
console.log(selectedValue)
|
||||
if (selectedValue == "On") {
|
||||
var values = "Off"
|
||||
}
|
||||
if (selectedValue == "Off") {
|
||||
var values = "On"
|
||||
}
|
||||
// Make an AJAX request to update the database
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/alert-toggle', // Replace with the actual path to your update script
|
||||
data: {
|
||||
projectId: projectId,
|
||||
selectedValue: values
|
||||
},
|
||||
success: function(response) {
|
||||
// Handle the response from the server (if needed)
|
||||
console.log(response);
|
||||
window.location.reload()
|
||||
},
|
||||
error: function(error) {
|
||||
console.error('Error updating data:', error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function dupl(element) {
|
||||
var projectId = $(element).data("id");
|
||||
$('#submit_cal').attr('data-id', projectId);
|
||||
$("#calModal").modal("show");
|
||||
|
||||
}
|
||||
|
||||
function checkCal(element) {
|
||||
event.preventDefault();
|
||||
|
||||
|
||||
// var calendarId = $("#calendar_id").val();
|
||||
// var projectId = $("#submit_cal").attr("data-id");
|
||||
var projectId = $(element).data("id");
|
||||
|
||||
// console.log(calendarId)
|
||||
console.log(projectId)
|
||||
|
||||
var spinner = document.createElement('span');
|
||||
spinner.className = 'spinner-border spinner-border-sm';
|
||||
element.appendChild(spinner);
|
||||
|
||||
// Disable the button
|
||||
element.classList.add('disabled');
|
||||
try {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/check-calendar',
|
||||
data: {
|
||||
project_id: projectId,
|
||||
},
|
||||
success: function(response) {
|
||||
|
||||
element.removeChild(spinner);
|
||||
element.classList.remove('disabled');
|
||||
console.log(typeof response);
|
||||
if (response == "Accepted") {
|
||||
alert("Webhook triggered")
|
||||
} else {
|
||||
if (response == "low" || response == 'Low') {
|
||||
alert("Point is higher than mininum point set")
|
||||
} else {
|
||||
alert(response)
|
||||
}
|
||||
|
||||
}
|
||||
// Optionally, handle success actions or update UI
|
||||
// $("#calModal").modal("hide");
|
||||
// $("#calForm")[0].reset();
|
||||
|
||||
window.location.reload();
|
||||
},
|
||||
error: function(error) {
|
||||
console.error('Error updating data:', error);
|
||||
// Optionally, display an error message or handle errors
|
||||
// $("#calModal").modal("hide");
|
||||
// $("#calForm")[0].reset();
|
||||
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
notify("Unknown error occurred!");
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
function duplicate(element) {
|
||||
event.preventDefault();
|
||||
|
||||
var calendarId = $("#calendar_id").val();
|
||||
// var projectId = $("#submit_cal").attr("data-id");
|
||||
var projectId = $(element).data("id");
|
||||
if (calendarId == "") {
|
||||
alert('Calendar Id is empty')
|
||||
return
|
||||
}
|
||||
// console.log(calendarId)
|
||||
console.log(projectId)
|
||||
try {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/<?php echo $_SESSION['role'] ?>/duplicate',
|
||||
data: {
|
||||
project_id: projectId,
|
||||
calendar_id: calendarId
|
||||
},
|
||||
success: function(response) {
|
||||
console.log(typeof response);
|
||||
if (response == "Accepted") {
|
||||
alert("Webhook triggered")
|
||||
} else {
|
||||
if (response == "low" || response == 'Low') {
|
||||
alert("Point is higher than mininum point set")
|
||||
} else {
|
||||
alert(response)
|
||||
}
|
||||
|
||||
}
|
||||
// Optionally, handle success actions or update UI
|
||||
window.location.reload()
|
||||
|
||||
|
||||
|
||||
},
|
||||
error: function(error) {
|
||||
console.error('Error updating data:', error);
|
||||
alert('Error updating data:')
|
||||
window.location.reload()
|
||||
|
||||
// Optionally, display an error message or handle errors
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
notify("Unknown error occurred!");
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Event binding
|
||||
$(document).ready(function() {
|
||||
// $('#submit_cal').on('click', submitCal);
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Get all checkboxes
|
||||
var checkboxes = document.querySelectorAll('input[type="checkbox"]');
|
||||
|
||||
// Add event listener to checkboxes
|
||||
checkboxes.forEach(function(checkbox) {
|
||||
checkbox.addEventListener('change', function() {
|
||||
// Check if any checkbox is checked
|
||||
var anyChecked = Array.from(checkboxes).some(function(cb) {
|
||||
return cb.checked;
|
||||
});
|
||||
|
||||
// Show/hide the buttons based on checkbox state
|
||||
document.getElementById('edit-selected').style.display = anyChecked ? 'inline-block' : 'none';
|
||||
document.getElementById('delete-selected').style.display = anyChecked ? 'inline-block' : 'none';
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function authorize(element) {
|
||||
var projectId = $(element).data("id");
|
||||
// Get current page from URL query parameter
|
||||
var urlParams = new URLSearchParams(window.location.search);
|
||||
var currentPage = urlParams.get('page') || 1;
|
||||
window.location.href = '/ghl/authorize/' + projectId + 'p' + currentPage + '?redirect_uri=' + window.location.pathname;
|
||||
|
||||
|
||||
}
|
||||
|
||||
// document.getElementById("multiselect-form").addEventListener("submit", function(event) {
|
||||
// event.preventDefault(); // Prevent default form submission
|
||||
|
||||
// // Display confirmation dialog
|
||||
// if (confirm("Are you sure you want to proceed?")) {
|
||||
// // If user confirms, submit the form
|
||||
// this.submit();
|
||||
// } else {
|
||||
// // If user cancels, do nothing
|
||||
// return false;
|
||||
// }
|
||||
// });
|
||||
</script>
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
Flight::route('PUT /v1/api/image/edit/@id:[0-9]+', function($id)
|
||||
{
|
||||
$model = new ImagesModel();
|
||||
$allow_fields = ['url', 'user_id', 'caption'];
|
||||
$validation = new ValidationService();
|
||||
$request = new Auth_common_function();
|
||||
$validation->save_rules($model->get_all_edit_validation_rule());
|
||||
$_POST = Flight::request()->data->getData();
|
||||
|
||||
$model_row = $model->get($id);
|
||||
|
||||
if (!$model_row)
|
||||
{
|
||||
echo json_encode([
|
||||
'code' => 404,
|
||||
'error' => false
|
||||
]);
|
||||
http_response_code(404);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($validation->validate($_POST))
|
||||
{
|
||||
$result = $model->edit([
|
||||
'url' => $request->get_input_post('url'),
|
||||
'user_id' => $request->get_input_post('user_id'),
|
||||
'caption' => $request->get_input_post('caption'),
|
||||
], $id);
|
||||
|
||||
if ($result)
|
||||
{
|
||||
echo json_encode([
|
||||
'code' => 200,
|
||||
'error' => false,
|
||||
'id' => $id,
|
||||
'message' => "Data has been updated"
|
||||
]);
|
||||
http_response_code(200);
|
||||
exit;
|
||||
}
|
||||
else
|
||||
{
|
||||
echo json_encode([
|
||||
'code' => 409,
|
||||
'error' => false,
|
||||
'id' => $id,
|
||||
'message' => "Error!"
|
||||
]);
|
||||
http_response_code(409);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$message = $validation->get_errors();
|
||||
$result['error'] = true;
|
||||
$result['http_code'] = 403;
|
||||
$result['message'] = $message;
|
||||
|
||||
echo json_encode($result);
|
||||
http_response_code(403);
|
||||
exit;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
/*Powered By: Manaknightdigital Inc. https://manaknightdigital.com/ Year: 2021*/
|
||||
/**
|
||||
* Query Service
|
||||
* @copyright 2021 Manaknightdigital Inc.
|
||||
* @link https://manaknightdigital.com
|
||||
* @license Proprietary Software licensing
|
||||
* @author Samyam kafle <samyam1kafle@gmail.com>
|
||||
*
|
||||
*/
|
||||
class QueryService
|
||||
{
|
||||
public function create_where($type_array, $fields=[], $operators=[], $field_values=[])
|
||||
{
|
||||
$where = [];
|
||||
$model_fields = array_keys($type_array);
|
||||
|
||||
if (count($fields) != count($operators) || count($operators) != count($field_values))
|
||||
{
|
||||
return $where;
|
||||
}
|
||||
|
||||
for ($i=0; $i < count($fields); $i++)
|
||||
{
|
||||
if (in_array($fields[$i], $model_fields))
|
||||
{
|
||||
$type = $type_array[$fields[$i]];
|
||||
$single_field_values = $field_values[$i];
|
||||
|
||||
switch( $single_field_values )
|
||||
{
|
||||
case 'integer':
|
||||
$single_field_values = filter_var($single_field_values, FILTER_SANITIZE_NUMBER_INT );
|
||||
break;
|
||||
case 'string':
|
||||
case 'date':
|
||||
case 'datetime':
|
||||
if ($operators[$i] == 'LIKE')
|
||||
{
|
||||
$single_field_values = "'%" . filter_var( $single_field_values, FILTER_SANITIZE_STRING ) . "%'";
|
||||
}
|
||||
else
|
||||
{
|
||||
$single_field_values = "'" . filter_var( $single_field_values, FILTER_SANITIZE_STRING ) . "'";
|
||||
}
|
||||
break;
|
||||
case 'float':
|
||||
$single_field_values = filter_var( $single_field_values, FILTER_SANITIZE_NUMBER_FLOAT );
|
||||
break;
|
||||
}
|
||||
|
||||
switch ($operators[$i])
|
||||
{
|
||||
case 'EQUAL':
|
||||
$where[] = " `{$fields[$i]}` = '{$single_field_values}' ";
|
||||
break;
|
||||
case 'NOT_EQUAL':
|
||||
$where[] = "{ `$fields[$i]}` != '{$single_field_values}' ";
|
||||
break;
|
||||
case 'GREATER_THAN':
|
||||
if ($type == 'integer' || $type == 'float')
|
||||
{
|
||||
$where[] = " `{$fields[$i]}` > {$single_field_values}";
|
||||
}
|
||||
break;
|
||||
case 'GREATER_THAN_EQUAL':
|
||||
if ($type == 'integer' || $type == 'float')
|
||||
{
|
||||
$where[] = " `{$fields[$i]}` >= {$single_field_values}";
|
||||
}
|
||||
break;
|
||||
case 'LESS_THAN':
|
||||
if ($type == 'integer' || $type == 'float')
|
||||
{
|
||||
$where[] = " `{$fields[$i]}` < {$single_field_values}";
|
||||
}
|
||||
break;
|
||||
case 'LESS_THAN_EQUAL':
|
||||
if ($type == 'integer' || $type == 'float')
|
||||
{
|
||||
$where[] = " `{$fields[$i]}` <= {$single_field_values}";
|
||||
}
|
||||
break;
|
||||
case 'LIKE':
|
||||
if ($type != 'integer' && $type != 'float' && $type != 'date' && $type != 'datetime')
|
||||
{
|
||||
$where[] = " `{$fields[$i]}` LIKE {$single_field_values}";
|
||||
}
|
||||
break;
|
||||
//TODO: IN Operator
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
public function create_join_query ($where_query, $column_fields, $join_tables, $join_field, $fields, $operators, $values, $page, $per_page, $sort, $direction)
|
||||
{
|
||||
$select = [];
|
||||
|
||||
$from = "FROM {$join_tables['a']} a ";
|
||||
$from_list = [];
|
||||
$join = [];
|
||||
|
||||
foreach ($join_tables as $key => $value)
|
||||
{
|
||||
$from_list[] = " $value $key";
|
||||
if ($key != 'a')
|
||||
{
|
||||
$join[] = " INNER JOIN $value $key ON a.id = {$key}.{$join_field} ";
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($column_fields as $key => $value)
|
||||
{
|
||||
$select[] = " $value ";
|
||||
}
|
||||
|
||||
$from = $from . implode (' ', $join);
|
||||
$sql = 'SELECT ' . implode(',', $select) . $from;
|
||||
$total_sql = 'SELECT count(*) as total ' . $from;
|
||||
|
||||
if ($sort)
|
||||
{
|
||||
$sql .= " ORDER BY {$sort} {$direction}";
|
||||
}
|
||||
|
||||
if ($per_page && $page)
|
||||
{
|
||||
$offset = ($page - 1) * $per_page;
|
||||
$sql .= " LIMIT $per_page, $offset";
|
||||
}
|
||||
|
||||
return [
|
||||
'total' => $total_sql,
|
||||
'query' => $sql
|
||||
];
|
||||
}
|
||||
|
||||
public function perform_join ($model, $query, $page, $per_page)
|
||||
{
|
||||
$total = $model->raw_query($query['total']);
|
||||
$list = $model->raw_query($query['query']);
|
||||
$last_id = $list[array_key_last($list)]['id'];
|
||||
return [
|
||||
'total' => $total->total,
|
||||
'num_page' => ceil($total->total / $per_page),
|
||||
'page' => $page,
|
||||
'list' => $list,
|
||||
'id' => $last_id
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
include_once __DIR__ . "/mysql-database-service.php";
|
||||
|
||||
class ReportModel extends MySqlDatabaseService
|
||||
{
|
||||
protected $_table = 'report';
|
||||
protected $_primary_key = 'id';
|
||||
protected $_return_type = 'array';
|
||||
protected $_allowed_fields = [
|
||||
'id', 'project', 'date', 'new_lead', 'outbound_dial', 'pickup', 'conversation', 'booked_appointment', 'callback_request'
|
||||
];
|
||||
protected $_label_fields = [
|
||||
'ID', 'Project', 'Date', 'New Lead', 'Outbound Dial', 'Pickup', 'Conversation', 'Booked Appointment', 'Callback Request'
|
||||
];
|
||||
protected $_use_timestamps = true;
|
||||
protected $_created_field = 'created_at';
|
||||
protected $_updated_field = 'updated_at';
|
||||
protected $_validation_rules = [
|
||||
['project', 'Project', 'required'],
|
||||
|
||||
['date', 'Date', 'required'],
|
||||
['new_lead', 'New Lead', 'required'],
|
||||
['outbound_dial', 'Outbound Dial', 'required'],
|
||||
['pickup', 'Pickup', 'required'],
|
||||
['conversation', 'Conversation', 'required'],
|
||||
['booked_appointment', 'Booked Appointment', 'required'],
|
||||
['callback_request', 'Callback Request', 'required']
|
||||
];
|
||||
|
||||
protected $_validation_edit_rules = [
|
||||
['project', 'Project', 'required'],
|
||||
['date', 'Date', 'required'],
|
||||
|
||||
['new_lead', 'New Lead', 'required'],
|
||||
['outbound_dial', 'Outbound Dial', 'required'],
|
||||
['pickup', 'Pickup', 'required'],
|
||||
['conversation', 'Conversation', 'required'],
|
||||
['booked_appointment', 'Booked Appointment', 'required'],
|
||||
['callback_request', 'Callback Request', 'required']
|
||||
];
|
||||
protected $_validation_messages = [
|
||||
['project', 'Project', 'required'],
|
||||
['date', 'Date', 'required'],
|
||||
|
||||
['new_lead', 'New Lead', 'required'],
|
||||
['outbound_dial', 'Outbound Dial', 'required'],
|
||||
['pickup', 'Pickup', 'required'],
|
||||
['conversation', 'Conversation', 'required'],
|
||||
['booked_appointment', 'Booked Appointment', 'required'],
|
||||
['callback_request', 'Callback Request', 'required']
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function get_mapping()
|
||||
{
|
||||
return [
|
||||
// TODO: ADD MAPPING
|
||||
];
|
||||
}
|
||||
|
||||
public function csvToObject($csvString) {
|
||||
$lines = explode("\n", $csvString);
|
||||
$headers = array_map(function($header) {
|
||||
return str_replace(' ', '_', trim(strtolower($header)));
|
||||
}, explode(',', $lines[0])); // Convert headers to snake case
|
||||
|
||||
$object = [];
|
||||
|
||||
foreach ($headers as $header) {
|
||||
$object[$header] = [];
|
||||
}
|
||||
|
||||
for ($i = 1; $i < count($lines) - 1; $i++) {
|
||||
$values = explode(',', $lines[$i]);
|
||||
for ($j = 0; $j < count($headers) && $j < count($values); $j++) {
|
||||
$value = trim($values[$j]);
|
||||
if ($headers[$j] == 'duration' && $value === "-") {
|
||||
$object[$headers[$j]][] = '00:00';
|
||||
continue;
|
||||
}
|
||||
$object[$headers[$j]][] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// public function get_all_for_csv($where = [], $order_by = '', $direction = 'ASC')
|
||||
// {
|
||||
|
||||
// // if ($order_by === '') {
|
||||
// // $order_by = $this->_primary_key;
|
||||
// // }
|
||||
|
||||
// // $this->db->order_by($this->clean_alpha_num_field($order_by), $this->clean_alpha_field($direction));
|
||||
|
||||
// // if (!empty($where)) {
|
||||
// // foreach ($where as $field => $value) {
|
||||
// // if (is_numeric($field) && strlen($value) > 0) {
|
||||
// // $this->db->where($value);
|
||||
// // continue;
|
||||
// // }
|
||||
|
||||
// // if ($field === NULL && $value === NULL) {
|
||||
// // continue;
|
||||
// // }
|
||||
|
||||
// // if ($value !== NULL) {
|
||||
// // if (is_numeric($value)) {
|
||||
// // $this->db->where($field, $value);
|
||||
// // continue;
|
||||
// // }
|
||||
|
||||
// // if (is_string($value)) {
|
||||
// // $this->db->like($field, $value);
|
||||
// // continue;
|
||||
// // }
|
||||
|
||||
// // $this->db->where($field, $value);
|
||||
// // }
|
||||
// // }
|
||||
// // }
|
||||
|
||||
// // $query = $this->db->get($this->_table);
|
||||
|
||||
// $sql = [];
|
||||
// foreach ($where as $key => $value) {
|
||||
// if (is_string($value) && strlen($value) > 0) {
|
||||
// $sql[] = "$key";
|
||||
// } else {
|
||||
// $sql[] = "$key = $value";
|
||||
// }
|
||||
// }
|
||||
|
||||
// return R::findAll($this->_table, implode(' AND ', $sql));
|
||||
// $result = [];
|
||||
|
||||
// // if ($query->num_rows() > 0) {
|
||||
// // foreach ($query->result() as $row) {
|
||||
// // $result[] = $row;
|
||||
// // }
|
||||
// // }
|
||||
|
||||
// return $result;
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
<style>
|
||||
.dark-header thead th {
|
||||
background-color: #343a40;
|
||||
/* Dark gray background */
|
||||
color: white;
|
||||
/* White text color */
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="container">
|
||||
<h2 class="text-left">Report <a class="btn btn-primary d-none" href="/admin/report/csv?format=csv&date=<?php echo $data['date']; ?>">Export</a></h2>
|
||||
<div class="row mt-2 mb-2">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<form action="?" method="GET">
|
||||
<div class="input-group">
|
||||
<input type="date" name="date" class="form-control mr-2" placeholder="" value="<?php echo $data['date']; ?>">
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-primary" type="submit">Search</button>
|
||||
</span>
|
||||
</div><!-- /input-group -->
|
||||
</form>
|
||||
</div><!-- /.col-md-6 -->
|
||||
</div><!-- /.row -->
|
||||
<!-- Table Responsive Wrapper -->
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover dark-header">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Project</th>
|
||||
<th>Date</th>
|
||||
<th>Type</th>
|
||||
|
||||
|
||||
<!-- <th>New Lead</th>
|
||||
<th>Outbound Dial</th>
|
||||
<th>Pickup</th>
|
||||
<th>Conversation</th>
|
||||
<th>Booked Appointment</th>
|
||||
<th>Callback Request</th> -->
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<?php foreach ($data['data'] as $key => $value) {
|
||||
echo ' <tr>';
|
||||
// echo ' <td>' . $value->id . ' <br/><a class=" text-info" href="/admin/project/edit/' . $value->id . '">edit</a> <a class="text-danger" href="/admin/project/delete/' . $value->id . '">delete</a></td>';
|
||||
// echo ' <td>' . $value->id . ' <br/><a class="text-danger" href="/admin/project/delete/' . $value->id . '">delete</a></td>';
|
||||
echo ' <td>' . $value->id . ' </td>';
|
||||
echo ' <td>' . $value->project . ' </td>';
|
||||
echo ' <td>' . $value->date . ' </td>';
|
||||
echo ' <td>' . $value->type . ' </td>';
|
||||
|
||||
|
||||
// echo ' <td>' . $value->new_lead . ' </td>';
|
||||
// echo ' <td>' . $value->outbound_dial . ' </td>';
|
||||
// echo ' <td>' . $value->pickup . ' </td>';
|
||||
// echo ' <td>' . $value->conversation . ' </td>';
|
||||
// echo ' <td>' . $value->booked_appointment . ' </td>';
|
||||
// echo ' <td>' . $value->callback_request . ' </td>';
|
||||
echo ' <td>
|
||||
<form action="/admin/report/webhook/send/'. $value->id .'" method="POST">
|
||||
|
||||
<button class="btn btn-primary" type = "submit">
|
||||
Send Webhook
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
';
|
||||
|
||||
echo '</tr>';
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<?php
|
||||
// Retrieve parameters
|
||||
$total = $data['total'];
|
||||
$currentPage = $data['page'];
|
||||
$perPage = 10;
|
||||
|
||||
// Calculate the number of pages
|
||||
$totalPages = ceil($total / $perPage);
|
||||
|
||||
// Define a range of pages to show at any given time
|
||||
$range = 2; // This can be adjusted as needed
|
||||
|
||||
$startPage = ($currentPage - $range) > 0 ? ($currentPage - $range) : 1;
|
||||
$endPage = ($currentPage + $range) < $totalPages ? ($currentPage + $range) : $totalPages;
|
||||
|
||||
?>
|
||||
<!-- Pagination -->
|
||||
<nav aria-label="Page navigation">
|
||||
<ul class="pagination">
|
||||
<li class="ml-2">
|
||||
<a href="?page=1" aria-label="Previous">
|
||||
<span aria-hidden="true">««</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="ml-2">
|
||||
<a href="?page=<?= ($currentPage - 1) > 0 ? $currentPage - 1 : 1 ?>" aria-label="Previous">
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
<?php
|
||||
for ($i = $startPage; $i <= $endPage; $i++) {
|
||||
echo '<li class="ml-2' . ($currentPage == $i ? ' active' : '') . '"><a href="?page=' . $i . '">' . $i . '</a></li>';
|
||||
}
|
||||
?>
|
||||
<li class="ml-2">
|
||||
<a href="?page=<?= ($currentPage + 1) < $totalPages ? $currentPage + 1 : $totalPages ?>" aria-label="Next">
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="ml-2">
|
||||
<a href="?page=<?= $totalPages ?>" aria-label="Next">
|
||||
<span aria-hidden="true">»»</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
function send(element, url, data) {
|
||||
|
||||
// Get the selected value (Yes or No)
|
||||
var projectId = $(element).attr("data-id");
|
||||
var selectedValue = $(element).val();
|
||||
console.log(projectId)
|
||||
console.log(selectedValue)
|
||||
if (selectedValue == "On") {
|
||||
var values = "Off"
|
||||
}
|
||||
if (selectedValue == "Off") {
|
||||
var values = "On"
|
||||
}
|
||||
// Make an AJAX request to update the database
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/alert-toggle', // Replace with the actual path to your update script
|
||||
data: {
|
||||
projectId: projectId,
|
||||
selectedValue: values
|
||||
},
|
||||
success: function(response) {
|
||||
// Handle the response from the server (if needed)
|
||||
console.log(response);
|
||||
window.location.reload()
|
||||
},
|
||||
error: function(error) {
|
||||
console.error('Error updating data:', error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,436 @@
|
||||
|
||||
<?php
|
||||
|
||||
include_once 'core.php';
|
||||
include_once 'config.php';
|
||||
include_once 'license-model.php';
|
||||
include_once 'user-model.php';
|
||||
include_once 'calendar-model.php';
|
||||
include_once 'project-model.php';
|
||||
include_once 'report-model.php';
|
||||
include_once 'location-model.php';
|
||||
include_once 'accesslog-model.php';
|
||||
// Instantiate ProjectModel
|
||||
$locationModel = new LocationModel();
|
||||
|
||||
// Retrieve all projects
|
||||
$loctions = $locationModel->get_all();
|
||||
// Log the start of the script
|
||||
error_log('Cron job started: ' . date('Y-m-d H:i:s'));
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// $config = MkdConfig::get_instance()->get_config();
|
||||
// $apikey = $config['gohighlevel_key'];
|
||||
foreach ($loctions as $key => $location) {
|
||||
# code...
|
||||
$apikey = $location->apikey;
|
||||
$curlOptions1 = [
|
||||
CURLOPT_URL => "https://rest.gohighlevel.com/v1/locations",
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_ENCODING => "",
|
||||
CURLOPT_MAXREDIRS => 10,
|
||||
CURLOPT_TIMEOUT => 30,
|
||||
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||
CURLOPT_CUSTOMREQUEST => "GET",
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"Accept: application/json",
|
||||
"Authorization: Bearer " . $apikey,
|
||||
"Version: 2021-04-15"
|
||||
],
|
||||
];
|
||||
|
||||
// Initialize cURL session
|
||||
$ch1 = curl_init();
|
||||
|
||||
// Set cURL options
|
||||
curl_setopt_array($ch1, $curlOptions1);
|
||||
|
||||
// Execute cURL session and get the result
|
||||
$response1 = curl_exec($ch1);
|
||||
|
||||
if (curl_errno($ch1)) {
|
||||
error_log('Curl error ' . curl_error($ch1));
|
||||
} else {
|
||||
// Log the webhook response
|
||||
error_log(' response ' . $response1);
|
||||
|
||||
$response1 = json_decode($response1);
|
||||
|
||||
|
||||
$currentTimestamp = time();
|
||||
$previousDayStartTimestamp2 = strtotime('yesterday', strtotime(date('Y-m-d', $currentTimestamp))) * 1000;
|
||||
// Set up cURL options
|
||||
$curlOptions = [
|
||||
CURLOPT_URL => "https://rest.gohighlevel.com/v1/contacts?startAfter=" . $previousDayStartTimestamp2,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_ENCODING => "",
|
||||
CURLOPT_MAXREDIRS => 10,
|
||||
CURLOPT_TIMEOUT => 30,
|
||||
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||
CURLOPT_CUSTOMREQUEST => "GET",
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"Accept: application/json",
|
||||
"Authorization: Bearer " . $apikey,
|
||||
"Version: 2021-04-15"
|
||||
],
|
||||
];
|
||||
|
||||
// Initialize cURL session
|
||||
$ch = curl_init();
|
||||
|
||||
// Set cURL options
|
||||
curl_setopt_array($ch, $curlOptions);
|
||||
|
||||
// Execute cURL session and get the result
|
||||
$response = curl_exec($ch);
|
||||
|
||||
// Check for cURL errors
|
||||
if (curl_errno($ch)) {
|
||||
error_log('Curl error ' . curl_error($ch));
|
||||
} else {
|
||||
// Log the webhook response
|
||||
// error_log(' response ' . $response);
|
||||
|
||||
$response = json_decode($response);
|
||||
$con_count = count($response->contacts);
|
||||
// error_log(' con ' . $con_count);
|
||||
}
|
||||
|
||||
// Close cURL session
|
||||
curl_close($ch);
|
||||
// foreach ($response->contacts as $con) {
|
||||
// error_log(' response ' . json_encode($res));
|
||||
// if ($app->status == "booked") {
|
||||
// $con_count[] = $con;
|
||||
// }
|
||||
// Log the webhook response
|
||||
// }
|
||||
// Set up cURL options
|
||||
|
||||
// foreach ($response1 as $res1) {
|
||||
// error_log(' response ' . json_encode($res1));
|
||||
|
||||
$curlOptions2 = [
|
||||
CURLOPT_URL => "https://rest.gohighlevel.com/v1/users?locationId={$response1->locations[0]->id}",
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_ENCODING => "",
|
||||
CURLOPT_MAXREDIRS => 10,
|
||||
CURLOPT_TIMEOUT => 30,
|
||||
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||
CURLOPT_CUSTOMREQUEST => "GET",
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"Accept: application/json",
|
||||
"Authorization: Bearer " . $apikey,
|
||||
"Version: 2021-04-15"
|
||||
],
|
||||
];
|
||||
|
||||
// Initialize cURL session
|
||||
$ch2 = curl_init();
|
||||
|
||||
// Set cURL options
|
||||
curl_setopt_array($ch2, $curlOptions2);
|
||||
|
||||
// Execute cURL session and get the result
|
||||
$response2 = curl_exec($ch2);
|
||||
if (curl_errno($ch2)) {
|
||||
error_log('Curl error ' . curl_error($ch2));
|
||||
} else {
|
||||
|
||||
$currentTimestamp = time();
|
||||
|
||||
|
||||
|
||||
// Calculate start timestamp for the previous day
|
||||
$previousDayStartTimestamp = strtotime('yesterday', strtotime(date('Y-m-d', $currentTimestamp))) * 1000;
|
||||
|
||||
// Calculate end timestamp for the previous day
|
||||
$previousDayEndTimestamp = $previousDayStartTimestamp + (24 * 60 * 60 * 1000) - 1;
|
||||
|
||||
// error_log(' startOfYesterdayTimestamp ' . $previousDayStartTimestamp);
|
||||
// error_log(' endOfYesterdayTimestamp ' . $previousDayEndTimestamp);
|
||||
$response2 = json_decode($response2);
|
||||
$app_count = [];
|
||||
|
||||
foreach ($response2->users as $res2) {
|
||||
|
||||
$curlOptions3 = [
|
||||
CURLOPT_URL => "https://rest.gohighlevel.com/v1/appointments?userId=" . $res2->id . "&startDate=" . $previousDayStartTimestamp . "&endDate=" . $previousDayEndTimestamp,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_ENCODING => "",
|
||||
CURLOPT_MAXREDIRS => 10,
|
||||
CURLOPT_TIMEOUT => 30,
|
||||
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||
CURLOPT_CUSTOMREQUEST => "GET",
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"Accept: application/json",
|
||||
"Authorization: Bearer " . $apikey,
|
||||
"Version: 2021-04-15"
|
||||
],
|
||||
];
|
||||
|
||||
// Initialize cURL session
|
||||
$ch3 = curl_init();
|
||||
|
||||
// Set cURL options
|
||||
curl_setopt_array($ch3, $curlOptions3);
|
||||
|
||||
// Execute cURL session and get the result
|
||||
$response3 = curl_exec($ch3);
|
||||
$user_app_count = [];
|
||||
|
||||
if (curl_errno($ch3)) {
|
||||
error_log('Curl error ' . curl_error($ch3));
|
||||
} else {
|
||||
$response3 = json_decode($response3);
|
||||
foreach ($response3->appointments as $app) {
|
||||
if ($app->status == "booked") {
|
||||
$user_app_count[] = $app;
|
||||
}
|
||||
// Log the webhook response
|
||||
error_log(' response ' . $response3);
|
||||
}
|
||||
$app_count[] = $user_app_count;
|
||||
}
|
||||
curl_close($ch3);
|
||||
}
|
||||
}
|
||||
curl_close($ch2);
|
||||
|
||||
// Convert to seconds
|
||||
$previousDayStartTimestampInSeconds = $previousDayStartTimestamp / 1000;
|
||||
|
||||
// Format the timestamp
|
||||
$formattedDate = date('Y-m-d', $previousDayStartTimestampInSeconds);
|
||||
$project = $response1->locations[0]->name;
|
||||
$date = $formattedDate;
|
||||
|
||||
$new_lead = $con_count;
|
||||
$outbound_dial = 0;
|
||||
$pickup = 0;
|
||||
$conversation = 0;
|
||||
$booked_appointment = count($app_count);
|
||||
$callback_request = 0;
|
||||
$current_date = date('Y-m-d H:i:s');
|
||||
|
||||
$data = [
|
||||
'project' => $project,
|
||||
'date' => $date,
|
||||
'new_lead' => $new_lead,
|
||||
'outbound_dial' => $outbound_dial,
|
||||
'pickup' => $pickup,
|
||||
'conversation' => $conversation,
|
||||
'booked_appointment' => $booked_appointment,
|
||||
'callback_request' => $callback_request,
|
||||
'created_at' => $current_date
|
||||
];
|
||||
|
||||
error_log(' data ' . json_encode($data));
|
||||
|
||||
// Insert data into the database using LicenseModel
|
||||
// $reportModel = new ReportModel();
|
||||
// $reportModel->create($data);
|
||||
}
|
||||
curl_close($ch1);
|
||||
// error_log(' response ' . $response1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// // Check for cURL errors
|
||||
// if (curl_errno($ch)) {
|
||||
// error_log('Curl error ' . curl_error($ch));
|
||||
// } else {
|
||||
// // Log the webhook response
|
||||
// // error_log(' response ' . $response);
|
||||
|
||||
// $response = json_decode($response);
|
||||
// foreach ($response->users as $res) {
|
||||
// error_log(' response ' . json_encode($res));
|
||||
|
||||
// // Set up cURL options
|
||||
// $curlOptions2 = [
|
||||
// CURLOPT_URL => "https://rest.gohighlevel.com/v1/locations/" . $res->roles->locationIds[0],
|
||||
// CURLOPT_RETURNTRANSFER => true,
|
||||
// CURLOPT_ENCODING => "",
|
||||
// CURLOPT_MAXREDIRS => 10,
|
||||
// CURLOPT_TIMEOUT => 30,
|
||||
// CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||
// CURLOPT_CUSTOMREQUEST => "GET",
|
||||
// CURLOPT_HTTPHEADER => [
|
||||
// "Accept: application/json",
|
||||
// "Authorization: Bearer " . $apikey,
|
||||
// "Version: 2021-04-15"
|
||||
// ],
|
||||
// ];
|
||||
|
||||
// // Initialize cURL session
|
||||
// $ch2 = curl_init();
|
||||
|
||||
// // Set cURL options
|
||||
// curl_setopt_array($ch2, $curlOptions2);
|
||||
|
||||
// // Execute cURL session and get the result
|
||||
// $response2 = curl_exec($ch2);
|
||||
|
||||
// if (curl_errno($ch2)) {
|
||||
// error_log('Curl error ' . curl_error($ch2));
|
||||
// } else {
|
||||
// // Log the webhook response
|
||||
// // error_log(' response ' . $response2);
|
||||
|
||||
// // $response2 = json_decode($response2);
|
||||
// // foreach ($response2 as $res2) {
|
||||
// // error_log(' response ' . json_encode($res2));
|
||||
// // }
|
||||
// }
|
||||
// curl_close($ch2);
|
||||
|
||||
// $currentTimestamp = time();
|
||||
|
||||
|
||||
|
||||
// // Calculate start timestamp for the previous day
|
||||
// $previousDayStartTimestamp = strtotime('yesterday', strtotime(date('Y-m-d', $currentTimestamp))) * 1000;
|
||||
|
||||
// // Calculate end timestamp for the previous day
|
||||
// $previousDayEndTimestamp = $previousDayStartTimestamp + (24 * 60 * 60 * 1000) - 1;
|
||||
|
||||
// // error_log(' startOfYesterdayTimestamp ' . $previousDayStartTimestamp);
|
||||
// // error_log(' endOfYesterdayTimestamp ' . $previousDayEndTimestamp);
|
||||
|
||||
// $curlOptions3 = [
|
||||
// CURLOPT_URL => "https://rest.gohighlevel.com/v1/appointments?userId=" . $res->id . "&startDate=" . $previousDayStartTimestamp . "&endDate=" . $previousDayEndTimestamp,
|
||||
// CURLOPT_RETURNTRANSFER => true,
|
||||
// CURLOPT_ENCODING => "",
|
||||
// CURLOPT_MAXREDIRS => 10,
|
||||
// CURLOPT_TIMEOUT => 30,
|
||||
// CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||
// CURLOPT_CUSTOMREQUEST => "GET",
|
||||
// CURLOPT_HTTPHEADER => [
|
||||
// "Accept: application/json",
|
||||
// "Authorization: Bearer " . $apikey,
|
||||
// "Version: 2021-04-15"
|
||||
// ],
|
||||
// ];
|
||||
|
||||
// // Initialize cURL session
|
||||
// $ch3 = curl_init();
|
||||
|
||||
// // Set cURL options
|
||||
// curl_setopt_array($ch3, $curlOptions3);
|
||||
|
||||
// // Execute cURL session and get the result
|
||||
// $response3 = curl_exec($ch3);
|
||||
// $app_count = [];
|
||||
|
||||
// if (curl_errno($ch3)) {
|
||||
// error_log('Curl error ' . curl_error($ch3));
|
||||
// } else {
|
||||
// $response3 = json_decode($response3);
|
||||
// foreach ($response3->appointments as $app) {
|
||||
// if ($app->status == "booked") {
|
||||
// $app_count[] = $app;
|
||||
// }
|
||||
// // Log the webhook response
|
||||
// error_log(' response ' . $response3);
|
||||
// }
|
||||
// // $response2 = json_decode($response2);
|
||||
// // foreach ($response2 as $res2) {
|
||||
// // error_log(' response ' . json_encode($res2));
|
||||
// // }
|
||||
// }
|
||||
// curl_close($ch3);
|
||||
// // $curlOptions4 = [
|
||||
// // CURLOPT_URL => "https://rest.gohighlevel.com/v1/contacts?startAfter=" . $previousDayStartTimestamp,
|
||||
// // CURLOPT_RETURNTRANSFER => true,
|
||||
// // CURLOPT_ENCODING => "",
|
||||
// // CURLOPT_MAXREDIRS => 10,
|
||||
// // CURLOPT_TIMEOUT => 30,
|
||||
// // CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||
// // CURLOPT_CUSTOMREQUEST => "GET",
|
||||
// // CURLOPT_HTTPHEADER => [
|
||||
// // "Accept: application/json",
|
||||
// // "Authorization: Bearer " . $apikey,
|
||||
// // "Version: 2021-04-15"
|
||||
// // ],
|
||||
// // ];
|
||||
|
||||
// // // Initialize cURL session
|
||||
// // $ch4 = curl_init();
|
||||
|
||||
// // // Set cURL options
|
||||
// // curl_setopt_array($ch4, $curlOptions4);
|
||||
|
||||
// // // Execute cURL session and get the result
|
||||
// // $response4 = curl_exec($ch4);
|
||||
// // $con_count = [];
|
||||
|
||||
// // if (curl_errno($ch4)) {
|
||||
// // error_log('Curl error ' . curl_error($ch4));
|
||||
// // } else {
|
||||
// // $response4 = json_decode($response4);
|
||||
// // foreach ($response4->contacts as $con) {
|
||||
// // if ($con->status == "booked") {
|
||||
// // $con_count[] = $app;
|
||||
// // }
|
||||
// // // Log the webhook response
|
||||
// // error_log(' response ' . $response4);
|
||||
// // }
|
||||
// // // $response2 = json_decode($response2);
|
||||
// // // foreach ($response2 as $res2) {
|
||||
// // // error_log(' response ' . json_encode($res2));
|
||||
// // // }
|
||||
// // }
|
||||
// // curl_close($ch4);
|
||||
// // Convert to seconds
|
||||
// $previousDayStartTimestampInSeconds = $previousDayStartTimestamp / 1000;
|
||||
|
||||
// // Format the timestamp
|
||||
// $formattedDate = date('Y-m-d', $previousDayStartTimestampInSeconds);
|
||||
// $project = "";
|
||||
// $date = $formattedDate;
|
||||
// $ghl_user_id = $res->id;
|
||||
// $username = $res->name;
|
||||
// $new_lead = 0;
|
||||
// $outbound_dial = 0;
|
||||
// $pickup = 0;
|
||||
// $conversation = 0;
|
||||
// $booked_appointment = count($app_count);
|
||||
// $callback_request = 0;
|
||||
// $current_date = date('Y-m-d H:i:s');
|
||||
|
||||
// $data = [
|
||||
// 'project' => $project,
|
||||
// 'date' => $date,
|
||||
// 'ghl_user_id' => $ghl_user_id,
|
||||
// 'username' => $username,
|
||||
// 'new_lead' => $new_lead,
|
||||
// 'outbound_dial' => $outbound_dial,
|
||||
// 'pickup' => $pickup,
|
||||
// 'conversation' => $conversation,
|
||||
// 'booked_appointment' => $booked_appointment,
|
||||
// 'callback_request' => $callback_request,
|
||||
// 'created_at' => $current_date
|
||||
// ];
|
||||
|
||||
// error_log(' data ' . json_encode($data));
|
||||
|
||||
// // Insert data into the database using LicenseModel
|
||||
// $reportModel = new ReportModel();
|
||||
// $reportModel->create($data);
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Close cURL session
|
||||
// curl_close($ch);
|
||||
|
||||
|
||||
|
||||
// Log the end of the script
|
||||
error_log('Cron job completed: ' . date('Y-m-d H:i:s'));
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
Date Campaign Name Ad Set Name Ad Name Amount spent Reach Impressions CPM Unique Outbound Clicks Unique Outbound Click CTR Cost Per Unique Outbound Click New Leads CPL Optin % Appointments Booked CPA Appointments Booked % Showed Appointments CPS Qualified Appointments CPQ Sales CAC Cash Collected CC ROI Contract Value CV ROI
|
||||
2024-11-19 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 1 + Body 1 $124.37 293 318 $124.37 4 1.37% $9.89 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-19 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 2 + Body 1 $51.18 553 652 $78.50 6 1.08% $8.53 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-19 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 3 + Body 1 $42.20 648 715 $59.02 1 0.15% $42.20 1 $42.20 100.00% 1 $42.20 100.00% 1 $42.20 1 $42.20 1 $42.20 $2,000.00 47.39 $8,000.00 189.57
|
||||
2024-11-19 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 4 + Body 1 $103.86 973 1064 $97.61 12 1.23% $8.65 7 $14.84 58.33% 1 $103.86 14.29% 1 $103.86 1 $103.86 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-19 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 5 + Body 1 $9.29 157 160 $58.06 0 0.00% $0.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-20 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 1 + Body 1 $11.40 364 397 $28.72 0 0.00% $0.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-20 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 2 + Body 1 $10.96 274 326 $33.62 2 0.73% $5.48 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-20 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 3 + Body 1 $35.29 1038 1149 $28.36 5 0.48% $6.52 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-20 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 4 + Body 1 $182.91 1943 2219 $82.43 18 0.93% $10.16 4 $45.73 22.22% 2 $91.46 50.00% 1 $182.91 1 $182.91 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-20 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 5 + Body 1 $6.65 351 395 $16.84 1 0.28% $6.65 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-21 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 1 + Body 1 $7.62 332 359 $21.23 3 0.90% $2.54 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-21 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 2 + Body 1 $1.03 27 31 $33.23 0 0.00% $0.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-21 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 3 + Body 1 $28.29 517 572 $49.46 6 1.16% $4.72 2 $14.15 33.33% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-21 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 4 + Body 1 $208.82 2303 2581 $80.91 19 0.83% $10.99 4 $52.21 21.05% 3 $69.61 75.00% 1 $208.82 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-21 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 5 + Body 1 $9.94 154 172 $57.79 0 0.00% $0.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-22 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 1 + Body 1 $5.77 151 170 $33.94 2 1.32% $2.89 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-22 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 3 + Body 1 $29.85 352 452 $66.04 4 1.14% $7.46 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-22 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 4 + Body 1 $204.92 1998 2289 $89.52 15 0.75% $13.66 3 $68.31 20.00% 1 $204.92 33.33% 1 $204.92 1 $204.92 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-22 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 5 + Body 1 $7.26 111 120 $60.50 2 1.80% $3.63 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-23 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 1 + Body 1 $0.35 22 23 $15.22 0 0.00% $0.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-23 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 3 + Body 1 $9.46 168 210 $45.05 2 1.19% $4.73 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-23 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 4 + Body 1 $192.97 2312 2599 $74.25 21 0.91% $9.19 7 $27.57 33.33% 2 $96.49 28.57% 1 $192.97 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-23 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 5 + Body 1 $2.28 38 42 $54.29 0 0.00% $0.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-24 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 3 + Body 1 $9.72 313 348 $27.93 0 0.00% $0.00 0 $0.00 0.00% 1 $9.72 0.00% 1 $9.72 1 $9.72 1 $9.72 $2,000.00 205.76 $8,000.00 823.05
|
||||
2024-11-24 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 4 + Body 1 $226.34 3092 3522 $64.26 35 1.13% $6.47 3 $75.45 8.57% 1 $226.34 33.33% 1 $226.34 1 $226.34 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-24 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 5 + Body 1 $2.90 52 55 $52.73 0 0.00% $0.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-25 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 3 + Body 1 $19.81 596 675 $29.35 4 0.67% $4.95 1 $19.81 25.00% 2 $9.91 200.00% 2 $9.91 2 $9.91 1 $19.81 $2,000.00 100.96 $8,000.00 403.84
|
||||
2024-11-25 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 4 + Body 1 $235.19 3081 3504 $67.12 31 1.01% $7.59 11 $21.38 35.48% 3 $78.40 27.27% 1 $235.19 1 $235.19 1 $235.19 $500.00 2.13 $8,000.00 34.02
|
||||
2024-11-25 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 5 + Body 1 $2.35 53 61 $38.52 1 1.89% $2.35 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-26 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 3 + Body 1 $19.89 318 385 $51.66 2 0.63% $9.95 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-26 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 4 + Body 1 $206.76 2932 3322 $62.24 24 0.82% $8.62 5 $41.35 20.83% 2 $103.38 40.00% 1 $206.76 1 $206.76 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-26 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 5 + Body 1 $5.17 104 114 $45.35 0 0.00% $0.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-27 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 3 + Body 1 $34.21 347 424 $80.68 3 0.86% $11.40 1 $34.21 33.33% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-27 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 4 + Body 1 $212.45 2130 2415 $87.97 20 0.94% $10.62 3 $70.82 15.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-27 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 5 + Body 1 $6.62 187 210 $31.52 1 0.53% $6.62 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-28 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 3 + Body 1 $13.13 105 124 $105.89 0 0.00% $0.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-28 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 4 + Body 1 $224.50 2431 2824 $79.50 21 0.86% $10.69 3 $74.83 14.29% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-28 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 5 + Body 1 $5.93 64 77 $77.01 0 0.00% $0.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-29 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 3 + Body 1 $3.77 48 56 $67.32 0 0.00% $0.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-29 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 4 + Body 1 $221.51 2070 2438 $90.86 16 0.77% $13.84 2 $110.76 12.50% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-29 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 5 + Body 1 $10.21 61 74 $137.97 2 3.28% $5.11 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-30 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 3 + Body 1 $12.08 54 66 $183.03 1 1.85% $12.08 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-30 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 4 + Body 1 $206.63 1776 2101 $98.35 19 1.07% $10.88 1 $206.63 5.26% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-11-30 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 5 + Body 1 $6.16 58 69 $89.28 1 1.72% $6.16 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-01 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 3 + Body 1 $8.38 80 85 $98.59 1 1.25% $8.38 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-01 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 4 + Body 1 $271.01 2241 2659 $101.92 15 0.67% $18.07 3 $90.34 20.00% 1 $271.01 33.33% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-01 [UNKNOWN] [UNKNOWN] [UNKNOWN] 1 1 1 $0.00 1 $0.00 1 $0.00 $2,000.00 #DIV/0! $8,000.00 #DIV/0!
|
||||
2024-12-02 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 3 + Body 1 $156.42 1032 1235 $126.66 9 0.87% $17.38 1 $156.42 11.11% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-02 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 4 + Body 1 $92.71 949 1025 $90.45 4 0.42% $23.18 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-02 Agencies W/ Call Centers | ABO | 1 Video | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Video | Hook 3 + Body 1 $34.08 226 238 $143.19 1 0.44% $34.08 1 $34.08 100.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-02 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 5 $16.33 147 224 $72.90 2 1.36% $8.16 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-02 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 3 $40.62 468 565 $71.89 8 1.71% $5.08 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-02 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 1 $1.82 11 12 $151.67 0 0.00% $0.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-02 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 4 $23.13 262 297 $77.88 4 1.53% $5.78 1 $23.13 25.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-02 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 2 $31.41 406 504 $62.32 5 1.23% $6.28 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-03 Agencies W/ Call Centers | ABO | 2 Videos | USA, CA, AUS | Conversions [Schedules] [2024-12-03] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-12-03] Mixed Ad Copy | Video | Hook 4b + Body 1 $3.22 54 60 $53.67 1 1.85% $3.22 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-03 Agencies W/ Call Centers | ABO | 2 Videos | USA, CA, AUS | Conversions [Schedules] [2024-12-03] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-12-03] Mixed Ad Copy | Video | Hook 4a + Body 1 $118.94 1186 1293 $91.99 14 1.18% $8.50 3 $39.65 21.43% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-03 Agencies W/ Call Centers | ABO | 1 Video | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Video | Hook 3 + Body 1 $51.38 585 653 $78.68 2 0.34% $25.69 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-03 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 5 $48.65 452 741 $65.65 7 1.55% $6.95 1 $48.65 14.29% 1 $48.65 100.00% 1 $48.65 1 $48.65 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-03 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 3 $23.18 337 382 $60.68 5 1.48% $4.64 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-03 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 1 $3.36 51 52 $64.62 0 0.00% $0.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-03 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 4 $15.01 145 160 $93.81 3 2.07% $5.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-03 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 2 $6.13 88 98 $62.55 0 0.00% $0.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-03 Agencies W/ Call Centers | ABO | 5 Videos | USA, CA, AUS | Conversions [Schedules] [2024-11-18] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-11-18] Mixed Ad Copy | Video | Hook 3 + Body 1 $95.98 752 845 $113.59 5 0.66% $19.20 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-04 Agencies W/ Call Centers | ABO | 2 Videos | USA, CA, AUS | Conversions [Schedules] [2024-12-03] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-12-03] Mixed Ad Copy | Video | Hook 4b + Body 1 $7.75 55 68 $113.97 0 0.00% $0.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-04 Agencies W/ Call Centers | ABO | 2 Videos | USA, CA, AUS | Conversions [Schedules] [2024-12-03] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-12-03] Mixed Ad Copy | Video | Hook 4a + Body 1 $201.15 1871 2211 $90.98 21 1.12% $9.58 5 $40.23 23.81% 1 $201.15 20.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-04 Agencies W/ Call Centers | ABO | 1 Video | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Video | Hook 3 + Body 1 $59.56 632 740 $80.49 2 0.32% $29.78 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-04 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 5 $54.63 518 754 $72.45 7 1.35% $7.80 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-04 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 3 $19.26 194 211 $91.28 2 1.03% $9.63 1 $19.26 50.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-04 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 1 $2.01 17 17 $118.24 0 0.00% $0.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-04 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 4 $12.08 115 150 $80.53 0 0.00% $0.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-04 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 2 $4.96 48 55 $90.18 0 0.00% $0.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-05 Agencies W/ Call Centers | ABO | 2 Videos | USA, CA, AUS | Conversions [Schedules] [2024-12-03] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-12-03] Mixed Ad Copy | Video | Hook 4b + Body 1 $7.54 68 81 $93.09 1 1.47% $7.54 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-05 Agencies W/ Call Centers | ABO | 2 Videos | USA, CA, AUS | Conversions [Schedules] [2024-12-03] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-12-03] Mixed Ad Copy | Video | Hook 4a + Body 1 $187.92 1818 2056 $91.40 15 0.83% $12.53 2 $93.96 13.33% 2 $93.96 100.00% 2 $93.96 2 $93.96 1 $187.92 $500.00 2.66 $8,000.00 42.57
|
||||
2024-12-05 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 5 $39.97 491 618 $64.68 7 1.43% $5.71 1 $39.97 14.29% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-05 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 3 $30.22 322 384 $78.70 7 2.17% $4.32 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-05 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 1 $0.13 1 1 $130.00 0 0.00% $0.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-05 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 4 $24.55 252 304 $80.76 3 1.19% $8.18 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-05 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 2 $6.78 95 102 $66.47 0 0.00% $0.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-06 Agencies W/ Call Centers | ABO | 2 Videos | USA, CA, AUS | Conversions [Schedules] [2024-12-03] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-12-03] Mixed Ad Copy | Video | Hook 4b + Body 1 $8.51 75 91 $93.52 0 0.00% $0.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-06 Agencies W/ Call Centers | ABO | 2 Videos | USA, CA, AUS | Conversions [Schedules] [2024-12-03] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-12-03] Mixed Ad Copy | Video | Hook 4a + Body 1 $181.20 1642 1869 $96.95 20 1.22% $9.06 8 $22.65 40.00% 1 $181.20 12.50% 1 $181.20 1 $181.20 1 $181.20 $2,000.00 11.04 $8,000.00 44.15
|
||||
2024-12-06 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 5 $54.81 581 807 $67.92 7 1.20% $7.83 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-06 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 3 $27.29 331 372 $73.36 7 2.11% $3.90 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-06 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 1 $0.10 2 2 $50.00 0 0.00% $0.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-06 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 4 $7.38 86 94 $78.51 0 0.00% $0.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-06 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 2 $3.89 51 54 $72.04 0 0.00% $0.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-07 Agencies W/ Call Centers | ABO | 2 Videos | USA, CA, AUS | Conversions [Schedules] [2024-12-03] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-12-03] Mixed Ad Copy | Video | Hook 4b + Body 1 $10.21 111 134 $76.19 0 0.00% $0.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-07 Agencies W/ Call Centers | ABO | 2 Videos | USA, CA, AUS | Conversions [Schedules] [2024-12-03] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-12-03] Mixed Ad Copy | Video | Hook 4a + Body 1 $183.68 1975 2247 $81.74 14 0.71% $13.12 4 $45.92 28.57% 1 $183.68 25.00% 1 $183.68 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-07 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 5 $66.26 794 907 $73.05 17 2.14% $3.90 1 $66.26 5.88% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-07 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 3 $20.50 292 323 $63.47 2 0.68% $10.25 1 $20.50 50.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-07 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 1 $0.31 5 5 $62.00 0 0.00% $0.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-07 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 4 $10.63 130 155 $68.58 1 0.77% $10.63 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-07 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 2 $2.92 50 62 $47.10 0 0.00% $0.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-08 Agencies W/ Call Centers | ABO | 2 Videos | USA, CA, AUS | Conversions [Schedules] [2024-12-03] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-12-03] Mixed Ad Copy | Video | Hook 4b + Body 1 $5.16 91 104 $49.62 0 0.00% $0.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-08 Agencies W/ Call Centers | ABO | 2 Videos | USA, CA, AUS | Conversions [Schedules] [2024-12-03] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-12-03] Mixed Ad Copy | Video | Hook 4a + Body 1 $206.91 2188 2439 $84.83 15 0.69% $13.79 1 $206.91 6.67% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-08 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 5 $60.83 573 658 $92.45 8 1.40% $7.60 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-08 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 3 $20.98 140 150 $139.87 0 0.00% $0.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-08 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 1 $0.67 7 9 $74.44 0 0.00% $0.00 0 $0.00 0.00% 0 $0.00 0.00% 0 $0.00 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-08 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 2 $10.93 87 102 $107.16 3 3.45% $3.64 0 $0.00 $0.00 0.00% 0 $0.00 0.00% 0 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-09 Agencies W/ Call Centers | ABO | 2 Videos | USA, CA, AUS | Conversions [Schedules] [2024-12-03] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-12-03] Mixed Ad Copy | Video | Hook 4b + Body 1 $9.83 110 132 $74.47 1 0.91% $9.83 0 $0.00 $0.00 0.00% 0 $0.00 0.00% 0 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-09 Agencies W/ Call Centers | ABO | 2 Videos | USA, CA, AUS | Conversions [Schedules] [2024-12-03] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-12-03] Mixed Ad Copy | Video | Hook 4a + Body 1 $229.92 2340 2669 $86.14 21 0.90% $10.95 4 $57.48 $0.19 100.00% 230 $0.25 100.00% $229.92 1 $229.92 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-09 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 5 $71.02 928 1131 $62.79 8 0.86% $8.88 0 $0.00 $0.00 0.00% 0 $0.00 0.00% 0 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-09 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 3 $17.06 283 303 $56.30 6 2.12% $2.84 1 $17.06 $0.17 0.00% 0 $0.00 0.00% 0 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-09 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 1 $1.14 14 14 $81.43 0 0.00% $0.00 0 $0.00 $0.00 0.00% 0 $0.00 0.00% 0 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-09 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 2 $10.96 133 165 $66.42 0 0.00% $0.00 0 $0.00 $0.00 0.00% 0 $0.00 0.00% 0 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-10 Agencies W/ Call Centers | ABO | 2 Videos | USA, CA, AUS | Conversions [Schedules] [2024-12-03] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-12-03] Mixed Ad Copy | Video | Hook 4b + Body 1 $11.74 134 171 $68.65 1 0.75% $11.74 0 $0.00 $0.00 0.00% 0 $0.00 0.00% 0 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-10 Agencies W/ Call Centers | ABO | 2 Videos | USA, CA, AUS | Conversions [Schedules] [2024-12-03] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-12-03] Mixed Ad Copy | Video | Hook 4a + Body 1 $252.25 2746 3271 $77.12 17 0.62% $14.84 2 $126.13 $0.12 0.00% 0 $0.00 0.00% 0 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-10 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 5 $76.54 921 1227 $62.38 7 0.76% $10.93 0 $0.00 $0.00 0.00% 0 $0.00 0.00% 0 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-10 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 3 $25.28 179 205 $123.32 1 0.56% $25.28 0 $0.00 $0.00 0.00% 0 $0.00 0.00% 0 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-10 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 1 $0.96 10 13 $73.85 0 0.00% $0.00 0 $0.00 $0.00 0.00% 0 $0.00 0.00% 0 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-10 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 2 $5.99 47 59 $101.53 0 0.00% $0.00 0 $0.00 $0.00 0.00% 0 $0.00 0.00% 0 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-11 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 2 $1.48 26 29 $51.03 1 0.03846154 $1.48 0 $0.00 $0.00 0.00% 0 $0.00 0.00% 0 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-11 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 1 $0.34 4 4 $85.00 0 0 $0.00 0 $0.00 $0.00 0.00% 0 $0.00 0.00% 0 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-11 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 3 $10.02 111 122 $82.13 1 0.00900901 $10.02 0 $0.00 $0.00 0.00% 0 $0.00 0.00% 0 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-11 Agencies W/ Call Centers | ABO | 5 Insta Images | USA, CA, AUS | Conversions [Schedules] [2024-12-02] Broad | Insta Image Only | USA, CA, AUS | 18-45 | MF [2024-12-02] Mixed Ad Copy | Image | Image 5 $42.23 574 689 $61.29 10 0.0174216 $4.22 0 $0.00 $0.00 0.00% 0 $0.00 0.00% 0 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-11 Agencies W/ Call Centers | ABO | 2 Videos | USA, CA, AUS | Conversions [Schedules] [2024-12-03] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-12-03] Mixed Ad Copy | Video | Hook 4b + Body 1 $14.29 156 192 $74.43 1 0.00641026 $14.29 0 $0.00 $0.00 0.00% 0 $0.00 0.00% 0 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
2024-12-11 Agencies W/ Call Centers | ABO | 2 Videos | USA, CA, AUS | Conversions [Schedules] [2024-12-03] Broad | Video Only | USA, CA, AUS | 18-45 | MF [2024-12-03] Mixed Ad Copy | Video | Hook 4a + Body 1 $186.79 2149 2504 $74.60 17 0.00791066 $10.99 0 $0.00 $0.00 0.00% 0 $0.00 0.00% 0 0 $0.00 0 $0.00 $0.00 0.00 $0.00 0.00
|
||||
|
+170
@@ -0,0 +1,170 @@
|
||||
CREATE TABLE images(
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
url TEXT,
|
||||
user_id INT,
|
||||
caption TEXT,
|
||||
created_at DATE,
|
||||
updated_at DATETIME,
|
||||
PRIMARY KEY ( id )
|
||||
);
|
||||
|
||||
CREATE TABLE user(
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
username varchar(255) DEFAULT '',
|
||||
role varchar(255) DEFAULT '',
|
||||
email varchar(255) DEFAULT '',
|
||||
password varchar(255) DEFAULT '',
|
||||
profile_id INT,
|
||||
reset_token INT,
|
||||
reset_token_expire INT,
|
||||
gender INT,
|
||||
status INT,
|
||||
created_at DATE,
|
||||
updated_at DATETIME,
|
||||
PRIMARY KEY ( id )
|
||||
);
|
||||
|
||||
CREATE TABLE profile(
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
user_id INT,
|
||||
is_google INT,
|
||||
is_facebook INT,
|
||||
first_name varchar(100) DEFAULT '',
|
||||
last_name varchar(100) DEFAULT '',
|
||||
stripe_id varchar(255) DEFAULT '',
|
||||
phone varchar(15) DEFAULT '',
|
||||
street varchar(255) DEFAULT '',
|
||||
city varchar(255) DEFAULT '',
|
||||
state varchar(255) DEFAULT '',
|
||||
country varchar(255) DEFAULT '',
|
||||
zip varchar(10) DEFAULT '',
|
||||
created_at DATE,
|
||||
updated_at DATETIME,
|
||||
PRIMARY KEY ( id )
|
||||
);
|
||||
|
||||
CREATE TABLE role(
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
name varchar(255) DEFAULT '',
|
||||
created_at DATE,
|
||||
updated_at DATETIME,
|
||||
PRIMARY KEY ( id )
|
||||
);
|
||||
|
||||
CREATE TABLE permission(
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
name varchar(255) DEFAULT '',
|
||||
created_at DATE,
|
||||
updated_at DATETIME,
|
||||
PRIMARY KEY ( id )
|
||||
);
|
||||
|
||||
CREATE TABLE signup(
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
first_name varchar(255) DEFAULT '',
|
||||
last_name varchar(255) DEFAULT '',
|
||||
email varchar(255) DEFAULT '',
|
||||
phone varchar(255) DEFAULT '',
|
||||
postal_code varchar(255) DEFAULT '',
|
||||
created_at DATE,
|
||||
updated_at DATETIME,
|
||||
PRIMARY KEY ( id )
|
||||
);
|
||||
|
||||
CREATE TABLE volunteer(
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
first_name varchar(255) DEFAULT '',
|
||||
last_name varchar(255) DEFAULT '',
|
||||
email varchar(255) DEFAULT '',
|
||||
phone varchar(255) DEFAULT '',
|
||||
postal_code varchar(255) DEFAULT '',
|
||||
role varchar(255) DEFAULT '',
|
||||
created_at DATE,
|
||||
updated_at DATETIME,
|
||||
PRIMARY KEY ( id )
|
||||
);
|
||||
|
||||
CREATE TABLE lawnsign(
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
first_name varchar(255) DEFAULT '',
|
||||
last_name varchar(255) DEFAULT '',
|
||||
address varchar(255) DEFAULT '',
|
||||
email varchar(255) DEFAULT '',
|
||||
phone varchar(255) DEFAULT '',
|
||||
postal_code varchar(255) DEFAULT '',
|
||||
created_at DATE,
|
||||
updated_at DATETIME,
|
||||
PRIMARY KEY ( id )
|
||||
);
|
||||
|
||||
CREATE TABLE contact(
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
first_name varchar(255) DEFAULT '',
|
||||
last_name varchar(255) DEFAULT '',
|
||||
email varchar(255) DEFAULT '',
|
||||
phone varchar(255) DEFAULT '',
|
||||
postal_code varchar(255) DEFAULT '',
|
||||
contact_list varchar(255) DEFAULT '',
|
||||
comment TEXT,
|
||||
created_at DATE,
|
||||
updated_at DATETIME,
|
||||
PRIMARY KEY ( id )
|
||||
);
|
||||
|
||||
CREATE TABLE donation(
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
first_name varchar(255) DEFAULT '',
|
||||
last_name varchar(255) DEFAULT '',
|
||||
email varchar(255) DEFAULT '',
|
||||
phone varchar(255) DEFAULT '',
|
||||
postal_code varchar(255) DEFAULT '',
|
||||
amount varchar(255) DEFAULT '',
|
||||
created_at DATE,
|
||||
updated_at DATETIME,
|
||||
PRIMARY KEY ( id )
|
||||
);
|
||||
|
||||
CREATE TABLE permission_role_user(
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
role_id INT,
|
||||
user_id INT,
|
||||
permission_id INT,
|
||||
created_at DATE,
|
||||
updated_at DATETIME,
|
||||
PRIMARY KEY ( id )
|
||||
);
|
||||
|
||||
CREATE TABLE email(
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
slug varchar(255) DEFAULT '',
|
||||
subject TEXT,
|
||||
body TEXT,
|
||||
tags TEXT,
|
||||
created_at DATE,
|
||||
updated_at DATETIME,
|
||||
PRIMARY KEY ( id )
|
||||
);
|
||||
|
||||
CREATE TABLE sms(
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
slug varchar(255) DEFAULT '',
|
||||
body TEXT,
|
||||
tags TEXT,
|
||||
created_at DATE,
|
||||
updated_at DATETIME,
|
||||
PRIMARY KEY ( id )
|
||||
);
|
||||
|
||||
CREATE TABLE token(
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
token TEXT,
|
||||
data TEXT,
|
||||
type INT,
|
||||
user_id INT,
|
||||
ttl INT,
|
||||
issue_at DATETIME,
|
||||
expire_at DATETIME,
|
||||
status INT,
|
||||
PRIMARY KEY ( id )
|
||||
);
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
body.auth-pages {
|
||||
background-color: #060632;
|
||||
}
|
||||
|
||||
.login-widget {
|
||||
width: 30%;
|
||||
margin-top: 30%;
|
||||
background-color: white;
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
.remove-validation-custom {
|
||||
color: red;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
min-width: 240px;
|
||||
max-width: 240px;
|
||||
background: #151515;
|
||||
color: #fff;
|
||||
z-index: 2;
|
||||
transition: all 0.3s;
|
||||
-webkit-box-shadow: 2px 0px 4px 0px rgb(0 0 0 / 75%);
|
||||
-moz-box-shadow: 2px 0px 4px 0px rgba(0, 0, 0, 0.75);
|
||||
box-shadow: 2px 0px 4px 0px rgb(0 0 0 / 75%);
|
||||
}
|
||||
|
||||
#sidebar ul li a {
|
||||
padding: 10px;
|
||||
font-size: 1.1em;
|
||||
display: block;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#sidebar a.active {
|
||||
color: black;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
#sidebar .sidebar-header {
|
||||
padding: 15px;
|
||||
background: #2c5ed6;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
#sidebar ul.components {
|
||||
padding: 0px 0px 20px 0px;
|
||||
}
|
||||
|
||||
#sidebar a.active {
|
||||
color: black;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
#content {
|
||||
width: 100%;
|
||||
padding: 50px 0px 0px 0px;
|
||||
min-height: 100vh;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 767px) {
|
||||
.login-widget {
|
||||
width: 50%;
|
||||
margin-top: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 400px) {
|
||||
.login-widget {
|
||||
width: 90%;
|
||||
margin-top: 10%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
# ************************************************************
|
||||
# Sequel Pro SQL dump
|
||||
# Version 4541
|
||||
#
|
||||
# http://www.sequelpro.com/
|
||||
# https://github.com/sequelpro/sequelpro
|
||||
#
|
||||
# Host: 127.0.0.1 (MySQL 5.5.5-10.11.2-MariaDB)
|
||||
# Database: team_followup
|
||||
# Generation Time: 2023-10-16 23:15:41 +0000
|
||||
# ************************************************************
|
||||
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||
/*!40101 SET NAMES utf8 */;
|
||||
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||
|
||||
|
||||
# Dump of table accesslog
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `accesslog`;
|
||||
|
||||
CREATE TABLE `accesslog` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`relationship_num` varchar(255) NOT NULL,
|
||||
`ip` varchar(255) NOT NULL,
|
||||
`created_at` datetime NOT NULL,
|
||||
`updated_at` varchar(191) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
|
||||
|
||||
LOCK TABLES `accesslog` WRITE;
|
||||
/*!40000 ALTER TABLE `accesslog` DISABLE KEYS */;
|
||||
|
||||
INSERT INTO `accesslog` (`id`, `relationship_num`, `ip`, `created_at`, `updated_at`)
|
||||
VALUES
|
||||
(3,'1000','127.0.0.1','2023-09-06 21:56:17','2023-09-6 21:56:17');
|
||||
|
||||
/*!40000 ALTER TABLE `accesslog` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
|
||||
# Dump of table license
|
||||
# ------------------------------------------------------------
|
||||
|
||||
DROP TABLE IF EXISTS `license`;
|
||||
|
||||
CREATE TABLE `license` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`relationship_num` varchar(255) NOT NULL,
|
||||
`email` varchar(255) NOT NULL,
|
||||
`apikey` varchar(255) NOT NULL,
|
||||
`ip` varchar(255) NOT NULL,
|
||||
`status` varchar(255) NOT NULL,
|
||||
`created_at` date NOT NULL,
|
||||
`updated_at` datetime NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
|
||||
|
||||
LOCK TABLES `license` WRITE;
|
||||
/*!40000 ALTER TABLE `license` DISABLE KEYS */;
|
||||
|
||||
INSERT INTO `license` (`id`, `relationship_num`, `email`, `apikey`, `ip`, `status`, `created_at`, `updated_at`)
|
||||
VALUES
|
||||
(4,'4000','ryan@manaknight.com','abc','','active','2023-09-06','2023-09-06 13:18:30'),
|
||||
(5,'5000','ryan@manaknight.com','abc','','active','2023-09-06','2023-09-06 13:18:30'),
|
||||
(6,'2345','wongryan2001@gmail.com','e750b10010d5447b51ec35bd194b19e4','','active','2023-09-07','2023-09-07 09:28:15'),
|
||||
(7,'2345','wongryan2001@gmail.com','98f2acce631c7a146e01c4d69a8d6cd8','','active','2023-09-07','2023-09-07 09:28:46'),
|
||||
(8,'10012','wongryan2001@gmail.com','70e52251408159907c7fb5abfa32d5f2','','active','2023-09-07','2023-09-07 09:29:02'),
|
||||
(9,'2011','ryan11@manaknight.com','b9246f6914aa5f1e367871dfad3252a1f','','inactive','2023-09-07','2023-09-07 09:42:01');
|
||||
|
||||
/*!40000 ALTER TABLE `license` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
|
||||
|
||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||
@@ -0,0 +1,273 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
|
||||
<title>Terms & Conditions | Team Follow-up </title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container py-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-12 text-center">
|
||||
<h1>TERMS AND CONDITIONS</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<h2>General</h2>
|
||||
<p>This website (the “Site”) is owned and operated by Darcan LTD trading as Call Center Mastery (herein referred to as Call Center Mastery) (“Call Center Mastery,” “we” or “us”). By using the Site, you agree to be bound by these Terms of Service and to use the Site in accordance with these Terms of Service, our Privacy Policy and any additional terms and conditions that may apply to specific sections of the Site or to products and services available through the Site or from Call Center Mastery. Accessing the Site, in any manner, whether automated or otherwise, constitutes use of the Site and your agreement to be bound by these Terms of Service.</p>
|
||||
<p>We reserve the right to change these Terms of Service or to impose new conditions on use of the Site, from time to time, in which case we will post the revised Terms of Service on this website. By continuing to use the Site after we post any such changes, you accept the Terms of Service, as modified.</p>
|
||||
<h2>Intellectual Property Rights</h2>
|
||||
<h3>Our limited license to you</h3>
|
||||
<p>This Site and all the materials available on the Site are the property of us and/or our affiliates or licensors, and are protected by copyright, trademark, and other intellectual property laws. The Site is provided solely for your personal noncommercial use. You may not use the Site or the materials available on the Site in a manner that constitutes an infringement of our rights or that has not been authorized by us. More specifically, unless explicitly authorized in these Terms of Service or by the owner of the materials, you may not modify, copy, reproduce, republish, upload, post, transmit, translate, sell, create derivative works, exploit, or distribute in any manner or medium (including by email or other electronic means) any material from the Site. You may, however, from time to time, download and/or print one copy of individual pages of the Site for your personal, non-commercial use, provided that you keep intact all copyright and other proprietary notices.</p>
|
||||
<h3>Your license to us</h3>
|
||||
<p>By posting or submitting any material (including, without limitation, comments, blog entries, Facebook postings, photos and videos) to us via the Site, internet groups, social media venues, or to any of our staff via email, text or otherwise, you are representing: (i) that you are the owner of the material, or are making your posting or submission with the express consent of the owner of the material; and (ii) that you are thirteen years of age or older. In addition, when you submit, email, text or deliver or post any material, you are granting us, and anyone authorized by us, a royalty-free, perpetual, irrevocable, non-exclusive, unrestricted, worldwide license to use, copy, modify, transmit, sell, exploit, create derivative works from, distribute, and/or publicly perform or display such material, in whole or in part, in any manner or medium, now known or hereafter developed, for any purpose. The foregoing grant shall include the right to exploit any proprietary rights in such posting or submission, including, but not limited to, rights under copyright, trademark, service mark or patent laws under any relevant jurisdiction. Also, in connection with the exercise of such rights, you grant us, and anyone authorized by us, the right to identify you as the author of any of your postings or submissions by name, email address or screen name, as we deem appropriate.</p>
|
||||
<p>You acknowledge and agree that any contributions originally created by you for us shall be deemed a “work made for hire” when the work performed is within the scope of the definition of a work made for hire in Section 101 of the United States Copyright Law, as amended. As such, the copyrights in those works shall belong to Call Center Mastery from their creation. Thus, Call Center Mastery shall be deemed the author and exclusive owner thereof and shall have the right to exploit any or all of the results and proceeds in any and all media, now known or hereafter devised, throughout the universe, in perpetuity, in all languages, as Call Center Mastery determines. In the event that any of the results and proceeds of your submissions hereunder are not deemed a “work made for hire” under Section 101 of the Copyright Act, as amended, you hereby, without additional compensation, irrevocably assign, convey and transfer to Call Center Mastery all proprietary rights, including without limitation, all copyrights and trademarks throughout the universe, in perpetuity in every medium, whether now known or hereafter devised, to such material and any and all right, title and interest in and to all such proprietary rights in every medium, whether now known or hereafter devised, throughout the universe, in perpetuity. Any posted material which are reproductions of prior works by you shall be co-owned by us.</p>
|
||||
<p>You acknowledge that Call Center Mastery has the right but not the obligation to use and display any postings or contributions of any kind and that Call Center Mastery may elect to cease the use and display of any such materials (or any portion thereof), at any time for any reason whatsoever.</p>
|
||||
<h3>Limitations on Linking and Framing</h3>
|
||||
<p>You may establish a hypertext link to the Site so long as the link does not state or imply any sponsorship of your site by us or by the Site. However, you may not, without our prior written permission, frame or inline link any of the content of the Site, or incorporate into another website or other service any of our material, content or intellectual property.</p>
|
||||
<h2>Disclaimers</h2>
|
||||
<p>Throughout the Site, we may provide links and pointers to Internet sites maintained by third parties. Our linking to such third-party sites does not imply an endorsement or sponsorship of such sites, or the information, products or services offered on or through the sites. In addition, neither we nor affiliates operate or control in any respect any information, products or services that third parties may provide on or through the Site or on websites linked to by us on the Site. If applicable, any opinions, advice, statements, services, offers, or other information or content expressed or made available by third parties, including information providers, are those of the respective authors or distributors, and not Call Center Mastery. Neither Call Center Mastery nor any third-party provider of information guarantees the accuracy, completeness, or usefulness of any content. Furthermore, Call Center Mastery neither endorses nor is responsible for the accuracy and reliability of any opinion, advice, or statement made on any of the Sites by anyone other than an authorized Call Center Mastery representative while acting in his/her official capacity.</p>
|
||||
<p>THE INFORMATION, PRODUCTS AND SERVICES OFFERED ON OR THROUGH THE SITE AND BY Call Center Mastery AND ANY THIRD-PARTY SITES ARE PROVIDED “AS IS” AND WITHOUT WARRANTIES OF ANY KIND EITHER EXPRESS OR IMPLIED. TO THE FULLEST EXTENT PERMISSIBLE PURSUANT TO APPLICABLE LAW, WE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. WE DO NOT WARRANT THAT THE SITE OR ANY OF ITS FUNCTIONS WILL BE UNINTERRUPTED OR ERROR-FREE, THAT DEFECTS WILL BE CORRECTED, OR THAT ANY PART OF THIS SITE, INCLUDING BULLETIN BOARDS, OR THE SERVERS THAT MAKE IT AVAILABLE, ARE FREE OF VIRUSES OR OTHER HARMFUL COMPONENTS.</p>
|
||||
<p>WE DO NOT WARRANT OR MAKE ANY REPRESENTATIONS REGARDING THE USE OR THE RESULTS OF THE USE OF THE SITE OR MATERIALS ON THIS SITE OR ON THIRD-PARTY SITES IN TERMS OF THEIR CORRECTNESS, ACCURACY, TIMELINESS, RELIABILITY OR OTHERWISE.</p>
|
||||
<p>You agree at all times to defend, indemnify and hold harmless Call Center Mastery its affiliates, their successors, transferees, assignees and licensees and their respective parent and subsidiary companies, agents, associates, officers, directors, shareholders and employees of each from and against any and all claims, causes of action, damages, liabilities, costs and expenses, including legal fees and expenses, arising out of or related to your breach of any obligation, warranty, representation or covenant set forth herein.</p>
|
||||
<h2>Online commerce</h2>
|
||||
<p>Certain sections of the Site may allow you to purchase many different types of products and services online that are provided by third parties. We are not responsible for the quality, accuracy, timeliness, reliability or any other aspect of these products and services. If you make a purchase from a merchant on the Site or on a site linked to by the Site, the information obtained during your visit to that merchant’s online store or site, and the information that you give as part of the transaction, such as your credit card number and contact information, may be collected by both the merchant and us. A merchant may have privacy and data collection practices that are different from ours. We have no responsibility or liability for these independent policies. In addition, when you purchase products or services on or through the Site, you may be subject to additional terms and conditions that specifically apply to your purchase or use of such products or services. For more information regarding a merchant, its online store, its privacy policies, and/or any additional terms and conditions that may apply, visit that merchant’s website and click on its information links or contact the merchant directly. You release us and our affiliates from any damages that you incur, and agree not to assert any claims against us or them, arising from your purchase or use of any products or services made available by third parties through the Site.</p>
|
||||
<p>Your participation, correspondence or business dealings with any third party found on or through our Site, regarding payment and delivery of specific goods and services, and any other terms, conditions, representations or warranties associated with such dealings, are solely between you and such third party. You agree that Call Center Mastery shall not be responsible or liable for any loss, damage, or other matters of any sort incurred as the result of such dealings.</p>
|
||||
<p>You agree to be financially responsible for all purchases made by you or someone acting on your behalf through the Site. You agree to use the Site and to purchase services or products through the Site for legitimate, non-commercial purposes only. You also agree not to make any purchases for speculative, false or fraudulent purposes or for the purpose of anticipating demand for a particular product or service. You agree to only purchase goods or services for yourself or for another person for whom you are legally permitted to do so. When making a purchase for a third party that requires you to submit the third party’s personal information to us or a merchant, you represent that you have obtained the express consent of such third party to provide such third party’s personal information.</p>
|
||||
<p>Your purchase is for personal use only. Sharing of purchases is not permitted and will be considered unauthorized, an infringing use of our copyrighted material, and may subject violators to liability. If payment for a course is declined, our system will automatically disable access to our premium materials. (We understand. This usually happens because a credit card expires.) We want to help restore your access, so we’ll make every attempt to contact you to help resolve this issue. Once the billing issue is resolved, we’ll restore access.</p>
|
||||
<h2>Interactive features</h2>
|
||||
<p>This Site may include a variety of features, such as bulletin boards, web logs, chat rooms, and email services, which allow feedback to us and real-time interaction between users, and other features which allow users to communicate with others. Responsibility for what is posted on bulletin boards, web logs, chat rooms, and other public posting areas on the Site, or sent via any email services on the Site, lies with each user – you alone are responsible for the material you post or send. We do not control the messages, information or files that you or others may provide through the Site. It is a condition of your use of the Site that you do not:</p>
|
||||
<ul>
|
||||
<li>Restrict or inhibit any other user from using and enjoying the Site.</li>
|
||||
<li>Use the Site to impersonate any person or entity, or falsely state or otherwise misrepresent your affiliation with a person or entity.</li>
|
||||
<li>Interfere with or disrupt any servers or networks used to provide the Site or its features, or disobey any requirements, procedures, policies or regulations of the networks we use to provide the Site.</li>
|
||||
<li>Use the Site to instigate or encourage others to commit illegal activities or cause injury or property damage to any person.</li>
|
||||
<li>Gain unauthorized access to the Site, or any account, computer system, or network connected to this Site, by means such as hacking, password mining or other illicit means.</li>
|
||||
<li>Obtain or attempt to obtain any materials or information through any means not intentionally made available through this Site.</li>
|
||||
<li>Use the Site to post or transmit any unlawful, threatening, abusive, libelous, defamatory, obscene, vulgar, pornographic, profane or indecent information of any kind, including without limitation any transmissions constituting or encouraging conduct that would constitute a criminal offense, give rise to civil liability or otherwise violate any local, state, national or international law.</li>
|
||||
<li>Use the Site to post or transmit any information, software or other material that violates or infringes upon the rights of others, including material that is an invasion of privacy or publicity rights or that is protected by copyright, trademark or other proprietary right, or derivative works with respect thereto, without first obtaining permission from the owner or rights holder.</li>
|
||||
<li>Use the Site to post or transmit any information, software or other material that contains a virus or other harmful component.</li>
|
||||
<li>Use the Site to post, transmit or in any way exploit any information, software or other material for commercial purposes, or that contains advertising.</li>
|
||||
<li>Use the Site to advertise or solicit to anyone to buy or sell products or services, or to make donations of any kind, without our express written approval.</li>
|
||||
<li>Gather for marketing purposes any email addresses or other personal information that has been posted by other users of the Site.</li>
|
||||
</ul>
|
||||
<p>Call Center Mastery may host message boards, chats and other public forums on its Sites. Any user failing to comply with the terms and conditions of this Agreement may be expelled from and refused continued access to, the message boards, chats or other public forums in the future. Call Center Mastery or its designated agents may remove or alter any user-created content at any time for any reason. Message boards, chats and other public forums are intended to serve as discussion centers for users and subscribers. Information and content posted within these public forums may be provided by Call Center Mastery staff, Call Center Mastery’s outside contributors, or by users not connected with Call Center Mastery, some of whom may employ anonymous user names. Call Center Mastery expressly disclaims all responsibility and endorsement and makes no representation as to the validity of any opinion, advice, information or statement made or displayed in these forums by third parties, nor are we responsible for any errors or omissions in such postings, or for hyperlinks embedded in any messages. Under no circumstances will we, our affiliates, suppliers or agents be liable for any loss or damage caused by your reliance on information obtained through these forums. The opinions expressed in these forums are solely the opinions of the participants, and do not reflect the opinions of Call Center Mastery or any of its subsidiaries or affiliates.</p>
|
||||
<p>Call Center Mastery has no obligation whatsoever to monitor any of the content or postings on the message boards, chat rooms or other public forums on the Sites. However, you acknowledge and agree that we have the absolute right to monitor the same at our sole discretion. In addition, we reserve the right to alter, edit, refuse to post or remove any postings or content, in whole or in part, for any reason and to disclose such materials and the circumstances surrounding their transmission to any third party in order to satisfy any applicable law, regulation, legal process or governmental request and to protect ourselves, our clients, sponsors, users and visitors.</p>
|
||||
<p>We occasionally include access to an online community as part of our programs. We want every single member to add value to the group. Our goal is to make your community the most valuable community you’re a member of. Therefore, we reserve the right to remove anyone at any time. We rarely do this, but we want to let you know how seriously we take our communities.</p>
|
||||
<h2>Registration</h2>
|
||||
<p>To access certain features of the Site, we may ask you to provide certain demographic information including your gender, year of birth, zip code and country. In addition, if you elect to sign-up for a particular feature of the Site, such as chat rooms, web logs, or bulletin boards, you may also be asked to register with us on the form provided and such registration may require you to provide personally identifiable information such as your name and email address. You agree to provide true, accurate, current and complete information about yourself as prompted by the Site’s registration form. If we have reasonable grounds to suspect that such information is untrue, inaccurate, or incomplete, we have the right to suspend or terminate your account and refuse any and all current or future use of the Site (or any portion thereof). Our use of any personally identifiable information you provide to us as part of the registration process is governed by the terms of our Privacy Policy.</p>
|
||||
<h2>Passwords</h2>
|
||||
<p>To use certain features of the Site, you will need a username and password, which you will receive through the Site’s registration process. You are responsible for maintaining the confidentiality of the password and account, and are responsible for all activities (whether by you or by others) that occur under your password or account. You agree to notify us immediately of any unauthorized use of your password or account or any other breach of security, and to ensure that you exit from your account at the end of each session. We cannot and will not be liable for any loss or damage arising from your failure to protect your password or account information.</p>
|
||||
<h2>Limitation of liability</h2>
|
||||
General
|
||||
|
||||
|
||||
|
||||
This website (the “Site”) is owned and operated by Darcan LTD trading as Call Center Mastery (herein referred to as Call Center Mastery) (“Call Center Mastery,” “we” or “us”). By using the Site, you agree to be bound by these Terms of Service and to use the Site in accordance with these Terms of Service, our Privacy Policy and any additional terms and conditions that may apply to specific sections of the Site or to products and services available through the Site or from Call Center Mastery. Accessing the Site, in any manner, whether automated or otherwise, constitutes use of the Site and your agreement to be bound by these Terms of Service.
|
||||
|
||||
|
||||
|
||||
We reserve the right to change these Terms of Service or to impose new conditions on use of the Site, from time to time, in which case we will post the revised Terms of Service on this website. By continuing to use the Site after we post any such changes, you accept the Terms of Service, as modified. Intellectual Property Rights
|
||||
|
||||
|
||||
|
||||
Our limited license to you
|
||||
|
||||
|
||||
|
||||
This Site and all the materials available on the Site are the property of us and/or our affiliates or licensors, and are protected by copyright, trademark, and other intellectual property laws. The Site is provided solely for your personal noncommercial use. You may not use the Site or the materials available on the Site in a manner that constitutes an infringement of our rights or that has not been authorized by us. More specifically, unless explicitly authorized in these Terms of Service or by the owner of the materials, you may not modify, copy, reproduce, republish, upload, post, transmit, translate, sell, create derivative works, exploit, or distribute in any manner or medium (including by email or other electronic means) any material from the Site. You may, however, from time to time, download and/or print one copy of individual pages of the Site for your personal, non-commercial use, provided that you keep intact all copyright and other proprietary notices.
|
||||
|
||||
|
||||
|
||||
Your license to us
|
||||
|
||||
|
||||
|
||||
By posting or submitting any material (including, without limitation, comments, blog entries, Facebook postings, photos and videos) to us via the Site, internet groups, social media venues, or to any of our staff via email, text or otherwise, you are representing: (i) that you are the owner of the material, or are making your posting or submission with the express consent of the owner of the material; and (ii) that you are thirteen years of age or older. In addition, when you submit, email, text or deliver or post any material, you are granting us, and anyone authorized by us, a royalty-free, perpetual, irrevocable, non-exclusive, unrestricted, worldwide license to use, copy, modify, transmit, sell, exploit, create derivative works from, distribute, and/or publicly perform or display such material, in whole or in part, in any manner or medium, now known or hereafter developed, for any purpose. The foregoing grant shall include the right to exploit any proprietary rights in such posting or submission, including, but not limited to, rights under copyright, trademark, service mark or patent laws under any relevant jurisdiction. Also, in connection with the exercise of such rights, you grant us, and anyone authorized by us, the right to identify you as the author of any of your postings or submissions by name, email address or screen name, as we deem appropriate.
|
||||
|
||||
|
||||
|
||||
You acknowledge and agree that any contributions originally created by you for us shall be deemed a “work made for hire” when the work performed is within the scope of the definition of a work made for hire in Section 101 of the United States Copyright Law, as amended. As such, the copyrights in those works shall belong to Call Center Mastery from their creation. Thus, Call Center Mastery shall be deemed the author and exclusive owner thereof and shall have the right to exploit any or all of the results and proceeds in any and all media, now known or hereafter devised, throughout the universe, in perpetuity, in all languages, as Call Center Mastery determines. In the event that any of the results and proceeds of your submissions hereunder are not deemed a “work made for hire” under Section 101 of the Copyright Act, as amended, you hereby, without additional compensation, irrevocably assign, convey and transfer to Call Center Mastery all proprietary rights, including without limitation, all copyrights and trademarks throughout the universe, in perpetuity in every medium, whether now known or hereafter devised, to such material and any and all right, title and interest in and to all such proprietary rights in every medium, whether now known or hereafter devised, throughout the universe, in perpetuity. Any posted material which are reproductions of prior works by you shall be co-owned by us.
|
||||
|
||||
|
||||
|
||||
You acknowledge that Call Center Mastery has the right but not the obligation to use and display any postings or contributions of any kind and that Call Center Mastery may elect to cease the use and display of any such materials (or any portion thereof), at any time for any reason whatsoever.
|
||||
|
||||
|
||||
|
||||
Limitations on Linking and Framing. You may establish a hypertext link to the Site so long as the link does not state or imply any sponsorship of your site by us or by the Site. However, you may not, without our prior written permission, frame or inline link any of the content of the Site, or incorporate into another website or other service any of our material, content or intellectual property.
|
||||
|
||||
|
||||
|
||||
Disclaimers
|
||||
|
||||
|
||||
|
||||
Throughout the Site, we may provide links and pointers to Internet sites maintained by third parties. Our linking to such third-party sites does not imply an endorsement or sponsorship of such sites, or the information, products or services offered on or through the sites. In addition, neither we nor affiliates operate or control in any respect any information, products or services that third parties may provide on or through the Site or on websites linked to by us on the Site. If applicable, any opinions, advice, statements, services, offers, or other information or content expressed or made available by third parties, including information providers, are those of the respective authors or distributors, and not Call Center Mastery. Neither Call Center Mastery nor any third-party provider of information guarantees the accuracy, completeness, or usefulness of any content. Furthermore, Call Center Mastery neither endorses nor is responsible for the accuracy and reliability of any opinion, advice, or statement made on any of the Sites by anyone other than an authorized Call Center Mastery representative while acting in his/her official capacity.
|
||||
|
||||
|
||||
|
||||
THE INFORMATION, PRODUCTS AND SERVICES OFFERED ON OR THROUGH THE SITE AND BY Call Center Mastery AND ANY THIRD-PARTY SITES ARE PROVIDED “AS IS” AND WITHOUT WARRANTIES OF ANY KIND EITHER EXPRESS OR IMPLIED. TO THE FULLEST EXTENT PERMISSIBLE PURSUANT TO APPLICABLE LAW, WE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. WE DO NOT WARRANT THAT THE SITE OR ANY OF ITS FUNCTIONS WILL BE UNINTERRUPTED OR ERROR-FREE, THAT DEFECTS WILL BE CORRECTED, OR THAT ANY PART OF THIS SITE, INCLUDING BULLETIN BOARDS, OR THE SERVERS THAT MAKE IT AVAILABLE, ARE FREE OF VIRUSES OR OTHER HARMFUL COMPONENTS.
|
||||
|
||||
|
||||
|
||||
WE DO NOT WARRANT OR MAKE ANY REPRESENTATIONS REGARDING THE USE OR THE RESULTS OF THE USE OF THE SITE OR MATERIALS ON THIS SITE OR ON THIRD-PARTY SITES IN TERMS OF THEIR CORRECTNESS, ACCURACY, TIMELINESS, RELIABILITY OR OTHERWISE.
|
||||
|
||||
|
||||
|
||||
You agree at all times to defend, indemnify and hold harmless Call Center Mastery its affiliates, their successors, transferees, assignees and licensees and their respective parent and subsidiary companies, agents, associates, officers, directors, shareholders and employees of each from and against any and all claims, causes of action, damages, liabilities, costs and expenses, including legal fees and expenses, arising out of or related to your breach of any obligation, warranty, representation or covenant set forth herein.
|
||||
|
||||
|
||||
|
||||
Online commerce
|
||||
|
||||
|
||||
|
||||
Certain sections of the Site may allow you to purchase many different types of products and services online that are provided by third parties. We are not responsible for the quality, accuracy, timeliness, reliability or any other aspect of these products and services. If you make a purchase from a merchant on the Site or on a site linked to by the Site, the information obtained during your visit to that merchant’s online store or site, and the information that you give as part of the transaction, such as your credit card number and contact information, may be collected by both the merchant and us. A merchant may have privacy and data collection practices that are different from ours. We have no responsibility or liability for these independent policies. In addition, when you purchase products or services on or through the Site, you may be subject to additional terms and conditions that specifically apply to your purchase or use of such products or services. For more information regarding a merchant, its online store, its privacy policies, and/or any additional terms and conditions that may apply, visit that merchant’s website and click on its information links or contact the merchant directly. You release us and our affiliates from any damages that you incur, and agree not to assert any claims against us or them, arising from your purchase or use of any products or services made available by third parties through the Site.
|
||||
|
||||
Your participation, correspondence or business dealings with any third party found on or through our Site, regarding payment and delivery of specific goods and services, and any other terms, conditions, representations or warranties associated with such dealings, are solely between you and such third party. You agree that Call Center Mastery shall not be responsible or liable for any loss, damage, or other matters of any sort incurred as the result of such dealings.
|
||||
|
||||
|
||||
|
||||
You agree to be financially responsible for all purchases made by you or someone acting on your behalf through the Site. You agree to use the Site and to purchase services or products through the Site for legitimate, non-commercial purposes only. You also agree not to make any purchases for speculative, false or fraudulent purposes or for the purpose of anticipating demand for a particular product or service. You agree to only purchase goods or services for yourself or for another person for whom you are legally permitted to do so. When making a purchase for a third party that requires you to submit the third party’s personal information to us or a merchant, you represent that you have obtained the express consent of such third party to provide such third party’s personal information.
|
||||
|
||||
|
||||
|
||||
Your purchase is for personal use only. Sharing of purchases is not permitted and will be considered unauthorized, an infringing use of our copyrighted material, and may subject violators to liability. If payment for a course is declined, our system will automatically disable access to our premium materials. (We understand. This usually happens because a credit card expires.) We want to help restore your access, so we’ll make every attempt to contact you to help resolve this issue. Once the billing issue is resolved, we’ll restore access.
|
||||
|
||||
|
||||
|
||||
Interactive features
|
||||
|
||||
|
||||
|
||||
This Site may include a variety of features, such as bulletin boards, web logs, chat rooms, and email services, which allow feedback to us and real-time interaction between users, and other features which allow users to communicate with others. Responsibility for what is posted on bulletin boards, web logs, chat rooms, and other public posting areas on the Site, or sent via any email services on the Site, lies with each user – you alone are responsible for the material you post or send. We do not control the messages, information or files that you or others may provide through the Site. It is a condition of your use of the Site that you do not:
|
||||
|
||||
Restrict or inhibit any other user from using and enjoying the Site.
|
||||
|
||||
|
||||
|
||||
Use the Site to impersonate any person or entity, or falsely state or otherwise misrepresent your affiliation with a person or entity.
|
||||
|
||||
|
||||
|
||||
Interfere with or disrupt any servers or networks used to provide the Site or its features, or disobey any requirements, procedures, policies or regulations of the networks we use to provide the Site.
|
||||
|
||||
|
||||
|
||||
Use the Site to instigate or encourage others to commit illegal activities or cause injury or property damage to any person.
|
||||
|
||||
|
||||
|
||||
Gain unauthorized access to the Site, or any account, computer system, or network connected to this Site, by means such as hacking, password mining or other illicit means.
|
||||
|
||||
|
||||
|
||||
Obtain or attempt to obtain any materials or information through any means not intentionally made available through this Site.
|
||||
|
||||
|
||||
|
||||
Use the Site to post or transmit any unlawful, threatening, abusive, libelous, defamatory, obscene, vulgar, pornographic, profane or indecent information of any kind, including without limitation any transmissions constituting or encouraging conduct that would constitute a criminal offense, give rise to civil liability or otherwise violate any local, state, national or international law.
|
||||
|
||||
|
||||
|
||||
Use the Site to post or transmit any information, software or other material that violates or infringes upon the rights of others, including material that is an invasion of privacy or publicity rights or that is protected by copyright, trademark or other proprietary right, or derivative works with respect thereto, without first obtaining permission from the owner or rights holder.
|
||||
|
||||
Use the Site to post or transmit any information, software or other material that contains a virus or other harmful component.
|
||||
|
||||
|
||||
|
||||
Use the Site to post, transmit or in any way exploit any information, software or other material for commercial purposes, or that contains advertising.
|
||||
|
||||
|
||||
|
||||
Use the Site to advertise or solicit to anyone to buy or sell products or services, or to make donations of any kind, without our express written approval.
|
||||
|
||||
|
||||
|
||||
Gather for marketing purposes any email addresses or other personal information that has been posted by other users of the Site.
|
||||
|
||||
|
||||
|
||||
Call Center Mastery may host message boards, chats and other public forums on its Sites. Any user failing to comply with the terms and conditions of this Agreement may be expelled from and refused continued access to, the message boards, chats or other public forums in the future. Call Center Mastery or its designated agents may remove or alter any user-created content at any time for any reason. Message boards, chats and other public forums are intended to serve as discussion centers for users and subscribers. Information and content posted within these public forums may be provided by Call Center Mastery staff, Call Center Mastery’s outside contributors, or by users not connected with Call Center Mastery, some of whom may employ anonymous user names. Call Center Mastery expressly disclaims all responsibility and endorsement and makes no representation as to the validity of any opinion, advice, information or statement made or displayed in these forums by third parties, nor are we responsible for any errors or omissions in such postings, or for hyperlinks embedded in any messages. Under no circumstances will we, our affiliates, suppliers or agents be liable for any loss or damage caused by your reliance on information obtained through these forums. The opinions expressed in these forums are solely the opinions of the participants, and do not reflect the opinions of Call Center Mastery or any of its subsidiaries or affiliates.
|
||||
|
||||
|
||||
|
||||
Call Center Mastery has no obligation whatsoever to monitor any of the content or postings on the message boards, chat rooms or other public forums on the Sites. However, you acknowledge and agree that we have the absolute right to monitor the same at our sole discretion. In addition, we reserve the right to alter, edit, refuse to post or remove any postings or content, in whole or in part, for any reason and to disclose such materials and the circumstances surrounding their transmission to any third party in order to satisfy any applicable law, regulation, legal process or governmental request and to protect ourselves, our clients, sponsors, users and visitors.
|
||||
|
||||
|
||||
|
||||
We occasionally include access to an online community as part of our programs. We want every single member to add value to the group. Our goal is to make your community the most valuable community you’re a member of. Therefore, we reserve the right to remove anyone at any time. We rarely do this, but we want to let you know how seriously we take our communities.
|
||||
|
||||
|
||||
|
||||
Registration
|
||||
|
||||
|
||||
|
||||
To access certain features of the Site, we may ask you to provide certain demographic information including your gender, year of birth, zip code and country. In addition, if you elect to sign-up for a particular feature of the Site, such as chat rooms, web logs, or bulletin boards, you may also be asked to register with us on the form provided and such registration may require you to provide personally identifiable information such as your name and email address. You agree to provide true, accurate, current and complete information about yourself as prompted by the Site’s registration form. If we have reasonable grounds to suspect that such information is untrue, inaccurate, or incomplete, we have the right to suspend or terminate your account and refuse any and all current or future use of the Site (or any portion thereof). Our use of any personally identifiable information you provide to us as part of the registration process is governed by the terms of our Privacy Policy.
|
||||
|
||||
|
||||
|
||||
Passwords
|
||||
|
||||
|
||||
|
||||
To use certain features of the Site, you will need a username and password, which you will receive through the Site’s registration process. You are responsible for maintaining the confidentiality of the password and account, and are responsible for all activities (whether by you or by others) that occur under your password or account. You agree to notify us immediately of any unauthorized use of your password or account or any other breach of security, and to ensure that you exit from your account at the end of each session. We cannot and will not be liable for any loss or damage arising from your failure to protect your password or account information.
|
||||
|
||||
|
||||
|
||||
Limitation of liability
|
||||
|
||||
|
||||
|
||||
UNDER NO CIRCUMSTANCES, INCLUDING, BUT NOT LIMITED TO, NEGLIGENCE, SHALL WE, OUR SUBSIDIARY AND PARENT COMPANIES OR AFFILIATES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL OR CONSEQUENTIAL DAMAGES THAT RESULT FROM THE USE OF, OR THE INABILITY TO USE, THE SITE, INCLUDING OUR MESSAGING, BLOGS, COMMENTS OF OTHERS, BOOKS, EMAILS, PRODUCTS, OR SERVICES, OR THIRD-PARTY MATERIALS, PRODUCTS, OR SERVICES MADE AVAILABLE THROUGH THE SITE OR BY US IN ANY WAY, EVEN IF WE ARE ADVISED BEFOREHAND OF THE POSSIBILITY OF SUCH DAMAGES. (BECAUSE SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF CERTAIN CATEGORIES OF DAMAGES, THE ABOVE LIMITATION MAY NOT APPLY TO YOU. IN SUCH STATES, OUR LIABILITY AND THE LIABILITY OF OUR SUBSIDIARY AND PARENT COMPANIES OR AFFILIATES IS LIMITED TO THE FULLEST EXTENT PERMITTED BY SUCH STATE LAW.) YOU SPECIFICALLY ACKNOWLEDGE AND AGREE THAT WE ARE NOT LIABLE FOR ANY DEFAMATORY, OFFENSIVE OR ILLEGAL CONDUCT OF ANY USER. IF YOU ARE DISSATISFIED WITH THE SITE, ANY MATERIALS, PRODUCTS, OR SERVICES ON THE SITE, OR WITH ANY OF THE SITE’S TERMS AND CONDITIONS, YOUR SOLE AND EXCLUSIVE REMEDY IS TO DISCONTINUE USING THE SITE AND THE PRODUCTS, SERVICES AND/OR MATERIALS Call Center Mastery IS NOT AN INVESTMENT ADVISORY SERVICE, IS NOT AN INVESTMENT ADVISER, AND DOES NOT PROVIDE PERSONALIZED FINANCIAL ADVICE OR ACT AS A FINANCIAL ADVISOR.
|
||||
|
||||
|
||||
|
||||
WE EXIST FOR EDUCATIONAL PURPOSES ONLY, AND THE MATERIALS AND INFORMATION CONTAINED HEREIN AND IN OUR PRODUCTS AND SERVICES ARE FOR GENERAL INFORMATIONAL PURPOSES ONLY. NONE OF THE INFORMATION PROVIDED BY US IS INTENDED AS INVESTMENT, TAX, ACCOUNTING OR LEGAL ADVICE, AS AN OFFER OR SOLICITATION OF AN OFFER TO BUY OR SELL, OR AS AN ENDORSEMENT, RECOMMENDATION OR SPONSORSHIP OF ANY Call Center Mastery, SECURITY, OR FUND. OUR INFORMATION SHOULD NOT BE RELIED UPON FOR PURPOSES OF TRANSACTING IN SECURITIES OR OTHER INVESTMENTS.
|
||||
|
||||
|
||||
|
||||
WE DO NOT OFFER OR PROVIDE TAX, LEGAL OR INVESTMENT ADVICE AND YOU ARE RESPONSIBLE FOR CONSULTING TAX, LEGAL, OR FINANCIAL PROFESSIONALS BEFORE ACTING ON ANY INFORMATION PROVIDED BY US. THIS SITE IS CONTINUALLY UNDER DEVELOPMENT AND Call Center Mastery MAKES NO WARRANTY OF ANY KIND, IMPLIED OR EXPRESS, AS TO ITS ACCURACY, COMPLETENESS OR APPROPRIATENESS FOR ANY PURPOSE. YOU acknowledge and agrees that no representation has been made by Call Center Mastery OR ITS AFFILIATES and relied upon as to the future income, expenses, sales volume or potential profitability that may be derived from the participation in THIS PROGRAM.
|
||||
|
||||
|
||||
|
||||
Termination
|
||||
|
||||
|
||||
|
||||
We may cancel or terminate your right to use the Site or any part of the Site at any time without notice. In the event of cancellation or termination, you are no longer authorized to access the part of the Site affected by such cancellation or termination. The restrictions imposed on you with respect to material downloaded from the Site, and the disclaimers and limitations of liabilities set forth in these Terms of Service, shall survive.
|
||||
|
||||
|
||||
|
||||
Refund policy
|
||||
|
||||
|
||||
|
||||
Your purchase of a product or service or ticket to an event may or may not provide for any refund. Each specific product, service, event or course will specify its own refund policy.
|
||||
|
||||
|
||||
|
||||
Other
|
||||
|
||||
|
||||
|
||||
The Digital Millennium Copyright Act of 1998 (the “DMCA”) provides recourse for copyright owners who believe that material appearing on the Internet infringes their rights under the U.S. copyright law. If you believe in good faith that materials hosted by Call Center Mastery infringe your copyright, you, or your agent may send to Call Center Mastery a notice requesting that the material be removed or access to it be blocked. Any notification by a copyright owner or a person authorized to act on its behalf that fails to comply with requirements of the DMCA shall not be considered sufficient notice and shall not be deemed to confer upon Call Center Mastery actual knowledge of facts or circumstances from which infringing material or acts are evident. If you believe in good faith that a notice of copyright infringement has been wrongly filed against you, the DMCA permits you to send to Call Center Mastery a counter-notice. All notices and counter notices must meet the then current statutory requirements imposed by the DMCA; see http://www.loc.gov/copyright for details. Call Center Mastery’s Copyright Agent for notice of claims of copyright infringement or counter notices can be reached as follows: admin@cc-mastery.com
|
||||
|
||||
|
||||
|
||||
This Agreement shall be binding upon and inure to the benefit of Call Center Mastery and our respective assigns, successors, heirs, and legal representatives. Neither this Agreement nor any rights hereunder may be assigned without the prior written consent of Call Center Mastery. Notwithstanding the foregoing, all rights and obligations under this Agreement may be freely assigned by Call Center Mastery to any affiliated entity or any of its wholly owned subsidiaries These Terms of Use shall be governed by and construed in accordance with the laws of The United Kingdom and any dispute shall be subject to binding arbitration in The United Kingdom. If any provision of this agreement shall be unlawful, void or for any reason unenforceable, then that provision shall be deemed severable from this agreement and shall not affect the validity and enforceability of any remaining provisions.
|
||||
|
||||
|
||||
|
||||
Disclaimer
|
||||
|
||||
|
||||
|
||||
Although it is highly unlikely, This policy may be changed at any time at our discretion. If we should update this policy, we will post the updates to this page on our Website. If you have any questions or concerns regarding our privacy policy please direct them to: admin@cc-mastery.com
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,409 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title></title>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
<link
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<!-- <link href="/public/assets/css/tabulator_semanticui.min.css" rel="stylesheet"> -->
|
||||
<link href="https://parsleyjs.org/src/parsley.css" rel="stylesheet" />
|
||||
<script
|
||||
src="https://code.jquery.com/jquery-2.2.4.min.js"
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
<script
|
||||
src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
<script
|
||||
src="https://cdnjs.cloudflare.com/ajax/libs/stapes/1.0.0/stapes.js"
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
|
||||
<link href="https://parsleyjs.org/src/parsley.css" rel="stylesheet" />
|
||||
<link href="/style.css" rel="stylesheet" />
|
||||
|
||||
<style>
|
||||
.time-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.flex-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flex-item {
|
||||
flex: 1;
|
||||
/* margin-right: 10px; */
|
||||
/* Adjust margin as needed */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<nav id="sidebar">
|
||||
<div class="sidebar-header">Team Followup</div>
|
||||
<ul class="list-unstyled components">
|
||||
<li><a href="/admin/license"> License </a></li>
|
||||
<li><a href="/admin/accesslog"> Access log </a></li>
|
||||
<li><a href="/admin/project" class="active"> Project</a></li>
|
||||
<li><a href="/admin/users"> Users </a></li>
|
||||
<li><a href="/admin/logout"> Logout </a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<div id="content">
|
||||
<div class="container mt-5">
|
||||
<h2 class="text-center">Add Project</h2>
|
||||
<form action="" method="POST" class="mt-4">
|
||||
<!-- <div class="form-group">
|
||||
<label for="project_name">Project Name</label>
|
||||
<input type="text" name="project_name" id="project_name" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="calendar_id">Calendar ID</label>
|
||||
<input type="text" name="calendar_id" id="calendar_id" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="webhook">Webhook</label>
|
||||
<input type="text" name="webhook" id="webhook" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="score_threshold">Score Threshold</label>
|
||||
<input type="text" name="score_threshold" id="score_threshold" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="actual_score">Actual Score</label>
|
||||
<input type="text" name="actual_score" id="actual_score" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="alert">Alert</label>
|
||||
<input type="text" name="alert" id="alert" class="form-control" required>
|
||||
</div> -->
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="sundayCheckbox"
|
||||
value="sun"
|
||||
onclick="showTimeInput('sundayTime')"
|
||||
/>
|
||||
<label class="form-check-label" for="sundayCheckbox"
|
||||
>Sunday</label
|
||||
>
|
||||
|
||||
<!-- <div id="mondayTime" class="time-container">
|
||||
<label for="mondayFromInput">From:</label>
|
||||
<input type="time" id="mondayFromInput" class="time-input">
|
||||
<label for="mondayToInput">To:</label>
|
||||
<input type="time" id="mondayToInput" class="time-input">
|
||||
<button type="button" onclick="addTime('mondayTime')">Add Time</button>
|
||||
</div> -->
|
||||
|
||||
<div class="form-row time-container" id="sundayTime">
|
||||
<div class="time-set form-row">
|
||||
<div class="form-group col-md-6">
|
||||
<label for="sundayFromInput">From</label>
|
||||
<input
|
||||
type="time"
|
||||
name="from"
|
||||
id="sundayFromInput"
|
||||
class="form-control mb-2 time-input"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label for="sundayToInput">To</label>
|
||||
<input
|
||||
type="time"
|
||||
name="to"
|
||||
id="sundayToInput"
|
||||
class="form-control mb-2 time-input"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-12">
|
||||
<a
|
||||
type="button"
|
||||
class="btn btn-link"
|
||||
onclick="addTime('sundayTime')"
|
||||
>Add Time</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="inlineCheckbox2"
|
||||
value="mon"
|
||||
/>
|
||||
<label class="form-check-label" for="inlineCheckbox2"
|
||||
>Monday</label
|
||||
>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="inlineCheckbox3"
|
||||
value="tue"
|
||||
/>
|
||||
<label class="form-check-label" for="inlineCheckbox3"
|
||||
>Tuesday</label
|
||||
>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="inlineCheckbox3"
|
||||
value="wed"
|
||||
/>
|
||||
<label class="form-check-label" for="inlineCheckbox3"
|
||||
>Wednesday</label
|
||||
>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="inlineCheckbox3"
|
||||
value="thurs"
|
||||
/>
|
||||
<label class="form-check-label" for="inlineCheckbox3"
|
||||
>Thursday</label
|
||||
>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="inlineCheckbox3"
|
||||
value="fri"
|
||||
/>
|
||||
<label class="form-check-label" for="inlineCheckbox3"
|
||||
>Friday</label
|
||||
>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="inlineCheckbox3"
|
||||
value="sat"
|
||||
/>
|
||||
<label class="form-check-label" for="inlineCheckbox3"
|
||||
>Saturday</label
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary"
|
||||
onclick="submitForm(event)"
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
var formData = {};
|
||||
|
||||
function showTimeInput(containerId) {
|
||||
var checkbox = document.getElementById(
|
||||
containerId.replace("Time", "Checkbox")
|
||||
);
|
||||
var timeContainer = document.getElementById(containerId);
|
||||
|
||||
if (checkbox.checked) {
|
||||
timeContainer.style.display = "flex";
|
||||
} else {
|
||||
timeContainer.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
function addTime(containerId) {
|
||||
var timeContainer = document.getElementById(containerId);
|
||||
|
||||
var newTimeSet = document.createElement("div");
|
||||
newTimeSet.className = "time-set form-row";
|
||||
|
||||
var newFormGroup1 = document.createElement("div");
|
||||
newFormGroup1.className = "form-group col-md-6";
|
||||
var newLabelFrom = document.createElement("label");
|
||||
newLabelFrom.textContent = "From:";
|
||||
var newInputFrom = document.createElement("input");
|
||||
newInputFrom.type = "time";
|
||||
newInputFrom.name = "from";
|
||||
newInputFrom.className = "form-control mb-2 time-input";
|
||||
newFormGroup1.appendChild(newLabelFrom);
|
||||
newFormGroup1.appendChild(newInputFrom);
|
||||
|
||||
var newFormGroup2 = document.createElement("div");
|
||||
newFormGroup2.className = "form-group col-md-6";
|
||||
var newLabelTo = document.createElement("label");
|
||||
newLabelTo.textContent = "To:";
|
||||
var newInputTo = document.createElement("input");
|
||||
newInputTo.type = "time";
|
||||
newInputTo.name = "to";
|
||||
newInputTo.className = "form-control mb-2 time-input";
|
||||
newFormGroup2.appendChild(newLabelTo);
|
||||
newFormGroup2.appendChild(newInputTo);
|
||||
|
||||
newTimeSet.appendChild(newFormGroup1);
|
||||
newTimeSet.appendChild(newFormGroup2);
|
||||
|
||||
// Insert the new time set before the "Add Time" button
|
||||
timeContainer.insertBefore(
|
||||
newTimeSet,
|
||||
timeContainer.lastElementChild
|
||||
);
|
||||
|
||||
updateFormData(); // Call function to update formData when time is added
|
||||
}
|
||||
|
||||
// function updateFormDatass() {
|
||||
// // Reset formData
|
||||
// formData = {};
|
||||
|
||||
// // Loop through each day
|
||||
// ['sunday', 'monday'].forEach(function(day) {
|
||||
// var fromInputs = document.querySelectorAll('#' + day + 'Time .time-input:nth-child(odd)');
|
||||
// var toInputs = document.querySelectorAll('#' + day + 'Time .time-input:nth-child(even)');
|
||||
// console.log(fromInputs)
|
||||
// console.log(toInputs)
|
||||
// var dayData = [];
|
||||
|
||||
// // Check if the day is selected and both time inputs have values
|
||||
// for (var i = 0; i < fromInputs.length; i++) {
|
||||
// if (fromInputs[i].value.trim() !== '' && toInputs[i].value.trim() !== '') {
|
||||
// // Store the data in dayData array
|
||||
// dayData.push({
|
||||
// from: fromInputs[i].value,
|
||||
// to: toInputs[i].value
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// console.log(dayData)
|
||||
// // If there is data for the day, store it in formData
|
||||
// if (document.getElementById(day + 'Checkbox').checked && dayData.length > 0) {
|
||||
// formData[day] = dayData;
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
function updateFormData() {
|
||||
// Reset formData
|
||||
formData = {};
|
||||
|
||||
// Loop through each day
|
||||
["sunday", "monday"].forEach(function (day) {
|
||||
var checkbox = document.getElementById(day + "Checkbox");
|
||||
var timeContainer = document.getElementById(day + "Time");
|
||||
var timeSets = timeContainer.querySelectorAll(".time-set");
|
||||
console.log(checkbox);
|
||||
console.log(timeContainer);
|
||||
console.log(timeSets);
|
||||
|
||||
if (checkbox.checked) {
|
||||
var dayData = [];
|
||||
|
||||
// Iterate over each time set
|
||||
timeSets.forEach(function (timeSet) {
|
||||
console.log(timeSet);
|
||||
|
||||
var fromInput = timeSet.querySelector('input[name="from"]');
|
||||
var toInput = timeSet.querySelector('input[name="to"]');
|
||||
|
||||
// Check if both "From" and "To" inputs have values
|
||||
if (
|
||||
fromInput.value.trim() !== "" &&
|
||||
toInput.value.trim() !== ""
|
||||
) {
|
||||
// Store the data in dayData array
|
||||
dayData.push({
|
||||
from: fromInput.value,
|
||||
to: toInput.value,
|
||||
});
|
||||
}
|
||||
});
|
||||
console.log(dayData);
|
||||
|
||||
// If there is data for the day, store it in formData
|
||||
if (dayData.length > 0) {
|
||||
formData[day] = dayData;
|
||||
console.log(formData); // Log formData for debugging
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log(formData); // Log formData for debugging
|
||||
}
|
||||
|
||||
function submitForm(event) {
|
||||
event.preventDefault();
|
||||
|
||||
updateFormData(); // Ensure formData is updated before submission
|
||||
|
||||
$.ajax({
|
||||
url: "/admin/project/add",
|
||||
method: "POST",
|
||||
data: {
|
||||
project_name: project_name,
|
||||
slot: slot,
|
||||
days: days,
|
||||
},
|
||||
success: function (response) {
|
||||
// console.log(response.slots_available);
|
||||
|
||||
const slotsAvailable = response.slots_available;
|
||||
|
||||
$(".user-pick-time-slots").each(function () {
|
||||
const userTimeSlot = $(this).val();
|
||||
// console.log(slotsAvailable[$(this).val()])
|
||||
|
||||
if (slotsAvailable[userTimeSlot] > 0) {
|
||||
// Slot is available, enable the radio button
|
||||
$(this).prop("disabled", false);
|
||||
} else {
|
||||
// Slot is not available, disable the radio button
|
||||
$(this).prop("disabled", true);
|
||||
}
|
||||
});
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
console.log(error);
|
||||
// console.log("error oooo")
|
||||
},
|
||||
});
|
||||
// Display the collected data (you can replace this with your own logic)
|
||||
alert(JSON.stringify(formData));
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
<script
|
||||
src="https://cdnjs.cloudflare.com/ajax/libs/parsley.js/2.9.2/parsley.min.js"
|
||||
integrity="sha512-eyHL1atYNycXNXZMDndxrDhNAegH2BDWt1TmkXJPoGf1WLlNYt08CSjkqF5lnCRmdm3IrkHid8s2jOUY4NIZVQ=="
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
></script>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
include_once 'core.php';
|
||||
include_once 'config.php';
|
||||
include_once 'project-model.php';
|
||||
include_once 'calendar_functions.php';
|
||||
// check_login();
|
||||
$error = false;
|
||||
|
||||
$data = [
|
||||
'page_title' => 'Project'
|
||||
];
|
||||
// $config = MkdConfig::get_instance()->get_config();
|
||||
// $apikey = $config['gohighlevel_key'];
|
||||
// $cid = $_POST['calendar_id'];
|
||||
$pid = 412;
|
||||
// $pid = 169;
|
||||
$projectModel = new ProjectModel();
|
||||
$model = $projectModel->get((int)$pid);
|
||||
$apikey = $model->location;
|
||||
$curl = curl_init();
|
||||
|
||||
curl_setopt_array($curl, [
|
||||
CURLOPT_URL => "https://rest.gohighlevel.com/v1/calendars/services",
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_ENCODING => "",
|
||||
CURLOPT_MAXREDIRS => 10,
|
||||
CURLOPT_TIMEOUT => 30,
|
||||
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||
CURLOPT_CUSTOMREQUEST => "GET",
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"Accept: application/json",
|
||||
"Authorization: Bearer " . $apikey,
|
||||
"Version: 2021-04-15"
|
||||
],
|
||||
]);
|
||||
|
||||
$response = curl_exec($curl);
|
||||
$err = curl_error($curl);
|
||||
$status_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
||||
|
||||
curl_close($curl);
|
||||
|
||||
// print_r($response);
|
||||
|
||||
if ($status_code != 200) {
|
||||
echo "Something went wrong.";
|
||||
exit;
|
||||
}
|
||||
|
||||
$cid = $model->calendar;
|
||||
$data = json_decode($response, true);
|
||||
|
||||
// Search for the service with the specified ID
|
||||
$searchedService = null;
|
||||
if (!empty($data['services']) && count($data['services'] ) > 0) {
|
||||
foreach ($data['services'] as $service) {
|
||||
if ($service['id'] === $cid) {
|
||||
$searchedService = $service;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($searchedService == null) {
|
||||
echo "Calendar Service with ID '" . $cid . "' not found.";
|
||||
}
|
||||
|
||||
$slots = json_decode($model->slot);
|
||||
try {
|
||||
echo checkServiceInSlot($searchedService["availability"]["officeHours"],
|
||||
$slots,
|
||||
$model,
|
||||
$searchedService["availability"]["eventTiming"],
|
||||
$searchedService["availability"]["schedule"],
|
||||
$searchedService["id"]);
|
||||
} catch (\Throwable $th) {
|
||||
echo "Something went wrong.";
|
||||
print_r($th);
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
exit;
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
include_once __DIR__ . "/mysql-database-service.php";
|
||||
|
||||
class UserModel extends MySqlDatabaseService
|
||||
{
|
||||
protected $_table = 'user';
|
||||
protected $_primary_key = 'id';
|
||||
protected $_return_type = 'array';
|
||||
protected $_allowed_fields = [
|
||||
'id', 'email', 'status'
|
||||
];
|
||||
protected $_label_fields = [
|
||||
'ID', 'Email', 'Status'
|
||||
];
|
||||
protected $_use_timestamps = true;
|
||||
protected $_created_field = 'created_at';
|
||||
protected $_updated_field = 'updated_at';
|
||||
protected $_validation_rules = [
|
||||
['email', 'Email', 'required'],
|
||||
['Status', 'required'],
|
||||
];
|
||||
|
||||
protected $_validation_edit_rules = [
|
||||
['email', 'Email', 'required'],
|
||||
['status', 'Status', 'required']
|
||||
];
|
||||
protected $_validation_messages = [
|
||||
['email', 'Email', 'required'],
|
||||
['status', 'Status', 'required']
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function get_mapping()
|
||||
{
|
||||
return [
|
||||
// TODO: ADD MAPPING
|
||||
];
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
<div class="container mt-5">
|
||||
<h2 class="text-center">Add Users</h2>
|
||||
<?php if (isset($error) && $error): ?>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
Please fill in all required fields!
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<form action="/admin/users/add" method="POST" class="mt-4">
|
||||
<div class="form-group">
|
||||
<label for="email">Email</label>
|
||||
<input type="email" name="email" id="email" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="company">Company Name</label>
|
||||
<input type="text" name="company" id="companyS" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<input type="password" name="password" id="password" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="role">Role</label>
|
||||
<select name="role" id="role" class="form-control" >
|
||||
<option value="client">Client </option>
|
||||
<option value="admin">Admin </option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
@@ -0,0 +1,39 @@
|
||||
<div class="container mt-5">
|
||||
<h2 class="text-center">Edit User</h2>
|
||||
<?php if (isset($error) && $error): ?>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
Please fill in all required fields!
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<form action="/admin/users/edit/<?php echo $id?>" method="POST" class="mt-4">
|
||||
<div class="form-group">
|
||||
<label for="email">Email</label>
|
||||
<input type="email" name="email" id="email" class="form-control" required
|
||||
value="<?php echo isset($data['model']) ? $data['model']->email : ''?>">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="company">Company Name</label>
|
||||
<input type="text" name="company" id="company" class="form-control" required
|
||||
value="<?php echo isset($data['model']) ? $data['model']->company : ''?>">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<input type="password" name="password" id="password" class="form-control" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="role">Client</label>
|
||||
<select name="role" class="form-control">
|
||||
<option value="client" <?php echo $data['model']['role'] == 'client' ? 'selected' : ''?>>Client</option>
|
||||
<option value="admin" <?php echo $data['model']['role'] == 'admin' ? 'selected' : ''?>>Admin</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="status">Status</label>
|
||||
<select name="status" class="form-control">
|
||||
<option value="active" <?php echo $data['model']['status'] == 'active' ? 'selected' : ''?>>Active</option>
|
||||
<option value="inactive" <?php echo $data['model']['status'] == 'inactive' ? 'selected' : ''?>>Inactive</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
@@ -0,0 +1,83 @@
|
||||
<style>
|
||||
.dark-header thead th {
|
||||
background-color: #343a40; /* Dark gray background */
|
||||
color: white; /* White text color */
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="container">
|
||||
<h2 class="text-left">Users <a class="btn btn-primary" href="/admin/users/add">Add</a></h2>
|
||||
<!-- Table Responsive Wrapper -->
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover dark-header">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Email</th>
|
||||
<th>Company</th>
|
||||
<th>Role</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($data['data'] as $key => $value) {
|
||||
echo ' <tr>';
|
||||
echo ' <td>' . $value->id . ' <br/><a class=" text-info" href="/admin/users/edit/' . $value->id . '">edit</a> <a class="text-danger" href="/admin/users/delete/' . $value->id . '">delete</a></td>';
|
||||
echo ' <td>' . $value->email . ' </td>';
|
||||
echo ' <td>' . $value->company . ' </td>';
|
||||
echo ' <td>' . $value->role . ' </td>';
|
||||
echo ' <td>' . $value->status . ' </td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
// Retrieve parameters
|
||||
$total = $data['total'];
|
||||
$currentPage = $data['page'];
|
||||
$perPage = 10;
|
||||
|
||||
// Calculate the number of pages
|
||||
$totalPages = ceil($total / $perPage);
|
||||
|
||||
// Define a range of pages to show at any given time
|
||||
$range = 2; // This can be adjusted as needed
|
||||
|
||||
$startPage = ($currentPage - $range) > 0 ? ($currentPage - $range) : 1;
|
||||
$endPage = ($currentPage + $range) < $totalPages ? ($currentPage + $range) : $totalPages;
|
||||
|
||||
?>
|
||||
<!-- Pagination -->
|
||||
<nav aria-label="Page navigation">
|
||||
<ul class="pagination">
|
||||
<li class="ml-2">
|
||||
<a href="?page=1" aria-label="Previous">
|
||||
<span aria-hidden="true">««</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="ml-2">
|
||||
<a href="?page=<?= ($currentPage - 1) > 0 ? $currentPage - 1 : 1 ?>" aria-label="Previous">
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
<?php
|
||||
for ($i = $startPage; $i <= $endPage; $i++) {
|
||||
echo '<li class="ml-2' . ($currentPage == $i ? ' active' : '') . '"><a href="?page=' . $i . '">' . $i . '</a></li>';
|
||||
}
|
||||
?>
|
||||
<li class="ml-2">
|
||||
<a href="?page=<?= ($currentPage + 1) < $totalPages ? $currentPage + 1 : $totalPages ?>" aria-label="Next">
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="ml-2">
|
||||
<a href="?page=<?= $totalPages ?>" aria-label="Next">
|
||||
<span aria-hidden="true">»»</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
use Rakit\Validation\Validator;
|
||||
|
||||
/*Powered By: Manaknightdigital Inc. https://manaknightdigital.com/ Year: 2021*/
|
||||
/**
|
||||
* ValidationService
|
||||
* @copyright 2021 Manaknightdigital Inc.
|
||||
* @link https://manaknightdigital.com
|
||||
* @license Proprietary Software licensing
|
||||
* @author Ryan Wong
|
||||
*
|
||||
*/
|
||||
class ValidationService
|
||||
{
|
||||
protected $_validator;
|
||||
protected $_rules = [];
|
||||
protected $_errors = [];
|
||||
|
||||
public function __construct ()
|
||||
{
|
||||
//https://github.com/rakit/validation
|
||||
$this->_validator = new Validator();
|
||||
}
|
||||
|
||||
public function save_rules($rules)
|
||||
{
|
||||
$this->_rules = $rules;
|
||||
}
|
||||
|
||||
public function get_rules()
|
||||
{
|
||||
return $this->_rules;
|
||||
}
|
||||
|
||||
public function validate ($data)
|
||||
{
|
||||
$rules = $this->make_rules($this->_rules);
|
||||
$validation = $this->_validator->make($data, $rules['array_rules']);
|
||||
$validation->setAliases($rules['array_alias']);
|
||||
|
||||
// $validation->setMessages([
|
||||
// 'required' => ':attribute harus diisi',
|
||||
// 'email' => ':email tidak valid',
|
||||
// ]);
|
||||
$validation->validate();
|
||||
|
||||
if ($validation->fails())
|
||||
{
|
||||
// handling errors
|
||||
$this->_errors = $validation->errors();
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public function get_errors ()
|
||||
{
|
||||
return $this->_errors->toArray();
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected function make_rules()
|
||||
{
|
||||
$array_rules = [];
|
||||
$array_alias = [];
|
||||
foreach($this->_rules as $role_key => $role_value)
|
||||
{
|
||||
$array_rules[$role_value[0]] = $role_value[2];
|
||||
$array_alias[$role_value[0]] = $role_value[1];
|
||||
}
|
||||
|
||||
$response['array_alias'] = $array_alias;
|
||||
$response['array_rules'] = $array_rules;
|
||||
return $response;
|
||||
}
|
||||
|
||||
// 'name' => 'required',
|
||||
// 'email' => 'required|email',
|
||||
// 'password' => 'required|min:6',
|
||||
// 'confirm_password' => 'required|same:password',
|
||||
// 'avatar' => 'required|uploaded_file:0,500K,png,jpeg',
|
||||
// 'skills' => 'array',
|
||||
// 'skills.*.id' => 'required|numeric',
|
||||
// 'skills.*.percentage' => 'required|numeric'
|
||||
}
|
||||
Reference in New Issue
Block a user