3749 lines
144 KiB
PHP
3749 lines
144 KiB
PHP
<?php
|
||
//require_once ("/assets/dbCon.php");
|
||
require_once getenv("DOCUMENT_ROOT")."/assets/dbCon.php";
|
||
|
||
//Initialize date
|
||
$dateArr = array();
|
||
for($i=0;$i < 7;$i++) {
|
||
$dateValue = strtotime(date("Ymd")."+".$i." day");
|
||
//$dateValue = strtotime(date("20240425")."+".$i." day");
|
||
$dateArr[] = array(
|
||
"val" => date("Ymd", $dateValue),
|
||
"text" => date("Y-m-d", $dateValue)
|
||
);
|
||
}
|
||
|
||
//Load driver list
|
||
|
||
if ($_SESSION['ss_LEVEL'] == 9) {
|
||
$qrySTR = " AND (m_uid = '".$_SESSION['ss_UID']."') ";
|
||
} else {
|
||
$qrySTR = "";
|
||
}
|
||
|
||
$drivers = array();
|
||
$sqDriver = qry("SELECT
|
||
*
|
||
FROM
|
||
tbl_member
|
||
WHERE
|
||
m_level = 9 AND m_status = 'A' ".$qrySTR."
|
||
ORDER BY
|
||
m_initial ASC ");
|
||
while($rstDriver = fetch_array($sqDriver)){
|
||
$drivers[] = array(
|
||
"id" => $rstDriver['m_uid'],
|
||
"name" => $rstDriver['m_initial'],
|
||
"regionuid" => $rstDriver['m_regionuid'],
|
||
"region" => $rstDriver['m_region'],
|
||
"currentdriverid" => $rstDriver['m_currentdriver'],
|
||
"currentdriver" => $rstDriver['m_currentdriverinitial']
|
||
);
|
||
}
|
||
|
||
//Load quantity setting from cookie
|
||
$cookieValueArr = explode("_",$_COOKIE['qtySetting']);
|
||
$searchQty = array();
|
||
if(count($cookieValueArr) > 2){
|
||
for($k=0;$k < count($cookieValueArr);$k++) {
|
||
$cookieValue = explode(":",$cookieValueArr[$k]);
|
||
$searchQty[] = array(
|
||
"min" => $cookieValue[0],
|
||
"max" => $cookieValue[1]
|
||
);
|
||
}
|
||
}else{
|
||
$min = 0;
|
||
$max = 100;
|
||
for($k=0;$k < 5;$k++) {
|
||
$searchQty[] = array(
|
||
"min" => $min,
|
||
"max" => $max
|
||
);
|
||
$min = $max + 1;
|
||
$max = $min + 49;
|
||
}
|
||
}
|
||
|
||
|
||
if (($_SESSION['ss_DRUID'] != "")) {
|
||
|
||
if ($_POST['orderdate'] != "") $orderdateSTR = $_POST['orderdate'];
|
||
else $orderdateSTR = date('Y-m-d');
|
||
$orderdateSTR = str_replace('-', "", $orderdateSTR);
|
||
|
||
$sqQuantity = qry("SELECT
|
||
SUM(d_quantity) as pickupquantity,
|
||
COUNT(*) as pickupcnt
|
||
FROM
|
||
tbl_daily
|
||
WHERE
|
||
d_status = 'F' AND d_orderdate = '".$orderdateSTR."'
|
||
AND d_druid = '".$_SESSION['ss_DRUID']."' ");
|
||
|
||
$rstQuantity = fetch_array($sqQuantity);
|
||
|
||
$curPickupQty = (int)$rstQuantity['pickupquantity'];
|
||
$curVisitCnt = (int)$rstQuantity['pickupcnt'];
|
||
|
||
$sqTarget = qry("SELECT
|
||
dut_target_qty,
|
||
dut_target_visit_cnt
|
||
FROM
|
||
tbl_daily_uco_target
|
||
WHERE
|
||
dut_date = '".$orderdateSTR."'
|
||
AND dut_druid = '".$_SESSION['ss_DRUID']."' ");
|
||
|
||
$rstTarget = fetch_array($sqTarget);
|
||
|
||
$curTargetQty = $rstTarget ? (int)$rstTarget['dut_target_qty'] : 0;
|
||
$curTargetVisitCnt = $rstTarget ? (int)$rstTarget['dut_target_visit_cnt'] : 0;
|
||
|
||
}
|
||
|
||
$strstr = "[".$_SESSION['ss_LEVEL']."][".$_SESSION['ss_UID']."][".$orderdateSTR."][".$rstQuantity['pickupquantity']."]";
|
||
|
||
?>
|
||
|
||
|
||
<link href="/assets/css/mapMAPCSS.css?v=<?=filemtime($_SERVER['DOCUMENT_ROOT'].'/assets/css/mapMAPCSS.css')?>" rel="stylesheet" type="text/css" >
|
||
<!-- New2 -->
|
||
<link href="/assets/css/leftside-modal.css?v=<?=filemtime($_SERVER['DOCUMENT_ROOT'].'/assets/css/leftside-modal.css')?>" rel="stylesheet">
|
||
<!-- New2 -->
|
||
|
||
<!--script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script-->
|
||
<script src="https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js?features=default"></script>
|
||
<script src="https://use.fontawesome.com/releases/v6.2.0/js/all.js"></script>
|
||
|
||
<script>(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})
|
||
({key: "AIzaSyDg9u03mGrBhyOisp7VGc27CTPI9QXp8sY", v: "weekly"});</script>
|
||
<script src="https://unpkg.com/@googlemaps/markerclusterer/dist/index.min.js"></script>
|
||
|
||
<!-- New -->
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.0/jquery.validate.js"></script>
|
||
|
||
<div class="loading-overlay">Loading...</div>
|
||
|
||
<div id="popupMessage"></div>
|
||
|
||
<main class="pg-location">
|
||
<div class="width-20 floating-filter">
|
||
<form action="#" class="" >
|
||
<table id="sidebar-map" class="td-search">
|
||
|
||
<? //if (($_SESSION['ss_LEVEL'] == 9) && ($_SESSION['ss_UID'] != "")) { ?>
|
||
<? if (($_SESSION['ss_UID'] != "")) { ?>
|
||
<tr>
|
||
<td><button id="pickupQtyBtn" class="btn-blue-map btn-gauge" type="button"><span id="pickupquantity"><?=$curPickupQty?></span> / <span id="targetquantity"><?=$curTargetQty?></span>L<span class="gauge-sub"><span id="pickuppercent" class="gauge-pct"></span><span id="pickupvisit"><?=$curVisitCnt?></span> / <span id="targetvisit"><?=$curTargetVisitCnt?></span> visits</span></button></td>
|
||
</tr>
|
||
<? } ?>
|
||
|
||
<!-- <tr>
|
||
<td> <label for="orderBy">Order Date</label></td>
|
||
</tr> -->
|
||
<tr>
|
||
<td>
|
||
<input type="date" name="orderdate" id="orderdate" value="<?php echo date('Y-m-d');?>">
|
||
|
||
<!--select name="orderdate" id="orderdate" class="custom-select-map-orderby">
|
||
<?php
|
||
//foreach($dateArr as $dateDetail) {
|
||
// echo '<option value="'.$dateDetail['val'].'">'.$dateDetail['text'].'</option>';
|
||
//}
|
||
?>
|
||
</select-->
|
||
</td>
|
||
</tr>
|
||
<!--tr>
|
||
<td><label for="Type">Option</label></td>
|
||
</tr-->
|
||
<tr>
|
||
<td>
|
||
<label class="container-chk" style="clear:both">Ordered
|
||
<input type="checkbox" checked="checked" class="chk-ordered" disabled>
|
||
<span class="checkmark"></span>
|
||
</label>
|
||
|
||
<table>
|
||
<tr>
|
||
<td style="margin-left:4px;"><span style="width:15px; height:15px;display: block;" class="bg-color-ordered-normal"></span></td><td align=left>Nor </td>
|
||
<td><span style="width:15px; height:15px;display: block;" class="bg-color-ordered-requested"></span></td><td>Req</td>
|
||
<td><span style="width:15px; height:15px;display: block;" class="bg-color-ordered-scheduled"></span></td><td>Sch</td>
|
||
<td><span style="width:15px; height:15px;display: block;" class="bg-color-ordered-finished"></span></td><td>Fin</td>
|
||
</tr>
|
||
</table>
|
||
|
||
</td>
|
||
</tr>
|
||
|
||
<!-- <tr>
|
||
<td ><label for="orderBy">Driver</label></td>
|
||
</tr> -->
|
||
<tr>
|
||
<td>
|
||
<select name="name" id="driver" class="custom-select-map-orderby">
|
||
<?php
|
||
foreach($drivers as $driver) {
|
||
echo '<option value="'.$driver['currentdriverid'].'">'.$driver['name'].'</option>';
|
||
}
|
||
?>
|
||
</select>
|
||
</td>
|
||
</tr>
|
||
<!-- <tr>
|
||
<td><label for="Search">Search</label></td>
|
||
</tr> -->
|
||
<tr>
|
||
<td><input class="input-colour-bar" type="text" id="search-keyword" name="Search" placeholder="Search"></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<table>
|
||
<!-- <tr>
|
||
<td colspan="4"><label for="Quantity">Quantity</label></td>
|
||
</tr> -->
|
||
|
||
<tr>
|
||
<td class="box-chk">
|
||
<label class="container-chk">
|
||
<input type="checkbox" class="qty">
|
||
<span class="checkmark"></span>
|
||
</label>
|
||
</td>
|
||
|
||
<td class="td-col-qty col-line">
|
||
<span class="level-line bg-color-yellow"></span>
|
||
</td>
|
||
|
||
<td class="td-col-qty col-left">
|
||
<input class="input-colour-bar qty-min" type="text" value="0" disabled>
|
||
</td>
|
||
|
||
<td class="td-col-qty col-second">~</td>
|
||
|
||
<td class="td-col-qty col-third">
|
||
<input class="input-colour-bar qty-max" type="text" value="<?php echo $searchQty[0]['max'];?>">
|
||
</td>
|
||
</tr>
|
||
|
||
<tr>
|
||
<td class="box-chk">
|
||
<label class="container-chk">
|
||
<input type="checkbox" class="qty">
|
||
<span class="checkmark"></span>
|
||
</label>
|
||
</td>
|
||
|
||
<td class="td-col-qty col-line">
|
||
<span class="level-line bg-color-orange"></span>
|
||
</td>
|
||
|
||
<td class="td-col-qty col-left">
|
||
<input class="input-colour-bar qty-min" type="text" value="<?php echo $searchQty[1]['min'];?>">
|
||
</td>
|
||
|
||
<td class="td-col-qty col-second">~</td>
|
||
|
||
<td class="td-col-qty col-third">
|
||
<input class="input-colour-bar qty-max" type="text" value="<?php echo $searchQty[1]['max'];?>">
|
||
</td>
|
||
</tr>
|
||
|
||
<tr>
|
||
<td class="box-chk">
|
||
<label class="container-chk">
|
||
<input type="checkbox" class="qty">
|
||
<span class="checkmark"></span>
|
||
</label>
|
||
</td>
|
||
|
||
<td class="td-col-qty col-line">
|
||
<span class="level-line bg-color-green"></span>
|
||
</td>
|
||
|
||
<td class="td-col-qty col-left">
|
||
<input class="input-colour-bar qty-min" type="text" value="<?php echo $searchQty[2]['min'];?>">
|
||
</td>
|
||
|
||
<td class="td-col-qty col-second">~</td>
|
||
|
||
<td class="td-col-qty col-third">
|
||
<input class="input-colour-bar qty-max" type="text" value="<?php echo $searchQty[2]['max'];?>">
|
||
</td>
|
||
</tr>
|
||
|
||
<tr>
|
||
<td class="box-chk">
|
||
<label class="container-chk">
|
||
<input type="checkbox" class="qty">
|
||
<span class="checkmark"></span>
|
||
</label>
|
||
</td>
|
||
|
||
<td class="td-col-qty col-line">
|
||
<span class="level-line bg-color-blue"></span>
|
||
</td>
|
||
|
||
<td class="td-col-qty col-left">
|
||
<input class="input-colour-bar qty-min" type="text" value="<?php echo $searchQty[3]['min'];?>">
|
||
</td>
|
||
|
||
<td class="td-col-qty col-second">~</td>
|
||
|
||
<td class="td-col-qty col-third">
|
||
<input class="input-colour-bar qty-max" type="text" value="<?php echo $searchQty[3]['max'];?>">
|
||
</td>
|
||
</tr>
|
||
|
||
<tr>
|
||
<td class="box-chk">
|
||
<label class="container-chk">
|
||
<input type="checkbox" class="qty">
|
||
<span class="checkmark"></span>
|
||
</label>
|
||
</td>
|
||
|
||
<td class="td-col-qty col-line" >
|
||
<span class="level-line bg-color-black"></span>
|
||
</td>
|
||
|
||
<td class="td-col-qty col-left">
|
||
<input class="input-colour-bar qty-min" type="text" value="<?php echo $searchQty[4]['min'];?>">
|
||
</td>
|
||
|
||
<td class="td-col-qty col-second">~</td>
|
||
|
||
<td class="td-col-qty col-third">
|
||
<input class="input-colour-bar qty-max" type="text" value="MAX" disabled>
|
||
</td>
|
||
</tr>
|
||
|
||
</table>
|
||
|
||
<div>
|
||
<button class="btn-blue-map btn-current" type="button">CURRENT</button>
|
||
<button class="btn-primary-map btn-routeplanner" type="button" data-toggle="modal" data-target="#routePlannerModal">ROUTE PLANNER</button>
|
||
<button class="btn-orange-map btn-route-print hidden" type="button">PRINT</button>
|
||
<button class="btn-red-map btn-init" type="button" onClick="forceRefresh()">INIT</button>
|
||
</div>
|
||
|
||
</form>
|
||
</div>
|
||
|
||
<div id="map"></div>
|
||
</main>
|
||
|
||
<!-- ROUTE PLANNER MODAL -->
|
||
<div class="modal fade" id="routePlannerModal" tabindex="-1" aria-labelledby="routePlannerModalLabel" aria-hidden="true">
|
||
<div class="modal-dialog">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title" id="routePlannerModalLabel">Route Planner</h5>
|
||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||
<span aria-hidden="true">×</span>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<p>Make a list in the order you'd like to visit them. The place at the top will be counted as the Route you visit first.</p>
|
||
<div id="addressList" class="list-group">
|
||
</div>
|
||
<div id="addressInformationList" class="list-group">
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn btn-primary routePlannerApply" data-dismiss="modal">Apply</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- ROUTE PLANNER MODAL END -->
|
||
|
||
<SCRIPT LANGUAGE='JAVASCRIPT'>
|
||
function popup(){
|
||
|
||
var orderdate = jQuery("#map-modal-input .d_orderdate").val();
|
||
var customeruid = jQuery("#map-modal-input .d_customeruid").val();
|
||
var customerno = jQuery("#map-modal-input .d_customerno").val();
|
||
var uid = jQuery("#map-modal-input .d_uid").val();
|
||
var str = '/lib/signature.php?'+'uid=' + uid + '&orderdate=' + orderdate + '&customeruid=' + customeruid + '&customerno=' + customerno;
|
||
|
||
window.open(str, '_blank', 'width=450,height=450,resizable=0,scrollbars=0');
|
||
}
|
||
</SCRIPT>
|
||
|
||
|
||
<!-- INPUT MODAL-->
|
||
<div class="modal fade top map" id="map-modal-input" tabindex="-1" role="dialog">
|
||
<div id="modal-input" class="modal-dialog" role="document">
|
||
<div class="modal-content">
|
||
|
||
<div class="modal-header">
|
||
<h4 class="modal-title">ORDER DETAIL</h4>
|
||
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span></button>
|
||
</div>
|
||
|
||
<div class="modal-body">
|
||
<form name="pickupOrder" id="pickupOrder" method="POST" novalidate="novalidate">
|
||
<input type="hidden" class="d_uid">
|
||
<input type="hidden" class="d_customeruid">
|
||
<input type="hidden" class="d_customerno">
|
||
<input type="hidden" class="d_orderdate">
|
||
<input type="hidden" class="d_paymenttype">
|
||
<input type="hidden" class="d_driveruid">
|
||
|
||
<table class="tb-info-box">
|
||
<tbody>
|
||
<tr>
|
||
<td class="td-title-info">Restaurant Name</td>
|
||
<td class="td-text-info shortinput-store-name">Name</td>
|
||
</tr>
|
||
|
||
<tr>
|
||
<td class="td-title-info">Visited Date</td>
|
||
<td class="td-text-info">
|
||
<!--input type="datetime-local" class="d_visitdate"-->
|
||
<input type="date" class="d_visitdate">
|
||
</td>
|
||
</tr>
|
||
|
||
<tr>
|
||
<td class="td-title-info">Driver</td>
|
||
<td class="td-text-info">
|
||
<select class="custom-select d_druid">
|
||
<?php
|
||
foreach($drivers as $driver) {
|
||
echo '<option value="'.$driver['currentdriverid'].'">'.$driver['name'].'</option>';
|
||
}
|
||
?>
|
||
</select>
|
||
</td>
|
||
</tr>
|
||
|
||
<tr>
|
||
<td class="td-title-info">Rate</td>
|
||
<td class="td-text-info shortinput-rate">Rate</td>
|
||
</tr>
|
||
|
||
<tr>
|
||
<td class="td-title-info">Oil Quantity</td>
|
||
<td class="td-text-info">
|
||
<input type="number" class="d_quantity" placeholder="500" minlength="1" maxlength="10">
|
||
<div class="qty-error" style="color: red; font-size: 11px; display: none; margin-top: 4px;">Please input Oil Quantity</div>
|
||
</td>
|
||
</tr>
|
||
|
||
<tr>
|
||
<td class="td-title-info">Sludge (%)</td>
|
||
<td class="td-text-info">
|
||
|
||
<div class="number-input">
|
||
<button class="left" type="button">▼</button>
|
||
<input type="number" class="d_sludge" value="50" min="0" max="100" placeholder="50">
|
||
<button class="right" type="button">▲</button>
|
||
</div>
|
||
<div class="sludge-error" style="color: red; font-size: 11px; display: none; margin-top: 4px;">Please input the Sludge value.</div>
|
||
</td>
|
||
</tr>
|
||
|
||
</td>
|
||
</tr>
|
||
|
||
<tr class="CA_option">
|
||
<td class="td-title-info">Cash</td>
|
||
<td class="td-text-info">
|
||
<input type="text" class="d_payamount" placeholder="100">
|
||
</td>
|
||
</tr>
|
||
|
||
<tr class="CA_option">
|
||
<td class="td-title-info">Pay Status</td>
|
||
<td class="td-text-info" style="display:flex;width:100%">
|
||
<input type="radio" class="d_paystatuspaid" name="radio-group" value="P" style="margin-right:5px;width:auto;">
|
||
<span>Paid</span>
|
||
<input type="radio" class="d_paystatusunpaid" name="radio-group" value="N" style="margin-right:5px;margin-left:5px;width:auto;" checked>
|
||
<span>UnPaid</span>
|
||
</td>
|
||
</tr>
|
||
<tr class="CA_option">
|
||
<td class="td-title-info">Customer Name</td>
|
||
<td class="td-text-info">
|
||
<input type="text" class="d_payeename" placeholder="100">
|
||
</td>
|
||
</tr>
|
||
|
||
<tr class="CA_option">
|
||
<td class="td-title-info">Signature</td>
|
||
<td class="td-text-info">
|
||
<a href="#" onClick="popup();">CLICK</a>
|
||
</td>
|
||
</tr>
|
||
|
||
<tr>
|
||
<td class="td-title-info">Note</td>
|
||
<td class="td-text-info">
|
||
<textarea class="textarea d_note" rows="3" cols="20"></textarea>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<br>
|
||
<div class="text-center grid-layout-col-2">
|
||
<button type="button" class="btn-sub btn-input-save">SAVE</button>
|
||
<button type="button" class="btn-gray" data-dismiss="modal">CLOSE</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div><!-- modal-content ends -->
|
||
</div>
|
||
</div>
|
||
<!-- INPUT DETAIL MODAL Ends -->
|
||
|
||
<!-- ORDER MODAL-->
|
||
<div class="modal fade top map" id="map-modal-order" tabindex="-1" role="dialog">
|
||
<div id="modal-input" class="modal-dialog" role="document">
|
||
<div class="modal-content">
|
||
|
||
<div class="modal-header">
|
||
<h4 class="modal-title">ADD ORDER</h4>
|
||
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span></button>
|
||
|
||
</div>
|
||
|
||
<div class="modal-body">
|
||
<form name="order-form" method="POST">
|
||
<input type="hidden" class="d_customeruid">
|
||
<input type="hidden" class="d_customerno">
|
||
<input type="hidden" class="d_orderdate">
|
||
<input type="hidden" class="c_index">
|
||
|
||
<table class="tb-info-box">
|
||
<tbody>
|
||
<!-- Order Type -->
|
||
<tr>
|
||
<td class="td-title-info">Order Type</td>
|
||
<td class="td-text-info">
|
||
<select class="custom-select d_ordertype">
|
||
<option value="R">Request</option>
|
||
<option value="N">Normal</option>
|
||
<option value="S">Scheduled</option>
|
||
<option value="I">Installation</option>
|
||
<option value="O">Other</option>
|
||
</select>
|
||
</td>
|
||
</tr>
|
||
<!-- RUID -->
|
||
<tr>
|
||
<td class="td-title-info">RUID</td>
|
||
<td class="td-text-info">
|
||
<input type="text" class="d_ruid">
|
||
</td>
|
||
</tr>
|
||
<!-- Driver UID -->
|
||
<tr>
|
||
<td class="td-title-info">Driver UID</td>
|
||
<td class="td-text-info">
|
||
<select class="custom-select d_driveruid">
|
||
<?php
|
||
foreach($drivers as $driver) {
|
||
echo '<option value="'.$driver['currentdriverid'].'">'.$driver['name'].'</option>';
|
||
}
|
||
?>
|
||
</select>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<br>
|
||
<div class="text-center grid-layout-col-2">
|
||
<button type="button" class="btn-sub btn-order-save">SAVE</button>
|
||
<button type="button" class="btn-gray" data-dismiss="modal">CLOSE</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div><!-- modal-content ends -->
|
||
</div>
|
||
</div>
|
||
<!-- ORDER DETAIL MODAL Ends -->
|
||
|
||
<!-- HISTORY MODAL-->
|
||
<div class="modal fade top map" id="map-modal-history" tabindex="-1" role="dialog">
|
||
<div id="modal-history" class="modal-dialog" role="document">
|
||
<div class="modal-content modal-content-historyOrder">
|
||
|
||
<div class="modal-header">
|
||
<h4 class="modal-title">HISTORY</h4>
|
||
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span></button>
|
||
</div>
|
||
|
||
<div class="modal-body">
|
||
<div class="history-warp-scroll">
|
||
<form method="POST" action="#">
|
||
<table class="tb-info-box1">
|
||
<thead>
|
||
<tr>
|
||
<td class="td-title-info td-no">No</td>
|
||
<td class="td-title-info td-date-history">Date</td>
|
||
<td class="td-title-info td-oil-quantity-history">Oil Quantity</td>
|
||
<td class="td-title-info td-date-history">Driver</td>
|
||
<td class="td-title-info td-paid">Paid (Cash)</td>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td class="td-text-info">85</td>
|
||
<td class="td-text-info" style="text-align: center;">2024-03-06</td>
|
||
<td class="td-text-info" style="text-align: right;margin-right: 20px;">170</td>
|
||
<td class="td-text-info" style="text-align: center;">J.L</td>
|
||
<td class="td-text-info"></td>
|
||
</tr>
|
||
|
||
<tr>
|
||
<td class="td-text-info">84</td>
|
||
<td class="td-text-info" style="text-align: center;">2024-02-21</td>
|
||
<td class="td-text-info" style="text-align: right;margin-right: 20px;">100</td>
|
||
<td class="td-text-info" style="text-align: center;">J.L</td>
|
||
<td class="td-text-info"></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</form>
|
||
</div>
|
||
<br>
|
||
<div class="text-center">
|
||
<button type="button" class="btn-gray width-200" data-dismiss="modal">CLOSE</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- HISTORY DETAIL MODAL Ends -->
|
||
|
||
<!-- Access Time / Closing Days 표시 스타일 (customer_detail 과 동일) -->
|
||
<link rel="stylesheet" href="/assets/css/customer_access.css">
|
||
|
||
<!-- INFO MODAL-->
|
||
<div class="modal fade top map in" id="map-modal-info" tabindex="-1" role="dialog">
|
||
<div id="modal-info" class="modal-dialog" role="document">
|
||
<div class="modal-content modal-content-modifycustomerShortInfo">
|
||
|
||
<div class="modal-header">
|
||
<h4 class="modal-title">CUSTOMER INFORMATION</h4>
|
||
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span></button>
|
||
</div>
|
||
|
||
<div class="modal-body">
|
||
<form name="customerShortInfo" id="customerShortInfo" method="POST" novalidate="novalidate">
|
||
<input type="hidden" name="actionStr" value="SHORTINFO">
|
||
<input type="hidden" name="actionPage" value="">
|
||
<input type="hidden" name="c_uid" class="c_uid" value="">
|
||
<input type="hidden" name="c_accountno" class="c_accountno" value="">
|
||
<input type="hidden" name="mode" class="mode" value="update">
|
||
<table class="tb-info-box">
|
||
<tbody>
|
||
<tr>
|
||
<td class="td-title-info">Restaurant Name</td>
|
||
<td colspan="2" class="td-text-info shortinfo-store-name"></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="td-title-info">Payment Type</td>
|
||
<td colspan="2" class="td-text-info shortinfo-payment-type"></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="td-title-info">Pay Status</td>
|
||
<td colspan="2" class="td-text-info shortinfo-payment-string"></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="td-title-info">Comment</td>
|
||
<td colspan="2" class="td-text-info">
|
||
<textarea id="c_comment_ri" class="textarea shortinfo-comment" name="c_comment_ri" rows="4" cols="20"></textarea>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td class="td-title-info">Container Location</td>
|
||
<td colspan="2" class="td-text-info">
|
||
<textarea id="c_location" class="textarea shortinfo-container-location" name="c_location" rows="2" cols="20"></textarea>
|
||
</td>
|
||
</tr>
|
||
<!-- <tr>
|
||
<td class="td-title-info">Installation Image</td>
|
||
<td class="td-text-info" id="installImgArea" style="border-right:none;">
|
||
<span>Loading...</span>
|
||
</td>
|
||
<td style="text-align: right; border-right: 1px solid #e2e2e2; border-bottom:1px solid #e2e2e2;">
|
||
<span style="border:1px solid #72A864; border-radius:5px; padding:3px;">
|
||
<a href="#" onclick="imagepopup(jQuery('#customerShortInfo .c_uid').val());">ADD</a>
|
||
</span>
|
||
</td>
|
||
</tr> -->
|
||
</tbody>
|
||
<tbody id="mapAccessClosing"></tbody>
|
||
</table>
|
||
<br>
|
||
<div class="text-center grid-layout-col-2">
|
||
<button type="button" data-dismiss="modal" class="btn-sub" onClick="javscript:updtShortInfo();">SAVE</button>
|
||
<button type="button" class="btn-gray" data-dismiss="modal">CLOSE</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
|
||
<div class="modal fade" id="myModalPopup" tabindex="-1" role="dialog" aria-labelledby="myModalPopup" style="opacity: 0.5; padding-right: 0px !important;">
|
||
<div class="modal-dialog modal-dialog-centered modal-xl" role="document">
|
||
<div class="modal-content" style="background-color:#2A9B56 !important; max-width:260px; margin: 0 auto;">
|
||
<div class="myModalPopup-body" style="text-align: center; border-radius: 5px; max-height:60px; font-size:18px; background-color:#2A9B56; color: #FFFFFF; font-weight: bold;padding:0.5em 1em; ">
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- INFO DETAIL MODAL Ends -->
|
||
|
||
<!-- ==============================================================
|
||
Access Time / Closing Days 공용 편집 팝업 모달 (Bootstrap 3)
|
||
- 외부 PHP 를 iframe 으로 로드 (customer_detail 과 동일 팝업 재사용)
|
||
- 저장 완료 시 자식이 window.parent.closeCustomerPopup(true) 호출
|
||
============================================================== -->
|
||
<!-- ==============================================================
|
||
Access Time / Closing Days 공용 편집 팝업 (순수 오버레이)
|
||
- Bootstrap 모달을 쓰지 않음: BS3/BS5 focus-trap 충돌(무한재귀) 회피
|
||
- 외부 PHP 를 iframe 으로 로드 (customer_detail 과 동일 팝업 재사용)
|
||
- 저장 완료 시 자식이 window.parent.closeCustomerPopup(true) 호출
|
||
============================================================== -->
|
||
<div id="customerPopupOverlay" style="display:none; position:fixed; top:0; left:0; right:0; bottom:0; z-index:100000; background:rgba(0,0,0,0.5);">
|
||
<div id="customerPopupBox" style="position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); width:92%; max-width:760px; background:#fff; border-radius:6px; overflow:hidden; box-shadow:0 10px 40px rgba(0,0,0,0.35);">
|
||
<div style="display:flex; justify-content:space-between; align-items:center; padding:10px 14px; background:#2A9B56; color:#fff;">
|
||
<h4 id="customerPopupTitle" style="margin:0; font-size:16px;">Edit</h4>
|
||
<button type="button" id="customerPopupClose" style="background:none; border:none; color:#fff; font-size:24px; line-height:1; cursor:pointer;">×</button>
|
||
</div>
|
||
<iframe id="customerPopupFrame" src="about:blank" title="Edit popup" style="width:100%; height:70vh; border:0; display:block;"></iframe>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
(function () {
|
||
var popupConfig = {
|
||
accesstime: { url: '/lib/customer_access_time_popup.php', title: 'Access Time' },
|
||
closingdays: { url: '/lib/customer_closing_days_popup.php', title: 'Closing Days' }
|
||
};
|
||
var popupChanged = false;
|
||
|
||
// 클리핑/스택 방지: 오버레이를 body 직속으로 이동
|
||
jQuery('#customerPopupOverlay').appendTo(document.body);
|
||
|
||
// 현재 INFO MODAL 의 c_uid
|
||
function currentCuid() { return jQuery('#customerShortInfo .c_uid').val() || ''; }
|
||
|
||
// 표시 조각 로드 (모달 열릴 때 / 저장 후 갱신)
|
||
window.loadCustomerAccess = function (cuid) {
|
||
cuid = cuid || currentCuid();
|
||
if (!cuid) { jQuery('#mapAccessClosing').empty(); return; }
|
||
jQuery('#mapAccessClosing').load('/lib/customer_access_display.php?c_uid=' + encodeURIComponent(cuid));
|
||
};
|
||
|
||
function openPopup(key) {
|
||
var cfg = popupConfig[key];
|
||
if (!cfg) return;
|
||
var cuid = currentCuid();
|
||
if (!cuid) { alert('No customer selected.'); return; }
|
||
popupChanged = false;
|
||
jQuery('#customerPopupTitle').text(cfg.title);
|
||
jQuery('#customerPopupFrame').attr('src', cfg.url + '?c_uid=' + encodeURIComponent(cuid) + '&goStr=');
|
||
jQuery('#customerPopupOverlay').css('display', 'block');
|
||
}
|
||
|
||
function closePopup() {
|
||
jQuery('#customerPopupOverlay').css('display', 'none');
|
||
jQuery('#customerPopupFrame').attr('src', 'about:blank');
|
||
if (popupChanged) { window.loadCustomerAccess(); popupChanged = false; }
|
||
}
|
||
|
||
// 박스 클릭 → 편집 팝업 (동적 주입이라 위임 바인딩)
|
||
jQuery(document).on('click', '#mapAccessClosing .box-clickable[data-popup]', function () {
|
||
openPopup(jQuery(this).data('popup'));
|
||
});
|
||
|
||
// 닫기: X 버튼 / 배경 클릭(박스 내부 제외)
|
||
jQuery('#customerPopupClose').on('click', closePopup);
|
||
jQuery('#customerPopupOverlay').on('click', function (e) { if (e.target === this) closePopup(); });
|
||
|
||
// 자식 iframe(팝업 PHP)에서 호출하는 전역 핸들러
|
||
window.closeCustomerPopup = function (changed) {
|
||
if (changed) popupChanged = true;
|
||
closePopup();
|
||
};
|
||
})();
|
||
</script>
|
||
|
||
<!-- NOTE MODAL-->
|
||
<div class="modal fade top map" id="map-modal-note" tabindex="-1" role="dialog">
|
||
<div id="modal-note" class="modal-dialog" role="document">
|
||
<div class="modal-content modal-content-modifycustomerShortNote">
|
||
<div class="modal-header">
|
||
<h4 class="modal-title">REQUEST NOTE</h4>
|
||
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span></button>
|
||
</div>
|
||
|
||
<div class="modal-body">
|
||
<table class="tb-info-box">
|
||
<tbody>
|
||
<tr>
|
||
<td class="td-title-info">Note</td>
|
||
<td class="td-text-info">
|
||
<textarea id="r_note" class="textarea notice-info" name="r_note" rows="4" cols="20">굳지 않았을 것 같아서 요청드리니 교체가 필요한지 확인 부탁드리겠습니다.</textarea>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<br>
|
||
<div class="text-center">
|
||
<button type="button" class="btn-gray" data-dismiss="modal">CLOSE</button>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<div class="modal fade" id="myModalPopup" tabindex="-1" role="dialog" aria-labelledby="myModalPopup" style="opacity: 0.5; padding-right: 0px !important;">
|
||
<div class="modal-dialog modal-dialog-centered modal-xl" role="document">
|
||
<div class="modal-content" style="background-color:#2A9B56 !important; max-width:260px; margin: 0 auto;">
|
||
<div class="myModalPopup-body" style="text-align: center; border-radius: 5px; max-height:60px; font-size:18px; background-color:#2A9B56; color: #FFFFFF; font-weight: bold;padding:0.5em 1em; ">
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- NOTE DETAIL MODAL Ends -->
|
||
|
||
<!-- SIMPLE INSTALL REQUEST MODAL -->
|
||
<div class="modal fade top map" id="map-modal-container-request" tabindex="-1" role="dialog">
|
||
<div class="modal-dialog" role="document">
|
||
<div class="modal-content">
|
||
|
||
<!-- HEADER -->
|
||
<div class="modal-header">
|
||
<h4 class="modal-title">
|
||
CONTAINER REQUEST -
|
||
<span id="simpleInstallCustomerName"></span>
|
||
</h4>
|
||
<button type="button" class="close" data-dismiss="modal">
|
||
<span>×</span>
|
||
</button>
|
||
</div>
|
||
|
||
<!-- BODY -->
|
||
<div class="modal-body">
|
||
<div id="simpleInstallMeta" class="alert alert-warning py-2 px-3 mb-3" style="display:none; font-size:13px;">
|
||
<div class="fw-bold mb-2 d-flex align-items-center gap-2">
|
||
<i class="bi bi-exclamation-triangle-fill"></i>
|
||
Existing Container Request Found
|
||
</div>
|
||
<div id="simpleInstallPhotoWrap" class="mb-3" style="display:none;">
|
||
<div id="simpleInstallPhotoList" class="d-flex flex-wrap gap-2 mt-2"></div>
|
||
</div>
|
||
<div><strong>Status:</strong> <span id="siStatus"></span></div>
|
||
<div><strong>Requested by:</strong> <span id="siAddedBy"></span></div>
|
||
<div><strong>Requested at:</strong> <span id="siAddedAt"></span></div>
|
||
<div><strong>Scheduled:</strong> <span id="siScheduled"></span></div>
|
||
</div>
|
||
|
||
<!-- JOB TYPE -->
|
||
<div class="mb-3">
|
||
<label class="fw-bold">Request Type</label>
|
||
<div class="d-flex gap-2 mt-2">
|
||
<button type="button" class="btn btn-outline-primary simple-job-btn" data-job="INSTALL"> <i class="bi bi-plus-circle"></i> Add </button>
|
||
<button type="button" class="btn btn-outline-danger simple-job-btn" data-job="PICKUP"> <i class="bi bi-dash-circle"></i> Pickup </button>
|
||
<button type="button" class="btn btn-outline-warning simple-job-btn" data-job="EXCHANGE"> <i class="bi bi-arrow-left-right"></i> Exchange </button>
|
||
</div>
|
||
</div>
|
||
<!-- NOTE -->
|
||
<div class="mb-3">
|
||
<label class="fw-bold">Request Note</label>
|
||
<textarea id="simpleInstallNote" class="form-control" rows="2" placeholder="Enter request note"></textarea>
|
||
</div>
|
||
|
||
<!-- PHOTO -->
|
||
<div class="mb-3">
|
||
<label class="fw-bold">Photos</label>
|
||
<input type="file" id="simpleInstallPhotos" class="form-control" multiple accept="image/*">
|
||
</div>
|
||
|
||
<!-- HIDDEN DATA -->
|
||
<input type="hidden" id="simpleInstallCustomerUid">
|
||
<input type="hidden" id="simpleInstallDriverUid">
|
||
|
||
<!-- FOOTER BUTTON -->
|
||
<div class="text-center mt-4">
|
||
<button type="button" id="btnSimpleInstallDelete" class="btn btn-danger" style="display:none;">Delete Request</button>
|
||
<button type="button" id="btnSimpleInstallSubmit" class="btn btn-success" disabled>Submit Request</button>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="modal fade" id="myModalPopup" tabindex="-1" role="dialog" aria-labelledby="myModalPopup" style="opacity: 0.5; padding-right: 0px !important;">
|
||
<div class="modal-dialog modal-dialog-centered modal-xl" role="document">
|
||
<div class="modal-content" style="background-color:#2A9B56 !important; max-width:260px; margin: 0 auto;">
|
||
<div class="myModalPopup-body" style="text-align: center; border-radius: 5px; max-height:60px; font-size:18px; background-color:#2A9B56; color: #FFFFFF; font-weight: bold;padding:0.5em 1em; ">
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- image modal -->
|
||
<script src="/js/customer_image.js?v=<?=time()?>"></script>
|
||
<div class="modal fade top map" id="customerImageModal" tabindex="-1" role="dialog">
|
||
<div class="modal-dialog modal-xl modal-dialog-scrollable">
|
||
<div class="modal-content" style="position: relative;">
|
||
|
||
<div class="modal-header">
|
||
<h4 class="modal-title">
|
||
CUSTOMER IMAGES - <span id="imageCustomerName"></span>
|
||
</h4>
|
||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||
</div>
|
||
|
||
<div style="position: absolute; top: 60px; left: 15px; z-index: 1060;">
|
||
<button type="button"
|
||
id="btn-modal-upload-action"
|
||
onclick="openCustomerImageUploadDirect(this.getAttribute('data-cuid'));"
|
||
style="display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 65px;
|
||
height: 65px;
|
||
background: rgba(255, 140, 46, 0.7);
|
||
color: white;
|
||
border: 2px solid rgba(255, 255, 255, 0.5);
|
||
border-radius: 50%;
|
||
box-shadow: 0 4px 10px rgba(0,0,0,0.3);
|
||
cursor: pointer;
|
||
transition: transform 0.1s;">
|
||
<i class="bi bi-camera-fill" style="font-size: 24px;"></i>
|
||
<span style="font-size: 9px; font-weight: bold; margin-top: -2px;">ADD</span>
|
||
</button>
|
||
</div>
|
||
|
||
<div class="modal-body" id="customerImageModalBody" style="padding-top: 40px;">
|
||
<div class="text-center text-muted py-5">
|
||
Loading images...
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<div class="container">
|
||
<!-- Modal -->
|
||
<div class="modal fade" id="myModal" data-backdrop="static" data-keyboard="false" role="dialog">
|
||
<div class="modal-dialog">
|
||
<!-- Modal content-->
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<!--button type="button" class="close" data-dismiss="modal">×</button-->
|
||
<h4>MESSAGE</h4>
|
||
</div>
|
||
<div class="modal-body">
|
||
<p> </p>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="modal-close" data-dismiss="modal" >OK</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Modal -->
|
||
<div class="modal fade top map" id="modal-confirm" tabindex="-1" role="dialog">
|
||
<div id="modal-input" class="modal-dialog" role="document">
|
||
<!--
|
||
<div class="modal-content">
|
||
<div class="modal-body">
|
||
<p>Are you sure to remove order?</p>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<input type="hidden" class="order-date" value="">
|
||
<input type="hidden" class="id" value="">
|
||
<input type="hidden" class="c_index" value="">
|
||
<button type="button" class="btnRemoveConfirm" onClick="javascript:confirmRemoveOrder();">Confirm</button>
|
||
<button type="button" class="btn-gray" data-dismiss="modal" >Cancel</button>
|
||
</div>
|
||
</div>--><!-- modal-content ends -->
|
||
|
||
<div class="modal-content">
|
||
|
||
<div class="modal-header">
|
||
<h4 class="modal-title">Confirm</h4>
|
||
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span></button>
|
||
</div>
|
||
|
||
<div class="modal-body">
|
||
<input type="hidden" class="order-date" value="">
|
||
<input type="hidden" class="id" value="">
|
||
<input type="hidden" class="duid" value="">
|
||
<input type="hidden" class="c_index" value="">
|
||
<input type="hidden" class="accountno" value="">
|
||
|
||
<p>Are you sure to remove order?</p>
|
||
<br>
|
||
<div class="text-center grid-layout-col-2">
|
||
<button type="button" class="btn-sub" onClick="javascript:confirmRemoveOrder();" data-dismiss="modal">Confirm</button>
|
||
<button type="button" class="btn-gray" data-dismiss="modal">Cancel</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- ORDER DETAIL MODAL Ends -->
|
||
|
||
<!-- Modal -->
|
||
|
||
<input type="hidden" id="routeplanner_total_duration">
|
||
<input type="hidden" id="routeplanner_total_distance">
|
||
<input type="hidden" id="routeplanner_encoded_path">
|
||
|
||
<a href="#" class="scroll-top d-flex align-items-center justify-content-center"><i class="bi bi-arrow-up-short"></i></a>
|
||
|
||
<div id="preloader"></div>
|
||
<p></p><p></p><p></p>
|
||
<div class="route-planner-waypoints">
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>No</th>
|
||
<th>Name</th>
|
||
<th>Account</th>
|
||
<th>Container</th>
|
||
<th>Payment</th>
|
||
<th>Rate</th>
|
||
<th>Address</th>
|
||
<th>City</th>
|
||
<th>Last</th>
|
||
<th>Forecast</th>
|
||
<th>Actual(L)</th>
|
||
<th>Last Paid</th>
|
||
<th>2023</th>
|
||
<th>2024</th>
|
||
<th>Cycle</th>
|
||
<th>Last PU</th>
|
||
<th>Km</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
|
||
|
||
</body>
|
||
</html>
|
||
|
||
|
||
<script>
|
||
const SS_UID = <?= (int)($_SESSION['ss_UID'] ?? 0) ?>;
|
||
const SS_DRUID = <?= (int)($_SESSION['ss_DRUID'] ?? 0) ?>;
|
||
var center = { lat: 43.732188, lng: -79.571618 };
|
||
var currentLocation = { lat: 43.764693323038315, lon: -79.47762958134186 };
|
||
|
||
/* $(window).ready(function(){
|
||
|
||
jQuery("#driver").change(function(e){
|
||
let data = {
|
||
"driver" : jQuery("#driver").val()
|
||
}
|
||
|
||
api('inqDriverGeo', data, rstDriverGeo);
|
||
});
|
||
|
||
var rstDriverGeo = function(json) {
|
||
|
||
if (json.result['geolat']) {
|
||
var driverlat = json.result['geolat'];
|
||
var driverlon = json.result['geolon'];
|
||
}
|
||
else {
|
||
var driverlat = 43.732188;
|
||
var driverlon = -79.571618;
|
||
}
|
||
alert (driverlat);
|
||
center = { lat: driverlat, lng: driverlon };
|
||
}
|
||
|
||
});
|
||
*/
|
||
|
||
var map;
|
||
var directionsRenderer;
|
||
var currentMarker;
|
||
const {AdvancedMarkerElement} = google.maps.importLibrary("marker");
|
||
|
||
|
||
$(window).ready(function(){
|
||
|
||
<? if ($_SESSION['ss_LEVEL'] == 9) { ?>
|
||
$("#driver").val("<?=$_SESSION['ss_DRUID']?>").change();
|
||
<? } ?>
|
||
|
||
initMap();
|
||
|
||
/*Redraw marker when search option changed*/
|
||
|
||
jQuery("#orderdate").change(function(e){
|
||
try { sessionStorage.setItem('map_orderdate', jQuery(this).val()); } catch(err) {}
|
||
jQuery('.qty').prop('checked', false);
|
||
jQuery("#search-keyword").val("");
|
||
drawPoint();
|
||
|
||
let pickupdata = {
|
||
"route_date" : jQuery("#orderdate").val(),
|
||
"driver" : jQuery("#driver").val()
|
||
}
|
||
api('inqPickupQty', pickupdata, rstInqPickupQty);
|
||
|
||
});
|
||
|
||
|
||
jQuery(".chk-ordered").click(function(e){
|
||
drawPoint();
|
||
});
|
||
|
||
jQuery("#driver").change(function(e){
|
||
try { sessionStorage.setItem('map_driver', jQuery(this).val()); } catch(err) {}
|
||
jQuery('.qty').prop('checked', false);
|
||
jQuery("#search-keyword").val("");
|
||
drawPoint();
|
||
|
||
//map ceneter change
|
||
let data = {
|
||
"route_date" : jQuery("#orderdate").val(),
|
||
"driver" : jQuery("#driver").val()
|
||
}
|
||
|
||
api('inqMapCenter', data, rstInqMapCenter);
|
||
|
||
api('inqPickupQty', data, rstInqPickupQty);
|
||
|
||
});
|
||
|
||
// 선택한 날짜/드라이버 복원 (F5·INIT 리로드 후 유지)
|
||
(function(){
|
||
var changed = false;
|
||
try {
|
||
var savedDate = sessionStorage.getItem('map_orderdate');
|
||
if (savedDate) { jQuery('#orderdate').val(savedDate); changed = true; }
|
||
<? if ($_SESSION['ss_LEVEL'] != 9) { ?>
|
||
var savedDriver = sessionStorage.getItem('map_driver');
|
||
if (savedDriver !== null && jQuery('#driver option[value="'+savedDriver+'"]').length) {
|
||
jQuery('#driver').val(savedDriver); changed = true;
|
||
}
|
||
<? } ?>
|
||
} catch(err) {}
|
||
if (changed) jQuery('#driver').trigger('change');
|
||
})();
|
||
|
||
jQuery("#search-keyword").keyup(function(e){
|
||
//if(jQuery(e.target).val().length > 0) {
|
||
drawPoint();
|
||
//}
|
||
});
|
||
|
||
jQuery(".qty").click(function(e){
|
||
drawPoint();
|
||
});
|
||
|
||
jQuery(".qty-min").change(function(e){
|
||
let index = jQuery(".qty-min").index(e.target);
|
||
|
||
if(parseInt(jQuery(e.target).val()) > jQuery(".qty-max:eq("+(index-1)+")").val()) {
|
||
for(let i=index;i < jQuery(".qty-min").length;i++) {
|
||
let unit = 49;
|
||
if(jQuery(".qty-min:eq("+i+")").val() > 200){
|
||
unit = 99;
|
||
}
|
||
|
||
if((i+1) < jQuery(".qty-min").length){
|
||
jQuery(".qty-max:eq("+i+")").val(parseInt(jQuery(".qty-min:eq("+i+")").val()) + unit);
|
||
}
|
||
jQuery(".qty-min:eq("+(i+1)+")").val(parseInt(jQuery(".qty-max:eq("+i+")").val()) + 1);
|
||
}
|
||
|
||
drawPoint();
|
||
}else{
|
||
jQuery(e.target).val(parseInt(jQuery(".qty-max:eq("+(index-1)+")").val())+1);
|
||
}
|
||
});
|
||
|
||
jQuery(".qty-max").change(function(e){
|
||
let index = jQuery(".qty-max").index(e.target);
|
||
if(parseInt(jQuery(e.target).val()) > jQuery(".qty-min:eq("+index+")").val()) {
|
||
jQuery(".qty-min:eq("+(index+1)+")").val(parseInt(jQuery(e.target).val())+1).trigger('change');
|
||
|
||
drawPoint();
|
||
}else{
|
||
jQuery(e.target).val(parseInt(jQuery(".qty-min:eq("+(index+1)+")").val()) - 1);
|
||
}
|
||
|
||
|
||
});
|
||
|
||
|
||
jQuery(".btn-current").click(function(e){
|
||
inqCurrent();
|
||
});
|
||
|
||
jQuery(".btn-routeplanner").click(function(e){
|
||
jQuery("#routePlannerModal #addressList").html('<p class="text-center">Loading...</p>');
|
||
|
||
// 각 드라이버의 data 를 가져오는 문제가 있어서 강제로 회사 주소로 셋팅 (2025.06.16)
|
||
currentLocation = { lat: 43.764693323038315, lon: -79.47762958134186 };
|
||
//alert(currentLocation['lat']);
|
||
|
||
let data = {
|
||
"route_date" : jQuery("#orderdate").val(),
|
||
"m_lat" : currentLocation['lat'],
|
||
"m_lon" : currentLocation['lon'],
|
||
"driver" : jQuery("#driver").val()
|
||
}
|
||
|
||
api('inqRouteAddress', data, rstInqRouteAddress);
|
||
|
||
});
|
||
|
||
jQuery(".routePlannerApply").click(function(e){
|
||
removeCircle();
|
||
for(let j=0; j < marker.length; j++) {
|
||
marker[j].setMap(null);
|
||
marker[j] = null;
|
||
}
|
||
marker = [];
|
||
overlay = [];
|
||
let waypoints = [];
|
||
let dispWaypoints = [];
|
||
for(let i=0; i < jQuery("#routePlannerModal #addressList .draggable-item").length; i++) {
|
||
let dataArr = jQuery("#routePlannerModal #addressList .draggable-item:eq("+i+")").attr("data").split("*&^&*");
|
||
|
||
if(waypoints.length < 24) {
|
||
if(i < (jQuery("#routePlannerModal #addressList .draggable-item").length - 1)){
|
||
let point = {
|
||
'location' : {
|
||
'lat' : parseFloat(dataArr[0]),
|
||
'lng' : parseFloat(dataArr[1])
|
||
},
|
||
stopover : true
|
||
}
|
||
waypoints.push(point);
|
||
}
|
||
}
|
||
|
||
// Print
|
||
let dispWaypoint = {
|
||
'point' : (i < 25)?String.fromCharCode(65+(i+1)):'',
|
||
'name' : dataArr[2],
|
||
'address' : dataArr[3],
|
||
'accountno' : jQuery("#routePlannerModal #addressInformationList .item-information-"+dataArr[4]+" .account").val(),
|
||
'maincontainer' : jQuery("#routePlannerModal #addressInformationList .item-information-"+dataArr[4]+" .maincontainer").val(),
|
||
'paymenttype' : jQuery("#routePlannerModal #addressInformationList .item-information-"+dataArr[4]+" .paymenttype").val(),
|
||
'rate' : jQuery("#routePlannerModal #addressInformationList .item-information-"+dataArr[4]+" .rate").val(),
|
||
'city' : jQuery("#routePlannerModal #addressInformationList .item-information-"+dataArr[4]+" .city").val(),
|
||
'lastpickupquantity' : jQuery("#routePlannerModal #addressInformationList .item-information-"+dataArr[4]+" .lastpickupquantity").val(),
|
||
'estquantity' : jQuery("#routePlannerModal #addressInformationList .item-information-"+dataArr[4]+" .estquantity").val(),
|
||
'quantity' : jQuery("#routePlannerModal #addressInformationList .item-information-"+dataArr[4]+" .quantity").val(),
|
||
'lastpaiddate' : jQuery("#routePlannerModal #addressInformationList .item-information-"+dataArr[4]+" .lastpaiddate").val(),
|
||
'this_year' : jQuery("#routePlannerModal #addressInformationList .item-information-"+dataArr[4]+" .this_year").val(),
|
||
'last_year' : jQuery("#routePlannerModal #addressInformationList .item-information-"+dataArr[4]+" .last_year").val(),
|
||
'fullcycle' : jQuery("#routePlannerModal #addressInformationList .item-information-"+dataArr[4]+" .fullcycle").val(),
|
||
'lastpickupdate' : jQuery("#routePlannerModal #addressInformationList .item-information-"+dataArr[4]+" .lastpickupdate").val()
|
||
}
|
||
dispWaypoints.push(dispWaypoint);
|
||
}
|
||
|
||
let destinationDataArr = jQuery("#routePlannerModal #addressList .draggable-item:eq("+(jQuery("#routePlannerModal #addressList .draggable-item").length - 1)+")").attr("data").split("*&^&*");
|
||
if(jQuery("#routePlannerModal #addressList .draggable-item").length > 23) {
|
||
destinationDataArr = jQuery("#routePlannerModal #addressList .draggable-item:eq(23)").attr("data").split("*&^&*");
|
||
}
|
||
|
||
|
||
let directionsService = new google.maps.DirectionsService();
|
||
directionsRenderer = new google.maps.DirectionsRenderer({
|
||
map: map,
|
||
suppressMarkers: false
|
||
});
|
||
|
||
directionsService.route({
|
||
origin: {lat: parseFloat(currentLocation['lat']), lng: parseFloat(currentLocation['lon'])}, // 시작 위치
|
||
destination: {lat: parseFloat(destinationDataArr[0]), lng: parseFloat(destinationDataArr[1])}, // 종료 위치 (시작 위치와 같을 수 있음)
|
||
waypoints: waypoints,
|
||
optimizeWaypoints: true, // Google에게 경유지 최적화를 요청
|
||
travelMode: google.maps.TravelMode.DRIVING,
|
||
}, (response, status) => {
|
||
if (status === 'OK') {
|
||
directionsRenderer.setDirections(response);
|
||
|
||
var route = response.routes[0];
|
||
var totalDuration = 0;
|
||
var totalDistance = 0;
|
||
let waypointsTable = '<tr><td>A</td><td>Current Location</td><td colspan="13"> </td></tr>';
|
||
for (var i = 0; i < route.legs.length; i++) {
|
||
totalDuration += route.legs[i].duration.value;
|
||
totalDistance += route.legs[i].distance.value;
|
||
|
||
/* suppressMarkers: true
|
||
var marker = new google.maps.Marker({
|
||
position: route.legs[i].start_location,
|
||
label: ""+(i+1),
|
||
map: map
|
||
});
|
||
*/
|
||
|
||
if(i != 0) {
|
||
waypointsTable += '<tr>';
|
||
waypointsTable += '<td>'+dispWaypoints[i-1]['point']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[i-1]['name']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[i-1]['accountno']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[i-1]['maincontainer']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[i-1]['paymenttype']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[i-1]['rate']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[i-1]['address']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[i-1]['city']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[i-1]['lastpickupquantity']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[i-1]['estquantity']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[i-1]['quantity']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[i-1]['lastpaiddate']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[i-1]['this_year']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[i-1]['last_year']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[i-1]['fullcycle']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[i-1]['lastpickupdate']+'</td>';
|
||
waypointsTable += '<td>'+(route.legs[i-1].distance.value/1000)+'</td>';
|
||
waypointsTable += '</tr>';
|
||
}
|
||
}
|
||
// Convert the duration to minutes
|
||
totalDuration = totalDuration / 60;
|
||
// 총 이동 거리를 킬로미터 단위로 변환 (미터 단위에서 킬로미터 단위로 변환하기 위해 1000으로 나눔)
|
||
totalDistance = totalDistance / 1000;
|
||
|
||
jQuery("#routeplanner_total_duration").val(totalDuration);
|
||
jQuery("#routeplanner_total_distance").val(totalDistance);
|
||
|
||
waypointsTable += '<tr>';
|
||
waypointsTable += '<td>'+dispWaypoints[route.legs.length-1]['point']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[route.legs.length-1]['name']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[route.legs.length-1]['accountno']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[route.legs.length-1]['maincontainer']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[route.legs.length-1]['paymenttype']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[route.legs.length-1]['rate']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[route.legs.length-1]['address']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[route.legs.length-1]['city']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[route.legs.length-1]['lastpickupquantity']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[route.legs.length-1]['estquantity']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[route.legs.length-1]['quantity']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[route.legs.length-1]['lastpaiddate']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[route.legs.length-1]['this_year']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[route.legs.length-1]['last_year']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[route.legs.length-1]['fullcycle']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[route.legs.length-1]['lastpickupdate']+'</td>';
|
||
waypointsTable += '<td>'+(route.legs[route.legs.length-1].distance.value/1000)+'</td>';
|
||
waypointsTable += '</tr>';
|
||
|
||
|
||
if(dispWaypoints.length > route.legs.length) {
|
||
for (let i=route.legs.length;i < dispWaypoints.length;i++) {
|
||
waypointsTable += '<tr>';
|
||
waypointsTable += '<td>'+dispWaypoints[i]['point']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[i]['name']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[i]['accountno']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[i]['maincontainer']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[i]['paymenttype']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[i]['rate']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[i]['address']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[i]['city']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[i]['lastpickupquantity']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[i]['estquantity']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[i]['quantity']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[i]['lastpaiddate']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[i]['this_year']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[i]['last_year']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[i]['fullcycle']+'</td>';
|
||
waypointsTable += '<td>'+dispWaypoints[i]['lastpickupdate']+'</td>';
|
||
waypointsTable += '<td></td>';
|
||
waypointsTable += '</tr>';
|
||
}
|
||
}
|
||
|
||
|
||
jQuery(".route-planner-waypoints table tbody").html(waypointsTable);
|
||
|
||
/*
|
||
//overlay 텍스트 마커 생성
|
||
let waypointCnt = 0;
|
||
waypoints.forEach(waypoint => {
|
||
let labelArr = jQuery("#routePlannerModal #addressList .draggable-item:eq("+waypointCnt+")").attr("data").split("/");
|
||
|
||
let canvas = document.createElement('canvas');
|
||
canvas.width = 100;
|
||
canvas.height = 50;
|
||
let context = canvas.getContext('2d');
|
||
|
||
// 배경 그리기
|
||
context.fillStyle = '#FF6347'; // 배경색
|
||
context.fillRect(0, 0, canvas.width, canvas.height);
|
||
|
||
// 텍스트 그리기
|
||
context.fillStyle = '#FFFFFF'; // 텍스트 색상
|
||
context.font = '12px Arial';
|
||
context.fillText(labelArr[2], 10, 25); // 텍스트와 위치
|
||
|
||
let markerIcon = canvas.toDataURL(); // 캔버스를 데이터 URL로 변환
|
||
|
||
new google.maps.Marker({
|
||
position: waypoint.location,
|
||
icon: markerIcon,
|
||
map: map,
|
||
});
|
||
waypointCnt += 1;
|
||
});*/
|
||
jQuery(".btn-route-print").removeClass("hidden");
|
||
|
||
} else {
|
||
window.alert('Directions request failed due to ' + status);
|
||
}
|
||
});
|
||
|
||
jQuery("#routePlannerModal .close").trigger("click");
|
||
});
|
||
|
||
jQuery(".btn-route-print").click(function(e){
|
||
window.print();
|
||
});
|
||
|
||
jQuery(".btn-order-save").click(function(e){
|
||
// 폼 데이터 유효성 검사
|
||
if (addOrderValidation()) {
|
||
let data = {
|
||
"orderdate" : jQuery("#map-modal-order .d_orderdate").val(),
|
||
"ordertype" : jQuery("#map-modal-order .d_ordertype").val(),
|
||
"ruid" : jQuery("#map-modal-order .d_ruid").val(),
|
||
"driveruid" : jQuery("#map-modal-order .d_driveruid").val(),
|
||
"customer_uid" : jQuery("#map-modal-order .d_customeruid").val(),
|
||
"customer_no" : jQuery("#map-modal-order .d_customerno").val(),
|
||
"c_index" : jQuery("#map-modal-order .c_index").val()
|
||
}
|
||
|
||
api('addOrder', data, rstAddOrder);
|
||
}
|
||
});
|
||
|
||
jQuery(".btn-input-save").click(function(e){
|
||
// 폼 데이터 유효성 검사
|
||
if (saveInputValidation()) {
|
||
let data = {
|
||
"uid" : jQuery("#map-modal-input .d_uid").val(),
|
||
"customeruid" : jQuery("#map-modal-input .d_customeruid").val(),
|
||
"customerno" : jQuery("#map-modal-input .d_customerno").val(),
|
||
"orderdate" : jQuery("#map-modal-input .d_orderdate").val(),
|
||
//"ordertype" : jQuery("#map-modal-input .d_ordertype").val(),
|
||
//"ruid" : jQuery("#map-modal-input .d_ruid").val(),
|
||
"visitdate" : replaceAll(jQuery("#map-modal-input .d_visitdate").val(), '-' ,''),
|
||
"driveruid" : jQuery("#map-modal-input .d_driveruid").val(),
|
||
"druid" : jQuery("#map-modal-input .d_druid").val(),
|
||
"quantity" : jQuery("#map-modal-input .d_quantity").val(),
|
||
"sludge" : jQuery("#map-modal-input .d_sludge").val(),
|
||
"note" : jQuery("#map-modal-input .d_note").val(),
|
||
"createruid" : <?=$_SESSION['ss_UID']?>,
|
||
"paymenttype" : jQuery("#map-modal-input .d_paymenttype").val(),
|
||
"payamount" : jQuery("#map-modal-input .d_payamount").val(),
|
||
"payeename" : jQuery("#map-modal-input .d_payeename").val(),
|
||
"paystatus" : jQuery("#map-modal-input input[name='radio-group']:checked").val()
|
||
|
||
}
|
||
|
||
// api('saveInput', data, rstSaveInput);
|
||
|
||
api('saveInput', data, function(rst){
|
||
rstSaveInput(rst);
|
||
drawPoint(); // 저장시 깃발 색깔 변경
|
||
});
|
||
|
||
//let pickupdata = {
|
||
// "route_date" : jQuery("#orderdate").val(),
|
||
// "driver" : jQuery("#driver").val()
|
||
//}
|
||
//api('inqPickupQty', pickupdata, rstInqPickupQty);
|
||
|
||
}
|
||
});
|
||
});
|
||
|
||
|
||
function updatePickupQty(){
|
||
|
||
let pickupdata = {
|
||
"route_date" : jQuery("#orderdate").val(),
|
||
"driver" : jQuery("#driver").val()
|
||
}
|
||
api('inqPickupQty', pickupdata, rstInqPickupQty);
|
||
|
||
}
|
||
|
||
function smoothZoom(map, maxZoom, currentZoom) {
|
||
if (currentZoom >= maxZoom) {
|
||
return;
|
||
} else {
|
||
google.maps.event.addListenerOnce(map, 'zoom_changed', function(event) {
|
||
smoothZoom(map, maxZoom, currentZoom + 1);
|
||
});
|
||
setTimeout(function() {
|
||
map.setZoom(currentZoom);
|
||
}, 80); // Adjust the speed of zooming
|
||
}
|
||
}
|
||
|
||
var rstInqPickupQty = function(json) {
|
||
jQuery("#targetquantity").text(json.curTargetQty);
|
||
jQuery("#pickupquantity").text(json.curPickupQty);
|
||
jQuery("#pickupvisit").text(json.curVisitCnt);
|
||
jQuery("#targetvisit").text(json.curTargetVisitCnt);
|
||
setPickupGauge(json.curPickupQty, json.curTargetQty);
|
||
}
|
||
|
||
// Pickup Qty 버튼을 타겟 대비 비율만큼 파란색으로 채움
|
||
function setPickupGauge(pickup, target){
|
||
var p = parseInt(String(pickup).replace(/[^0-9]/g, ''), 10) || 0;
|
||
var t = parseInt(String(target).replace(/[^0-9]/g, ''), 10) || 0;
|
||
var pct = (t > 0) ? Math.round(p / t * 100) : 0; // 실제 퍼센트 (초과 시 100+ 그대로)
|
||
var ratio = Math.min(100, pct); // 채움 비율 (100% 상한)
|
||
|
||
jQuery("#pickupQtyBtn").css(
|
||
"background",
|
||
"linear-gradient(to right, #007bff 0%, #007bff " + ratio + "%, #a9cdff " + ratio + "%, #a9cdff 100%)"
|
||
);
|
||
jQuery("#pickuppercent").text(t > 0 ? pct + "% · " : "");
|
||
|
||
// 타겟 100% 도달 시 폭죽 (도달하는 순간 1회, 미만으로 떨어지면 재무장)
|
||
var reached = (t > 0 && pct >= 100);
|
||
if (reached && !window._pickupGoalReached) {
|
||
launchFireworks();
|
||
}
|
||
window._pickupGoalReached = reached;
|
||
}
|
||
|
||
// 미리 만든 글로우 스프라이트 캐시 (shadowBlur 대체 → 모바일에서 빠름)
|
||
var _fwSprites = {};
|
||
function fwSprite(color){
|
||
if (_fwSprites[color]) return _fwSprites[color];
|
||
var R = 16;
|
||
var s = document.createElement('canvas');
|
||
s.width = s.height = R * 2;
|
||
var g = s.getContext('2d');
|
||
var grad = g.createRadialGradient(R, R, 0, R, R, R);
|
||
grad.addColorStop(0, color);
|
||
grad.addColorStop(0.35, color);
|
||
grad.addColorStop(1, 'rgba(0,0,0,0)');
|
||
g.fillStyle = grad;
|
||
g.beginPath(); g.arc(R, R, R, 0, Math.PI*2); g.fill();
|
||
_fwSprites[color] = s;
|
||
return s;
|
||
}
|
||
|
||
// 폭죽 애니메이션 (canvas, 자체 제거) - 로켓 발사 + 글로우(스프라이트)
|
||
function launchFireworks(){
|
||
// 모션 최소화 설정이면 생략
|
||
if (window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches) return;
|
||
|
||
var canvas = document.createElement('canvas');
|
||
canvas.style.cssText = 'position:fixed;left:0;top:0;width:100%;height:100%;pointer-events:none;z-index:99999;';
|
||
document.body.appendChild(canvas);
|
||
var ctx = canvas.getContext('2d');
|
||
var W = canvas.width = window.innerWidth;
|
||
var H = canvas.height = window.innerHeight;
|
||
|
||
// 저사양(터치/작은 화면) 감지 → 개수 감소
|
||
var lowPower = (window.matchMedia && matchMedia('(pointer: coarse)').matches) || Math.min(W, H) < 760;
|
||
var NUM_ROCKETS = lowPower ? 7 : 12;
|
||
var P_MIN = lowPower ? 28 : 60;
|
||
var P_RANGE = lowPower ? 26 : 50;
|
||
var LAUNCH_GAP = lowPower ? 300 : 230;
|
||
|
||
var colors = ['#ff3b3b','#ffd23f','#3bd16f','#3b9dff','#c14bff','#ff8c1a','#ff5fa2','#ffffff'];
|
||
var rockets = [];
|
||
var particles = [];
|
||
|
||
function rand(a, b){ return a + Math.random()*(b - a); }
|
||
function pick(){ return colors[Math.floor(Math.random()*colors.length)]; }
|
||
|
||
function launchRocket(){
|
||
rockets.push({
|
||
x: rand(W*0.15, W*0.85),
|
||
y: H + 10,
|
||
vy: -rand(9, 13),
|
||
targetY: rand(H*0.10, H*0.42),
|
||
color: pick()
|
||
});
|
||
}
|
||
|
||
function explode(x, y, color){
|
||
var n = P_MIN + Math.floor(Math.random()*P_RANGE);
|
||
var base = rand(3, 7);
|
||
for(var i=0;i<n;i++){
|
||
var angle = Math.random()*Math.PI*2;
|
||
var speed = base * (0.3 + Math.random());
|
||
particles.push({
|
||
x:x, y:y,
|
||
vx:Math.cos(angle)*speed,
|
||
vy:Math.sin(angle)*speed,
|
||
life:1,
|
||
decay: rand(0.008, 0.018),
|
||
size: rand(1.5, 3.5),
|
||
color: Math.random() < 0.18 ? '#ffffff' : color
|
||
});
|
||
}
|
||
}
|
||
|
||
// 중앙 배너 팝업
|
||
var banner = document.createElement('div');
|
||
banner.textContent = '🎉 TARGET 100%! 🎉';
|
||
banner.style.cssText = 'position:fixed;left:50%;top:24%;transform:translate(-50%,-50%);'
|
||
+ 'font-size:38px;font-weight:800;color:#fff;text-shadow:0 2px 12px rgba(0,0,0,0.6),0 0 22px #ffd23f;'
|
||
+ 'z-index:100000;pointer-events:none;white-space:nowrap;';
|
||
document.body.appendChild(banner);
|
||
if (banner.animate) {
|
||
banner.animate([
|
||
{ opacity:0, transform:'translate(-50%,-50%) scale(0.5)' },
|
||
{ opacity:1, transform:'translate(-50%,-50%) scale(1.15)', offset:0.2 },
|
||
{ opacity:1, transform:'translate(-50%,-50%) scale(1)', offset:0.8 },
|
||
{ opacity:0, transform:'translate(-50%,-50%) scale(1.25)' }
|
||
], { duration:3200, easing:'ease-out' }).onfinish = function(){ banner.remove(); };
|
||
} else {
|
||
setTimeout(function(){ banner.remove(); }, 3200);
|
||
}
|
||
|
||
// 순차 발사
|
||
for(var s=0;s<NUM_ROCKETS;s++){
|
||
(function(delay){ setTimeout(launchRocket, delay); })(s*LAUNCH_GAP);
|
||
}
|
||
|
||
var start = performance.now();
|
||
var DURATION = 3500;
|
||
|
||
function frame(now){
|
||
var elapsed = now - start;
|
||
ctx.clearRect(0, 0, W, H);
|
||
ctx.globalCompositeOperation = 'lighter'; // 겹칠수록 밝게 → 글로우
|
||
|
||
// 로켓
|
||
for(var i=rockets.length-1;i>=0;i--){
|
||
var r = rockets[i];
|
||
r.y += r.vy;
|
||
var rs = fwSprite(r.color);
|
||
ctx.globalAlpha = 1;
|
||
ctx.drawImage(rs, r.x-7, r.y-7, 14, 14);
|
||
ctx.globalAlpha = 0.35;
|
||
ctx.drawImage(rs, r.x-5, r.y+4, 10, 10); // 꼬리
|
||
if(r.y <= r.targetY){ explode(r.x, r.y, r.color); rockets.splice(i,1); }
|
||
}
|
||
|
||
// 파티클
|
||
for(var j=particles.length-1;j>=0;j--){
|
||
var p = particles[j];
|
||
p.vy += 0.04; // 중력
|
||
p.vx *= 0.985; p.vy *= 0.985;
|
||
p.x += p.vx; p.y += p.vy;
|
||
p.life -= p.decay;
|
||
if(p.life<=0){ particles.splice(j,1); continue; }
|
||
var d = p.size * (0.6 + p.life*0.6) * 3; // 스프라이트 표시 크기
|
||
ctx.globalAlpha = Math.max(0, p.life);
|
||
ctx.drawImage(fwSprite(p.color), p.x - d/2, p.y - d/2, d, d);
|
||
}
|
||
|
||
ctx.globalAlpha = 1;
|
||
ctx.globalCompositeOperation = 'source-over';
|
||
|
||
if(elapsed < DURATION || rockets.length > 0 || particles.length > 0){
|
||
requestAnimationFrame(frame);
|
||
} else if(canvas.parentNode){
|
||
canvas.parentNode.removeChild(canvas);
|
||
}
|
||
}
|
||
requestAnimationFrame(frame);
|
||
}
|
||
|
||
jQuery(function(){
|
||
setPickupGauge(jQuery("#pickupquantity").text(), jQuery("#targetquantity").text());
|
||
});
|
||
|
||
var rstSaveInput = function(json) {
|
||
$('#map-modal-input').modal('hide'); // Bootstrap 정상 닫기 시도
|
||
$('#map-modal-input').removeClass('show in').css('display', 'none').attr('aria-hidden', 'true'); // 남아 있을 수 있는 클래스 제거
|
||
$('.modal-backdrop').remove(); // backdrop 제거
|
||
|
||
showPopupMessage(json.msg);
|
||
//drawPoint();
|
||
updatePickupQty();
|
||
}
|
||
|
||
function replaceAll(str, searchStr, replaceStr) {
|
||
|
||
return str.split(searchStr).join(replaceStr);
|
||
}
|
||
|
||
function initAddOrder(customer_id, index){
|
||
//jQuery("#map-modal-order .d_customeruid").val(customer_id);
|
||
//jQuery("#map-modal-order .c_index").val(index);
|
||
//jQuery("#map-modal-order .d_orderdate").val(jQuery("#orderdate").val());
|
||
let data = {
|
||
"orderdate" : jQuery("#orderdate").val(),
|
||
"driveruid" : jQuery("#driver").val(),
|
||
"customer_uid" : customer_id,
|
||
"d_createruid" : <?=$_SESSION['ss_UID']?>,
|
||
"c_index" : index
|
||
}
|
||
|
||
api('addOrderDirect', data, rstAddOrder);
|
||
}
|
||
|
||
var rstAddOrder = function(json) {
|
||
//$('#map-modal-order .close').trigger('click');
|
||
|
||
//jQuery(".modal").removeClass("show");
|
||
//jQuery(".modal-backdrop").remove();
|
||
if (json.c_return == 0) {
|
||
jQuery(".addOrderBtn_"+json.c_index).addClass('hidden');
|
||
jQuery(".removeOrderBtn_"+json.c_index).removeClass('hidden');
|
||
showPopupMessage(json.msg);
|
||
} else {
|
||
jQuery(".addOrderBtn_"+json.c_index).addClass('hidden');
|
||
jQuery(".removeOrderBtn_"+json.c_index).removeClass('hidden');
|
||
showPopupMessage(json.msg);
|
||
drawPoint(); // Add order시 flag 가 변경되지 않으므로 필요함
|
||
}
|
||
|
||
|
||
}
|
||
|
||
function showPopupMessage(msg) {
|
||
jQuery("#popupMessage").text(msg);
|
||
$('#popupMessage').fadeIn().delay(2000).fadeOut();
|
||
}
|
||
|
||
function removeOrder(id, duid, index, accountno) {
|
||
jQuery("#modal-confirm .order-date").val(jQuery("#orderdate").val());
|
||
jQuery("#modal-confirm .id").val(id);
|
||
jQuery("#modal-confirm .duid").val(duid);
|
||
jQuery("#modal-confirm .c_index").val(index);
|
||
jQuery("#modal-confirm .accountno").val(accountno);
|
||
/*
|
||
if(confirm('Are you sure you want to remove the order?')) {
|
||
let data = {
|
||
"orderdate" : jQuery("#orderdate").val(),
|
||
"id" : id,
|
||
"c_index" : index
|
||
}
|
||
|
||
api('removeOrder', data, rstRemoveOrder);
|
||
}*/
|
||
|
||
|
||
}
|
||
|
||
function confirmRemoveOrder(){
|
||
let data = {
|
||
"orderdate" : jQuery("#modal-confirm .order-date").val(),
|
||
"id" : jQuery("#modal-confirm .id").val(),
|
||
"duid" : jQuery("#modal-confirm .duid").val(),
|
||
"c_index" : jQuery("#modal-confirm .c_index").val(),
|
||
"accountno" : jQuery("#modal-confirm .accountno").val(),
|
||
"createruid" : <?=$_SESSION['ss_UID']?>
|
||
}
|
||
|
||
api('removeOrder', data, rstRemoveOrder);
|
||
|
||
//let pickupdata = {
|
||
// "route_date" : jQuery("#orderdate").val(),
|
||
// "driver" : jQuery("#driver").val()
|
||
//}
|
||
//api('inqPickupQty', pickupdata, rstInqPickupQty);
|
||
|
||
}
|
||
|
||
var rstRemoveOrder = function(json) {
|
||
showPopupMessage(json.msg);
|
||
jQuery(".addOrderBtn_"+json.c_index).removeClass('hidden');
|
||
jQuery(".removeOrderBtn_"+json.c_index).addClass('hidden');
|
||
drawPoint(); // remove order시 flag 가 변경되지 않으므로 필요함
|
||
|
||
updatePickupQty();
|
||
}
|
||
|
||
|
||
function addOrderValidation(){
|
||
jQuery("#map-modal-order .d_ruid").css("border","1px solid black");
|
||
|
||
if(jQuery("#map-modal-order .d_ruid").val().trim() === '') {
|
||
jQuery("#map-modal-order .d_ruid").css("border","1px solid red");
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
function initInput(customer_id, customer_no){
|
||
// input modal 에서 저장버튼 클릭 후 다시 오픈할 때, modal 내부에 포커스가 남아있어서 modal 이 오픈되지 않는 이슈 (modal-backdrop 만 열려서 전체 클릭이 막힘)
|
||
// 1. 현재 포커스를 가진 요소에서 포커스를 제거하고 body에 포커스
|
||
jQuery(':focus').blur();
|
||
jQuery('body').focus();
|
||
// 2. 모달 자체의 속성 초기화 (Bootstrap의 동작을 돕기 위해 유지)
|
||
jQuery('#map-modal-input').attr('aria-hidden', 'false').removeAttr('tabindex');
|
||
// end input modal
|
||
|
||
let loadingTxt = 'Loading...';
|
||
jQuery("#map-modal-input .shortinput-store-name").text(loadingTxt);
|
||
// =슬러지 에러메세지 초기화=====================================================
|
||
jQuery(".qty-error, .sludge-error").hide();
|
||
jQuery("#map-modal-input .d_quantity, #map-modal-input .d_sludge").css("border", "1px solid #ced4da");
|
||
// ===========================================================================
|
||
jQuery("#map-modal-input .d_uid, #map-modal-input .d_customeruid, #map-modal-input .d_customerno, #map-modal-input .orderdate, #map-modal-input .d-ruid, #map-modal-input .d_visitdate, #map-modal-input .d_quantity, #map-modal-input .d_sludge, #map-modal-input .d_note").val('');
|
||
jQuery("#map-modal-input .d_payamount, #map-modal-input .d_payeename").val('');
|
||
jQuery("#map-modal-input .d_customeruid").val(customer_id);
|
||
jQuery("#map-modal-input .d_customerno").val(customer_no);
|
||
jQuery("#map-modal-input .d_paystatusunpaid").prop("checked", true);
|
||
jQuery("#map-modal-input .d_orderdate").val(jQuery("#orderdate").val());
|
||
<? if ($_SESSION['ss_LEVEL'] == 9) { ?>
|
||
jQuery("#map-modal-input .d_visitdate").prop("readonly", true);
|
||
|
||
let orderdateSTR = jQuery("#orderdate").val();
|
||
let orderdateRT = orderdateSTR.replaceAll("-","");
|
||
//alert(orderdateRT);
|
||
if(orderdateRT < <?=date("Ymd")?>) {
|
||
jQuery("#map-modal-input .btn-input-save").prop("disabled", true);
|
||
}else {
|
||
jQuery("#map-modal-input .btn-input-save").prop("disabled", false);
|
||
}
|
||
|
||
<? } ?>
|
||
let data = {
|
||
"c_uid" : customer_id,
|
||
"orderdate" : jQuery("#orderdate").val()
|
||
}
|
||
|
||
api('initInput', data, rstInitInput);
|
||
}
|
||
|
||
var rstInitInput = function(json) {
|
||
jQuery("#map-modal-input .shortinput-store-name").text(json.result['d_name']);
|
||
jQuery("#map-modal-input .shortinput-rate").text(json.result['d_rate']);
|
||
jQuery("#map-modal-input .d_driveruid").val(json.result['d_driveruid']);
|
||
jQuery("#map-modal-input .d_druid").val(json.result['d_druid']);
|
||
jQuery("#map-modal-input .d_paymenttype").val(json.result['d_paymenttype']);
|
||
jQuery("#map-modal-input .d_sludge").val(json.result['d_sludge']);
|
||
|
||
if(json.result['d_uid'].length > 0) {
|
||
jQuery("#map-modal-input .d_uid").val(json.result['d_uid']);
|
||
jQuery("#map-modal-input .d_quantity").val(json.result['d_quantity']);
|
||
jQuery("#map-modal-input .d_note").val(json.result['d_note']);
|
||
|
||
if(json.result['d_paymenttype'] === 'CA') {
|
||
jQuery("#map-modal-input .CA_option").removeClass("hide");
|
||
|
||
jQuery("#map-modal-input .d_payamount").val(json.result['d_payamount']);
|
||
jQuery("#map-modal-input .d_payeename").val(json.result['d_payeename']);
|
||
|
||
if(json.result['d_paystatus'] === 'P') {
|
||
jQuery("#map-modal-input .d_paystatuspaid").prop("checked", true);
|
||
jQuery("#map-modal-input .d_paystatusunpaid").prop("unchecked", true);
|
||
} else if (json.result['d_paystatus'] === 'N') {
|
||
jQuery("#map-modal-input .d_paystatuspaid").prop("unchecked", true);
|
||
jQuery("#map-modal-input .d_paystatusunpaid").prop("checked", true);
|
||
}
|
||
|
||
} else{
|
||
jQuery("#map-modal-input .CA_option").addClass("hide");
|
||
}
|
||
}else{
|
||
if(json.result['d_paymenttype'] === 'CA') {
|
||
jQuery("#map-modal-input .CA_option").removeClass("hide");
|
||
}else{
|
||
jQuery("#map-modal-input .CA_option").addClass("hide");
|
||
}
|
||
}
|
||
|
||
//alert (jQuery("#map-modal-input .d_uid").val());
|
||
//alert (jQuery("#map-modal-input .d_paymenttype").val());
|
||
|
||
//visitdate setting
|
||
let visitdate = json.result['d_orderdate'];
|
||
//jQuery("#map-modal-input .d_visitdate").val(visitdate.substring(0, 4) + '-' + visitdate.substring(4, 6) + '-' + visitdate.substring(6, 8) + 'T' + visitdate.substring(8, 10) + ':' + visitdate.substring(10, 12));
|
||
jQuery("#map-modal-input .d_visitdate").val(visitdate.substring(0, 4) + '-' + visitdate.substring(4, 6) + '-' + visitdate.substring(6, 8));
|
||
|
||
}
|
||
|
||
function saveInputValidation(){
|
||
// 1. 초기화 (테두리 원복 및 에러메시지 숨김)
|
||
jQuery("#map-modal-input .d_quantity, #map-modal-input .d_sludge").css("border", "1px solid black");
|
||
jQuery(".qty-error, .sludge-error").hide();
|
||
|
||
// 2. 수량(Quantity) 검사
|
||
if(jQuery("#map-modal-input .d_quantity").val().trim() === '') {
|
||
jQuery("#map-modal-input .d_quantity").css("border", "1px solid red");
|
||
jQuery(".qty-error").show(); // 수량 에러 문구 표시
|
||
jQuery("#map-modal-input .d_quantity").focus();
|
||
return false;
|
||
}
|
||
|
||
// 3. 슬러지(Sludge) 검사
|
||
if(jQuery("#map-modal-input .d_sludge").val().trim() === '') {
|
||
// .number-input 박스 자체나 input에 빨간 테두리
|
||
jQuery("#map-modal-input .d_sludge").css("border", "1px solid red");
|
||
jQuery(".sludge-error").show(); // 슬러지 에러 문구 표시
|
||
jQuery("#map-modal-input .d_sludge").focus();
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
// 경로를 지도에서 제거하는 함수
|
||
function clearRoute() {
|
||
if (directionsRenderer) {
|
||
//directionsRenderer.setDirections({routes: []}); // 경로 데이터를 비우기
|
||
// 또는
|
||
directionsRenderer.setMap(null); // 지도에서 경로 제거
|
||
}
|
||
}
|
||
|
||
var rstInqRouteAddress = function(json) {
|
||
let output = '';
|
||
let tableData = '';
|
||
for(let i=0; i < json.result.length; i++) {
|
||
output += '<div class="draggable-item" draggable="true" id="visit_'+i+'" data="'+json.result[i]['lat']+'*&^&*'+json.result[i]['lon']+'*&^&*'+json.result[i]['name']+'*&^&*'+json.result[i]['address']+'*&^&*'+i+'">';
|
||
output += '<table>';
|
||
output += '<tr>';
|
||
output += '<td>'+(i+1)+'</td>';
|
||
output += '<td><b>'+json.result[i]['name']+'</b></td>';
|
||
output += '<td>'+json.result[i]['address']+', '+json.result[i]['city']+', '+json.result[i]['postal']+'</td>';
|
||
output += '</tr>';
|
||
output += '</table>';
|
||
output += '</div>';
|
||
|
||
tableData += '<div class="item-information-'+i+'">';
|
||
tableData += '<input type="text" class="account" value="'+json.result[i]['accountno']+'">';
|
||
tableData += '<input type="text" class="maincontainer" value="'+json.result[i]['maincontainer']+'">';
|
||
tableData += '<input type="text" class="paymenttype" value="'+json.result[i]['paymenttype']+'">';
|
||
tableData += '<input type="text" class="rate" value="'+json.result[i]['rate']+'">';
|
||
tableData += '<input type="text" class="address" value="'+json.result[i]['address']+'">';
|
||
tableData += '<input type="text" class="city" value="'+json.result[i]['city']+'">';
|
||
tableData += '<input type="text" class="lastpickupquantity" value="'+json.result[i]['lastpickupquantity']+'">';
|
||
tableData += '<input type="text" class="estquantity" value="'+json.result[i]['estquantity']+'">';
|
||
tableData += '<input type="text" class="quantity" value="'+json.result[i]['quantity']+'">';
|
||
tableData += '<input type="text" class="lastpaiddate" value="'+json.result[i]['lastpaiddate']+'">';
|
||
tableData += '<input type="text" class="this_year" value="'+json.result[i]['THIS_YEAR']+'">';
|
||
tableData += '<input type="text" class="last_year" value="'+json.result[i]['LAST_YEAR']+'">';
|
||
tableData += '<input type="text" class="fullcycle" value="'+json.result[i]['fullcycle']+'">';
|
||
tableData += '<input type="text" class="lastpickupdate" value="'+json.result[i]['lastpickupdate']+'">';
|
||
tableData += '</div>';
|
||
}
|
||
jQuery("#routePlannerModal #addressList").html(output);
|
||
jQuery("#routePlannerModal #addressInformationList").html(tableData);
|
||
|
||
let draggedItem = null;
|
||
document.querySelectorAll('.draggable-item').forEach(item => {
|
||
item.addEventListener('dragstart', function(e) {
|
||
draggedItem = this;
|
||
});
|
||
|
||
item.addEventListener('dragover', function(e) {
|
||
e.preventDefault(); // Necessary for allowing dropping.
|
||
});
|
||
|
||
item.addEventListener('drop', function(e) {
|
||
if (this !== draggedItem) {
|
||
let list = document.getElementById('addressList');
|
||
let currentPos = Array.from(list.children).indexOf(this);
|
||
let draggedPos = Array.from(list.children).indexOf(draggedItem);
|
||
if (currentPos < draggedPos) {
|
||
list.insertBefore(draggedItem, this);
|
||
} else {
|
||
list.insertBefore(draggedItem, this.nextSibling);
|
||
}
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
// Nearby 반경 원 생성 및 추가 함수
|
||
var circle;
|
||
function addCircle(center) {
|
||
circle = new google.maps.Circle({
|
||
strokeColor: '#FF0000', // 원의 선 색상
|
||
strokeOpacity: 0.8, // 원의 선 불투명도
|
||
strokeWeight: 2, // 원의 선 굵기
|
||
fillColor: '#FF0000', // 원의 채우기 색상
|
||
fillOpacity: 0.05, // 원의 채우기 불투명도
|
||
map: map, // 원이 추가될 지도 객체
|
||
center: center, // 원의 중심점 (위도, 경도)
|
||
radius: 1000 // 원의 반경 (미터 단위) 1km
|
||
});
|
||
map.fitBounds(circle.getBounds());
|
||
}
|
||
|
||
// Nearby 반경 원 삭제 함수
|
||
function removeCircle() {
|
||
if (circle) {
|
||
circle.setMap(null);
|
||
}
|
||
}
|
||
|
||
function drawPoint(){
|
||
if(!jQuery(".btn-route-print").hasClass("hidden")){
|
||
jQuery(".btn-route-print").addClass("hidden")
|
||
}
|
||
removeCircle();
|
||
clearRoute();
|
||
|
||
if (currentMarker) {
|
||
currentMarker.setMap(null); // Remove existing marker
|
||
currentMarker = null;
|
||
offLoading();
|
||
}
|
||
|
||
let quantity_array = [];
|
||
for(let i=0; i < jQuery(".qty").length; i++) {
|
||
quantity_array.push(
|
||
{
|
||
"checked" : jQuery(".qty:eq("+i+")").is(":checked"),
|
||
"min" : jQuery(".qty-min:eq("+i+")").val(),
|
||
"max" : jQuery(".qty-max:eq("+i+")").val()
|
||
}
|
||
);
|
||
}
|
||
|
||
let data = {
|
||
"search_date" : jQuery("#orderdate").val(),
|
||
"option" : {
|
||
"ordered" : jQuery(".chk-ordered").is(":checked")
|
||
},
|
||
"driver" : jQuery("#driver").val(),
|
||
"search" : jQuery("#search-keyword").val(),
|
||
"quantity" : quantity_array
|
||
}
|
||
|
||
api('inqPoint', data, rstInqPoint);
|
||
|
||
/*COOKIE UPDATE*/
|
||
let cookieValue = '0:' + jQuery(".qty-max:eq(0)").val();
|
||
for(let i=1;i < jQuery(".qty-min").length-1;i++) {
|
||
cookieValue += '_';
|
||
cookieValue += jQuery(".qty-min:eq("+i+")").val() + ':' + jQuery(".qty-max:eq("+i+")").val();
|
||
}
|
||
cookieValue += '_' + jQuery(".qty-min:eq("+(jQuery(".qty-min").length-1)+")").val() + ':MAX';
|
||
setCookie('qtySetting',cookieValue);
|
||
}
|
||
|
||
var marker = [];
|
||
var markerwindow = [];
|
||
var overlay = [];
|
||
|
||
var clusterer = null;
|
||
var clusterInfoGlobal = null;
|
||
var typeOrder = { "#FF0000": 1, "#800080": 2, "#FF80FF": 3, "#FFFDD5": 4, "#7B7A7A": 5 }; // Request > Scheduled > Normal > Normal(driver add) > Finished
|
||
|
||
var rstInqPoint = function(json) {
|
||
if (clusterInfoGlobal) {
|
||
clusterInfoGlobal.close();
|
||
clusterInfoGlobal = null;
|
||
}
|
||
|
||
for(let j=0; j < marker.length; j++) {
|
||
marker[j].setMap(null);
|
||
marker[j] = null;
|
||
if(overlay[j] !== undefined) {
|
||
overlay[j].setMap(null);
|
||
overlay[j] = null;
|
||
}
|
||
}
|
||
|
||
if (clusterer) {
|
||
clusterer.setMap(null);
|
||
clusterer = null;
|
||
}
|
||
|
||
// 닫힘 배지 마커 정리
|
||
if (typeof closedBadgeMarker !== 'undefined' && closedBadgeMarker) {
|
||
Object.keys(closedBadgeMarker).forEach(function (idx) {
|
||
if (closedBadgeMarker[idx]) closedBadgeMarker[idx].setMap(null);
|
||
});
|
||
closedBadgeMarker = {};
|
||
}
|
||
if (window.closedNowSet) window.closedNowSet = new Set();
|
||
|
||
marker = [];
|
||
overlay = [];
|
||
let pointGeocoder = new google.maps.Geocoder();
|
||
let location = '';
|
||
|
||
for(let i=0; i < json.result.length; i++) {
|
||
let property = {
|
||
index : i,
|
||
store: json.result[i]['name'],
|
||
accountno: json.result[i]['accountno'],
|
||
paymentcycle: json.result[i]['paymentcycle'],
|
||
maincontainer: json.result[i]['maincontainer'],
|
||
rate: json.result[i]['rate'],
|
||
estqty: json.result[i]['estqty'],
|
||
phone: json.result[i]['phone'],
|
||
address: json.result[i]['address'],
|
||
paymentType: json.result[i]['payment_type'],
|
||
date: json.result[i]['order_date'],
|
||
ordertype: json.result[i]['ordertype'],
|
||
customerid: json.result[i]['id'],
|
||
type: json.result[i]['type'],
|
||
d_status: json.result[i]['d_status'],
|
||
schedule: json.result[i]['schedule'],
|
||
colour: "transparency",
|
||
color: json.result[i]['color'],
|
||
lat: json.result[i]['lat'],
|
||
lon: json.result[i]['lon'],
|
||
comment: json.result[i]['comment'],
|
||
container_location: json.result[i]['container_location'],
|
||
lastPickupDate: json.result[i]['last_pickup_date'],
|
||
lastPaidDate: json.result[i]['last_paid_date'],
|
||
duid: json.result[i]['duid'],
|
||
druid: json.result[i]['druid'],
|
||
rnote: json.result[i]['rnote'],
|
||
containers: json.result[i]['containers'] || [],
|
||
|
||
<? if ($_SESSION['ss_LEVEL'] == 9) { ?>
|
||
orderFlag: json.result[i]['orderFlag']
|
||
<? } else { ?>
|
||
orderFlag: ''
|
||
<? } ?>
|
||
|
||
};
|
||
let icon = (json.result[i]['type'] === 'flag')?getFlagIcon(json.result[i]['color']):getMarkerIcon(json.result[i]['color']);
|
||
//let orderFlagValue = (json.result[i]['orderFlag'] === '1')?"":"DISABLED";
|
||
|
||
if(json.result[i]['lat'].length < 1) {
|
||
pointGeocoder.geocode({ address: json.result[i]['address'] }, (results, status) => {
|
||
if (status === "OK" && results[0]) {
|
||
|
||
location = results[0].geometry.location;
|
||
latitude = results[0].geometry.location.lat();
|
||
longitude = results[0].geometry.location.lng();
|
||
|
||
let updtPointdata = {
|
||
"marker_index" : i,
|
||
"id" : json.result[i]['id'],
|
||
"accountno" : json.result[i]['accountno'],
|
||
"lat" : latitude,
|
||
"lon" : longitude,
|
||
"property" : property,
|
||
"name" : json.result[i]['name'],
|
||
"qty" : json.result[i]['estqty']
|
||
// 2025.06.19
|
||
//"qty" : json.result[i]['qty']
|
||
}
|
||
|
||
api('updtPoint', updtPointdata, rstUpdtPoint);
|
||
} else {
|
||
console.error("Geocoding failed for the account address: " + status);
|
||
}
|
||
});
|
||
}else{
|
||
|
||
location = {lat: parseFloat(json.result[i]['lat']), lng: parseFloat(json.result[i]['lon'])};
|
||
|
||
marker[i] = new google.maps.Marker({
|
||
position: location,
|
||
map: map,
|
||
icon: icon,
|
||
animation: (property.rnote && /\S/.test(property.rnote) && property.d_status !== 'F') ? google.maps.Animation.BOUNCE : null
|
||
});
|
||
|
||
marker[i].customType = property.type; //'flag' 또는 'map-marker'
|
||
marker[i].property = property;
|
||
|
||
// Add an info window with company information
|
||
markerwindow[i] = new google.maps.InfoWindow({
|
||
content: `<strong>`+json.result[i]['name']+`</strong><br>Est. Quantity : `+json.result[i]['estqty']
|
||
});
|
||
|
||
marker[i].addListener('mouseover', () => {
|
||
markerwindow[i].open(map, marker[i]);
|
||
});
|
||
|
||
google.maps.event.addListener(markerwindow[i], 'domready', function() {
|
||
// InfoWindow의 닫기 버튼만 숨기기
|
||
const iwOuter = jQuery(".gm-style-iw").last();
|
||
const closeBtn = iwOuter.parent().find(".gm-ui-hover-effect");
|
||
closeBtn.css("display","none");
|
||
});
|
||
|
||
marker[i].addListener('mouseout', () => {
|
||
markerwindow[i].close();
|
||
});
|
||
|
||
marker[i].addListener('click', function() {
|
||
if(overlay[i]) {
|
||
overlay[i].setMap(null);
|
||
}
|
||
let content = buildContent(property); // 클릭 시점 생성 → closedNowSet(닫힘 배지) 반영
|
||
overlay[i] = new CustomOverlay(map, marker[i].getPosition(), content.innerHTML, i);
|
||
});
|
||
|
||
}
|
||
|
||
}
|
||
|
||
// 클러스터 생성
|
||
if (marker.length > 0) {
|
||
// flag 타입만 필터링
|
||
const flagMarkers = marker.filter(m => m.customType === 'flag');
|
||
|
||
if (flagMarkers.length > 0) {
|
||
// 사이즈 줄이기
|
||
const algorithm = new markerClusterer.GridAlgorithm({
|
||
gridSize: 5,
|
||
maxDistance: 50,
|
||
});
|
||
|
||
clusterer = new markerClusterer.MarkerClusterer({
|
||
map,
|
||
markers: flagMarkers,
|
||
algorithm,
|
||
renderer: {
|
||
render: ({ count, position, markers }) => {
|
||
// 클러스터 안에 rnote 있는 flag 마커가 한 개라도 포함됐는지 확인
|
||
const hasRnoteMarker = markers.some(m =>
|
||
m.property && m.property.rnote && /\S/.test(m.property.rnote)
|
||
);
|
||
|
||
markers.sort((a, b) => {
|
||
const t1 = a.property.color || "#FF80FF";
|
||
const t2 = b.property.color || "#FF80FF";
|
||
return typeOrder[t1] - typeOrder[t2];
|
||
});
|
||
|
||
const topColor = markers[0]?.property?.color || "#4285f4";
|
||
const size = Math.min(50, 30 + Math.log(count) * 10);
|
||
const textColor = (String(topColor).toUpperCase() === "#FFFDD5") ? "#000" : "#fff";
|
||
|
||
// 클러스터 내 닫힘 마커: 남은시간 최대값을 알약으로 표시(느낌표 최소화).
|
||
// 시간이 있는 닫힘이 하나도 없고 "오늘 더 안 열림"만 있으면 !
|
||
const closedProps = markers.map(m => m.property).filter(p => isClosedNowProp(p));
|
||
const closedMins = closedProps
|
||
.map(p => closedMinutesFor(p.customerid))
|
||
.filter(v => v != null && v > 0);
|
||
const clusterMaxLabel = closedMins.length ? remainLabel(Math.max.apply(null, closedMins)) : null;
|
||
|
||
let W = 34;
|
||
let closedBadgeSvg = '';
|
||
if (clusterMaxLabel) {
|
||
const pw = Math.max(22, 12 + clusterMaxLabel.length * 7);
|
||
W = Math.max(34, 21 + pw + 2);
|
||
closedBadgeSvg =
|
||
`<rect x="21" y="0.5" width="${pw}" height="15" rx="7.5" fill="#f0ad4e" stroke="#ffffff" stroke-width="1"/>
|
||
<text x="${21 + pw / 2}" y="12" text-anchor="middle" font-size="11" font-weight="bold" font-family="${BADGE_FONT}" fill="#5a3d00">${clusterMaxLabel}</text>`;
|
||
} else if (closedProps.length > 0) {
|
||
closedBadgeSvg =
|
||
`<circle cx="27" cy="7" r="6.5" fill="#e53935" stroke="#fff" stroke-width="1.5"/>
|
||
<text x="27" y="10.5" text-anchor="middle" font-size="9" font-weight="bold" font-family="${BADGE_FONT}" fill="#fff">!</text>`;
|
||
}
|
||
|
||
return new google.maps.Marker({
|
||
position,
|
||
icon: {
|
||
url:
|
||
"data:image/svg+xml;charset=UTF-8," +
|
||
encodeURIComponent(`
|
||
<svg xmlns="http://www.w3.org/2000/svg" width="${W}" height="34">
|
||
<circle cx="15" cy="15" r="13"
|
||
fill="${topColor}" />
|
||
<text x="15" y="20" text-anchor="middle"
|
||
font-size="12" font-family="${BADGE_FONT}" fill="${textColor}">${count}</text>
|
||
${closedBadgeSvg}
|
||
</svg>
|
||
`),
|
||
scaledSize: new google.maps.Size(W, 34),
|
||
anchor: new google.maps.Point(15, 15),
|
||
},
|
||
zIndex: Number(google.maps.Marker.MAX_ZINDEX) + count,
|
||
|
||
animation: (
|
||
// 클러스터 안에 rnote가 있으면서 finished가 아닌 마커가 1개라도 있는 경우만 Bounce
|
||
markers.some(m =>
|
||
m.property &&
|
||
m.property.rnote && /\S/.test(m.property.rnote) &&
|
||
m.property.d_status !== 'F'
|
||
)
|
||
) ? google.maps.Animation.BOUNCE : null,
|
||
});
|
||
},
|
||
},
|
||
|
||
onClusterClick: (...args) => {
|
||
let clusterObj = null;
|
||
|
||
if (args.length === 1 && args[0]) {
|
||
const a = args[0];
|
||
clusterObj = a.cluster || a;
|
||
} else if (args.length >= 2) {
|
||
clusterObj = args[1];
|
||
}
|
||
|
||
if (!clusterObj) return false;
|
||
|
||
const markersInCluster =
|
||
clusterObj.markers ||
|
||
(typeof clusterObj.getMarkers === "function"
|
||
? clusterObj.getMarkers()
|
||
: []) ||
|
||
[];
|
||
|
||
const clusterCenter =
|
||
clusterObj.position ||
|
||
(typeof clusterObj.getCenter === "function"
|
||
? clusterObj.getCenter()
|
||
: null);
|
||
|
||
if (!markersInCluster.length || !clusterCenter) return false;
|
||
|
||
if (clusterInfoGlobal) {
|
||
clusterInfoGlobal.close();
|
||
}
|
||
|
||
markersInCluster.sort((a, b) => {
|
||
const t1 = a.property.color || "#FF80FF";
|
||
const t2 = b.property.color || "#FF80FF";
|
||
return typeOrder[t1] - typeOrder[t2];
|
||
});
|
||
|
||
let html =
|
||
"<div style='min-width:220px;max-height:240px;overflow:auto'>";
|
||
|
||
markersInCluster.forEach((m) => {
|
||
const p = m.property || {};
|
||
const hasRnote = p.rnote && /\S/.test(p.rnote);
|
||
const isClosed = isClosedNowProp(p);
|
||
const openInLbl = isClosed ? remainLabel(closedMinutesFor(p.customerid)) : null;
|
||
|
||
html += `
|
||
<div class="cluster-item" data-marker-index="${p.index}" style="display:flex; align-items:center; margin-bottom:4px; cursor:pointer;">
|
||
<span style="width:15px; height:15px; margin-right:6px; background-color:${p.color || "#ccc"}; display:inline-block; border:1px solid #999;"></span>
|
||
<strong>${p.store ?? "—"}</strong>
|
||
${
|
||
isClosed
|
||
? (openInLbl
|
||
? `<span title="Opens in ${openInLbl}" style="margin-left:6px; display:inline-flex; align-items:center; padding:0 7px; height:16px; border-radius:8px; background:#f0ad4e; color:#5a3d00; font-size:11px; font-weight:bold;">${openInLbl}</span>`
|
||
: `<span title="Closed today" style="margin-left:6px; display:inline-flex; align-items:center; justify-content:center; width:16px; height:16px; border-radius:8px; background:#e53935; color:#fff; font-size:11px; font-weight:bold;">!</span>`)
|
||
: ""
|
||
}
|
||
${
|
||
hasRnote
|
||
? `<span style="margin-left:6px; padding:2px 4px; background:#FF9800; color:white; border-radius:3px; font-size:10px;">
|
||
notice
|
||
</span>`
|
||
: ""
|
||
}
|
||
</div>
|
||
<div style="margin-left:22px; font-size:12px; color:#333;">
|
||
Est. Quantity : ${p.estqty ?? "-"}
|
||
</div>
|
||
<hr style="margin:7px 0;">
|
||
`;
|
||
});
|
||
html += "</div>";
|
||
|
||
const clusterInfo = new google.maps.InfoWindow({
|
||
content: html,
|
||
position: clusterCenter,
|
||
pixelOffset: new google.maps.Size(0, -25),
|
||
});
|
||
|
||
clusterInfoGlobal = clusterInfo;
|
||
clusterInfo.open(map);
|
||
|
||
google.maps.event.addListenerOnce(
|
||
clusterInfo,
|
||
"domready",
|
||
function () {
|
||
document.querySelectorAll(".cluster-item").forEach((el) => {
|
||
el.addEventListener("click", () => {
|
||
const idx = el.getAttribute("data-marker-index");
|
||
if (idx !== null && marker[idx]) {
|
||
google.maps.event.trigger(marker[idx], "click");
|
||
}
|
||
clusterInfo.close();
|
||
});
|
||
});
|
||
}
|
||
);
|
||
return false;
|
||
},
|
||
});
|
||
}
|
||
|
||
// 클러스터링이 끝났을 때 실행되는 이벤트
|
||
clusterer.addListener('clusteringend', () => {
|
||
marker.forEach(m => {
|
||
if (!m.getMap()) return;
|
||
if (m.property?.rnote && /\S/.test(m.property.rnote) && m.property.d_status !== 'F') {
|
||
m.setAnimation(google.maps.Animation.BOUNCE);
|
||
} else {
|
||
m.setAnimation(null);
|
||
}
|
||
});
|
||
// 클러스터로 숨겨진 마커의 닫힘 배지도 같이 숨김/표시
|
||
if (typeof syncClosedBadges === 'function') syncClosedBadges();
|
||
});
|
||
}
|
||
|
||
applyClosedBadges();
|
||
}
|
||
|
||
function showInfoWindow(position, title, quantity, infoType) {
|
||
const showClose = infoType === 'cluster'; // 클러스터일 때만 X 표시
|
||
|
||
const infoContent = `
|
||
<div class="info-window">
|
||
${showClose ? '<button class="close" onclick="closeInfoWindow()">×</button>' : ''}
|
||
<div class="info-body">
|
||
<strong>${title}</strong><br>
|
||
Est. Quantity: ${quantity}
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
infoWindow.setContent(infoContent);
|
||
infoWindow.setPosition(position);
|
||
infoWindow.open(map);
|
||
}
|
||
|
||
|
||
function addDragEvent(customOverlay) {
|
||
let div = customOverlay.div;
|
||
|
||
//for mobile
|
||
let table = div.querySelector('table');;
|
||
let title = div.querySelector('h4');
|
||
|
||
let isDragging = false;
|
||
let startX, startY;
|
||
let clickPrevent = false;
|
||
|
||
let startDrag = (e) => {
|
||
isDragging = true;
|
||
clickPrevent = false;
|
||
startX = (e.clientX || e.touches[0].clientX) - div.offsetLeft;
|
||
startY = (e.clientY || e.touches[0].clientY) - div.offsetTop;
|
||
e.preventDefault();
|
||
// Prevent map from dragging
|
||
map.setOptions({ draggable: false });
|
||
};
|
||
|
||
let onDrag = (e) => {
|
||
if (isDragging) {
|
||
const currentX = e.clientX || e.touches[0].clientX;
|
||
const currentY = e.clientY || e.touches[0].clientY;
|
||
|
||
const dx = currentX - startX;
|
||
const dy = currentY - startY;
|
||
|
||
if (Math.abs(dx) > 5 || Math.abs(dy) > 5) {
|
||
clickPrevent = true;
|
||
}
|
||
|
||
div.style.left = dx + 'px';
|
||
div.style.top = dy + 'px';
|
||
|
||
e.preventDefault();
|
||
}
|
||
};
|
||
|
||
let endDrag = (e) => {
|
||
if (isDragging) {
|
||
isDragging = false;
|
||
e.preventDefault();
|
||
// Re-enable map dragging
|
||
map.setOptions({ draggable: true });
|
||
|
||
if (!clickPrevent) {
|
||
// If not dragging, it is a click event
|
||
const target = e.target || e.touches[0].target;
|
||
if (target.tagName === 'BUTTON') {
|
||
target.click();
|
||
}
|
||
}
|
||
|
||
// Update the overlay position to the new location
|
||
const overlayProjection = customOverlay.getProjection();
|
||
const divPosition = new google.maps.Point(parseInt(div.style.left, 10), parseInt(div.style.top, 10));
|
||
const newPosition = overlayProjection.fromDivPixelToLatLng(divPosition);
|
||
customOverlay.position = newPosition;
|
||
customOverlay.draw();
|
||
}
|
||
};
|
||
|
||
div.addEventListener('mousedown', startDrag);
|
||
table.addEventListener('touchstart', startDrag, { passive: false });
|
||
title.addEventListener('touchstart', startDrag, { passive: false });
|
||
|
||
document.addEventListener('mousemove', onDrag);
|
||
document.addEventListener('touchmove', onDrag, { passive: false });
|
||
|
||
document.addEventListener('mouseup', endDrag);
|
||
document.addEventListener('touchend', endDrag, { passive: false });
|
||
}
|
||
|
||
|
||
// 깃발 모양 마커 아이콘 생성 함수에 16진수 색상 코드 사용
|
||
function getFlagIcon(color) {
|
||
return {
|
||
path: 'M64 32C64 14.3 49.7 0 32 0S0 14.3 0 32V64 368 480c0 17.7 14.3 32 32 32s32-14.3 32-32V352l64.3-16.1c41.1-10.3 84.6-5.5 122.5 13.4c44.2 22.1 95.5 24.8 141.7 7.4l34.7-13c12.5-4.7 20.8-16.6 20.8-30V66.1c0-23-24.2-38-44.8-27.7l-9.6 4.8c-46.3 23.2-100.8 23.2-147.1 0c-35.1-17.6-75.4-22-113.5-12.5L64 48V32z', // 깃발 모양의 간단한 SVG 경로
|
||
fillColor: color, // 16진수 색상 코드
|
||
fillOpacity: 1,
|
||
strokeColor: 'black',
|
||
strokeWeight: 0.5,
|
||
scale: 0.04,
|
||
};
|
||
}
|
||
|
||
// 원 모양 마커 아이콘 생성 함수에 16진수 색상 코드 사용
|
||
function getMarkerIcon(color) {
|
||
return {
|
||
path: 'M215.7 499.2C267 435 384 279.4 384 192C384 86 298 0 192 0S0 86 0 192c0 87.4 117 243 168.3 307.2c12.3 15.3 35.1 15.3 47.4 0zM192 128a64 64 0 1 1 0 128 64 64 0 1 1 0-128z',
|
||
fillColor: color,
|
||
fillOpacity: 1,
|
||
strokeColor: '#000',
|
||
strokeWeight: 0.5,
|
||
scale: 0.04,
|
||
};
|
||
}
|
||
|
||
// 현재위치 마커 아이콘 생성 함수에 16진수 색상 코드 사용
|
||
function getCurrentIcon(color) {
|
||
return {
|
||
path: 'M256 0c17.7 0 32 14.3 32 32V66.7C368.4 80.1 431.9 143.6 445.3 224H480c17.7 0 32 14.3 32 32s-14.3 32-32 32H445.3C431.9 368.4 368.4 431.9 288 445.3V480c0 17.7-14.3 32-32 32s-32-14.3-32-32V445.3C143.6 431.9 80.1 368.4 66.7 288H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H66.7C80.1 143.6 143.6 80.1 224 66.7V32c0-17.7 14.3-32 32-32zM128 256a128 128 0 1 0 256 0 128 128 0 1 0 -256 0zm128-80a80 80 0 1 1 0 160 80 80 0 1 1 0-160z',
|
||
fillColor: color,
|
||
fillOpacity: 1,
|
||
strokeColor: '#000',
|
||
strokeWeight: 0.5,
|
||
scale: 0.04,
|
||
};
|
||
}
|
||
|
||
function CustomOverlay(map, position, content, index) {
|
||
this.position = position;
|
||
this.content = content;
|
||
this.index = index;
|
||
this.map = map;
|
||
this.div = null;
|
||
this.setMap(map);
|
||
}
|
||
|
||
var rstUpdtPoint = function(json) {
|
||
let location = {lat: parseFloat(json.lat), lng: parseFloat(json.lon)};
|
||
let icon = (json.property['type'] === 'flag')?getFlagIcon(json.property['color']):getMarkerIcon(json.property['color']);
|
||
marker[parseInt(json.markerIndex)] = new google.maps.Marker({
|
||
position: location,
|
||
map: map,
|
||
icon: icon
|
||
});
|
||
|
||
// Add an info window with company information
|
||
markerwindow[parseInt(json.markerIndex)] = new google.maps.InfoWindow({
|
||
content: `<strong>`+json.name+`</strong><br>Est. Quantity : `+json.estqty
|
||
});
|
||
|
||
marker[parseInt(json.markerIndex)].addListener('mouseover', () => {
|
||
markerwindow[parseInt(json.markerIndex)].open(map, marker[parseInt(json.markerIndex)]);
|
||
});
|
||
|
||
marker[parseInt(json.markerIndex)].addListener('mouseout', () => {
|
||
markerwindow[parseInt(json.markerIndex)].close();
|
||
});
|
||
|
||
marker[parseInt(json.markerIndex)].addListener('click', function() {
|
||
let content = buildContent(json.property); // 클릭 시점 생성 → closedNowSet 반영
|
||
overlay[parseInt(json.markerIndex)] = new CustomOverlay(map, marker[parseInt(json.markerIndex)].getPosition(), content.innerHTML, parseInt(json.markerIndex));
|
||
});
|
||
}
|
||
|
||
async function initMap() {
|
||
// Request needed libraries.
|
||
const { Map } = await google.maps.importLibrary("maps");
|
||
|
||
map = new Map(document.getElementById("map"), {
|
||
zoom: 11,
|
||
center,
|
||
mapId: "4504f8b37365c3d0",
|
||
gestureHandling: 'greedy',
|
||
disableDefaultUI: true,
|
||
clickableIcons: false
|
||
});
|
||
|
||
/*
|
||
//find current position
|
||
if (navigator.geolocation) {
|
||
navigator.geolocation.getCurrentPosition(
|
||
(position) => {
|
||
center = { lat: position.coords.latitude, lng: position.coords.longitude };
|
||
currentLocation = { lat: position.coords.latitude, lon: position.coords.longitude };
|
||
map.setCenter(center);
|
||
}
|
||
);
|
||
}
|
||
*/
|
||
|
||
jQuery("#driver").trigger("change");
|
||
|
||
drawPoint();
|
||
|
||
CustomOverlay.prototype = new google.maps.OverlayView();
|
||
|
||
CustomOverlay.prototype.onAdd = function() {
|
||
var div = document.createElement('div');
|
||
div.style.position = 'absolute';
|
||
div.innerHTML = this.content;
|
||
|
||
this.div = div;
|
||
|
||
var panes = this.getPanes();
|
||
panes.overlayMouseTarget.appendChild(div);
|
||
|
||
// 닫기 버튼에 이벤트 리스너 추가
|
||
var closeButton = div.getElementsByClassName('overlay-close-'+this.index)[0];
|
||
closeButton.addEventListener('click', () => {
|
||
this.hide();
|
||
});
|
||
|
||
jQuery(".btn-add-order:eq("+this.index+")").click(function(){
|
||
addOrder(this.id);
|
||
});
|
||
|
||
addDragEvent(this);
|
||
|
||
};
|
||
|
||
CustomOverlay.prototype.draw = function() {
|
||
var overlayProjection = this.getProjection();
|
||
var position = overlayProjection.fromLatLngToDivPixel(this.position);
|
||
|
||
var div = this.div;
|
||
div.style.left = position.x + 'px';
|
||
div.style.top = position.y + 'px';
|
||
};
|
||
|
||
CustomOverlay.prototype.onRemove = function() {
|
||
if (this.div) {
|
||
this.div.parentNode.removeChild(this.div);
|
||
this.div = null;
|
||
}
|
||
};
|
||
|
||
CustomOverlay.prototype.hide = function() {
|
||
if (this.div) {
|
||
this.div.style.visibility = 'hidden';
|
||
}
|
||
};
|
||
|
||
CustomOverlay.prototype.show = function() {
|
||
if (this.div) {
|
||
this.div.style.visibility = 'visible';
|
||
}
|
||
};
|
||
}
|
||
|
||
function toggleHighlight(markerView, property) {
|
||
if (!markerView.content.classList.contains("highlight")) {
|
||
markerView.content.classList.add("highlight");
|
||
markerView.zIndex = 1;
|
||
}else{
|
||
|
||
}
|
||
/*
|
||
|
||
//info modal init
|
||
jQuery("#customerShortInfo .c_uid").val(property['customerid']);
|
||
jQuery(".shortinfo-payment-type").text(property['paymentType']);
|
||
jQuery(".shortinfo-comment").val(property['comment']);
|
||
jQuery(".shortinfo-container-location").val(property['container_location']);
|
||
|
||
//history modal init
|
||
|
||
}*/
|
||
}
|
||
|
||
function showShortInfo(id){
|
||
|
||
let loadingTxt = 'Loading...';
|
||
jQuery("#customerShortInfo .shortinfo-payment-type").text(loadingTxt);
|
||
jQuery("#customerShortInfo #c_comment_ri,#customerShortInfo #c_location").val(loadingTxt).attr("disabled","disabled");
|
||
jQuery("#customerShortInfo .btn-sub").attr("disabled","disabled");
|
||
|
||
// Access Time / Closing Days 표시 조각 로드
|
||
jQuery("#customerShortInfo .c_uid").val(id);
|
||
if (typeof loadCustomerAccess === 'function') loadCustomerAccess(id);
|
||
|
||
let data = {
|
||
"id" : id
|
||
}
|
||
|
||
api('inqShortInfo', data, rstInqShortInfo);
|
||
|
||
}
|
||
|
||
var rstInqShortInfo = function(json) {
|
||
jQuery("#customerShortInfo .c_uid").val(json.result['id']);
|
||
jQuery("#customerShortInfo .c_accountno").val(json.result['accountno']);
|
||
jQuery("#customerShortInfo .shortinfo-store-name").text(json.result['name']);
|
||
jQuery("#customerShortInfo .shortinfo-payment-type").text(json.result['payment_type']);
|
||
jQuery("#customerShortInfo .shortinfo-payment-string").text(json.result['paymentstring']);
|
||
jQuery("#customerShortInfo #c_comment_ri").val(json.result['comment']).attr("disabled",false);
|
||
jQuery("#customerShortInfo #c_location").val(json.result['location']).attr("disabled",false);
|
||
jQuery("#customerShortInfo .btn-sub").attr("disabled",false);
|
||
|
||
if (json.result.install_img && json.result.install_img !== "") {
|
||
jQuery("#installImgArea").html(
|
||
'<a href="'+ json.result.install_img +'" target="_blank">View Image</a>'
|
||
);
|
||
} else {
|
||
jQuery("#installImgArea").html('<span>No image</span>');
|
||
}
|
||
}
|
||
|
||
function updtShortInfo(){
|
||
let data = {
|
||
"id" : jQuery("#customerShortInfo .c_uid").val(),
|
||
"accountno" : jQuery("#customerShortInfo .c_accountno").val(),
|
||
"comment" : jQuery(".shortinfo-comment").val(),
|
||
"location" : jQuery(".shortinfo-container-location").val()
|
||
}
|
||
|
||
api('updtShortInfo', data, rstUpdtShortInfo);
|
||
}
|
||
|
||
var rstUpdtShortInfo = function(json) {
|
||
drawPoint();
|
||
}
|
||
|
||
function showShortNotice(id){
|
||
let loadingTxt = 'Loading...';
|
||
jQuery("#map-modal-note #r_note").val('');
|
||
jQuery("#map-modal-note #r_note").text(loadingTxt);
|
||
|
||
let data = {
|
||
"id" : id
|
||
}
|
||
|
||
api('inqShortNotice', data, rstInqShortNotice);
|
||
}
|
||
|
||
var rstInqShortNotice = function(json) {
|
||
jQuery("#map-modal-note #r_note").val(json.result['rnote']).attr("disabled",false);
|
||
}
|
||
|
||
window.SimpleInstall = {
|
||
customer_uid: null,
|
||
driver_uid: null,
|
||
job_type: null
|
||
};
|
||
|
||
|
||
document.addEventListener('click', function(e){
|
||
const btn = e.target.closest('.btn-container-request');
|
||
if (!btn) return;
|
||
|
||
const payload = {
|
||
c_uid: btn.dataset.cuid,
|
||
c_name: btn.dataset.cname,
|
||
driver_uid: btn.dataset.driver,
|
||
member_uid: btn.dataset.member
|
||
};
|
||
|
||
api('install_wait_get_active', { c_uid: payload.c_uid }, function(res){
|
||
if (!res || !res.ok) return;
|
||
if (res.exists) openSimpleInstallModal(payload, res.data);
|
||
else openSimpleInstallModal(payload, null);
|
||
});
|
||
});
|
||
|
||
document.addEventListener('click', function(e){
|
||
const btn = e.target.closest('.btn-image-modal');
|
||
if (!btn) return;
|
||
|
||
openCustomerImages(
|
||
btn.dataset.cuid,
|
||
btn.dataset.cname
|
||
);
|
||
});
|
||
|
||
function openSimpleInstallModal(data, existing = null) {
|
||
// 기본 세팅
|
||
SimpleInstall.customer_uid = data.c_uid;
|
||
SimpleInstall.driver_uid = data.driver_uid;
|
||
SimpleInstall.member_uid = data.member_uid;
|
||
|
||
document.getElementById('simpleInstallCustomerName').innerText = data.c_name;
|
||
document.getElementById('simpleInstallCustomerUid').value = data.c_uid;
|
||
document.getElementById('simpleInstallDriverUid').value = data.driver_uid;
|
||
|
||
const deleteBtn = document.getElementById('btnSimpleInstallDelete');
|
||
deleteBtn.style.display = 'none';
|
||
|
||
// 초기화
|
||
document.getElementById('simpleInstallPhotos').value = '';
|
||
document.getElementById('simpleInstallNote').value = '';
|
||
document.querySelectorAll('.simple-job-btn').forEach(b => b.classList.remove('active'));
|
||
|
||
// 메타 영역
|
||
const metaWrap = document.getElementById('simpleInstallMeta');
|
||
metaWrap.style.display = 'none';
|
||
document.getElementById('siStatus').innerText = '';
|
||
document.getElementById('siAddedBy').innerText = '';
|
||
document.getElementById('siAddedAt').innerText = '';
|
||
document.getElementById('siScheduled').innerText = '';
|
||
|
||
// 기본: 새로 생성 모드
|
||
SimpleInstall.iw_uid = null;
|
||
SimpleInstall.job_type = null;
|
||
|
||
// 기본: 입력 가능
|
||
setSimpleInstallEditable(true);
|
||
|
||
if (!existing) {
|
||
document.getElementById('btnSimpleInstallSubmit').disabled = true;
|
||
renderSimpleInstallPhotos([]);
|
||
return;
|
||
}
|
||
|
||
// 기존 요청 있음
|
||
SimpleInstall.iw_uid = existing.iw_uid;
|
||
SimpleInstall.job_type = existing.iw_work_type || null;
|
||
|
||
// 메타 표시
|
||
metaWrap.style.display = 'block';
|
||
document.getElementById('siStatus').innerText = existing.iw_status || '';
|
||
document.getElementById('siAddedBy').innerText = existing.iw_request_by_name || '';
|
||
document.getElementById('siAddedAt').innerText = (existing.iw_created_at || existing.iw_request_at || '');
|
||
|
||
const sched = existing.di_install_date ? (existing.di_install_date + (existing.di_install_time ? (' ' + existing.di_install_time) : '')) : '-';
|
||
document.getElementById('siScheduled').innerText = sched;
|
||
document.getElementById('simpleInstallNote').value = existing.iw_request_note || '';
|
||
|
||
// job 버튼 active
|
||
if (existing.iw_work_type) {
|
||
document.querySelectorAll('.simple-job-btn').forEach(btn=>{
|
||
btn.classList.toggle('active', btn.dataset.job === existing.iw_work_type);
|
||
});
|
||
}
|
||
|
||
// 상태별 권한
|
||
if (existing.iw_status === 'DRAFT') {
|
||
deleteBtn.style.display = 'inline-block';
|
||
setSimpleInstallEditable(true);
|
||
document.getElementById('btnSimpleInstallSubmit').disabled = false;
|
||
} else if (existing.iw_status === 'WAITING' || existing.iw_status === 'ASSIGNED') {
|
||
deleteBtn.style.display = 'none';
|
||
setSimpleInstallEditable(false);
|
||
document.getElementById('btnSimpleInstallSubmit').disabled = true;
|
||
} else {
|
||
// 안전장치
|
||
deleteBtn.style.display = 'none';
|
||
setSimpleInstallEditable(false);
|
||
document.getElementById('btnSimpleInstallSubmit').disabled = true;
|
||
}
|
||
|
||
// 사진 표시
|
||
if (existing && existing.photos) {
|
||
renderSimpleInstallPhotos(existing.photos);
|
||
} else {
|
||
renderSimpleInstallPhotos([]);
|
||
}
|
||
}
|
||
|
||
function setSimpleInstallEditable(canEdit) {
|
||
// note
|
||
document.getElementById('simpleInstallNote').disabled = !canEdit;
|
||
// photos
|
||
document.getElementById('simpleInstallPhotos').disabled = !canEdit;
|
||
// job buttons
|
||
document.querySelectorAll('.simple-job-btn').forEach(btn=>{
|
||
btn.disabled = !canEdit;
|
||
});
|
||
// Submit 버튼 텍스트는?
|
||
// document.getElementById('btnSimpleInstallSubmit').innerText = canEdit ? 'Save' : 'Locked';
|
||
}
|
||
|
||
function renderSimpleInstallPhotos(photos){
|
||
|
||
const wrap = document.getElementById('simpleInstallPhotoList');
|
||
const outer = document.getElementById('simpleInstallPhotoWrap');
|
||
|
||
if (!wrap || !outer) return;
|
||
|
||
wrap.innerHTML = '';
|
||
|
||
if (!photos || photos.length === 0) {
|
||
outer.style.display = 'none';
|
||
return;
|
||
}
|
||
|
||
outer.style.display = 'block';
|
||
|
||
photos.forEach(photo => {
|
||
|
||
if (!photo.i_filename) return;
|
||
|
||
const basePath = (photo.i_filepath || '').trim();
|
||
|
||
const filenames = photo.i_filename
|
||
.split(',')
|
||
.map(f => f.trim())
|
||
.filter(Boolean);
|
||
|
||
filenames.forEach(name => {
|
||
|
||
const fullUrl = basePath.endsWith('/')
|
||
? basePath + name
|
||
: basePath + '/' + name;
|
||
|
||
const div = document.createElement('div');
|
||
div.style.width = '120px';
|
||
|
||
div.innerHTML = `
|
||
<img src="${fullUrl}"
|
||
style="width:100%; height:100px; object-fit:cover; border-radius:8px; border:1px solid #ddd;">
|
||
`;
|
||
|
||
wrap.appendChild(div);
|
||
});
|
||
|
||
});
|
||
}
|
||
|
||
function escapeHtml(str) {
|
||
if (str == null) return '';
|
||
return String(str)
|
||
.replace(/&/g, "&")
|
||
.replace(/</g, "<")
|
||
.replace(/>/g, ">")
|
||
.replace(/"/g, """)
|
||
.replace(/'/g, "'");
|
||
}
|
||
|
||
document.querySelectorAll('.simple-job-btn').forEach(btn => {
|
||
|
||
btn.onclick = function() {
|
||
|
||
document.querySelectorAll('.simple-job-btn')
|
||
.forEach(b => b.classList.remove('active'));
|
||
|
||
this.classList.add('active');
|
||
|
||
SimpleInstall.job_type = this.dataset.job;
|
||
|
||
document.getElementById('btnSimpleInstallSubmit').disabled = false;
|
||
};
|
||
});
|
||
|
||
document.getElementById('btnSimpleInstallSubmit').onclick = function(){
|
||
|
||
if (!SimpleInstall.job_type) return;
|
||
|
||
this.disabled = true;
|
||
|
||
$installNote = document.getElementById('simpleInstallNote').value;
|
||
const payload = {
|
||
c_uid: SimpleInstall.customer_uid,
|
||
driver_uid: SimpleInstall.driver_uid,
|
||
member_uid: SimpleInstall.member_uid,
|
||
job_type: SimpleInstall.job_type,
|
||
request_note: $installNote
|
||
};
|
||
|
||
// 기존 draft면 update, 아니면 create
|
||
const action = SimpleInstall.iw_uid ? 'install_wait_update_draft' : 'install_wait_create_draft';
|
||
|
||
if (SimpleInstall.iw_uid) {
|
||
payload.iw_uid = SimpleInstall.iw_uid;
|
||
}
|
||
|
||
api(action, payload, function(res){
|
||
|
||
if (!res || !res.ok){
|
||
alert('Failed to save request');
|
||
document.getElementById('btnSimpleInstallSubmit').disabled = false;
|
||
return;
|
||
}
|
||
|
||
const iw_uid = SimpleInstall.iw_uid || res.iw_uid;
|
||
|
||
// 사진 처리
|
||
const files = document.getElementById('simpleInstallPhotos').files;
|
||
|
||
if (!files || files.length === 0){
|
||
finishSimpleInstall();
|
||
return;
|
||
}
|
||
|
||
const fd = new FormData();
|
||
fd.append('actionStr', 'ADDIMAGE');
|
||
fd.append('i_type', 'install_order');
|
||
fd.append('i_customeruid', SimpleInstall.customer_uid);
|
||
fd.append('i_memberuid', SimpleInstall.member_uid);
|
||
fd.append('i_createdby', <?=$_SESSION['ss_UID']?>);
|
||
fd.append('i_note', $installNote);
|
||
fd.append('i_sourceuid', iw_uid);
|
||
// 파일명 순서대로
|
||
[...files].sort((a, b) => a.name.localeCompare(b.name, undefined, { numeric: true }))
|
||
.forEach(file => {
|
||
fd.append('upload_file[]', file);
|
||
});
|
||
|
||
fetch('/lib/user_process.php', {
|
||
method: 'POST',
|
||
body: fd
|
||
})
|
||
.then(() => finishSimpleInstall())
|
||
.catch(() => finishSimpleInstall());
|
||
});
|
||
};
|
||
|
||
document.getElementById('btnSimpleInstallDelete').onclick = function(){
|
||
|
||
if (!SimpleInstall.iw_uid) return;
|
||
|
||
if (!confirm('Are you sure you want to delete this draft?')) return;
|
||
|
||
this.disabled = true;
|
||
|
||
api('install_wait_delete_draft', {
|
||
iw_uid: SimpleInstall.iw_uid
|
||
}, function(res){
|
||
|
||
if (!res || !res.ok){
|
||
alert('Failed to delete draft');
|
||
document.getElementById('btnSimpleInstallDelete').disabled = false;
|
||
return;
|
||
}
|
||
|
||
$('#map-modal-container-request').modal('hide');
|
||
|
||
});
|
||
};
|
||
|
||
function finishSimpleInstall(){
|
||
$('#map-modal-container-request').modal('hide');
|
||
setTimeout(()=>{
|
||
location.reload(); // Map 리로드
|
||
}, 300);
|
||
}
|
||
|
||
function showPopupMessage_container(message, type = 'success', duration = 1200) {
|
||
|
||
const body = document.querySelector('#myModalPopup .myModalPopup-body');
|
||
if (!body) return;
|
||
|
||
body.innerHTML = message;
|
||
|
||
if (type === 'success') {
|
||
body.style.backgroundColor = '#2A9B56'; // green
|
||
} else {
|
||
body.style.backgroundColor = '#FF8205'; // orange
|
||
}
|
||
|
||
$('#myModalPopup').modal('show');
|
||
|
||
setTimeout(function () {
|
||
$('#myModalPopup').modal('hide');
|
||
}, duration);
|
||
}
|
||
|
||
function inqCurrent() {
|
||
onLoading();
|
||
removeCircle();
|
||
|
||
//set to init
|
||
jQuery(".chk-ordered").prop("checked",true);
|
||
jQuery(".chk-scheduled").prop("checked",false);
|
||
jQuery(".chk-requested").prop("checked",false);
|
||
jQuery("#search-keyword").val("");
|
||
jQuery(".qty").prop("checked", true);
|
||
|
||
if (currentMarker) {
|
||
currentMarker.setMap(null); // Remove existing marker
|
||
currentMarker = null;
|
||
|
||
// map-marker만 제거
|
||
marker.forEach(marker => {
|
||
if (marker.customType === 'map-marker') {
|
||
marker.setMap(null);
|
||
}
|
||
});
|
||
|
||
offLoading();
|
||
}else{
|
||
if (navigator.geolocation) {
|
||
navigator.geolocation.getCurrentPosition(function(position) {
|
||
const currentPosition = {
|
||
lat: position.coords.latitude,
|
||
lng: position.coords.longitude
|
||
};
|
||
|
||
if (currentMarker) {
|
||
currentMarker.setMap(null); // Remove existing marker
|
||
}
|
||
|
||
currentMarker = new google.maps.Marker({
|
||
position: currentPosition,
|
||
map: map,
|
||
icon: getCurrentIcon('#dc3545'),
|
||
title: "Current Location"
|
||
});
|
||
|
||
map.setCenter(currentPosition);
|
||
addCircle(currentPosition);
|
||
|
||
let quantity_array = [];
|
||
for(let i=0; i < jQuery(".qty").length; i++) {
|
||
quantity_array.push(
|
||
{
|
||
"checked" : jQuery(".qty:eq("+i+")").is(":checked"),
|
||
"min" : jQuery(".qty-min:eq("+i+")").val(),
|
||
"max" : jQuery(".qty-max:eq("+i+")").val()
|
||
}
|
||
);
|
||
}
|
||
|
||
let data = {
|
||
"center_lat" : currentPosition.lat,
|
||
"center_lon" : currentPosition.lng,
|
||
"search_date" : jQuery("#orderdate").val(),
|
||
"driver" : jQuery("#driver").val(),
|
||
"quantity" : quantity_array
|
||
}
|
||
|
||
api('inqNearby', data, rstInqPoint);
|
||
|
||
smoothZoom(map, 14, map.getZoom());
|
||
offLoading();
|
||
}, function() {
|
||
offLoading();
|
||
alert("Unable to retrieve your location.");
|
||
});
|
||
} else {
|
||
offLoading();
|
||
alert("Geolocation is not supported by this browser.");
|
||
}
|
||
}
|
||
}
|
||
|
||
$('#customerImageModal').on('shown.bs.modal', function () {
|
||
const body = document.getElementById('customerImageModalBody');
|
||
initCustomerImageSlider(body);
|
||
});
|
||
|
||
function openCustomerImages(customerUid, customerName) {
|
||
|
||
// const modal = new bootstrap.Modal(
|
||
// document.getElementById('customerImageModal')
|
||
// );
|
||
|
||
const nameEl = document.getElementById('imageCustomerName');
|
||
if (nameEl) {
|
||
nameEl.innerText = customerName || '';
|
||
}
|
||
|
||
const body = document.getElementById('customerImageModalBody');
|
||
body.innerHTML = '<div class="text-center text-muted py-5">Loading images...</div>';
|
||
|
||
//modal.show();
|
||
|
||
const params = new URLSearchParams({
|
||
customer_uid: customerUid,
|
||
i_type: 'ALL'
|
||
});
|
||
|
||
fetch('/lib/customer_image.php?' + params.toString())
|
||
.then(res => res.text())
|
||
.then(html => {
|
||
body.innerHTML = html;
|
||
initCustomerImageSlider(body);
|
||
})
|
||
.catch(err => {
|
||
console.error(err);
|
||
body.innerHTML = '<div class="text-danger text-center py-5">Failed to load images.</div>';
|
||
});
|
||
}
|
||
|
||
function inqNearby(coord){
|
||
removeCircle();
|
||
|
||
//set to init
|
||
jQuery(".chk-ordered").prop("checked",true);
|
||
jQuery(".chk-scheduled").prop("checked",false);
|
||
jQuery(".chk-requested").prop("checked",false);
|
||
|
||
jQuery("#search-keyword").val("");
|
||
|
||
jQuery(".qty").prop("checked",true);
|
||
|
||
if (currentMarker) {
|
||
currentMarker.setMap(null); // Remove existing marker
|
||
}
|
||
|
||
let coordArr = coord.split(":");
|
||
center = { lat: parseFloat(coordArr[0]), lng: parseFloat(coordArr[1]) };
|
||
map.setCenter(center);
|
||
|
||
addCircle(center);
|
||
|
||
let quantity_array = [];
|
||
for(let i=0; i < jQuery(".qty").length; i++) {
|
||
quantity_array.push(
|
||
{
|
||
"checked" : jQuery(".qty:eq("+i+")").is(":checked"),
|
||
"min" : jQuery(".qty-min:eq("+i+")").val(),
|
||
"max" : jQuery(".qty-max:eq("+i+")").val()
|
||
}
|
||
);
|
||
}
|
||
|
||
let data = {
|
||
"center_lat" : coordArr[0],
|
||
"center_lon" : coordArr[1],
|
||
"search_date" : jQuery("#orderdate").val(),
|
||
"driver" : jQuery("#driver").val(),
|
||
"quantity" : quantity_array
|
||
}
|
||
|
||
api('inqNearby', data, rstInqPoint);
|
||
}
|
||
|
||
function initHistory(id){
|
||
jQuery("#map-modal-history tbody").html("<tr><td colspan='5' class='text-center'>Loading</td></tr>");
|
||
|
||
let data = {
|
||
"id" : id
|
||
}
|
||
|
||
api('inqHistory', data, rstInqHistory);
|
||
}
|
||
|
||
var rstInqHistory = function(json) {
|
||
let output = '';
|
||
let j = json.result.length;
|
||
for(let i=0; i < json.result.length; i++) {
|
||
output += '<tr>';
|
||
output += '<td>' + j + '</td>';
|
||
output += '<td>' + json.result[i]['date'] + '</td>';
|
||
output += '<td>' + json.result[i]['quantity'] + '</td>';
|
||
output += '<td>' + json.result[i]['driver'] + '</td>';
|
||
output += '<td>' + json.result[i]['paid'] + '</td>';
|
||
output += '</tr>';
|
||
j -= 1;
|
||
}
|
||
jQuery("#map-modal-history tbody").html(output);
|
||
}
|
||
|
||
// 지금 닫힘(closed now) 고객 집합 + 상세(다음 오픈까지 남은 분 등)
|
||
window.closedNowSet = window.closedNowSet || new Set();
|
||
window.closedNowInfo = window.closedNowInfo || {}; // { customerid: {open, next_open, minutes_until_open, ...} }
|
||
|
||
// 고객의 "다음 오픈까지 남은 분" (없으면 null)
|
||
function closedMinutesFor(cid) {
|
||
var d = window.closedNowInfo && window.closedNowInfo[String(cid)];
|
||
return (d && typeof d.minutes_until_open === 'number') ? d.minutes_until_open : null;
|
||
}
|
||
// 남은 분 → 짧은 라벨. 오늘 더 안 열면(null/0 이하) null → '!' 로 표시됨
|
||
function remainLabel(mins) {
|
||
if (mins == null || mins <= 0) return null;
|
||
if (mins < 60) return mins + 'm';
|
||
var h = Math.floor(mins / 60), m = mins % 60;
|
||
return m ? (h + 'h ' + m + 'm') : (h + 'h');
|
||
}
|
||
// 뱃지 텍스트 폰트 (HTML 배지와 통일). 실제 UI 폰트에 맞게 바꿔도 됨.
|
||
var BADGE_FONT = "Arial, Helvetica, sans-serif";
|
||
|
||
// 남은시간 앰버 알약 배지 SVG
|
||
function timeBadgeSvg(label) {
|
||
var w = Math.max(22, 12 + label.length * 7);
|
||
return 'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(
|
||
'<svg xmlns="http://www.w3.org/2000/svg" width="' + w + '" height="18">' +
|
||
'<rect x="0.5" y="0.5" width="' + (w - 1) + '" height="17" rx="8.5" fill="#f0ad4e" stroke="#ffffff" stroke-width="1"/>' +
|
||
'<text x="' + (w / 2) + '" y="13" text-anchor="middle" font-size="12" font-weight="bold" font-family="' + BADGE_FONT + '" fill="#5a3d00">' + label + '</text>' +
|
||
'</svg>');
|
||
}
|
||
|
||
// 닫힘 배지(빨간 원 + 흰 !) 아이콘
|
||
var CLOSED_BADGE_SVG = 'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(
|
||
'<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20">' +
|
||
'<circle cx="10" cy="10" r="8.5" fill="#e53935" stroke="#ffffff" stroke-width="2"/>' +
|
||
'<text x="10" y="14.5" text-anchor="middle" font-size="13" font-weight="bold" font-family="' + BADGE_FONT + '" fill="#ffffff">!</text>' +
|
||
'</svg>');
|
||
|
||
// 깃발 대비 배지 위치.
|
||
// 깃발 심볼은 anchor (0,0) → latlng 이 깃발 좌상단, 깃발은 우/하로 그려짐(약 17x20px).
|
||
// 배지 아이콘 20x20 기준, 배지 중심 화면오프셋 = (10 - x, 10 - y).
|
||
// 아래 값 = 배지 중심이 latlng 기준 (17, -1) → 깃발 배너 우상단.
|
||
// 깃발과 안 맞으면 x/y 두 숫자만 조정.
|
||
var BADGE_ANCHOR = { x: -7, y: 11 };
|
||
|
||
var closedBadgeMarker = {}; // markerArrayIndex -> 배지 google.maps.Marker
|
||
|
||
function makeClosedBadge(pos, label) {
|
||
var icon;
|
||
if (label) {
|
||
var w = Math.max(22, 12 + label.length * 7);
|
||
icon = {
|
||
url: timeBadgeSvg(label),
|
||
scaledSize: new google.maps.Size(w, 18),
|
||
anchor: new google.maps.Point(-6, 15), // 깃발 우상단, 알약 좌측 기준
|
||
};
|
||
} else {
|
||
icon = {
|
||
url: CLOSED_BADGE_SVG,
|
||
scaledSize: new google.maps.Size(20, 20),
|
||
anchor: new google.maps.Point(BADGE_ANCHOR.x, BADGE_ANCHOR.y),
|
||
};
|
||
}
|
||
return new google.maps.Marker({
|
||
position: pos,
|
||
map: map,
|
||
clickable: false,
|
||
zIndex: Number(google.maps.Marker.MAX_ZINDEX) + 1,
|
||
icon: icon,
|
||
});
|
||
}
|
||
|
||
// 클러스터로 숨겨진(부모 마커가 map 에서 빠진) 배지는 같이 숨김
|
||
function syncClosedBadges() {
|
||
Object.keys(closedBadgeMarker).forEach(function (idx) {
|
||
var b = closedBadgeMarker[idx];
|
||
var m = marker[idx];
|
||
if (b && m) b.setMap(m.getMap() ? map : null);
|
||
});
|
||
}
|
||
|
||
// "지금 닫힘" 표시 대상인지 (Finished = d_status 'F' 는 제외)
|
||
function isClosedNowProp(p) {
|
||
return !!(p && p.d_status !== 'F' &&
|
||
window.closedNowSet && window.closedNowSet.has(String(p.customerid)));
|
||
}
|
||
|
||
// 마커들의 고객 id 로 "지금 닫힘" 조회 → 개별 배지 + 클러스터 재렌더
|
||
function applyClosedBadges() {
|
||
var ids = [];
|
||
marker.forEach(function (m) {
|
||
if (m && m.property && m.property.customerid != null) ids.push(String(m.property.customerid));
|
||
});
|
||
ids = Array.from(new Set(ids));
|
||
if (ids.length === 0) return;
|
||
|
||
jQuery.ajax({
|
||
url: '/lib/customer_open_status.php',
|
||
method: 'POST',
|
||
dataType: 'json',
|
||
data: { ids: ids.join(',') },
|
||
success: function (res) {
|
||
window.closedNowSet = new Set(((res && res.closed) ? res.closed : []).map(String));
|
||
window.closedNowInfo = (res && res.detail) ? res.detail : {};
|
||
|
||
// 기존 배지 제거
|
||
Object.keys(closedBadgeMarker).forEach(function (idx) {
|
||
if (closedBadgeMarker[idx]) closedBadgeMarker[idx].setMap(null);
|
||
});
|
||
closedBadgeMarker = {};
|
||
|
||
// 닫힌 마커마다 배지 생성 (오늘 이따 열면 남은시간, 아니면 !)
|
||
marker.forEach(function (m, idx) {
|
||
if (!m || !m.property) return;
|
||
if (isClosedNowProp(m.property)) {
|
||
var lbl = remainLabel(closedMinutesFor(m.property.customerid));
|
||
closedBadgeMarker[idx] = makeClosedBadge(m.getPosition(), lbl);
|
||
m.setTitle(lbl ? ('Opens in ' + lbl) : 'Closed today');
|
||
}
|
||
});
|
||
|
||
syncClosedBadges();
|
||
|
||
// 클러스터 아이콘도 ! 반영되도록 재렌더
|
||
if (typeof clusterer !== 'undefined' && clusterer && typeof clusterer.render === 'function') {
|
||
clusterer.render();
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
function buildContent(property) {
|
||
let content = document.createElement("div");
|
||
const chipsHtml = renderContainerChips(property.containers);
|
||
const closedBadge = isClosedNowProp(property)
|
||
? '<span class="closed-badge">!</span>' : '';
|
||
|
||
content.classList.add("property");
|
||
let innerHtml = `
|
||
<div class="point-overlay">
|
||
<div class="modal-header">
|
||
<h4 class="modal-title">${property.store} ${property.ordertype}</h4>
|
||
<!--h4 class="modal-title" onClick="javascript:jQuery('.overlay-close-${property.index}').trigger('click');">${property.store}</h4-->
|
||
<div class="close overlay-close-${property.index}">×</div>
|
||
</div><!--modal-header-->
|
||
<div class="store-info">
|
||
<table class="tb-info-box">
|
||
<!--table class="tb-info-box" onClick="javascript:jQuery('.overlay-close-${property.index}').trigger('click');"-->
|
||
<tr>
|
||
<td class="td-title-info">Account</td>
|
||
<td class="td-text-info">
|
||
<span>${property.accountno}</span>
|
||
</td>
|
||
<td class="td-title-info">Pay & Rate</td>
|
||
<td class="td-text-info">
|
||
<span style="border: 1px solid #000000; padding:2px; margin-right:8px;">${property.paymentType}</span><span>${property.rate}</span>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td class="td-title-info">Phone</td>
|
||
<td class="td-text-info">
|
||
<span>${property.phone}</span>
|
||
</td>
|
||
<td class="td-title-info">Forecast</td>
|
||
<td class="td-text-info">
|
||
<span>${property.estqty}</span>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td class="td-title-info">Last Pickup</td>
|
||
<td class="td-text-info">
|
||
<span>${property.lastPickupDate}</span>
|
||
</td>
|
||
<td class="td-title-info">Last Paid</td>
|
||
<td class="td-text-info">
|
||
<span>${property.lastPaidDate}</span>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
|
||
<div class="address-row" style="margin-bottom:0px;">
|
||
<div class="address-title">Address</div>
|
||
<div class="address-content">
|
||
<a href="https://www.google.com/maps/search/?api=1&query=${property.lat},${property.lon}" target="_blank"
|
||
class="address-link"
|
||
style="letter-spacing: 0.5px;"
|
||
target="_blank"
|
||
rel="noopener noreferrer">
|
||
<b>${property.address}</b>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="address-row">
|
||
<div class="address-title">Container</div>
|
||
<div class="address-content">
|
||
|
||
${chipsHtml}
|
||
</div>
|
||
</div>`;
|
||
|
||
innerHtml += `
|
||
<div class="text-center map-grid-layout-col-4">
|
||
|
||
<!-- Row 1 -->
|
||
<button type="button"
|
||
class="mapBtn btn-info-modal"
|
||
data-toggle="modal"
|
||
data-target="#map-modal-info"
|
||
onClick="showShortInfo(${property.customerid});">
|
||
<i class="fa-solid fa-question mapBtn-ico"></i>
|
||
<div class="mapBtn-txt">Info ${closedBadge}</div>
|
||
</button>
|
||
|
||
<button type="button"
|
||
class="mapBtn btn-grey-modal"
|
||
data-toggle="modal"
|
||
data-target="#map-modal-history"
|
||
onClick="initHistory(${property.customerid});">
|
||
<i class="bi bi-folder2-open mapBtn-ico"></i>
|
||
<div class="mapBtn-txt">History</div>
|
||
</button>
|
||
|
||
<button type="button"
|
||
class="mapBtn btn-orange-modal ${(property.rnote && /\S/.test(property.rnote)) ? 'blink' : ''}"
|
||
data-toggle="modal"
|
||
data-target="#map-modal-note"
|
||
onClick="showShortNotice(${property.duid});">
|
||
<i class="fa fa-thumb-tack mapBtn-ico"></i>
|
||
<div class="mapBtn-txt">Notice</div>
|
||
</button>`;
|
||
if(`${property.date}` === 'null') {
|
||
|
||
innerHtml += `
|
||
<button type="button"
|
||
class="mapBtn btn-primary-modal addOrderBtn_${property.index}"
|
||
onClick="initAddOrder(${property.customerid},${property.index});">
|
||
<i class="fa-solid fa-plus mapBtn-ico"></i>
|
||
<div class="mapBtn-txt">Add</div>
|
||
</button>
|
||
`;
|
||
|
||
} else {
|
||
|
||
innerHtml += `
|
||
<button type="button"
|
||
${property.orderFlag}
|
||
class="mapBtn btn-red-modal removeOrderBtn_${property.index}"
|
||
data-toggle="modal"
|
||
data-target="#modal-confirm"
|
||
onClick="removeOrder(${property.customerid},${property.duid},${property.index},'${property.accountno}');">
|
||
<i class="fa-solid fa-xmark mapBtn-ico"></i>
|
||
<div class="mapBtn-txt">Remove</div>
|
||
</button>
|
||
`;
|
||
}
|
||
innerHtml += `
|
||
<!-- Row 2 -->
|
||
<button type="button"
|
||
class="mapBtn btn-nearby"
|
||
onClick="inqNearby('${property.lat}:${property.lon}');">
|
||
<i class="fa-solid fa-diamond-turn-right mapBtn-ico"></i>
|
||
<div class="mapBtn-txt">Nearby</div>
|
||
</button>
|
||
|
||
<button type="button"
|
||
class="mapBtn btn-container-request"
|
||
data-toggle="modal"
|
||
data-target="#map-modal-container-request"
|
||
data-cuid="${property.customerid}"
|
||
data-cname="${property.store}"
|
||
data-member="${SS_UID}"
|
||
data-driver="${SS_DRUID}">
|
||
<i class="bi bi-database-fill mapBtn-ico"></i>
|
||
<div class="mapBtn-txt">Container</div>
|
||
</button>
|
||
|
||
<button type="button"
|
||
class="mapBtn btn-image-modal"
|
||
data-toggle="modal"
|
||
data-target="#customerImageModal"
|
||
data-cuid="${property.customerid}"
|
||
data-cname="${property.store}">
|
||
<i class="bi bi-image mapBtn-ico"></i>
|
||
<div class="mapBtn-txt">Image</div>
|
||
</button>
|
||
|
||
<button type="button"
|
||
class="mapBtn btn-purple-modal"
|
||
data-toggle="modal"
|
||
data-target="#map-modal-input"
|
||
onClick="initInput(${property.customerid},'${property.accountno}');">
|
||
<i class="bi bi-truck mapBtn-ico"></i>
|
||
<div class="mapBtn-txt">Input</div>
|
||
</button>
|
||
`;
|
||
|
||
innerHtml += `</div>`;
|
||
|
||
|
||
|
||
|
||
content.innerHTML = innerHtml;
|
||
return content;
|
||
}
|
||
|
||
var rstInqMapCenter = function(json) {
|
||
center = { lat: parseFloat(json.geo['lat']), lng: parseFloat(json.geo['lon']) };
|
||
currentLocation = { lat: parseFloat(json.geo['lat']), lon: parseFloat(json.geo['lon']) };
|
||
map.setCenter(center);
|
||
}
|
||
|
||
function onLoading(){
|
||
jQuery(".loading-overlay").css("display","flex");
|
||
}
|
||
|
||
function offLoading(){
|
||
jQuery(".loading-overlay").css("display","none");
|
||
}
|
||
|
||
function imagepopup(c_uid) {
|
||
const url = `/lib/customer_image_upload_popup.php?c_uid=${c_uid}`;
|
||
window.open(url, "uploadImage", "width=600,height=750,scrollbars=yes");
|
||
}
|
||
|
||
function renderContainerChips(containers) {
|
||
if (!Array.isArray(containers) || containers.length === 0) return '';
|
||
|
||
const grouped = new Map();
|
||
|
||
for (const cc of containers) {
|
||
const key = [
|
||
cc.cc_type,
|
||
cc.cc_owner_type,
|
||
cc.cc_has_lock,
|
||
cc.cc_has_wheel,
|
||
cc.cc_has_woodframe
|
||
].join('|');
|
||
|
||
if (!grouped.has(key)) grouped.set(key, { cc, cnt: 0 });
|
||
grouped.get(key).cnt++;
|
||
}
|
||
|
||
let html = `<div class="container-chips inline-chips">`;
|
||
|
||
for (const { cc, cnt } of grouped.values()) {
|
||
const type = String(cc.cc_type || '').toUpperCase();
|
||
|
||
let icon = 'bi-box2';
|
||
if (type.includes('D')) icon = 'bi-database-fill';
|
||
else if (type.includes('B') && !type.includes('BUCKET')) icon = 'bi-archive-fill';
|
||
|
||
const isRestaurantOwned = (cc.cc_owner_type === 'O');
|
||
const chipClass = isRestaurantOwned ? 'chip chip-restaurant' : 'chip chip-company';
|
||
|
||
let optIcons = '';
|
||
if (cc.cc_has_lock === 'Y') optIcons += ' <i class="bi bi-lock-fill"></i>';
|
||
if (cc.cc_has_wheel === 'Y') optIcons += ' <i class="bi bi-gear-fill"></i>';
|
||
if (cc.cc_has_woodframe === 'Y') optIcons += ' <i class="bi bi-bounding-box-circles"></i>';
|
||
|
||
html += `
|
||
<span class="${chipClass}" title="${isRestaurantOwned ? 'Restaurant-owned (Do not remove)' : 'Customer container'}">
|
||
<i class="bi ${icon}"></i>
|
||
${cc.cc_type || ''}
|
||
${optIcons}
|
||
${isRestaurantOwned ? '<span class="chip-flag">O</span>' : ''}
|
||
${cnt > 1 ? `<span class="chip-count">x${cnt}</span>` : ''}
|
||
</span>
|
||
`;
|
||
}
|
||
|
||
html += `</div>`;
|
||
return html;
|
||
}
|
||
|
||
|
||
</script>
|
||
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
document.querySelectorAll('.number-input').forEach(container => {
|
||
const input = container.querySelector('.d_sludge');
|
||
const leftBtn = container.querySelector('.left');
|
||
const rightBtn = container.querySelector('.right');
|
||
const step = 5;
|
||
|
||
leftBtn.addEventListener('click', () => {
|
||
const min = input.min ? parseInt(input.min) : -Infinity;
|
||
let value = parseInt(input.value || 0);
|
||
if (value - step >= min) input.value = value - step;
|
||
});
|
||
|
||
rightBtn.addEventListener('click', () => {
|
||
const max = input.max ? parseInt(input.max) : Infinity;
|
||
let value = parseInt(input.value || 0);
|
||
if (value + step <= max) input.value = value + step;
|
||
});
|
||
});
|
||
});
|
||
|
||
async function forceRefresh() {
|
||
if ('caches' in window) {
|
||
const keys = await caches.keys();
|
||
await Promise.all(keys.map(key => caches.delete(key)));
|
||
}
|
||
window.location.reload();
|
||
/*
|
||
const cssFiles = [
|
||
'mapMAPCSS.css',
|
||
'main.css',
|
||
'leftside-modal.css'
|
||
];
|
||
|
||
cssFiles.forEach(file => {
|
||
const oldLink = Array.from(document.querySelectorAll('link[rel="stylesheet"]'))
|
||
.find(l => l.href.endsWith(file));
|
||
if (oldLink) {
|
||
const newLink = oldLink.cloneNode();
|
||
newLink.href = oldLink.href.split('?')[0] + '?view=map&_=' + new Date().getTime();
|
||
oldLink.parentNode.replaceChild(newLink, oldLink);
|
||
console.log(`CSS replaced: ${newLink.href}`);
|
||
} else {
|
||
console.warn(`CSS not found: ${file}`);
|
||
}
|
||
});
|
||
|
||
if ('caches' in window) {
|
||
const keys = await caches.keys();
|
||
console.log('Cache keys found:', keys);
|
||
await Promise.all(keys.map(key => caches.delete(key)));
|
||
console.log('Caches cleared');
|
||
}
|
||
|
||
console.log('CSS force refresh completed without page reload');
|
||
*/
|
||
}
|
||
|
||
$('.point-overlay').on(
|
||
'click mousedown mouseup touchstart touchend pointerdown pointerup',
|
||
function (e) {
|
||
e.stopPropagation();
|
||
}
|
||
);
|
||
|
||
$('.point-overlay *').on(
|
||
'click mousedown mouseup touchstart touchend pointerdown pointerup',
|
||
function (e) {
|
||
e.stopPropagation();
|
||
}
|
||
);
|
||
|
||
|
||
/**
|
||
* 1. IMAGE 모달이 열릴 때, 클릭한 버튼의 Customer ID를
|
||
* 주황색 [UPLOAD NEW IMAGE] 버튼에게 전달해주는 코드입니다.
|
||
*/
|
||
jQuery('#customerImageModal').on('show.bs.modal', function (event) {
|
||
var button = jQuery(event.relatedTarget); // 지도의 [IMAGE] 버튼
|
||
var cuid = button.data('cuid'); // 고객 번호 가져오기
|
||
|
||
// 모달 내부에 새로 만든 주황색 버튼에 ID를 심어줍니다.
|
||
jQuery('#btn-modal-upload-action').attr('data-cuid', cuid);
|
||
|
||
// (선택사항) 업체명 표시
|
||
var cname = button.data('cname');
|
||
jQuery('#imageCustomerName').text(cname);
|
||
});
|
||
|
||
/**
|
||
* 2. 주황색 버튼을 눌렀을 때 실제로 팝업창을 띄우는 함수입니다.
|
||
*/
|
||
function openCustomerImageUploadDirect(custUid) {
|
||
if (!custUid || custUid === 'undefined') {
|
||
alert("Customer ID를 찾을 수 없습니다.");
|
||
return;
|
||
}
|
||
|
||
var width = 600;
|
||
var height = 750;
|
||
var left = (window.screen.width / 2) - (width / 2);
|
||
var top = (window.screen.height / 2) - (height / 2);
|
||
|
||
// 경로와 변수명을 어제 확인한 /lib/ 폴더와 c_uid로 맞췄습니다.
|
||
var url = "/lib/customer_image_upload_popup.php?c_uid=" + custUid;
|
||
|
||
window.open(url, "uploadImage", "width=" + width + ",height=" + height + ",left=" + left + ",top=" + top + ",scrollbars=yes");
|
||
}
|
||
|
||
</script>
|