Compare commits

...

11 Commits

9 changed files with 315 additions and 102 deletions

View File

@ -7,7 +7,6 @@ $(document).ready(function() {
fetchData()
.then(function (response) {
responseData = response.data;
// console.log(responseData);
const completedOrdersCount = countCompletedOrders(responseData);
const toPayOrdersCount = countToPayOrders(responseData);
const toShipOrdersCount = countToShipOrders(responseData);
@ -339,7 +338,6 @@ $(document).ready(function() {
}
}
function initializeSalesChart(cod_daily_totals, obpay_daily_totals, cod_monthly_totals, obpay_monthly_totals, months, cod_yearly_totals, obpay_yearly_totals, years) {
var acquisition = document.getElementById("salesChart");
if (acquisition !== null) {
@ -486,6 +484,7 @@ $(document).ready(function() {
}
};
var ctx = document.getElementById("salesChart").getContext("2d");
var lineAcq = new Chart(ctx, configAcq);
document.getElementById("acqLegend").innerHTML = lineAcq.generateLegend();
@ -493,8 +492,8 @@ $(document).ready(function() {
var items = document.querySelectorAll(
"#user-acquisition .nav-tabs .nav-item"
);
items.forEach(function (item, index) {
item.addEventListener("click", function() {
items.forEach(function (item, index) {
item.addEventListener("click", function() {
// Determine which tab was clicked
var selectedTab = this.textContent.trim();
@ -587,4 +586,196 @@ $(document).ready(function() {
event.preventDefault();
downloadCSV('orders_data.csv');
});
let customer_data;
let vendor_data;
function getDataAndInitializeChart () {
userData()
.then(function (users){
vendorData()
.then(function (venues){
customer_data = users.data;
vendor_data = venues.data;
const {customer_monthly_total, months} = countCustomerReg(customer_data)
const { vendor_monthly_total} = countVendorReg(vendor_data);
initializeActivityChart(customer_monthly_total, vendor_monthly_total, months);
})
})
}
getDataAndInitializeChart();
function userData(){
return axios.get('https://api.obanana.shop/api/v1/customers');
}
function vendorData(){
return axios.get('https://api.obanana.shop/api/v1/vendors');
}
function countCustomerReg(customers) {
const currentDate = new Date();
const monthLabels = [];
const customer_monthly_total = Array(12).fill(0);
for (let i = 11; i >= 0; i--) {
const month = new Date(currentDate.getFullYear(), currentDate.getMonth() - i, 1);
monthLabels.push(month);
}
console.log(monthLabels);
customers.forEach(customer => {
const regDate = new Date(customer.createdAt);
regDate.setHours(0, 0, 0, 0);
monthLabels.forEach((month, index) => {
let total_count = 0;
if (regDate.getFullYear() === month.getFullYear() && regDate.getMonth() === month.getMonth()) {
total_count ++;
customer_monthly_total[11 - index] += total_count;
}
});
});
console.log(customer_monthly_total)
customer_monthly_total.reverse();
return {
customer_monthly_total,
months: monthLabels.map(month => `${month.toLocaleString('default', { month: 'short' })} ${month.getFullYear()}`) // Return month labels for use in charts or displays
};
}
function countVendorReg(vendors) {
const currentDate = new Date();
const monthLabels = [];
const vendor_monthly_total = Array(12).fill(0);
for (let i = 11; i >= 0; i--) {
const month = new Date(currentDate.getFullYear(), currentDate.getMonth() - i, 1);
monthLabels.push(month);
}
console.log(monthLabels);
vendors.forEach(vendor => {
const regDate = new Date(vendor.createdAt);
regDate.setHours(0, 0, 0, 0);
monthLabels.forEach((month, index) => {
let total_count = 0;
if (regDate.getFullYear() === month.getFullYear() && regDate.getMonth() === month.getMonth()) {
total_count ++;
vendor_monthly_total[11 - index] += total_count;
}
});
});
console.log(vendor_monthly_total)
vendor_monthly_total.reverse();
return {
vendor_monthly_total,
};
}
function initializeActivityChart (customer_monthly_total, vendor_monthly_total, months){
var activity = document.getElementById("registry");
if (activity !== null) {
var activityData = [
{
first: customer_monthly_total,
second: vendor_monthly_total
}
];
var config = {
// The type of chart we want to create
type: "line",
// The data for our dataset
data: {
labels: months,
datasets: [
{
label: "Customer",
backgroundColor: "rgba(255, 165, 0, .3)",
borderColor: "rgba(255, 165, 0, .7)",
data: activityData[0].first,
lineTension: 0.3,
pointBackgroundColor: "rgba(255, 165, 0, 0)",
pointHoverBackgroundColor: "rgba(255, 165, 0, 1)",
pointHoverRadius: 3,
pointHitRadius: 30,
pointBorderWidth: 2,
pointStyle: "rectRounded"
},
{
label: "Vendors",
backgroundColor: "rgba(135, 206, 235, .3)",
borderColor: "rgba(135, 206, 235, .7)",
data: activityData[0].second,
lineTension: 0.3,
pointBackgroundColor: "rgba(135, 206, 235, 0)",
pointHoverBackgroundColor: "rgba(135, 206, 235, 1)",
pointHoverRadius: 3,
pointHitRadius: 30,
pointBorderWidth: 2,
pointStyle: "rectRounded"
}
]
},
// Configuration options go here
options: {
responsive: true,
maintainAspectRatio: false,
legend: {
display: false
},
scales: {
xAxes: [
{
gridLines: {
display: false
}
}
],
yAxes: [
{
gridLines: {
display: true,
color: "#eee",
zeroLineColor: "#eee"
},
ticks: {
beginAtZero: true,
stepSize: 10,
max: 30
}
}
]
},
tooltips: {
mode: "index",
titleFontColor: "#888",
bodyFontColor: "#555",
titleFontSize: 12,
bodyFontSize: 15,
backgroundColor: "rgba(256,256,256,0.95)",
displayColors: true,
xPadding: 20,
yPadding: 10,
borderColor: "rgba(220, 220, 220, 0.9)",
borderWidth: 2,
caretSize: 10,
caretPadding: 15
}
}
};
var ctx = document.getElementById("registry").getContext("2d");
var actLine = new Chart(ctx, config);
document.getElementById("actLegend").innerHTML = actLine.generateLegend();
}
}
});

View File

@ -108,12 +108,13 @@ $allSignups = array_merge($all_customers, $all_vendors);
<ul class="dropdown-menu dropdown-menu-right ec-dropdown-menu">
<!-- User image -->
<li class="dropdown-header">
<img loading="lazy" src="assets/img/user/user.png" class="img-circle" alt="User Image" />
<div class="d-inline-block">
John Deo <small class="pt-1"><?php echo $_SESSION['email']?></small>
<div class="d-flex align-items-center">
<img loading="lazy" src="assets/img/user/user.png" class="img-circle" alt="User Image" />
<small class="pt-1"><?php echo $_SESSION['email']?></small>
</div>
</li>
<li>
<!-- Commented out for future use or functionality -->
<!-- <li>
<a href="#">
<i class="mdi mdi-account"></i> My Profile
</a>
@ -125,7 +126,7 @@ $allSignups = array_merge($all_customers, $all_vendors);
</li>
<li>
<a href="#"> <i class="mdi mdi-diamond-stone"></i> Projects </a>
</li>
</li> -->
<li class="right-sidebar-in">
<a href="javascript:0"> <i class="mdi mdi-settings-outline"></i> Setting </a>
</li>
@ -134,7 +135,8 @@ $allSignups = array_merge($all_customers, $all_vendors);
</li>
</ul>
</li>
<li class="dropdown notifications-menu custom-dropdown">
<!-- Commented out for future usage or functionality -->
<!-- <li class="dropdown notifications-menu custom-dropdown">
<button class="dropdown-toggle notify-toggler custom-dropdown-toggler">
<i class="mdi mdi-bell-outline"></i>
</button>
@ -697,7 +699,7 @@ $allSignups = array_merge($all_customers, $all_vendors);
<a class="text-center" href="#"> View All </a>
</li>
</ul>
</li>
</li> -->
<li class="right-sidebar-in right-sidebar-2-menu">
<i class="mdi mdi-settings-outline mdi-spin"></i>
</li>
@ -825,7 +827,7 @@ $allSignups = array_merge($all_customers, $all_vendors);
<div class="col-xl-4 col-md-12 p-b-15">
<!-- Doughnut Chart -->
<div class="card card-default">
<div class="card card-default" style="height: 100%">
<div class="card-header justify-content-center">
<h2>Orders Overview</h2>
</div>
@ -841,7 +843,7 @@ $allSignups = array_merge($all_customers, $all_vendors);
<li class="mb-2"><i class="mdi mdi-checkbox-blank-circle-outline mr-2"
style="color: #4c84ff"></i>Order Completed</li>
<li class="mb-2"><i class="mdi mdi-checkbox-blank-circle-outline mr-2"
style="color: #80e1c1 "></i>Order To Pay</li>
style="color: #80e1c1"></i>Order To Pay</li>
<li><i class="mdi mdi-checkbox-blank-circle-outline mr-2"
style="color: #ff7b7b "></i>Order Returned</li>
</ul>
@ -861,32 +863,35 @@ $allSignups = array_merge($all_customers, $all_vendors);
</div>
<div class="row">
<div class="col-xl-8 col-md-12 p-b-15">
<div class="col-xl-12 col-md-12 p-b-15">
<!-- User activity statistics -->
<div class="card card-default" id="user-activity">
<div class="no-gutters">
<div>
<div class="card-header justify-content-between">
<h2>User Activity</h2>
<div class="date-range-report ">
<h2>Monthly Users Registry</h2>
<!-- <div class="date-range-report ">
<span></span>
</div>
</div> -->
</div>
<div class="card-body">
<div class="tab-content" id="userActivityContent">
<div class="tab-pane fade show active" id="user" role="tabpanel">
<canvas id="activity" class="chartjs"></canvas>
<canvas id="registry" class="chartjs"></canvas>
</div>
<div>
<div id="actLegend" class="customLegend mb-2"></div>
</div>
</div>
</div>
<div class="card-footer d-flex flex-wrap bg-white border-top">
<!-- <div class="card-footer d-flex flex-wrap bg-white border-top">
<a href="#" class="text-uppercase py-3">In-Detail Overview</a>
</div>
</div> -->
</div>
</div>
</div>
</div>
<div class="col-xl-4 col-md-12 p-b-15">
<!-- <div class="col-xl-4 col-md-12 p-b-15">
<div class="card card-default">
<div class="card-header flex-column align-items-start">
<h2>Current Users</h2>
@ -898,11 +903,11 @@ $allSignups = array_merge($all_customers, $all_vendors);
<a href="#" class="text-uppercase py-3">In-Detail Overview</a>
</div>
</div>
</div>
</div> -->
</div>
<div class="row">
<div class="col-xl-8 col-12 p-b-15">
<div class="col-xl-12 col-md-12 p-b-15">
<!-- World Chart -->
<div class="card card-default" id="analytics-country">
<div class="card-header justify-content-between">
@ -933,8 +938,8 @@ $allSignups = array_merge($all_customers, $all_vendors);
</div>
</div>
</div>
<div class="col-xl-4 col-12 p-b-15">
<!-- Top Sell Table -->
<!-- Top Sell Table -->
<!-- <div class="col-xl-4 col-12 p-b-15">
<div class="card card-default Sold-card-table">
<div class="card-header justify-content-between">
<h2>Sold by Items</h2>
@ -1034,7 +1039,7 @@ $allSignups = array_merge($all_customers, $all_vendors);
<a href="#" class="text-uppercase py-3">View Report</a>
</div>
</div>
</div>
</div> -->
</div>
<div class="row">
@ -1073,21 +1078,40 @@ $allSignups = array_merge($all_customers, $all_vendors);
$orderStatus = strtoupper($val['status']);
$returnStatus = strtoupper($val['return_order']['status']);
$style = '';
$textColor = '';
$backgroundColor = '';
$statusClass = '';
if ($orderStatus === 'UNPAID' || $orderStatus === 'RETURNED') {
$statusClass = '#cb3747';
} elseif ($orderStatus === 'TO PAY') {
$statusClass = '#50d7ab';
} elseif ($orderStatus === 'TO SHIP') {
$statusClass = '#9586cd';
}
elseif ($orderStatus === 'TO RECEIVE') {
$statusClass = '#ffc319';
} elseif ($orderStatus === 'COMPLETED') {
$statusClass = '#88aaf3';
switch ($orderStatus) {
case 'TO SHIP':
$backgroundColor = '#8061ef';
$textColor = 'white';
break;
case 'TO PAY':
$backgroundColor = '#1E6E58';
$textColor = 'white';
break;
case 'TO RECEIVE':
$backgroundColor = '#FFD700';
$textColor = 'black';
break;
case 'COMPLETED':
$backgroundColor = '#4c84ff';
$textColor = 'white';
break;
case 'RETURNED':
$backgroundColor = '#ff7b7b';
$textColor = 'white';
break;
default:
$backgroundColor = '#464646';
$textColor = 'white';
}
$style = "display: flex; height: 15px; font-weight: bold;
width: 90px; font-size: 10px !important; padding: 10px;
justify-content: center; align-items: center;
background-color: $backgroundColor; border-radius: 30px; color: $textColor;";
if ($formattedOrderDate == $currentDate) {
$ordersDisplayed = true;
@ -1100,7 +1124,7 @@ $allSignups = array_merge($all_customers, $all_vendors);
<td><?php echo $vendorName ?></td>
<td> <?php echo number_format($totalAmount, 2, '.', ',') ?></td>
<td><?php echo $displayDate ?></td>
<td><span style="color: <?php echo $statusClass; ?>"><?php echo $orderStatus ?></span></td>
<td><span style="<?php echo $style; ?>"><?php echo $orderStatus ?></span></td>
<td><?php echo $returnStatus?: "NONE" ?></td>
</tr>
@ -1175,7 +1199,8 @@ $allSignups = array_merge($all_customers, $all_vendors);
<div class="media">
<div class="media-image mr-3 rounded-circle">
<img loading="lazy"
class="profile-img rounded-circle w-45"
class="profile-img rounded-circle"
style="width: 50px; height: 50px; border-style: solid; border-color: #FFAA00;"
src="<?php echo $imageUrl !== null ? $imageUrl : 'assets/img/user/u1.jpg'; ?>"
alt="customer image">
</div>
@ -1254,7 +1279,8 @@ $allSignups = array_merge($all_customers, $all_vendors);
<div class="media">
<div class="media-image mr-3 rounded-circle">
<img loading="lazy"
class="profile-img rounded-circle w-45"
class="profile-img rounded-circle"
style="width: 50px; height: 50px; border-style: solid; border-color: #FFAA00;"
src="<?php echo $imageUrl !== null ? $imageUrl : 'assets/img/user/u1.jpg'; ?>"
alt="customer image">
</div>

View File

@ -115,7 +115,7 @@ $orders = getAllOrder();
<td><?php echo $order['return_order']['status'] ?></td>
<!-- <td><span class="mb-2 mr-2 badge badge-secondary">Cancel</span></td> -->
<td>
<a class="btn btn-lg btn-primary" data-bs-toggle="modal" data-bs-target="#modal-order-<?php echo $order['_id']; ?>">Details</a>
<a class="btn btn-md btn-primary" data-bs-toggle="modal" data-bs-target="#modal-order-<?php echo $order['_id']; ?>">Details</a>
</td>
</tr>
<div class="modal fade" id="modal-order-<?php echo $order['_id']; ?>" tabindex="-1" role="dialog" aria-hidden="true">

View File

@ -240,13 +240,9 @@ if (isset($customer_data[0]["address"]) && is_array($customer_data[0]["address"]
<div class="sName">
<h5>Name:
<span id="selectedFName"><?php echo strtoupper($customer['address'][0]['first_name']); ?></span>
<span id="selectedLName"><?php echo strtoupper($customer['address'][0]['last_name']); ?></span>
</h5>
<!-- <h5>Name:
<span id="selectedFName"><?php echo $customer['address'][0]['first_name']; ?></span>
<span id="selectedLName"> <?php echo $customer['address'][0]['last_name']; ?></span>
</h5> -->
<span id="selectedLName"><?php echo $customer['address'][0]['last_name']; ?></span>
</h5>
</div>
<div class="sContact">
<h5>Contact #:

View File

@ -20,7 +20,6 @@ if ($_SESSION["userId"] <> "") {
}
?>
<?php ?>
<header class="ec-header">
<style>
/*---- Newsletter Page On load Style ----*/
@ -186,13 +185,14 @@ if ($_SESSION["userId"] <> "") {
<div class="col header-top-right d-none d-lg-block">
<div class="header-top-lan-curr d-flex justify-content-end">
<!-- Download Modal -->
<div class="header-top-download">
<div class="header-top-download">
<!-- 04-01-2024 Stacy modified this block of code -->
<!-- 05-13-2024 John Louie modified this block of code -->
<!-- <button class="text-upper" onclick="displayPopup()">Download App <i aria-hidden="true"></i></button> -->
<?php if(isset($customer_data)) { ?>
<button style="cursor:default;">Hi <span class="text-upper"><?php echo $customer_data[0]['first_name'] ?>!</span></button>
<?php if(!empty($customer_data)) { ?>
<button style="cursor:default;">Hi <span class="text-upper"><?php echo $customer_data[0]['first_name'] ?> !</span></button>
<?php } else if (isset($vendorData)) {?>
<button style="cursor:default;">Hi <span class="text-upper"><?php echo ($vendorData['user_login']) ?>!</span></button>
<button style="cursor:default;">Hi <span class="text-upper"><?php echo ($vendorData['user_login']) ?> !</span></button>
<?php } else {?>
<button class="text-upper"><a href="login.php">Login</a></button>
<?php } ?>

View File

@ -223,11 +223,11 @@ $array = json_decode($result, true);
if ($array['status'] === 'TO SHIP' || $array['status'] === 'To Ship') {
echo '<div class="col-md-6">
<label for="inputEmail4" class="form-label">Tracking Number</label>
<input type="text" name="tracking_number" class="form-control slug-title" value="' . $array['tracking_number'] . '" >
<input type="text" name="tracking_number" class="form-control slug-title" value="' . $array['tracking_number'] . '" required>
</div>';
echo '<div class="col-md-6">
<label for="inputEmail4" class="form-label">COURIER NAME</label>
<input type="text" name="courier_name" class="form-control slug-title" value="' . $array['courier_name'] . '" >
<input type="text" name="courier_name" class="form-control slug-title" value="' . $array['courier_name'] . '" required>
</div>';
} elseif ($array['status'] === 'TO RECEIVE' || $array['status'] === 'To Receive') {
echo '<div class="col-md-6">

View File

@ -297,45 +297,45 @@ if ($_SESSION["isCustomer"] == true) {
</td>
<td style="max-width:300px;"><span class="text-truncate"><?php echo $item['product']['name']; ?></span></td>
<?php
$status = $orderItems['status'];
$style = '';
$textColor = '';
$borderColor = '';
$status = $orderItems['status'];
$style = '';
$textColor = '';
$backgroundColor = '';
switch ($status) {
case 'TO SHIP':
$borderColor = 'mint';
$textColor = 'min'; // Change this to match the text color you prefer
break;
case 'TO PAY':
$borderColor = '#E0EA00';
$textColor = '#E0EA00';
break;
case 'TO RECEIVE':
$borderColor = '#20FF5A';
$textColor = '#20FF5A';
break;
case 'COMPLETED':
$borderColor = '#2098FF';
$textColor = '#2098FF'; // Change this to match the text color you prefer
break;
case 'TO REFUND':
$borderColor = '#FF5320';
$textColor = '#FF5320';
break;
default:
// Default styles if the status doesn't match any case
$borderColor = '#464646';
$textColor = '#464646';
}
switch (strtoupper($status)) {
case 'TO SHIP':
$backgroundColor = '#8061ef';
$textColor = 'white';
break;
case 'TO PAY':
$backgroundColor = '#1E6E58';
$textColor = 'white';
break;
case 'TO RECEIVE':
$backgroundColor = '#FFD700';
$textColor = 'black';
break;
case 'COMPLETED':
$backgroundColor = '#4c84ff';
$textColor = 'white';
break;
case 'RETURNED':
$backgroundColor = '#ff7b7b';
$textColor = 'white';
break;
default:
$backgroundColor = '#464646';
$textColor = 'white';
}
// Generating style attribute based on the selected colors
$style = "display: flex; height: 15px;font-weight:400; width:90px; margin-top:10px; font-size:10px !important; justify-content:center; align-items:center; padding: 10px; border: 3px solid $borderColor; border-radius: 30px; color: $textColor;";
?>
<td><span style="<?php echo $style; ?>"> <p><?php echo $status; ?></p></span></td>
// Generating style attribute based on the selected colors
$style = "display: flex; height: 15px; font-weight: bold;
width: 90px; font-size: 10px !important;
justify-content: center; align-items: center; padding: 10px;
background-color: $backgroundColor; border-radius: 30px; color: $textColor;";
?>
<td><span style="<?php echo $style; ?>"> <p><?php echo $status; ?></p></span></td>
<td><span style="text-transform:capitalize"><?php echo $orderItems['shipping_address']['shipping_first_name']; ?></span></td>
<td ><span><?php echo $orderItems['total_amount']; ?></span></td>
<td style="display:flex; justify-content:center; margin-top:-4px;">

View File

@ -315,7 +315,7 @@ $vendorPayoutData = json_decode($response, true);
<div class="ec-vendor-card-body">
<div class="ec-vendor-card-table">
<table class="table ec-table"
id="order-table" style="overflow-x: auto;"
id="payout-table" style="overflow-x: auto;"
data-role="table"
data-pagination="true"
data-searching="true"
@ -339,7 +339,7 @@ $vendorPayoutData = json_decode($response, true);
</thead>
<tbody class='table-group-divider'>
<?php
foreach ($vendorPayoutData as $x => $val) {
foreach (array_reverse($vendorPayoutData) as $x => $val) {
$vendorIdCheck = $val['vendor_details'][0]['vendor_id'];
$status = ucfirst(strtolower($val['status']));
$payoutDate = date("F d, Y", strtotime($val['createdAt']));

View File

@ -584,6 +584,7 @@ if ($_SESSION["isCustomer"] == true) {
</div>
<div id="editDelete" style="display:flex; float:right; width:20px; margin-top:20px; margin-right:-20px; margin-top:30px;">
<!-- <i class="fa-solid fa-pen" style="font-size:15px; float:right;"></i></a> -->
<i onclick="deleteBank('<?php echo $vendor['_id']; ?>', <?php echo $bank_index; ?>, true)"
class="fa-regular fa-trash-can" style="font-size:15px; float:right;"></i>
</div>
@ -1473,7 +1474,6 @@ if ($_SESSION["isCustomer"] == true) {
<!-- Script added by Louie for Dynamic Edit Modal Box. May 08, 2024 -->
<script>
const vendorid = `<?php echo $_SESSION["LoggedInVendorId"]; ?>`;
$('#editBankModal').on('shown.bs.modal', function (event) {
var jsonString = $(event.relatedTarget).data('value');
var jsonObject = JSON.parse(jsonString);
@ -1488,9 +1488,8 @@ if ($_SESSION["isCustomer"] == true) {
$('#editBankBtn').on('click', function() {
console.log(vendorid)
// function updateAddress(){
// Retrieve existing addresses from the API
const vendorid = `<?php echo $_SESSION["LoggedInVendorId"]; ?>`;
console.log("The vendorid is", vendorid)
// fetch('https://api.obanana.shop/api/v1/customers/65482e8d209ff8d348bd30fd')
fetch('https://<?php echo $_SESSION["data_endpoint"]; ?>/api/v1/vendors/' + vendorid)
.then(response => response.json())
@ -1551,15 +1550,16 @@ if ($_SESSION["isCustomer"] == true) {
<!-- Script added by Louie for Deleting Bank Information. May 08, 2024 -->
<script>
const vendorid = `<?php echo $_SESSION["LoggedInVendorId"]; ?>`;
function deleteBank(vendorid, bankIndex) {
fetch('https://<?php echo $_SESSION["data_endpoint"]; ?>/api/v1/vendors/' + vendorid)
const vendorId = `<?php echo $_SESSION["LoggedInVendorId"]; ?>`;
function deleteBank(vendorId, bankIndex) {
fetch('https://<?php echo $_SESSION["data_endpoint"]; ?>/api/v1/vendors/' + vendorId)
.then(response => response.json())
.then(data => {
let existingBank = data.bank_acount_details || [];
if (bankIndex >= 0 && bankIndex < existingBank.length) {
existingBank.splice(bankIndex, 1);
fetch('https://<?php echo $_SESSION["data_endpoint"]; ?>/api/v1/vendors/' + vendorid, {
fetch('https://<?php echo $_SESSION["data_endpoint"]; ?>/api/v1/vendors/' + vendorId, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json'