Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 39 additions & 3 deletions core/tpl/public_vehicle_logbook_form.tpl.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
/**
* Rendered by public/agenda/public_vehicle_logbook.php (inherits its scope).
* Expects: $actionType, $vehicleUrl, $logoUrl, $langs, $registrationCertificateFR, $form,
* $preselectedDriverId, $conf, $user, $lastArrivalMileage, $lastUnfinishedActionComm,
* $preselectedDriverId, $preselectedDriverLastname, $preselectedDriverFirstname,
* $preselectedDriverPhone, $conf, $user, $lastArrivalMileage, $lastUnfinishedActionComm,
* $tripUploadSubDir, $publicInterfaceUseSignatory
*/

Expand Down Expand Up @@ -57,13 +58,16 @@
<div class="plv2-card">
<h3><i class="fas fa-user"></i> <?php echo $langs->trans('Driver'); ?> <span class="plv2-req">*</span></h3>

<div class="plv2-seg" id="plv2-driver-type">
<div class="plv2-seg plv2-seg--three" id="plv2-driver-type">
<button type="button" class="plv2-seg__btn active" data-type="internal">
<i class="fas fa-user-tie"></i> <?php echo $langs->trans('DriverInternal'); ?>
</button>
<button type="button" class="plv2-seg__btn" data-type="external">
<i class="fas fa-user-friends"></i> <?php echo $langs->trans('DriverExternal'); ?>
</button>
<button type="button" class="plv2-seg__btn" data-type="free">
<i class="fas fa-pen"></i> <?php echo $langs->trans('DriverFree'); ?>
</button>
</div>
<input type="hidden" name="driver_type" id="plv2-driver-type-value" value="internal">

Expand All @@ -73,7 +77,7 @@
</div>

<!-- Conducteur externe : tiers puis contact -->
<div id="plv2-driver-external" style="display: none;">
<div id="plv2-driver-external" hidden>
<div class="plv2-form-group">
<label><?php echo $langs->trans('ThirdParty'); ?></label>
<?php
Expand All @@ -88,6 +92,38 @@
<?php echo $form->selectcontacts(-1, '', 'driver_contact_id', 1); ?>
</div>
</div>

<!-- Conducteur libre : personne qui n'est ni un utilisateur ni un contact connu.
Les valeurs sont pre-remplies depuis les cookies poses au precedent depart
(vehicleLogbook.js) pour que le meme conducteur n'ait plus qu'a valider. -->
<div id="plv2-driver-free" hidden>
<div class="plv2-field-row">
<div class="plv2-form-group">
<label><?php echo $langs->trans('DriverLastname'); ?></label>
<input type="text"
name="driver_free_lastname"
id="plv2-driver-free-lastname"
value="<?php echo dol_escape_htmltag($preselectedDriverLastname); ?>"
autocomplete="family-name">
</div>
<div class="plv2-form-group">
<label><?php echo $langs->trans('DriverFirstname'); ?></label>
<input type="text"
name="driver_free_firstname"
id="plv2-driver-free-firstname"
value="<?php echo dol_escape_htmltag($preselectedDriverFirstname); ?>"
autocomplete="given-name">
</div>
</div>
<div class="plv2-form-group">
<label><?php echo $langs->trans('DriverPhone'); ?></label>
<input type="tel"
name="driver_free_phone"
id="plv2-driver-free-phone"
value="<?php echo dol_escape_htmltag($preselectedDriverPhone); ?>"
autocomplete="tel">
</div>
</div>
</div>
<?php endif; ?>

Expand Down
8 changes: 7 additions & 1 deletion core/tpl/public_vehicle_logbook_vehicle.tpl.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,14 @@
<div>
<strong><?php echo $langs->trans('VehicleCurrentlyOut'); ?></strong>
<?php if (!empty($lastUnfinishedActionComm[0])) :
$driverName = json_decode($lastUnfinishedActionComm[0]->array_options['options_json'] ?? '{}', true)['driver'] ?? '';
$outTripJson = json_decode($lastUnfinishedActionComm[0]->array_options['options_json'] ?? '{}', true);
$driverName = $outTripJson['driver'] ?? '';
$driverPhone = $outTripJson['driver_phone'] ?? '';
echo '<span>' . ($driverName ? dol_escape_htmltag($driverName) . ' · ' : '') . dol_print_date($lastUnfinishedActionComm[0]->datep, 'dayhour') . '</span>';
// A free driver left a phone number: make it callable, it is the only way back to them
if (!empty($driverPhone)) :
echo '<a class="plv2-driver-phone" href="tel:' . dol_escape_htmltag($driverPhone) . '"><i class="fas fa-phone"></i> ' . dol_escape_htmltag($driverPhone) . '</a>';
endif;
endif; ?>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion css/dolicar.min.css

Large diffs are not rendered by default.

42 changes: 42 additions & 0 deletions css/scss/pages/_vehicle-logbook.scss
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,19 @@
strong { display: block; font-weight: 600; }
span { opacity: 0.75; font-size: 11px; }

// Phone of a free driver: own line under the name, tappable on a phone
.plv2-driver-phone {
display: flex;
align-items: center;
gap: 4px;
margin-top: 2px;
font-size: 11px;
font-weight: 600;
text-decoration: none;

i { font-size: 11px; }
}

&--out {
background: #eff6ff;
color: #1e3a8a;
Expand Down Expand Up @@ -551,6 +564,7 @@

input[type="text"],
input[type="number"],
input[type="tel"],
input[type="datetime-local"],
select,
textarea {
Expand All @@ -573,6 +587,10 @@
}
}

// The Dolibarr theme pins a height on text inputs but leaves tel ones alone, where pico's own
// 50px height then wins: size the phone field from its padding, like the fields next to it.
input[type="tel"] { height: auto; }

// Compact height for the date & mileage fields
input[type="datetime-local"],
input.plv2-km-input {
Expand Down Expand Up @@ -683,6 +701,13 @@
.plv2-card { margin-bottom: 0; }
}

// Two fields side by side inside a single card (free driver name / first name)
.plv2-field-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 6px;
}

// ── Inline card: one label + field on a single row (Conducteur) ────────
.plv2-card--inline {
display: flex;
Expand Down Expand Up @@ -745,6 +770,23 @@
gap: 6px;
margin-bottom: 6px;

// Driver picker: three choices (internal / external / free identity). Labels are stacked
// under the icon, and min-width lets a column shrink under its label (a grid item defaults
// to min-content, which would push the third button off a phone screen).
&--three {
grid-template-columns: repeat(3, 1fr);

.plv2-seg__btn {
flex-direction: column;
gap: 3px;
min-width: 0;
padding-inline: 2px;
text-align: center;
line-height: 1.15;
overflow-wrap: anywhere;
}
}

&__btn {
display: flex;
align-items: center;
Expand Down
2 changes: 1 addition & 1 deletion js/dolicar.min.js

Large diffs are not rendered by default.

86 changes: 86 additions & 0 deletions js/modules/vehicleLogbook.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ window.dolicar.vehicleLogbook.init = function() {
*/
window.dolicar.vehicleLogbook.event = function() {
$(document).on('input change', '.plv2-km-input[data-warning-mileage]', window.dolicar.vehicleLogbook.checkMileageWarning);
$(document).on('click', '#plv2-driver-type .plv2-seg__btn', window.dolicar.vehicleLogbook.selectDriverType);
$(document).on('change', '#driver_user_id', window.dolicar.vehicleLogbook.rememberDriverUser);
$(document).on('change', '#plv2-driver-free input', window.dolicar.vehicleLogbook.rememberFreeDriver);
};

/**
Expand All @@ -80,3 +83,86 @@ window.dolicar.vehicleLogbook.checkMileageWarning = function() {

$('#plv2-km-warning').toggleClass('is-visible', !isNaN(warnFrom) && !isNaN(mileage) && mileage > warnFrom);
};

/**
* Switch the driver picker: internal user, external third party / contact, or free identity.
* The name of a free driver is only required while its block is the visible one, otherwise the
* browser would refuse to submit a form holding a required field it cannot focus.
*
* @memberof DoliCar_VehicleLogbook
*
* @since 1.5.0
* @version 1.5.0
*
* @return {void}
*/
window.dolicar.vehicleLogbook.selectDriverType = function() {
var type = $(this).data('type');

$('#plv2-driver-type .plv2-seg__btn').removeClass('active');
$(this).addClass('active');
$('#plv2-driver-type-value').val(type);

$('#plv2-driver-internal').prop('hidden', type !== 'internal');
$('#plv2-driver-external').prop('hidden', type !== 'external');
$('#plv2-driver-free').prop('hidden', type !== 'free');
$('#plv2-driver-free-lastname').prop('required', type === 'free');
};

/**
* Remember the picked internal driver so the next departure form comes pre-selected
*
* @memberof DoliCar_VehicleLogbook
*
* @since 1.5.0
* @version 1.5.0
*
* @return {void}
*/
window.dolicar.vehicleLogbook.rememberDriverUser = function() {
var driverId = $(this).val();

if (driverId) {
window.dolicar.vehicleLogbook.setCookie('plv2_driver_id', driverId);
}
};

/**
* Remember the typed free identity (name, first name, phone) for the next departure
*
* @memberof DoliCar_VehicleLogbook
*
* @since 1.5.0
* @version 1.5.0
*
* @return {void}
*/
window.dolicar.vehicleLogbook.rememberFreeDriver = function() {
var cookies = {
'plv2_driver_lastname' : $('#plv2-driver-free-lastname').val(),
'plv2_driver_firstname': $('#plv2-driver-free-firstname').val(),
'plv2_driver_phone' : $('#plv2-driver-free-phone').val()
};

for (var name in cookies) {
if (cookies.hasOwnProperty(name)) {
window.dolicar.vehicleLogbook.setCookie(name, cookies[name] || '');
}
}
};

/**
* Write a one year cookie on the public interface
*
* @memberof DoliCar_VehicleLogbook
*
* @since 1.5.0
* @version 1.5.0
*
* @param {string} name Cookie name
* @param {string} value Cookie value
* @return {void}
*/
window.dolicar.vehicleLogbook.setCookie = function(name, value) {
document.cookie = name + '=' + encodeURIComponent(value) + '; path=/; max-age=31536000; SameSite=Lax';
};
6 changes: 5 additions & 1 deletion langs/fr_FR/dolicar.lang
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,12 @@ DeclareReturn = Déclarer un retour
ReturnExpected = Le véhicule étant sorti, le retour est attendu
WhoAreYou = Qui êtes-vous ?
SelectDriver = Sélectionner un conducteur
DriverInternal = Utilisateur interne
DriverInternal = Interne
DriverExternal = Externe
DriverFree = Saisie libre
DriverLastname = Nom
DriverFirstname = Prénom
DriverPhone = Téléphone
SelectContact = Sélectionner un contact
SelectAThirdPartyFirst = Choisir d'abord un tiers
LinkedThirdParty = Client / Tiers
Expand Down
38 changes: 18 additions & 20 deletions public/agenda/public_vehicle_logbook.php
Original file line number Diff line number Diff line change
Expand Up @@ -440,10 +440,15 @@ function dolicarGrantThirdpartyView(User $user): void
}

if ($action == 'add') {
// Resolve driver full name: internal user (select_dolusers) or external third-party contact
$driverType = GETPOST('driver_type', 'aZ09');
$driverName = '';
if ($driverType === 'external') {
// Resolve driver full name: internal user (select_dolusers), external third-party contact,
// or a free identity typed on the spot by someone who is in neither list (issue #487).
$driverType = GETPOST('driver_type', 'aZ09');
$driverName = '';
$driverPhone = '';
if ($driverType === 'free') {
$driverName = trim(GETPOST('driver_free_firstname', 'alphanohtml') . ' ' . GETPOST('driver_free_lastname', 'alphanohtml'));
$driverPhone = GETPOST('driver_free_phone', 'alphanohtml');
} elseif ($driverType === 'external') {
$driverContactId = GETPOSTINT('driver_contact_id');
if ($driverContactId > 0) {
require_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php';
Expand Down Expand Up @@ -482,11 +487,12 @@ function dolicarGrantThirdpartyView(User $user): void
// The client can set HTTP header information (like $_SERVER['HTTP_CLIENT_IP'] ...) to any arbitrary value it wants. As such it's far more reliable to use $_SERVER['REMOTE_ADDR'], as this cannot be set by the user
$actionComm->note_private = (isset($_SERVER['REMOTE_ADDR']) && !empty($_SERVER['REMOTE_ADDR']) ? $langs->transnoentities('IPAddress') . ' : ' . $_SERVER['REMOTE_ADDR'] . '<br>' : $langs->transnoentities('NoData'));
$actionComm->note_private .= $langs->transnoentities('Driver') . ' : ' . $driverName . '<br>';
$actionComm->note_private .= !empty($driverPhone) ? $langs->transnoentities('DriverPhone') . ' : ' . $driverPhone . '<br>' : '';
$actionComm->note_private .= GETPOSTISSET('start_comment') && !empty(GETPOST('start_comment', 'restricthtml')) ? $langs->transnoentities('StartComment') . ' : ' . GETPOST('start_comment', 'restricthtml') . '<br>' : '';
$actionComm->note_private .= GETPOSTISSET('end_comment') && !empty(GETPOST('end_comment', 'restricthtml')) ? $langs->transnoentities('EndComment') . ' : ' . GETPOST('end_comment', 'restricthtml') : '';
$actionComm->label = $langs->transnoentities('ObjectAddPublicVehicleLogBook', $registrationCertificateFR->a_registration_number, $productLot->batch);

$actionComm->array_options['json'] = json_encode(['driver' => $driverName, 'fuel_level' => GETPOST('options_fuel_level', 'alpha'), 'start_comment' => GETPOST('start_comment', 'restricthtml'), 'end_comment' => GETPOST('end_comment', 'restricthtml')]);
$actionComm->array_options['json'] = json_encode(['driver' => $driverName, 'driver_phone' => $driverPhone, 'fuel_level' => GETPOST('options_fuel_level', 'alpha'), 'start_comment' => GETPOST('start_comment', 'restricthtml'), 'end_comment' => GETPOST('end_comment', 'restricthtml')]);

$extraFields->setOptionalsFromPost([], $actionComm);

Expand Down Expand Up @@ -566,6 +572,12 @@ function dolicarGrantThirdpartyView(User $user): void
// Pre-select driver from cookie
$preselectedDriverId = isset($_COOKIE['plv2_driver_id']) ? (int) $_COOKIE['plv2_driver_id'] : 0;

// Same idea for the free driver identity (issue #487): a driver who is neither a user nor a known
// contact types name and phone once, and finds them pre-filled on the next departure.
$preselectedDriverLastname = dol_string_nohtmltag($_COOKIE['plv2_driver_lastname'] ?? '');
$preselectedDriverFirstname = dol_string_nohtmltag($_COOKIE['plv2_driver_firstname'] ?? '');
$preselectedDriverPhone = dol_string_nohtmltag($_COOKIE['plv2_driver_phone'] ?? '');

// This NOLOGIN page never loads $user, but a Dolibarr session may still be active (e.g. a manager
// opening the page while logged in). Default the control "controller" picker to that session user.
$loggedUserId = 0;
Expand Down Expand Up @@ -708,12 +720,6 @@ function plv2SelectFuel(btn) {
$('#plv2-fuel-value').val($(btn).data('value'));
}

$(document).on('change', '#driver_user_id', function () {
var driverId = $(this).val();
if (driverId) {
document.cookie = 'plv2_driver_id=' + encodeURIComponent(driverId) + '; path=/; max-age=31536000; SameSite=Lax';
}
});

// Live client-side filter of the registration certificates list
$(document).on('input', '#plv2-cg-search', function () {
Expand All @@ -729,15 +735,7 @@ function plv2SelectFuel(btn) {
$('.plv2-cg-noresult').toggle(visible === 0);
});

// Driver type toggle: internal user picker vs external third-party + contact pickers
$(document).on('click', '#plv2-driver-type .plv2-seg__btn', function () {
var type = $(this).data('type');
$('#plv2-driver-type .plv2-seg__btn').removeClass('active');
$(this).addClass('active');
$('#plv2-driver-type-value').val(type);
$('#plv2-driver-internal').toggle(type === 'internal');
$('#plv2-driver-external').toggle(type === 'external');
});
// Driver type toggle (internal / external / free identity) lives in js/modules/vehicleLogbook.js

// When the third party changes, refresh the native contact select2 with that company's contacts
$(document).on('change', '#driver_socid', function () {
Expand Down
Loading