Add vehicle repair functionality in Falukant module
- Implemented a new repairVehicle method in FalukantService to handle vehicle repairs, including cost calculation and precondition checks. - Updated FalukantController to expose the repairVehicle endpoint, allowing users to initiate repairs via the API. - Enhanced FalukantRouter to include a new route for vehicle repairs. - Modified BranchView component to add UI elements for repairing vehicles, including a dialog for repair confirmation and displaying repair details. - Updated German localization files to include translations related to vehicle repair actions, improving user experience for German-speaking users.
This commit is contained in:
@@ -217,6 +217,10 @@ class FalukantController {
|
|||||||
this.getBranchTransports = this._wrapWithUser(
|
this.getBranchTransports = this._wrapWithUser(
|
||||||
(userId, req) => this.service.getBranchTransports(userId, req.params.branchId)
|
(userId, req) => this.service.getBranchTransports(userId, req.params.branchId)
|
||||||
);
|
);
|
||||||
|
this.repairVehicle = this._wrapWithUser(
|
||||||
|
(userId, req) => this.service.repairVehicle(userId, req.params.vehicleId),
|
||||||
|
{ successStatus: 200 }
|
||||||
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ router.get('/products/prices-in-cities', falukantController.getProductPricesInCi
|
|||||||
router.get('/vehicles/types', falukantController.getVehicleTypes);
|
router.get('/vehicles/types', falukantController.getVehicleTypes);
|
||||||
router.post('/vehicles', falukantController.buyVehicles);
|
router.post('/vehicles', falukantController.buyVehicles);
|
||||||
router.get('/vehicles', falukantController.getVehicles);
|
router.get('/vehicles', falukantController.getVehicles);
|
||||||
|
router.post('/vehicles/:vehicleId/repair', falukantController.repairVehicle);
|
||||||
router.post('/transports', falukantController.createTransport);
|
router.post('/transports', falukantController.createTransport);
|
||||||
router.get('/transports/route', falukantController.getTransportRoute);
|
router.get('/transports/route', falukantController.getTransportRoute);
|
||||||
router.get('/transports/branch/:branchId', falukantController.getBranchTransports);
|
router.get('/transports/branch/:branchId', falukantController.getBranchTransports);
|
||||||
|
|||||||
@@ -1118,6 +1118,93 @@ class FalukantService extends BaseService {
|
|||||||
return { success: true };
|
return { success: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async repairVehicle(hashedUserId, vehicleId) {
|
||||||
|
const user = await getFalukantUserOrFail(hashedUserId);
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
|
// Fahrzeug laden mit Typ-Informationen
|
||||||
|
const vehicle = await Vehicle.findOne({
|
||||||
|
where: {
|
||||||
|
id: vehicleId,
|
||||||
|
falukantUserId: user.id,
|
||||||
|
},
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: VehicleType,
|
||||||
|
as: 'type',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: Transport,
|
||||||
|
as: 'transports',
|
||||||
|
required: false,
|
||||||
|
attributes: ['id'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!vehicle) {
|
||||||
|
throw new Error('Vehicle not found or does not belong to user');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prüfen, ob Fahrzeug in Benutzung ist
|
||||||
|
const hasActiveTransport = Array.isArray(vehicle.transports) && vehicle.transports.length > 0;
|
||||||
|
const isBuilding = vehicle.availableFrom && new Date(vehicle.availableFrom).getTime() > now.getTime();
|
||||||
|
|
||||||
|
if (hasActiveTransport) {
|
||||||
|
throw new PreconditionError('vehicleInUse');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBuilding) {
|
||||||
|
throw new PreconditionError('vehicleAlreadyBuilding');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prüfen, ob Reparatur nötig ist (Zustand < 100)
|
||||||
|
if (vehicle.condition >= 100) {
|
||||||
|
throw new PreconditionError('vehicleAlreadyPerfect');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kosten berechnen: Baupreis * 0.8 * (100 - zustand) / 100
|
||||||
|
const baseCost = vehicle.type.cost;
|
||||||
|
const repairCost = Math.round(baseCost * 0.8 * (100 - vehicle.condition) / 100);
|
||||||
|
|
||||||
|
if (user.money < repairCost) {
|
||||||
|
throw new PreconditionError('insufficientFunds');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bauzeit verwenden (buildTimeMinutes)
|
||||||
|
const buildTimeMinutes = vehicle.type.buildTimeMinutes || 0;
|
||||||
|
const buildMs = buildTimeMinutes * 60 * 1000;
|
||||||
|
const availableFrom = new Date(now.getTime() + buildMs);
|
||||||
|
|
||||||
|
// Reparatur durchführen
|
||||||
|
await sequelize.transaction(async (tx) => {
|
||||||
|
// Geld abziehen
|
||||||
|
const moneyResult = await updateFalukantUserMoney(
|
||||||
|
user.id,
|
||||||
|
-repairCost,
|
||||||
|
'repair_vehicle',
|
||||||
|
user.id
|
||||||
|
);
|
||||||
|
if (!moneyResult.success) {
|
||||||
|
throw new Error('Failed to update money');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fahrzeug reparieren: Zustand auf 100, availableFrom auf jetzt + Bauzeit
|
||||||
|
await vehicle.update({
|
||||||
|
condition: 100,
|
||||||
|
availableFrom: availableFrom,
|
||||||
|
}, { transaction: tx });
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
repairCost,
|
||||||
|
buildTimeMinutes,
|
||||||
|
availableFrom,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
async createStock(hashedUserId, branchId, stockData) {
|
async createStock(hashedUserId, branchId, stockData) {
|
||||||
const u = await getFalukantUserOrFail(hashedUserId);
|
const u = await getFalukantUserOrFail(hashedUserId);
|
||||||
const b = await getBranchOrFail(u.id, branchId);
|
const b = await getBranchOrFail(u.id, branchId);
|
||||||
|
|||||||
@@ -133,6 +133,45 @@ const syncDatabaseForDeployment = async () => {
|
|||||||
console.warn('⚠️ Konnte traffic_light-Spalte nicht vorab sicherstellen:', e?.message || e);
|
console.warn('⚠️ Konnte traffic_light-Spalte nicht vorab sicherstellen:', e?.message || e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Migration: Transport product_id und size nullable machen
|
||||||
|
console.log("Making transport product_id and size nullable...");
|
||||||
|
try {
|
||||||
|
await sequelize.query(`
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
-- Prüfe ob product_id NOT NULL Constraint existiert
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1 FROM information_schema.columns
|
||||||
|
WHERE table_schema = 'falukant_data'
|
||||||
|
AND table_name = 'transport'
|
||||||
|
AND column_name = 'product_id'
|
||||||
|
AND is_nullable = 'NO'
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE falukant_data.transport
|
||||||
|
ALTER COLUMN product_id DROP NOT NULL;
|
||||||
|
RAISE NOTICE 'product_id NOT NULL Constraint entfernt';
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- Prüfe ob size NOT NULL Constraint existiert
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1 FROM information_schema.columns
|
||||||
|
WHERE table_schema = 'falukant_data'
|
||||||
|
AND table_name = 'transport'
|
||||||
|
AND column_name = 'size'
|
||||||
|
AND is_nullable = 'NO'
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE falukant_data.transport
|
||||||
|
ALTER COLUMN size DROP NOT NULL;
|
||||||
|
RAISE NOTICE 'size NOT NULL Constraint entfernt';
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
|
`);
|
||||||
|
console.log("✅ Transport product_id und size sind jetzt nullable");
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('⚠️ Konnte Transport-Spalten nicht nullable machen:', e?.message || e);
|
||||||
|
}
|
||||||
|
|
||||||
console.log("Setting up associations...");
|
console.log("Setting up associations...");
|
||||||
setupAssociations();
|
setupAssociations();
|
||||||
|
|
||||||
|
|||||||
@@ -316,7 +316,21 @@
|
|||||||
"noVehiclesSelected": "Keine Fahrzeuge ausgewählt.",
|
"noVehiclesSelected": "Keine Fahrzeuge ausgewählt.",
|
||||||
"sendSuccess": "Fahrzeuge erfolgreich versendet!",
|
"sendSuccess": "Fahrzeuge erfolgreich versendet!",
|
||||||
"sendError": "Fehler beim Versenden der Fahrzeuge.",
|
"sendError": "Fehler beim Versenden der Fahrzeuge.",
|
||||||
"cancel": "Abbrechen"
|
"cancel": "Abbrechen",
|
||||||
|
"close": "Schließen",
|
||||||
|
"repair": "Reparieren",
|
||||||
|
"repairTooltip": "Fahrzeug reparieren",
|
||||||
|
"repairVehicleTitle": "Fahrzeug reparieren",
|
||||||
|
"repairVehicleType": "Fahrzeugtyp",
|
||||||
|
"repairCurrentCondition": "Aktueller Zustand",
|
||||||
|
"repairCost": "Reparaturkosten",
|
||||||
|
"repairBuildTime": "Reparaturdauer",
|
||||||
|
"repairBuildTimeInstant": "Sofort",
|
||||||
|
"repairBuildTimeHours": "Stunden",
|
||||||
|
"repairBuildTimeMinutes": "Minuten",
|
||||||
|
"repairConfirm": "Reparieren",
|
||||||
|
"repairSuccess": "Fahrzeug wird repariert!",
|
||||||
|
"repairError": "Fehler beim Reparieren des Fahrzeugs."
|
||||||
},
|
},
|
||||||
"stocktype": {
|
"stocktype": {
|
||||||
"wood": "Holzlager",
|
"wood": "Holzlager",
|
||||||
|
|||||||
@@ -108,6 +108,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
<div class="vehicle-actions">
|
||||||
<button
|
<button
|
||||||
v-if="v.status === 'available'"
|
v-if="v.status === 'available'"
|
||||||
@click="openSendVehicleDialog(v.id)"
|
@click="openSendVehicleDialog(v.id)"
|
||||||
@@ -115,7 +116,16 @@
|
|||||||
>
|
>
|
||||||
{{ $t('falukant.branch.transport.send') }}
|
{{ $t('falukant.branch.transport.send') }}
|
||||||
</button>
|
</button>
|
||||||
<span v-else class="no-action">—</span>
|
<button
|
||||||
|
v-if="v.status === 'available' && v.condition < 100"
|
||||||
|
@click="openRepairVehicleDialog(v)"
|
||||||
|
:title="$t('falukant.branch.transport.repairTooltip')"
|
||||||
|
class="repair-button"
|
||||||
|
>
|
||||||
|
{{ $t('falukant.branch.transport.repair') }}
|
||||||
|
</button>
|
||||||
|
<span v-if="v.status !== 'available'" class="no-action">—</span>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -154,7 +164,7 @@
|
|||||||
<div v-if="sendVehicleDialog.show" class="modal-overlay" @click.self="closeSendVehicleDialog">
|
<div v-if="sendVehicleDialog.show" class="modal-overlay" @click.self="closeSendVehicleDialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<h3>{{ $t('falukant.branch.transport.sendVehiclesTitle') }}</h3>
|
<h3>{{ $t('falukant.branch.transport.sendVehiclesTitle') }}</h3>
|
||||||
<div class="send-vehicle-form">
|
<div v-if="!sendVehicleDialog.success" class="send-vehicle-form">
|
||||||
<label>
|
<label>
|
||||||
{{ $t('falukant.branch.transport.selectTargetBranch') }}
|
{{ $t('falukant.branch.transport.selectTargetBranch') }}
|
||||||
<select v-model.number="sendVehicleDialog.targetBranchId">
|
<select v-model.number="sendVehicleDialog.targetBranchId">
|
||||||
@@ -173,6 +183,49 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-else class="send-vehicle-success">
|
||||||
|
<p>{{ $t('falukant.branch.transport.sendSuccess') }}</p>
|
||||||
|
<div class="modal-buttons">
|
||||||
|
<button @click="closeSendVehicleDialog">
|
||||||
|
{{ $t('falukant.branch.transport.close') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Dialog zum Reparieren von Fahrzeugen -->
|
||||||
|
<div v-if="repairVehicleDialog.show" class="modal-overlay" @click.self="closeRepairVehicleDialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<h3>{{ $t('falukant.branch.transport.repairVehicleTitle') }}</h3>
|
||||||
|
<div class="repair-vehicle-form" v-if="repairVehicleDialog.vehicle">
|
||||||
|
<div class="repair-info">
|
||||||
|
<p>
|
||||||
|
<strong>{{ $t('falukant.branch.transport.repairVehicleType') }}:</strong>
|
||||||
|
{{ $t(`falukant.branch.vehicles.${repairVehicleDialog.vehicle.type.tr}`) }}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>{{ $t('falukant.branch.transport.repairCurrentCondition') }}:</strong>
|
||||||
|
{{ repairVehicleDialog.vehicle.condition }}%
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>{{ $t('falukant.branch.transport.repairCost') }}:</strong>
|
||||||
|
{{ formatMoney(repairVehicleDialog.repairCost) }}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>{{ $t('falukant.branch.transport.repairBuildTime') }}:</strong>
|
||||||
|
{{ formatBuildTime(repairVehicleDialog.buildTimeMinutes) }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-buttons">
|
||||||
|
<button @click="repairVehicle" class="repair-confirm-button">
|
||||||
|
{{ $t('falukant.branch.transport.repairConfirm') }}
|
||||||
|
</button>
|
||||||
|
<button @click="closeRepairVehicleDialog">
|
||||||
|
{{ $t('falukant.branch.transport.cancel') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -240,6 +293,13 @@ export default {
|
|||||||
vehicleIds: null,
|
vehicleIds: null,
|
||||||
vehicleTypeId: null,
|
vehicleTypeId: null,
|
||||||
targetBranchId: null,
|
targetBranchId: null,
|
||||||
|
success: false,
|
||||||
|
},
|
||||||
|
repairVehicleDialog: {
|
||||||
|
show: false,
|
||||||
|
vehicle: null,
|
||||||
|
repairCost: null,
|
||||||
|
buildTimeMinutes: null,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -625,6 +685,7 @@ export default {
|
|||||||
vehicleIds: null,
|
vehicleIds: null,
|
||||||
vehicleTypeId: null,
|
vehicleTypeId: null,
|
||||||
targetBranchId: null,
|
targetBranchId: null,
|
||||||
|
success: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -635,6 +696,7 @@ export default {
|
|||||||
vehicleIds: vehicleList.map(v => v.id),
|
vehicleIds: vehicleList.map(v => v.id),
|
||||||
vehicleTypeId: vehicleTypeId,
|
vehicleTypeId: vehicleTypeId,
|
||||||
targetBranchId: null,
|
targetBranchId: null,
|
||||||
|
success: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -645,6 +707,7 @@ export default {
|
|||||||
vehicleIds: null,
|
vehicleIds: null,
|
||||||
vehicleTypeId: null,
|
vehicleTypeId: null,
|
||||||
targetBranchId: null,
|
targetBranchId: null,
|
||||||
|
success: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -685,13 +748,85 @@ export default {
|
|||||||
|
|
||||||
await apiClient.post('/api/falukant/transports', payload);
|
await apiClient.post('/api/falukant/transports', payload);
|
||||||
await this.loadVehicles();
|
await this.loadVehicles();
|
||||||
this.closeSendVehicleDialog();
|
this.sendVehicleDialog.success = true;
|
||||||
alert(this.$t('falukant.branch.transport.sendSuccess'));
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error sending vehicles:', error);
|
console.error('Error sending vehicles:', error);
|
||||||
alert(this.$t('falukant.branch.transport.sendError'));
|
alert(this.$t('falukant.branch.transport.sendError'));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
openRepairVehicleDialog(vehicle) {
|
||||||
|
if (!vehicle || vehicle.status !== 'available' || vehicle.condition >= 100) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kosten berechnen: Baupreis * 0.8 * (100 - zustand) / 100
|
||||||
|
const baseCost = vehicle.type.cost || 0;
|
||||||
|
const repairCost = Math.round(baseCost * 0.8 * (100 - vehicle.condition) / 100);
|
||||||
|
const buildTimeMinutes = vehicle.type.buildTimeMinutes || 0;
|
||||||
|
|
||||||
|
this.repairVehicleDialog = {
|
||||||
|
show: true,
|
||||||
|
vehicle: vehicle,
|
||||||
|
repairCost: repairCost,
|
||||||
|
buildTimeMinutes: buildTimeMinutes,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
closeRepairVehicleDialog() {
|
||||||
|
this.repairVehicleDialog = {
|
||||||
|
show: false,
|
||||||
|
vehicle: null,
|
||||||
|
repairCost: null,
|
||||||
|
buildTimeMinutes: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
formatMoney(amount) {
|
||||||
|
if (amount == null) return '';
|
||||||
|
try {
|
||||||
|
return amount.toLocaleString(undefined, {
|
||||||
|
minimumFractionDigits: 0,
|
||||||
|
maximumFractionDigits: 0,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
return String(amount);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
formatBuildTime(minutes) {
|
||||||
|
if (!minutes || minutes === 0) {
|
||||||
|
return this.$t('falukant.branch.transport.repairBuildTimeInstant');
|
||||||
|
}
|
||||||
|
const hours = Math.floor(minutes / 60);
|
||||||
|
const mins = minutes % 60;
|
||||||
|
const parts = [];
|
||||||
|
if (hours > 0) {
|
||||||
|
parts.push(`${hours} ${this.$t('falukant.branch.transport.repairBuildTimeHours')}`);
|
||||||
|
}
|
||||||
|
if (mins > 0) {
|
||||||
|
parts.push(`${mins} ${this.$t('falukant.branch.transport.repairBuildTimeMinutes')}`);
|
||||||
|
}
|
||||||
|
return parts.length > 0 ? parts.join(' ') : '0 ' + this.$t('falukant.branch.transport.repairBuildTimeMinutes');
|
||||||
|
},
|
||||||
|
|
||||||
|
async repairVehicle() {
|
||||||
|
if (!this.repairVehicleDialog.vehicle) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await apiClient.post(`/api/falukant/vehicles/${this.repairVehicleDialog.vehicle.id}/repair`);
|
||||||
|
await this.loadVehicles();
|
||||||
|
this.closeRepairVehicleDialog();
|
||||||
|
alert(this.$t('falukant.branch.transport.repairSuccess'));
|
||||||
|
this.$refs.statusBar?.fetchStatus();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error repairing vehicle:', error);
|
||||||
|
const errorMessage = error.response?.data?.message || this.$t('falukant.branch.transport.repairError');
|
||||||
|
alert(errorMessage);
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@@ -808,4 +943,59 @@ h2 {
|
|||||||
.modal-buttons button:last-child:hover {
|
.modal-buttons button:last-child:hover {
|
||||||
background-color: #5a6268;
|
background-color: #5a6268;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vehicle-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repair-button {
|
||||||
|
background-color: #28a745;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repair-button:hover {
|
||||||
|
background-color: #218838;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repair-vehicle-form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repair-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repair-info p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repair-confirm-button {
|
||||||
|
background-color: #28a745;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repair-confirm-button:hover {
|
||||||
|
background-color: #218838;
|
||||||
|
}
|
||||||
|
|
||||||
|
.send-vehicle-success {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.send-vehicle-success p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.1em;
|
||||||
|
color: #28a745;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user