update camera dan otorisator

This commit is contained in:
majid
2024-10-30 14:29:01 +07:00
parent 4b08225403
commit e466e5e7ff
8 changed files with 660 additions and 35 deletions

View File

@@ -105,12 +105,19 @@ class PenilaianController extends Controller
$jenisPenilaian = JenisPenilaian::find($idPenilaian);
$teamPenilai = Teams::with(['regions', 'teamsUsers', 'teamsUsers.user', ])
$teamPenilai = Teams::with(['regions', 'teamsUsers', 'teamsUsers.user'])
->whereHas('regions', function ($q) use ($idRegion) {
$q->where('id', $idRegion);
})->get();
$existingTeamIds = $teamPenilai->pluck('id')->toArray();
$updateTeamPenilai = Teams::with(['regions', 'teamsUsers', 'teamsUsers.user'])
->whereNotIn('id', $existingTeamIds)
->get();
$regionName = null;
foreach ($teamPenilai as $item) {
$regionName = $item->regions;
@@ -120,7 +127,7 @@ class PenilaianController extends Controller
$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';
break;
case 'Pembayaran':
$status = 'proses pembayaran';
$status = 'proses';
break;
case 'Pembatalan':
$status = 'order';
break;
default:
$status = '';
@@ -300,8 +310,12 @@ class PenilaianController extends Controller
if (!empty($otorisator)) {
if ($status == 'proses') {
$query->whereIn('status_bayar', ['sudah_bayar', 'belum_bayar']);
}else{
$query->whereRaw('LOWER(status) = ?', [strtolower($status)]);
}
}
// Sorting berdasarkan sortField dan sortOrder
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {

View File

@@ -148,7 +148,7 @@
"classes": "",
"attributes": [],
"permission": "",
"roles": ["ssenior-officero"]
"roles": ["senior-officer"]
}
]
},

View File

@@ -352,7 +352,7 @@
<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">
<label class="form-label max-w-56">
Surveyor yang di tunjuk
@@ -385,7 +385,12 @@
<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>
<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>
</div>
@error('surveyor_id')
@@ -393,6 +398,7 @@
@enderror
</div>
</div>
<div id="penilaiId" class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
<label class="form-label max-w-56">
Penilai yang di tunjuk
@@ -405,16 +411,39 @@
@foreach ($teamPenilai->first()->teamsUsers as $item)
<option value="{{ $item->user->id }}">{{ $item->user->name }}</option>
@endforeach
<option value="pilih_dari_region">pilih dari region berdeda</option>
</select>
<button type="button" id="btnPenilai" class="btn btn-light">
<i class="ki-outline ki-notepad-edit"></i> Pilih dari region berbeda
</button>
</div>
@error('penilaian_id')
<em class="alert text-danger text-sm">{{ $message }}</em>
@enderror
</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 class="flex items-baseline flex-wrap lg:flex-nowrap gap-2.5">
@@ -528,6 +557,7 @@
} else if (selectedValue === 'berbeda') {
sameSurveyorPenilai.classList.add('hidden');
differentSurveyorPenilai.classList.remove('hidden');
} else {
sameSurveyorPenilai.classList.add('hidden');
differentSurveyorPenilai.classList.add('hidden');
@@ -541,23 +571,28 @@
if (selectedValue === 'pilih_dari_region') {
surveyorRegion.classList.remove('hidden');
surveyorRegion.classList.add('flex');
}else{
surveyorRegion.classList.add('hidden');
}
});
document.getElementById('penilaian_id').addEventListener('change', function() {
const selectedValue = this.value;
const penilaiRegion = document.getElementById('penilaiRegion');
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');
});
if (selectedValue === 'pilih_dari_region') {
penilaiRegion.classList.remove('hidden');
penilaiRegion.classList.add('flex');
}else{
penilaiRegion.classList.add('hidden');
}
});

View File

@@ -57,10 +57,7 @@
<span class="sort"> <span class="sort-label"> Tujuan Penilaian </span>
<span class="sort-icon"> </span> </span>
</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>
</tr>
</thead>
@@ -126,6 +123,8 @@
const element = document.querySelector('#permohonan-table');
const searchInput = document.getElementById('search');
const apiUrl = element.getAttribute('data-api-url');
const dataTableOptions = {
apiEndpoint: apiUrl,
@@ -171,19 +170,10 @@
return `${data.tujuan_penilaian.name}`;
},
},
status: {
title: 'Status'
},
actions: {
title: 'Status',
render: (item, data) => {
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}">
<i class="ki-outline ki-eye"></i>
</a>

View File

@@ -610,7 +610,7 @@
@push('scripts')
<script src="{{ asset('js/camera-editor.js') }}"></script>
@include('lpj::surveyor.js.camera-editor')
<script>
const style = document.createElement('style');
style.textContent = `

View File

@@ -162,7 +162,7 @@
}
}
if ($analisaType === 'tanah') {
$analisaType = 'kendaraan';
$analisaType = 'tanah_bangunan';
}
if ($analisaType === 'unit_rumah' || $analisaType === 'unit_gedung') {

View 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

View File

@@ -550,7 +550,8 @@ Breadcrumbs::for('basicdata.editData', function (BreadcrumbTrail $trail, $type =
$otorisatorSurveyor = [
'pelaporan' => 'Pelaporan',
'pembayaran' => 'Pembayaran',
'pembayaran' => 'Pembayaran'
'pembatalan' => 'Pembatalan',
'sla' => 'SLA',
];
foreach ($otorisatorSurveyor as $route => $title) {