update camera dan otorisator
This commit is contained in:
@@ -105,12 +105,19 @@ class PenilaianController extends Controller
|
|||||||
$jenisPenilaian = JenisPenilaian::find($idPenilaian);
|
$jenisPenilaian = JenisPenilaian::find($idPenilaian);
|
||||||
|
|
||||||
|
|
||||||
$teamPenilai = Teams::with(['regions', 'teamsUsers', 'teamsUsers.user', ])
|
$teamPenilai = Teams::with(['regions', 'teamsUsers', 'teamsUsers.user'])
|
||||||
->whereHas('regions', function ($q) use ($idRegion) {
|
->whereHas('regions', function ($q) use ($idRegion) {
|
||||||
$q->where('id', $idRegion);
|
$q->where('id', $idRegion);
|
||||||
})->get();
|
})->get();
|
||||||
|
|
||||||
|
|
||||||
|
$existingTeamIds = $teamPenilai->pluck('id')->toArray();
|
||||||
|
|
||||||
|
$updateTeamPenilai = Teams::with(['regions', 'teamsUsers', 'teamsUsers.user'])
|
||||||
|
->whereNotIn('id', $existingTeamIds)
|
||||||
|
->get();
|
||||||
|
|
||||||
|
|
||||||
$regionName = null;
|
$regionName = null;
|
||||||
foreach ($teamPenilai as $item) {
|
foreach ($teamPenilai as $item) {
|
||||||
$regionName = $item->regions;
|
$regionName = $item->regions;
|
||||||
@@ -120,7 +127,7 @@ class PenilaianController extends Controller
|
|||||||
|
|
||||||
$penilaian = Penilaian::where('nomor_registrasi', $permohonan->nomor_registrasi)->first();
|
$penilaian = Penilaian::where('nomor_registrasi', $permohonan->nomor_registrasi)->first();
|
||||||
|
|
||||||
return view('lpj::penilaian.form', compact('permohonan', 'teamPenilai', 'jenisPenilaian', 'penilaian', 'regionName'));
|
return view('lpj::penilaian.form', compact('permohonan', 'teamPenilai', 'jenisPenilaian', 'penilaian', 'regionName', 'updateTeamPenilai'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -274,7 +281,10 @@ class PenilaianController extends Controller
|
|||||||
$status = 'proses paparan';
|
$status = 'proses paparan';
|
||||||
break;
|
break;
|
||||||
case 'Pembayaran':
|
case 'Pembayaran':
|
||||||
$status = 'proses pembayaran';
|
$status = 'proses';
|
||||||
|
break;
|
||||||
|
case 'Pembatalan':
|
||||||
|
$status = 'order';
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
$status = '';
|
$status = '';
|
||||||
@@ -300,7 +310,11 @@ class PenilaianController extends Controller
|
|||||||
|
|
||||||
|
|
||||||
if (!empty($otorisator)) {
|
if (!empty($otorisator)) {
|
||||||
$query->whereRaw('LOWER(status) = ?', [strtolower($status)]);
|
if ($status == 'proses') {
|
||||||
|
$query->whereIn('status_bayar', ['sudah_bayar', 'belum_bayar']);
|
||||||
|
}else{
|
||||||
|
$query->whereRaw('LOWER(status) = ?', [strtolower($status)]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sorting berdasarkan sortField dan sortOrder
|
// Sorting berdasarkan sortField dan sortOrder
|
||||||
|
|||||||
@@ -148,7 +148,7 @@
|
|||||||
"classes": "",
|
"classes": "",
|
||||||
"attributes": [],
|
"attributes": [],
|
||||||
"permission": "",
|
"permission": "",
|
||||||
"roles": ["ssenior-officero"]
|
"roles": ["senior-officer"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -352,7 +352,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<div id="different_surveyor_penilai" class="hidden">
|
<div id="different_surveyor_penilai" class="hidden">
|
||||||
|
<div class="grid gap-2.5">
|
||||||
<div id="surveyorId" class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
|
<div id="surveyorId" class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
|
||||||
<label class="form-label max-w-56">
|
<label class="form-label max-w-56">
|
||||||
Surveyor yang di tunjuk
|
Surveyor yang di tunjuk
|
||||||
@@ -385,7 +385,12 @@
|
|||||||
<select id="surveyor_id" name="surveyor_id"
|
<select id="surveyor_id" name="surveyor_id"
|
||||||
class="tomselect input @error('surveyor_id') border-danger bg-danger-light @enderror w-full">
|
class="tomselect input @error('surveyor_id') border-danger bg-danger-light @enderror w-full">
|
||||||
<option value="">Pilih Region</option>
|
<option value="">Pilih Region</option>
|
||||||
<option value="pilih_dari_region">Pilih dari region berdeda</option>
|
|
||||||
|
@if (isset($updateTeamPenilai))
|
||||||
|
@foreach ($updateTeamPenilai as $item)
|
||||||
|
<option value="{{ $item->regions->id }}">{{ $item->regions->name }}</option>
|
||||||
|
@endforeach
|
||||||
|
@endif
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@error('surveyor_id')
|
@error('surveyor_id')
|
||||||
@@ -393,6 +398,7 @@
|
|||||||
@enderror
|
@enderror
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="penilaiId" class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
|
<div id="penilaiId" class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
|
||||||
<label class="form-label max-w-56">
|
<label class="form-label max-w-56">
|
||||||
Penilai yang di tunjuk
|
Penilai yang di tunjuk
|
||||||
@@ -405,16 +411,39 @@
|
|||||||
@foreach ($teamPenilai->first()->teamsUsers as $item)
|
@foreach ($teamPenilai->first()->teamsUsers as $item)
|
||||||
<option value="{{ $item->user->id }}">{{ $item->user->name }}</option>
|
<option value="{{ $item->user->id }}">{{ $item->user->name }}</option>
|
||||||
@endforeach
|
@endforeach
|
||||||
|
<option value="pilih_dari_region">pilih dari region berdeda</option>
|
||||||
</select>
|
</select>
|
||||||
<button type="button" id="btnPenilai" class="btn btn-light">
|
|
||||||
<i class="ki-outline ki-notepad-edit"></i> Pilih dari region berbeda
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
@error('penilaian_id')
|
@error('penilaian_id')
|
||||||
<em class="alert text-danger text-sm">{{ $message }}</em>
|
<em class="alert text-danger text-sm">{{ $message }}</em>
|
||||||
@enderror
|
@enderror
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="penilaiRegion" class="hidden items-baseline flex-wrap lg:flex-nowrap gap-2.5">
|
||||||
|
<label class="form-label max-w-56">
|
||||||
|
Pilih Region
|
||||||
|
</label>
|
||||||
|
<div class="flex flex-wrap items-baseline w-full">
|
||||||
|
<div class="input-group w-full">
|
||||||
|
<select id="surveyor_id" name="surveyor_id"
|
||||||
|
class="tomselect input @error('surveyor_id') border-danger bg-danger-light @enderror w-full">
|
||||||
|
<option value="">Pilih Region</option>
|
||||||
|
|
||||||
|
@if (isset($updateTeamPenilai))
|
||||||
|
@foreach ($updateTeamPenilai as $item)
|
||||||
|
<option value="{{ $item->regions->id }}">{{ $item->regions->name }}</option>
|
||||||
|
@endforeach
|
||||||
|
@endif
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
@error('surveyor_id')
|
||||||
|
<em class="alert text-danger text-sm">{{ $message }}</em>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
|
<div class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
|
||||||
@@ -528,6 +557,7 @@
|
|||||||
} else if (selectedValue === 'berbeda') {
|
} else if (selectedValue === 'berbeda') {
|
||||||
sameSurveyorPenilai.classList.add('hidden');
|
sameSurveyorPenilai.classList.add('hidden');
|
||||||
differentSurveyorPenilai.classList.remove('hidden');
|
differentSurveyorPenilai.classList.remove('hidden');
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
sameSurveyorPenilai.classList.add('hidden');
|
sameSurveyorPenilai.classList.add('hidden');
|
||||||
differentSurveyorPenilai.classList.add('hidden');
|
differentSurveyorPenilai.classList.add('hidden');
|
||||||
@@ -541,23 +571,28 @@
|
|||||||
|
|
||||||
if (selectedValue === 'pilih_dari_region') {
|
if (selectedValue === 'pilih_dari_region') {
|
||||||
surveyorRegion.classList.remove('hidden');
|
surveyorRegion.classList.remove('hidden');
|
||||||
|
surveyorRegion.classList.add('flex');
|
||||||
}else{
|
}else{
|
||||||
surveyorRegion.classList.add('hidden');
|
surveyorRegion.classList.add('hidden');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.getElementById('penilaian_id').addEventListener('change', function() {
|
||||||
|
const selectedValue = this.value;
|
||||||
|
const penilaiRegion = document.getElementById('penilaiRegion');
|
||||||
|
|
||||||
|
if (selectedValue === 'pilih_dari_region') {
|
||||||
|
penilaiRegion.classList.remove('hidden');
|
||||||
|
penilaiRegion.classList.add('flex');
|
||||||
|
}else{
|
||||||
|
penilaiRegion.classList.add('hidden');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function handleRegionBerbeda(params) {
|
|
||||||
const surveyor = document.getElementById('btnSurveyor');
|
|
||||||
const penilai = document.getElementById('btnPenilai');
|
|
||||||
const surveyorId = document.getElementById('surveyorRegion');
|
|
||||||
|
|
||||||
surveyor.addEventListener('click', function() {
|
|
||||||
surveyorId.classList.add('hidden');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -57,10 +57,7 @@
|
|||||||
<span class="sort"> <span class="sort-label"> Tujuan Penilaian </span>
|
<span class="sort"> <span class="sort-label"> Tujuan Penilaian </span>
|
||||||
<span class="sort-icon"> </span> </span>
|
<span class="sort-icon"> </span> </span>
|
||||||
</th>
|
</th>
|
||||||
<th class="min-w-[150px]" data-datatable-column="status">
|
|
||||||
<span class="sort"> <span class="sort-label"> Status </span>
|
|
||||||
<span class="sort-icon"> </span> </span>
|
|
||||||
</th>
|
|
||||||
<th class="min-w-[50px] text-center" data-datatable-column="actions">Action</th>
|
<th class="min-w-[50px] text-center" data-datatable-column="actions">Action</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -126,6 +123,8 @@
|
|||||||
const element = document.querySelector('#permohonan-table');
|
const element = document.querySelector('#permohonan-table');
|
||||||
const searchInput = document.getElementById('search');
|
const searchInput = document.getElementById('search');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const apiUrl = element.getAttribute('data-api-url');
|
const apiUrl = element.getAttribute('data-api-url');
|
||||||
const dataTableOptions = {
|
const dataTableOptions = {
|
||||||
apiEndpoint: apiUrl,
|
apiEndpoint: apiUrl,
|
||||||
@@ -171,19 +170,10 @@
|
|||||||
return `${data.tujuan_penilaian.name}`;
|
return `${data.tujuan_penilaian.name}`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
status: {
|
|
||||||
title: 'Status'
|
|
||||||
},
|
|
||||||
actions: {
|
actions: {
|
||||||
title: 'Status',
|
title: 'Status',
|
||||||
render: (item, data) => {
|
render: (item, data) => {
|
||||||
return `<div class="flex flex-nowrap justify-center">
|
return `<div class="flex flex-nowrap justify-center">
|
||||||
|
|
||||||
<a class="btn btn-sm btn-icon btn-clear btn-success " onclick="otorisator(${data.id})">
|
|
||||||
<i class="ki-filled ki-check-squared"></i>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
|
|
||||||
<a class="btn btn-sm btn-icon btn-clear btn-warning " href="otorisator/show/${data.id}">
|
<a class="btn btn-sm btn-icon btn-clear btn-warning " href="otorisator/show/${data.id}">
|
||||||
<i class="ki-outline ki-eye"></i>
|
<i class="ki-outline ki-eye"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -610,7 +610,7 @@
|
|||||||
|
|
||||||
|
|
||||||
@push('scripts')
|
@push('scripts')
|
||||||
<script src="{{ asset('js/camera-editor.js') }}"></script>
|
@include('lpj::surveyor.js.camera-editor')
|
||||||
<script>
|
<script>
|
||||||
const style = document.createElement('style');
|
const style = document.createElement('style');
|
||||||
style.textContent = `
|
style.textContent = `
|
||||||
|
|||||||
@@ -162,7 +162,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($analisaType === 'tanah') {
|
if ($analisaType === 'tanah') {
|
||||||
$analisaType = 'kendaraan';
|
$analisaType = 'tanah_bangunan';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($analisaType === 'unit_rumah' || $analisaType === 'unit_gedung') {
|
if ($analisaType === 'unit_rumah' || $analisaType === 'unit_gedung') {
|
||||||
|
|||||||
585
resources/views/surveyor/js/camera-editor.blade.php
Normal file
585
resources/views/surveyor/js/camera-editor.blade.php
Normal file
@@ -0,0 +1,585 @@
|
|||||||
|
@push('scripts')
|
||||||
|
<script>
|
||||||
|
class UniversalCameraEditor {
|
||||||
|
constructor() {
|
||||||
|
// Initialize elements
|
||||||
|
this.video = document.getElementById('video');
|
||||||
|
this.canvas = document.getElementById('canvas');
|
||||||
|
this.drawingCanvas = document.getElementById('drawingCanvas');
|
||||||
|
this.ctx = this.drawingCanvas.getContext('2d');
|
||||||
|
|
||||||
|
// Buttons
|
||||||
|
this.startButton = document.getElementById('startButton');
|
||||||
|
this.takePhotoButton = document.getElementById('takePhotoButton');
|
||||||
|
this.switchButton = document.getElementById('switchButton');
|
||||||
|
this.backButton = document.getElementById('backButton');
|
||||||
|
this.saveButton = document.getElementById('saveButton');
|
||||||
|
this.undoButton = document.getElementById('undoButton');
|
||||||
|
this.resetButton = document.getElementById('resetButton');
|
||||||
|
|
||||||
|
// Drawing controls
|
||||||
|
this.colorPicker = document.getElementById('colorPicker');
|
||||||
|
this.brushSize = document.getElementById('brushSize');
|
||||||
|
this.brushSizeValue = document.getElementById('brushSizeValue');
|
||||||
|
|
||||||
|
// Text modal elements
|
||||||
|
this.textModal = document.getElementById('textModal');
|
||||||
|
this.textInput = document.getElementById('textInput');
|
||||||
|
this.confirmTextBtn = document.getElementById('confirmTextBtn');
|
||||||
|
this.cancelTextBtn = document.getElementById('cancelTextBtn');
|
||||||
|
|
||||||
|
// Initialize state
|
||||||
|
this.stream = null;
|
||||||
|
this.currentFacingMode = 'environment';
|
||||||
|
this.isDrawing = false;
|
||||||
|
this.currentTool = 'brush';
|
||||||
|
this.drawingHistory = [];
|
||||||
|
this.historyIndex = -1;
|
||||||
|
|
||||||
|
// Drawing coordinates
|
||||||
|
this.startX = 0;
|
||||||
|
this.startY = 0;
|
||||||
|
this.lastX = 0;
|
||||||
|
this.lastY = 0;
|
||||||
|
|
||||||
|
// Setup
|
||||||
|
this.setupCanvas();
|
||||||
|
this.setupEventListeners();
|
||||||
|
this.setupDrawingContext();
|
||||||
|
|
||||||
|
|
||||||
|
// Add text overlay container
|
||||||
|
this.textOverlay = document.getElementById('textOverlay');
|
||||||
|
this.activeTextElement = null;
|
||||||
|
this.isDraggingText = false;
|
||||||
|
this.textOffset = {
|
||||||
|
x: 0,
|
||||||
|
y: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Current input field reference
|
||||||
|
this.currentInputField = null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
setupCanvas() {
|
||||||
|
// Set initial canvas size
|
||||||
|
this.canvas.width = 1280;
|
||||||
|
this.canvas.height = 960;
|
||||||
|
this.drawingCanvas.width = 1280;
|
||||||
|
this.drawingCanvas.height = 960;
|
||||||
|
}
|
||||||
|
|
||||||
|
setupDrawingContext() {
|
||||||
|
this.ctx.strokeStyle = this.colorPicker.value;
|
||||||
|
this.ctx.lineWidth = this.brushSize.value;
|
||||||
|
this.ctx.lineCap = 'round';
|
||||||
|
this.ctx.lineJoin = 'round';
|
||||||
|
}
|
||||||
|
|
||||||
|
async startCamera() {
|
||||||
|
try {
|
||||||
|
if (this.stream) {
|
||||||
|
this.stream.getTracks().forEach(track => track.stop());
|
||||||
|
}
|
||||||
|
|
||||||
|
const constraints = {
|
||||||
|
video: {
|
||||||
|
facingMode: this.currentFacingMode,
|
||||||
|
width: {
|
||||||
|
ideal: 1280
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
ideal: 960
|
||||||
|
}
|
||||||
|
},
|
||||||
|
audio: false
|
||||||
|
};
|
||||||
|
|
||||||
|
this.stream = await navigator.mediaDevices.getUserMedia(constraints);
|
||||||
|
this.video.srcObject = this.stream;
|
||||||
|
|
||||||
|
// Enable buttons after camera starts
|
||||||
|
this.takePhotoButton.disabled = false;
|
||||||
|
this.switchButton.disabled = false;
|
||||||
|
|
||||||
|
// Show video, hide canvas
|
||||||
|
this.video.style.display = 'block';
|
||||||
|
this.canvas.style.display = 'none';
|
||||||
|
this.drawingCanvas.style.display = 'none';
|
||||||
|
|
||||||
|
// Hide editor controls
|
||||||
|
document.getElementById('editorControls').classList.add('hidden');
|
||||||
|
document.getElementById('cameraControls').classList.remove('hidden');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error accessing camera:', error);
|
||||||
|
alert('Error accessing camera. Please make sure you have granted camera permissions.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
takePhoto() {
|
||||||
|
// Draw video frame to canvas
|
||||||
|
const context = this.canvas.getContext('2d');
|
||||||
|
context.drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);
|
||||||
|
|
||||||
|
// Hide video, show canvases
|
||||||
|
this.video.style.display = 'none';
|
||||||
|
this.canvas.style.display = 'block';
|
||||||
|
this.drawingCanvas.style.display = 'block';
|
||||||
|
|
||||||
|
// Show editor controls, hide camera controls
|
||||||
|
document.getElementById('editorControls').classList.remove('hidden');
|
||||||
|
document.getElementById('cameraControls').classList.add('hidden');
|
||||||
|
|
||||||
|
// Clear drawing history
|
||||||
|
this.drawingHistory = [];
|
||||||
|
this.historyIndex = -1;
|
||||||
|
this.saveDrawingState();
|
||||||
|
}
|
||||||
|
|
||||||
|
switchCamera() {
|
||||||
|
this.currentFacingMode = this.currentFacingMode === 'environment' ? 'user' : 'environment';
|
||||||
|
this.startCamera();
|
||||||
|
}
|
||||||
|
|
||||||
|
resetToCamera() {
|
||||||
|
// Clear canvases
|
||||||
|
const context = this.canvas.getContext('2d');
|
||||||
|
context.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||||
|
this.ctx.clearRect(0, 0, this.drawingCanvas.width, this.drawingCanvas.height);
|
||||||
|
|
||||||
|
// Reset history
|
||||||
|
this.drawingHistory = [];
|
||||||
|
this.historyIndex = -1;
|
||||||
|
|
||||||
|
// Restart camera
|
||||||
|
this.startCamera();
|
||||||
|
}
|
||||||
|
|
||||||
|
saveDrawingState() {
|
||||||
|
// Trim history if we're not at the end
|
||||||
|
if (this.historyIndex < this.drawingHistory.length - 1) {
|
||||||
|
this.drawingHistory = this.drawingHistory.slice(0, this.historyIndex + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save current state
|
||||||
|
this.drawingHistory.push(this.drawingCanvas.toDataURL());
|
||||||
|
this.historyIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
undo() {
|
||||||
|
if (this.historyIndex > 0) {
|
||||||
|
this.historyIndex--;
|
||||||
|
this.redrawHistory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
resetDrawing() {
|
||||||
|
this.ctx.clearRect(0, 0, this.drawingCanvas.width, this.drawingCanvas.height);
|
||||||
|
this.saveDrawingState();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement all event listeners from previous code here
|
||||||
|
setupEventListeners() {
|
||||||
|
// Camera controls
|
||||||
|
this.startButton.onclick = () => this.startCamera();
|
||||||
|
this.takePhotoButton.onclick = () => this.takePhoto();
|
||||||
|
this.switchButton.onclick = () => this.switchCamera();
|
||||||
|
this.backButton.onclick = () => this.resetToCamera();
|
||||||
|
|
||||||
|
// Drawing controls
|
||||||
|
this.setupDrawingEvents();
|
||||||
|
this.setupToolButtons();
|
||||||
|
|
||||||
|
// Settings changes
|
||||||
|
this.colorPicker.oninput = (e) => this.ctx.strokeStyle = e.target.value;
|
||||||
|
this.brushSize.oninput = (e) => {
|
||||||
|
this.ctx.lineWidth = e.target.value;
|
||||||
|
this.brushSizeValue.textContent = `${e.target.value}px`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// History controls
|
||||||
|
this.undoButton.onclick = () => this.undo();
|
||||||
|
this.resetButton.onclick = () => this.resetDrawing();
|
||||||
|
|
||||||
|
// Save functionality
|
||||||
|
this.saveButton.onclick = () => this.saveAndUpload();
|
||||||
|
|
||||||
|
|
||||||
|
// Text tool modal controls
|
||||||
|
this.confirmTextBtn.onclick = () => {
|
||||||
|
const text = this.textInput.value.trim();
|
||||||
|
if (text) {
|
||||||
|
this.addDraggableText(text);
|
||||||
|
this.textInput.value = '';
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
setupToolButtons() {
|
||||||
|
const toolButtons = document.querySelectorAll('.tool-btn');
|
||||||
|
toolButtons.forEach(button => {
|
||||||
|
button.onclick = () => {
|
||||||
|
toolButtons.forEach(btn => btn.classList.remove('active'));
|
||||||
|
button.classList.add('active');
|
||||||
|
this.currentTool = button.dataset.tool;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement drawing events from previous code here
|
||||||
|
setupDrawingEvents() {
|
||||||
|
// Mouse events
|
||||||
|
this.drawingCanvas.addEventListener('mousedown', (e) => this.startDrawing(e));
|
||||||
|
this.drawingCanvas.addEventListener('mousemove', (e) => this.draw(e));
|
||||||
|
this.drawingCanvas.addEventListener('mouseup', () => this.stopDrawing());
|
||||||
|
this.drawingCanvas.addEventListener('mouseout', () => this.stopDrawing());
|
||||||
|
|
||||||
|
// Touch events
|
||||||
|
this.drawingCanvas.addEventListener('touchstart', (e) => {
|
||||||
|
if (this.shouldPreventDefault(e)) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
const touch = e.touches[0];
|
||||||
|
const mouseEvent = new MouseEvent('mousedown', {
|
||||||
|
clientX: touch.clientX,
|
||||||
|
clientY: touch.clientY
|
||||||
|
});
|
||||||
|
this.startDrawing(mouseEvent);
|
||||||
|
}, {
|
||||||
|
passive: false
|
||||||
|
});
|
||||||
|
|
||||||
|
this.drawingCanvas.addEventListener('touchmove', (e) => {
|
||||||
|
if (this.shouldPreventDefault(e)) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
const touch = e.touches[0];
|
||||||
|
const mouseEvent = new MouseEvent('mousemove', {
|
||||||
|
clientX: touch.clientX,
|
||||||
|
clientY: touch.clientY
|
||||||
|
});
|
||||||
|
this.draw(mouseEvent);
|
||||||
|
}, {
|
||||||
|
passive: false
|
||||||
|
});
|
||||||
|
|
||||||
|
this.drawingCanvas.addEventListener('touchend', () => {
|
||||||
|
this.stopDrawing();
|
||||||
|
}, {
|
||||||
|
passive: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement drawing methods from previous code here
|
||||||
|
startDrawing(e) {
|
||||||
|
this.isDrawing = true;
|
||||||
|
const rect = this.drawingCanvas.getBoundingClientRect();
|
||||||
|
const scaleX = this.drawingCanvas.width / rect.width;
|
||||||
|
const scaleY = this.drawingCanvas.height / rect.height;
|
||||||
|
|
||||||
|
this.startX = (e.clientX - rect.left) * scaleX;
|
||||||
|
this.startY = (e.clientY - rect.top) * scaleY;
|
||||||
|
this.lastX = this.startX;
|
||||||
|
this.lastY = this.startY;
|
||||||
|
|
||||||
|
if (this.currentTool === 'brush') {
|
||||||
|
this.ctx.beginPath();
|
||||||
|
this.ctx.moveTo(this.startX, this.startY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addDraggableText(text) {
|
||||||
|
const textElement = document.createElement('div');
|
||||||
|
textElement.className = 'draggable-text';
|
||||||
|
textElement.style.cssText = `
|
||||||
|
position: absolute;
|
||||||
|
cursor: move;
|
||||||
|
user-select: none;
|
||||||
|
color: ${this.colorPicker.value};
|
||||||
|
font-size: ${this.brushSize.value * 2}px;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 3px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Create text span
|
||||||
|
const textSpan = document.createElement('span');
|
||||||
|
textSpan.textContent = text;
|
||||||
|
textElement.appendChild(textSpan);
|
||||||
|
|
||||||
|
// Create delete button
|
||||||
|
const deleteBtn = document.createElement('button');
|
||||||
|
deleteBtn.innerHTML = '×';
|
||||||
|
deleteBtn.style.cssText = `
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
color: #ff4444;
|
||||||
|
font-size: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0;
|
||||||
|
line-height: 1;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
`;
|
||||||
|
textElement.appendChild(deleteBtn);
|
||||||
|
|
||||||
|
// Show/hide delete button on hover
|
||||||
|
textElement.addEventListener('mouseenter', () => {
|
||||||
|
deleteBtn.style.opacity = '1';
|
||||||
|
});
|
||||||
|
textElement.addEventListener('mouseleave', () => {
|
||||||
|
deleteBtn.style.opacity = '0';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Delete functionality
|
||||||
|
deleteBtn.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
textElement.remove();
|
||||||
|
this.saveDrawingState();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Center the text initially
|
||||||
|
textElement.style.left = '50%';
|
||||||
|
textElement.style.top = '50%';
|
||||||
|
textElement.style.transform = 'translate(-50%, -50%)';
|
||||||
|
|
||||||
|
// Add drag functionality
|
||||||
|
this.setupDraggableText(textElement);
|
||||||
|
|
||||||
|
this.textOverlay.appendChild(textElement);
|
||||||
|
this.saveDrawingState();
|
||||||
|
}
|
||||||
|
|
||||||
|
setupDraggableText(element) {
|
||||||
|
let isDragging = false;
|
||||||
|
let currentX;
|
||||||
|
let currentY;
|
||||||
|
let initialX;
|
||||||
|
let initialY;
|
||||||
|
let xOffset = 0;
|
||||||
|
let yOffset = 0;
|
||||||
|
|
||||||
|
const dragStart = (e) => {
|
||||||
|
if (this.currentTool === 'text') {
|
||||||
|
const event = e.type === 'mousedown' ? e : e.touches[0];
|
||||||
|
initialX = event.clientX - xOffset;
|
||||||
|
initialY = event.clientY - yOffset;
|
||||||
|
|
||||||
|
if (e.target === element || e.target.parentNode === element) {
|
||||||
|
isDragging = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const drag = (e) => {
|
||||||
|
if (isDragging) {
|
||||||
|
e.preventDefault();
|
||||||
|
const event = e.type === 'mousemove' ? e : e.touches[0];
|
||||||
|
|
||||||
|
currentX = event.clientX - initialX;
|
||||||
|
currentY = event.clientY - initialY;
|
||||||
|
|
||||||
|
xOffset = currentX;
|
||||||
|
yOffset = currentY;
|
||||||
|
|
||||||
|
setTranslate(currentX, currentY, element);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const dragEnd = () => {
|
||||||
|
if (isDragging) {
|
||||||
|
isDragging = false;
|
||||||
|
this.saveDrawingState();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const setTranslate = (xPos, yPos, el) => {
|
||||||
|
el.style.transform = `translate(${xPos}px, ${yPos}px)`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mouse events
|
||||||
|
element.addEventListener('mousedown', dragStart);
|
||||||
|
document.addEventListener('mousemove', drag);
|
||||||
|
document.addEventListener('mouseup', dragEnd);
|
||||||
|
|
||||||
|
// Touch events
|
||||||
|
element.addEventListener('touchstart', dragStart);
|
||||||
|
document.addEventListener('touchmove', drag);
|
||||||
|
document.addEventListener('touchend', dragEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add this CSS to your stylesheet
|
||||||
|
|
||||||
|
|
||||||
|
draw(e) {
|
||||||
|
if (!this.isDrawing) return;
|
||||||
|
|
||||||
|
const rect = this.drawingCanvas.getBoundingClientRect();
|
||||||
|
const scaleX = this.drawingCanvas.width / rect.width;
|
||||||
|
const scaleY = this.drawingCanvas.height / rect.height;
|
||||||
|
const x = (e.clientX - rect.left) * scaleX;
|
||||||
|
const y = (e.clientY - rect.top) * scaleY;
|
||||||
|
|
||||||
|
switch (this.currentTool) {
|
||||||
|
case 'arrow':
|
||||||
|
// Restore the previous state before drawing new arrow
|
||||||
|
if (this.historyIndex >= 0) {
|
||||||
|
this.redrawHistory();
|
||||||
|
} else {
|
||||||
|
this.ctx.clearRect(0, 0, this.drawingCanvas.width, this.drawingCanvas.height);
|
||||||
|
}
|
||||||
|
this.drawArrow(this.startX, this.startY, x, y);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'brush':
|
||||||
|
this.ctx.lineTo(x, y);
|
||||||
|
this.ctx.stroke();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'rectangle':
|
||||||
|
// Restore the previous state before drawing new rectangle
|
||||||
|
if (this.historyIndex >= 0) {
|
||||||
|
this.redrawHistory();
|
||||||
|
} else {
|
||||||
|
this.ctx.clearRect(0, 0, this.drawingCanvas.width, this.drawingCanvas.height);
|
||||||
|
}
|
||||||
|
this.ctx.beginPath();
|
||||||
|
this.ctx.strokeRect(this.startX, this.startY, x - this.startX, y - this.startY);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'circle':
|
||||||
|
// Restore the previous state before drawing new circle
|
||||||
|
if (this.historyIndex >= 0) {
|
||||||
|
this.redrawHistory();
|
||||||
|
} else {
|
||||||
|
this.ctx.clearRect(0, 0, this.drawingCanvas.width, this.drawingCanvas.height);
|
||||||
|
}
|
||||||
|
const radius = Math.sqrt(Math.pow(x - this.startX, 2) + Math.pow(y - this.startY, 2));
|
||||||
|
this.ctx.beginPath();
|
||||||
|
this.ctx.arc(this.startX, this.startY, radius, 0, Math.PI * 2);
|
||||||
|
this.ctx.stroke();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lastX = x;
|
||||||
|
this.lastY = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
drawArrow(fromX, fromY, toX, toY) {
|
||||||
|
const headLength = 20;
|
||||||
|
const headAngle = Math.PI / 6;
|
||||||
|
|
||||||
|
// Calculate angle
|
||||||
|
const angle = Math.atan2(toY - fromY, toX - fromX);
|
||||||
|
|
||||||
|
// Draw main line
|
||||||
|
this.ctx.beginPath();
|
||||||
|
this.ctx.moveTo(fromX, fromY);
|
||||||
|
this.ctx.lineTo(toX, toY);
|
||||||
|
this.ctx.stroke();
|
||||||
|
|
||||||
|
// Draw arrowhead
|
||||||
|
this.ctx.beginPath();
|
||||||
|
this.ctx.moveTo(toX, toY);
|
||||||
|
this.ctx.lineTo(
|
||||||
|
toX - headLength * Math.cos(angle - headAngle),
|
||||||
|
toY - headLength * Math.sin(angle - headAngle)
|
||||||
|
);
|
||||||
|
this.ctx.moveTo(toX, toY);
|
||||||
|
this.ctx.lineTo(
|
||||||
|
toX - headLength * Math.cos(angle + headAngle),
|
||||||
|
toY - headLength * Math.sin(angle + headAngle)
|
||||||
|
);
|
||||||
|
this.ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
startDrawing(e) {
|
||||||
|
this.isDrawing = true;
|
||||||
|
const rect = this.drawingCanvas.getBoundingClientRect();
|
||||||
|
const scaleX = this.drawingCanvas.width / rect.width;
|
||||||
|
const scaleY = this.drawingCanvas.height / rect.height;
|
||||||
|
|
||||||
|
this.startX = (e.clientX - rect.left) * scaleX;
|
||||||
|
this.startY = (e.clientY - rect.top) * scaleY;
|
||||||
|
this.lastX = this.startX;
|
||||||
|
this.lastY = this.startY;
|
||||||
|
|
||||||
|
if (this.currentTool === 'brush') {
|
||||||
|
this.ctx.beginPath();
|
||||||
|
this.ctx.moveTo(this.startX, this.startY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stopDrawing() {
|
||||||
|
if (this.isDrawing) {
|
||||||
|
this.isDrawing = false;
|
||||||
|
if (this.currentTool === 'brush') {
|
||||||
|
this.ctx.closePath();
|
||||||
|
}
|
||||||
|
this.saveDrawingState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
redrawHistory() {
|
||||||
|
if (this.historyIndex >= 0 && this.drawingHistory[this.historyIndex]) {
|
||||||
|
const img = new Image();
|
||||||
|
img.src = this.drawingHistory[this.historyIndex];
|
||||||
|
this.ctx.clearRect(0, 0, this.drawingCanvas.width, this.drawingCanvas.height);
|
||||||
|
this.ctx.drawImage(img, 0, 0);
|
||||||
|
} else {
|
||||||
|
this.ctx.clearRect(0, 0, this.drawingCanvas.width, this.drawingCanvas.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closeEditor() {
|
||||||
|
if (this.stream) {
|
||||||
|
this.stream.getTracks().forEach(track => track.stop());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear canvases and text overlay
|
||||||
|
this.ctx.clearRect(0, 0, this.drawingCanvas.width, this.drawingCanvas.height);
|
||||||
|
const mainCtx = this.canvas.getContext('2d');
|
||||||
|
mainCtx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||||
|
this.textOverlay.innerHTML = '';
|
||||||
|
|
||||||
|
// Reset state
|
||||||
|
this.drawingHistory = [];
|
||||||
|
this.historyIndex = -1;
|
||||||
|
this.currentInputField = null;
|
||||||
|
|
||||||
|
// Close modals
|
||||||
|
this.cameraModal.hide();
|
||||||
|
this.textInputModal.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override this method in your implementation
|
||||||
|
saveAndUpload() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
closeCamera() {
|
||||||
|
// Stop the video stream
|
||||||
|
if (this.stream) {
|
||||||
|
this.stream.getTracks().forEach(track => track.stop());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide video and canvas elements
|
||||||
|
this.video.style.display = 'none';
|
||||||
|
this.canvas.style.display = 'none';
|
||||||
|
this.drawingCanvas.style.display = 'none';
|
||||||
|
|
||||||
|
|
||||||
|
// Reset any additional state if needed
|
||||||
|
this.stream = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
@endpush
|
||||||
@@ -550,7 +550,8 @@ Breadcrumbs::for('basicdata.editData', function (BreadcrumbTrail $trail, $type =
|
|||||||
$otorisatorSurveyor = [
|
$otorisatorSurveyor = [
|
||||||
'pelaporan' => 'Pelaporan',
|
'pelaporan' => 'Pelaporan',
|
||||||
'pembayaran' => 'Pembayaran',
|
'pembayaran' => 'Pembayaran',
|
||||||
'pembayaran' => 'Pembayaran'
|
'pembatalan' => 'Pembatalan',
|
||||||
|
'sla' => 'SLA',
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($otorisatorSurveyor as $route => $title) {
|
foreach ($otorisatorSurveyor as $route => $title) {
|
||||||
|
|||||||
Reference in New Issue
Block a user