Spaces:
Running
Running
Add 2 files
Browse files- index.html +175 -45
- prompts.txt +2 -1
index.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
| 6 |
<title>Rig Operation Procedure Builder - LEGO Style</title>
|
| 7 |
<script src="https://cdn.tailwindcss.com"></script>
|
| 8 |
-
<script src="https://
|
| 9 |
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/
|
| 10 |
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
| 11 |
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
| 12 |
<style>
|
|
@@ -60,6 +60,9 @@
|
|
| 60 |
<button id="downloadWordBtn" class="bg-purple-600 hover:bg-purple-700 text-white px-5 py-2 rounded-lg text-sm font-medium flex items-center gap-1 transition">
|
| 61 |
<i class="fas fa-file-word"></i> Download Word
|
| 62 |
</button>
|
|
|
|
|
|
|
|
|
|
| 63 |
<button id="approvalBtn" class="bg-orange-500 hover:bg-orange-600 text-white px-5 py-2 rounded-lg text-sm font-medium flex items-center gap-1 transition">
|
| 64 |
<i class="fas fa-check-circle"></i> Submit for Approval
|
| 65 |
</button>
|
|
@@ -332,6 +335,7 @@
|
|
| 332 |
const saveBtn = document.getElementById('saveBtn');
|
| 333 |
const printBtn = document.getElementById('printBtn');
|
| 334 |
const downloadWordBtn = document.getElementById('downloadWordBtn');
|
|
|
|
| 335 |
const approvalBtn = document.getElementById('approvalBtn');
|
| 336 |
|
| 337 |
// Initialize
|
|
@@ -402,9 +406,8 @@
|
|
| 402 |
// Click on builder to add
|
| 403 |
builder.addEventListener('click', (e) => {
|
| 404 |
if (e.target === builder || e.target === placeholder) {
|
| 405 |
-
// Show quick add menu
|
| 406 |
if (blocks.length === 0) {
|
| 407 |
-
addBlock('step');
|
| 408 |
}
|
| 409 |
}
|
| 410 |
});
|
|
@@ -417,11 +420,9 @@
|
|
| 417 |
template.id = id;
|
| 418 |
template.style.display = 'block';
|
| 419 |
|
| 420 |
-
// Set step number
|
| 421 |
const stepNumber = template.querySelector('.step-number');
|
| 422 |
stepNumber.textContent = procedureCounter++;
|
| 423 |
|
| 424 |
-
// Handle dynamic content
|
| 425 |
if (type === 'material') {
|
| 426 |
setupMaterialBlock(template);
|
| 427 |
} else if (type === 'personnel') {
|
|
@@ -432,7 +433,6 @@
|
|
| 432 |
setupUploadArea(template);
|
| 433 |
}
|
| 434 |
|
| 435 |
-
// Add event listeners to controls
|
| 436 |
template.querySelector('.remove-block').addEventListener('click', () => {
|
| 437 |
if (confirm('Are you sure you want to remove this block?')) {
|
| 438 |
template.remove();
|
|
@@ -441,13 +441,9 @@
|
|
| 441 |
});
|
| 442 |
|
| 443 |
template.querySelector('.edit-block').addEventListener('click', () => {
|
| 444 |
-
alert('Edit mode
|
| 445 |
-
// Just focus the first input/textarea
|
| 446 |
-
const firstInput = template.querySelector('input, textarea');
|
| 447 |
-
if (firstInput) firstInput.focus();
|
| 448 |
});
|
| 449 |
|
| 450 |
-
// Make block draggable
|
| 451 |
template.setAttribute('draggable', true);
|
| 452 |
template.addEventListener('dragstart', (e) => {
|
| 453 |
e.dataTransfer.setData('text/plain', 'move');
|
|
@@ -459,11 +455,9 @@
|
|
| 459 |
template.classList.remove('dragging');
|
| 460 |
});
|
| 461 |
|
| 462 |
-
// Append to builder
|
| 463 |
builder.appendChild(template);
|
| 464 |
placeholder.style.display = 'none';
|
| 465 |
|
| 466 |
-
// Save state
|
| 467 |
blocks.push({ id, type, element: template });
|
| 468 |
}
|
| 469 |
|
|
@@ -492,7 +486,7 @@
|
|
| 492 |
}
|
| 493 |
|
| 494 |
addButton.addEventListener('click', addMaterialItem);
|
| 495 |
-
addMaterialItem();
|
| 496 |
}
|
| 497 |
|
| 498 |
function setupPersonnelBlock(block) {
|
|
@@ -520,7 +514,7 @@
|
|
| 520 |
}
|
| 521 |
|
| 522 |
addButton.addEventListener('click', addPersonnelItem);
|
| 523 |
-
addPersonnelItem();
|
| 524 |
}
|
| 525 |
|
| 526 |
function setupChecklistBlock(block) {
|
|
@@ -542,7 +536,7 @@
|
|
| 542 |
}
|
| 543 |
|
| 544 |
addButton.addEventListener('click', addChecklistItem);
|
| 545 |
-
addChecklistItem();
|
| 546 |
}
|
| 547 |
|
| 548 |
function setupUploadArea(block) {
|
|
@@ -566,7 +560,6 @@
|
|
| 566 |
}
|
| 567 |
});
|
| 568 |
|
| 569 |
-
// Drag and drop for file
|
| 570 |
area.addEventListener('drop', (e) => {
|
| 571 |
e.preventDefault();
|
| 572 |
const file = e.dataTransfer.files[0];
|
|
@@ -584,7 +577,6 @@
|
|
| 584 |
});
|
| 585 |
}
|
| 586 |
|
| 587 |
-
// Update step numbers sequentially
|
| 588 |
function updateStepNumbers() {
|
| 589 |
procedureCounter = 1;
|
| 590 |
document.querySelectorAll('.procedure-block').forEach(block => {
|
|
@@ -595,9 +587,7 @@
|
|
| 595 |
});
|
| 596 |
}
|
| 597 |
|
| 598 |
-
// Setup global events
|
| 599 |
function setupEvents() {
|
| 600 |
-
// Reorder blocks via drag and drop
|
| 601 |
builder.addEventListener('dragover', (e) => {
|
| 602 |
e.preventDefault();
|
| 603 |
const afterElement = getDragAfterElement(builder, e.clientY);
|
|
@@ -609,7 +599,6 @@
|
|
| 609 |
}
|
| 610 |
});
|
| 611 |
|
| 612 |
-
// Save procedure
|
| 613 |
saveBtn.addEventListener('click', () => {
|
| 614 |
const title = prompt('Enter procedure title:', 'New Procedure') || 'Untitled';
|
| 615 |
const id = 'PRC-' + Date.now().toString().substr(-6).toUpperCase();
|
|
@@ -629,7 +618,6 @@
|
|
| 629 |
})
|
| 630 |
};
|
| 631 |
|
| 632 |
-
// Save to "database" (localStorage)
|
| 633 |
let procedures = JSON.parse(localStorage.getItem('rigProcedures') || '[]');
|
| 634 |
procedures.push(procedure);
|
| 635 |
localStorage.setItem('rigProcedures', JSON.stringify(procedures));
|
|
@@ -638,27 +626,178 @@
|
|
| 638 |
loadProcedures();
|
| 639 |
});
|
| 640 |
|
| 641 |
-
// Print
|
| 642 |
printBtn.addEventListener('click', () => {
|
| 643 |
window.print();
|
| 644 |
});
|
| 645 |
|
| 646 |
-
// Download as
|
| 647 |
downloadWordBtn.addEventListener('click', () => {
|
| 648 |
-
const {
|
| 649 |
-
|
| 650 |
-
|
| 651 |
-
|
| 652 |
-
|
| 653 |
-
|
| 654 |
-
const
|
| 655 |
-
|
| 656 |
-
|
| 657 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 658 |
});
|
| 659 |
});
|
| 660 |
|
| 661 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 662 |
approvalBtn.addEventListener('click', () => {
|
| 663 |
if (blocks.length === 0) {
|
| 664 |
alert('Please add at least one block before submitting.');
|
|
@@ -666,7 +805,6 @@
|
|
| 666 |
}
|
| 667 |
if (confirm('Submit this procedure for approval? It will be reviewed by the safety team.')) {
|
| 668 |
const procedures = JSON.parse(localStorage.getItem('rigProcedures') || '[]');
|
| 669 |
-
// Update the last saved one
|
| 670 |
if (procedures.length > 0) {
|
| 671 |
procedures[procedures.length - 1].status = 'Pending';
|
| 672 |
localStorage.setItem('rigProcedures', JSON.stringify(procedures));
|
|
@@ -690,7 +828,6 @@
|
|
| 690 |
}, { offset: Number.NEGATIVE_INFINITY }).element;
|
| 691 |
}
|
| 692 |
|
| 693 |
-
// Load procedures from localStorage
|
| 694 |
function loadProcedures() {
|
| 695 |
const tableBody = document.getElementById('procedureTableBody');
|
| 696 |
tableBody.innerHTML = '';
|
|
@@ -716,7 +853,6 @@
|
|
| 716 |
tableBody.appendChild(tr);
|
| 717 |
});
|
| 718 |
|
| 719 |
-
// Add event listeners
|
| 720 |
document.querySelectorAll('.edit-proc').forEach(btn => {
|
| 721 |
btn.addEventListener('click', (e) => {
|
| 722 |
const id = e.target.dataset.id;
|
|
@@ -737,19 +873,16 @@
|
|
| 737 |
const procedure = procedures.find(p => p.id === id);
|
| 738 |
if (!procedure) return;
|
| 739 |
|
| 740 |
-
// Clear current builder
|
| 741 |
builder.innerHTML = '';
|
| 742 |
blocks = [];
|
| 743 |
procedureCounter = 1;
|
| 744 |
|
| 745 |
-
// Add blocks
|
| 746 |
procedure.blocks.forEach(blockObj => {
|
| 747 |
const div = document.createElement('div');
|
| 748 |
div.innerHTML = blockObj.html;
|
| 749 |
const block = div.firstElementChild;
|
| 750 |
block.style.display = 'block';
|
| 751 |
|
| 752 |
-
// Remove events and make readonly if needed
|
| 753 |
if (readOnly) {
|
| 754 |
block.querySelectorAll('input, textarea, button').forEach(el => {
|
| 755 |
if (el.type !== 'checkbox') {
|
|
@@ -761,7 +894,6 @@
|
|
| 761 |
});
|
| 762 |
block.removeAttribute('draggable');
|
| 763 |
} else {
|
| 764 |
-
// Reattach events
|
| 765 |
block.querySelector('.remove-block').addEventListener('click', () => {
|
| 766 |
if (confirm('Remove this block?')) {
|
| 767 |
block.remove();
|
|
@@ -781,11 +913,9 @@
|
|
| 781 |
updateStepNumbers();
|
| 782 |
}
|
| 783 |
|
| 784 |
-
// Start app
|
| 785 |
window.addEventListener('DOMContentLoaded', init);
|
| 786 |
</script>
|
| 787 |
|
| 788 |
-
<!-- Print Styling -->
|
| 789 |
<style media="print">
|
| 790 |
.toolbox, .toolbox-item, #placeholder, .edit-block, .remove-block {
|
| 791 |
display: none !important;
|
|
|
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
| 6 |
<title>Rig Operation Procedure Builder - LEGO Style</title>
|
| 7 |
<script src="https://cdn.tailwindcss.com"></script>
|
| 8 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
|
| 9 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/docx/7.2.0/docx.js"></script>
|
| 10 |
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
| 11 |
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
| 12 |
<style>
|
|
|
|
| 60 |
<button id="downloadWordBtn" class="bg-purple-600 hover:bg-purple-700 text-white px-5 py-2 rounded-lg text-sm font-medium flex items-center gap-1 transition">
|
| 61 |
<i class="fas fa-file-word"></i> Download Word
|
| 62 |
</button>
|
| 63 |
+
<button id="downloadRTFBtn" class="bg-teal-600 hover:bg-teal-700 text-white px-5 py-2 rounded-lg text-sm font-medium flex items-center gap-1 transition">
|
| 64 |
+
<i class="fas fa-file-alt"></i> Download RTF
|
| 65 |
+
</button>
|
| 66 |
<button id="approvalBtn" class="bg-orange-500 hover:bg-orange-600 text-white px-5 py-2 rounded-lg text-sm font-medium flex items-center gap-1 transition">
|
| 67 |
<i class="fas fa-check-circle"></i> Submit for Approval
|
| 68 |
</button>
|
|
|
|
| 335 |
const saveBtn = document.getElementById('saveBtn');
|
| 336 |
const printBtn = document.getElementById('printBtn');
|
| 337 |
const downloadWordBtn = document.getElementById('downloadWordBtn');
|
| 338 |
+
const downloadRTFBtn = document.getElementById('downloadRTFBtn');
|
| 339 |
const approvalBtn = document.getElementById('approvalBtn');
|
| 340 |
|
| 341 |
// Initialize
|
|
|
|
| 406 |
// Click on builder to add
|
| 407 |
builder.addEventListener('click', (e) => {
|
| 408 |
if (e.target === builder || e.target === placeholder) {
|
|
|
|
| 409 |
if (blocks.length === 0) {
|
| 410 |
+
addBlock('step');
|
| 411 |
}
|
| 412 |
}
|
| 413 |
});
|
|
|
|
| 420 |
template.id = id;
|
| 421 |
template.style.display = 'block';
|
| 422 |
|
|
|
|
| 423 |
const stepNumber = template.querySelector('.step-number');
|
| 424 |
stepNumber.textContent = procedureCounter++;
|
| 425 |
|
|
|
|
| 426 |
if (type === 'material') {
|
| 427 |
setupMaterialBlock(template);
|
| 428 |
} else if (type === 'personnel') {
|
|
|
|
| 433 |
setupUploadArea(template);
|
| 434 |
}
|
| 435 |
|
|
|
|
| 436 |
template.querySelector('.remove-block').addEventListener('click', () => {
|
| 437 |
if (confirm('Are you sure you want to remove this block?')) {
|
| 438 |
template.remove();
|
|
|
|
| 441 |
});
|
| 442 |
|
| 443 |
template.querySelector('.edit-block').addEventListener('click', () => {
|
| 444 |
+
alert('Edit mode. Modify content directly.');
|
|
|
|
|
|
|
|
|
|
| 445 |
});
|
| 446 |
|
|
|
|
| 447 |
template.setAttribute('draggable', true);
|
| 448 |
template.addEventListener('dragstart', (e) => {
|
| 449 |
e.dataTransfer.setData('text/plain', 'move');
|
|
|
|
| 455 |
template.classList.remove('dragging');
|
| 456 |
});
|
| 457 |
|
|
|
|
| 458 |
builder.appendChild(template);
|
| 459 |
placeholder.style.display = 'none';
|
| 460 |
|
|
|
|
| 461 |
blocks.push({ id, type, element: template });
|
| 462 |
}
|
| 463 |
|
|
|
|
| 486 |
}
|
| 487 |
|
| 488 |
addButton.addEventListener('click', addMaterialItem);
|
| 489 |
+
addMaterialItem();
|
| 490 |
}
|
| 491 |
|
| 492 |
function setupPersonnelBlock(block) {
|
|
|
|
| 514 |
}
|
| 515 |
|
| 516 |
addButton.addEventListener('click', addPersonnelItem);
|
| 517 |
+
addPersonnelItem();
|
| 518 |
}
|
| 519 |
|
| 520 |
function setupChecklistBlock(block) {
|
|
|
|
| 536 |
}
|
| 537 |
|
| 538 |
addButton.addEventListener('click', addChecklistItem);
|
| 539 |
+
addChecklistItem();
|
| 540 |
}
|
| 541 |
|
| 542 |
function setupUploadArea(block) {
|
|
|
|
| 560 |
}
|
| 561 |
});
|
| 562 |
|
|
|
|
| 563 |
area.addEventListener('drop', (e) => {
|
| 564 |
e.preventDefault();
|
| 565 |
const file = e.dataTransfer.files[0];
|
|
|
|
| 577 |
});
|
| 578 |
}
|
| 579 |
|
|
|
|
| 580 |
function updateStepNumbers() {
|
| 581 |
procedureCounter = 1;
|
| 582 |
document.querySelectorAll('.procedure-block').forEach(block => {
|
|
|
|
| 587 |
});
|
| 588 |
}
|
| 589 |
|
|
|
|
| 590 |
function setupEvents() {
|
|
|
|
| 591 |
builder.addEventListener('dragover', (e) => {
|
| 592 |
e.preventDefault();
|
| 593 |
const afterElement = getDragAfterElement(builder, e.clientY);
|
|
|
|
| 599 |
}
|
| 600 |
});
|
| 601 |
|
|
|
|
| 602 |
saveBtn.addEventListener('click', () => {
|
| 603 |
const title = prompt('Enter procedure title:', 'New Procedure') || 'Untitled';
|
| 604 |
const id = 'PRC-' + Date.now().toString().substr(-6).toUpperCase();
|
|
|
|
| 618 |
})
|
| 619 |
};
|
| 620 |
|
|
|
|
| 621 |
let procedures = JSON.parse(localStorage.getItem('rigProcedures') || '[]');
|
| 622 |
procedures.push(procedure);
|
| 623 |
localStorage.setItem('rigProcedures', JSON.stringify(procedures));
|
|
|
|
| 626 |
loadProcedures();
|
| 627 |
});
|
| 628 |
|
|
|
|
| 629 |
printBtn.addEventListener('click', () => {
|
| 630 |
window.print();
|
| 631 |
});
|
| 632 |
|
| 633 |
+
// Download as DOCX
|
| 634 |
downloadWordBtn.addEventListener('click', () => {
|
| 635 |
+
const { Document, Paragraph, TextRun, ImageRun, Packer } = docx;
|
| 636 |
+
|
| 637 |
+
const blocks = document.querySelectorAll('.procedure-block');
|
| 638 |
+
const elements = [];
|
| 639 |
+
|
| 640 |
+
blocks.forEach(block => {
|
| 641 |
+
const type = block.querySelector('.text-sm').textContent.trim();
|
| 642 |
+
|
| 643 |
+
// Extract text content
|
| 644 |
+
let content = '';
|
| 645 |
+
const textarea = block.querySelector('textarea');
|
| 646 |
+
const inputs = block.querySelectorAll('input[type="text"], input[type="number"], select');
|
| 647 |
+
const timeInputs = block.querySelectorAll('input[type="time"]');
|
| 648 |
+
const checklist = block.querySelectorAll('.checklist-items input[type="checkbox"]');
|
| 649 |
+
const img = block.querySelector('img');
|
| 650 |
+
|
| 651 |
+
// Add step title
|
| 652 |
+
elements.push(new Paragraph({
|
| 653 |
+
children: [
|
| 654 |
+
new TextRun({
|
| 655 |
+
text: `Step ${block.querySelector('.step-number').textContent}: ${type}`,
|
| 656 |
+
bold: true,
|
| 657 |
+
size: 28
|
| 658 |
+
})
|
| 659 |
+
],
|
| 660 |
+
spacing: { after: 100 }
|
| 661 |
+
}));
|
| 662 |
+
|
| 663 |
+
if (textarea && textarea.value) {
|
| 664 |
+
elements.push(new Paragraph({
|
| 665 |
+
children: [new TextRun(textarea.value)],
|
| 666 |
+
spacing: { after: 100 }
|
| 667 |
+
}));
|
| 668 |
+
}
|
| 669 |
+
|
| 670 |
+
if (inputs.length > 0) {
|
| 671 |
+
const tableRows = Array.from(inputs).reduce((rows, input, i, arr) => {
|
| 672 |
+
if (i % 3 === 0) rows.push([]);
|
| 673 |
+
const value = input.value || input.options?.[input.selectedIndex]?.text || '';
|
| 674 |
+
rows[rows.length - 1].push(value);
|
| 675 |
+
return rows;
|
| 676 |
+
}, []).map(row => {
|
| 677 |
+
return new docx.TableRow({
|
| 678 |
+
children: row.map(cell => {
|
| 679 |
+
return new docx.TableCell({
|
| 680 |
+
children: [new Paragraph(cell)],
|
| 681 |
+
width: { size: 33, type: docx.WidthType.PERCENTAGE }
|
| 682 |
+
});
|
| 683 |
+
})
|
| 684 |
+
});
|
| 685 |
+
});
|
| 686 |
+
|
| 687 |
+
if (tableRows.length > 0) {
|
| 688 |
+
elements.push(new docx.Table({
|
| 689 |
+
rows: tableRows,
|
| 690 |
+
width: { size: 100, type: docx.WidthType.PERCENTAGE }
|
| 691 |
+
}));
|
| 692 |
+
}
|
| 693 |
+
}
|
| 694 |
+
|
| 695 |
+
if (timeInputs.length > 1) {
|
| 696 |
+
const start = timeInputs[0].value;
|
| 697 |
+
const end = timeInputs[1].value;
|
| 698 |
+
elements.push(new Paragraph({
|
| 699 |
+
children: [new TextRun(`Duration: ${start} - ${end}`)],
|
| 700 |
+
spacing: { after: 100 }
|
| 701 |
+
}));
|
| 702 |
+
}
|
| 703 |
+
|
| 704 |
+
if (checklist.length > 0) {
|
| 705 |
+
Array.from(block.querySelectorAll('.checklist-items > div')).forEach(itemDiv => {
|
| 706 |
+
const checkbox = itemDiv.querySelector('input[type="checkbox"]');
|
| 707 |
+
const label = itemDiv.querySelector('input[type="text"]');
|
| 708 |
+
elements.push(new Paragraph({
|
| 709 |
+
children: [
|
| 710 |
+
new TextRun({
|
| 711 |
+
text: `[${checkbox.checked ? 'x' : ' '}] ${label.value}`,
|
| 712 |
+
size: 24
|
| 713 |
+
})
|
| 714 |
+
]
|
| 715 |
+
}));
|
| 716 |
+
});
|
| 717 |
+
}
|
| 718 |
+
|
| 719 |
+
if (img && !img.classList.contains('hidden')) {
|
| 720 |
+
const imgData = img.src;
|
| 721 |
+
elements.push(new Paragraph({
|
| 722 |
+
children: [
|
| 723 |
+
new ImageRun({
|
| 724 |
+
data: atob(imgData.split(',')[1]),
|
| 725 |
+
transformation: {
|
| 726 |
+
width: 300,
|
| 727 |
+
height: 200
|
| 728 |
+
}
|
| 729 |
+
})
|
| 730 |
+
],
|
| 731 |
+
spacing: { after: 100 }
|
| 732 |
+
}));
|
| 733 |
+
}
|
| 734 |
+
|
| 735 |
+
elements.push(new Paragraph({ spacing: { after: 100 } })); // spacer
|
| 736 |
+
});
|
| 737 |
+
|
| 738 |
+
const doc = new Document({
|
| 739 |
+
sections: [{
|
| 740 |
+
properties: {},
|
| 741 |
+
children: elements
|
| 742 |
+
}]
|
| 743 |
+
});
|
| 744 |
+
|
| 745 |
+
Packer.toBlob(doc).then(blob => {
|
| 746 |
+
saveAs(blob, 'Rig_Operation_Procedure.docx');
|
| 747 |
});
|
| 748 |
});
|
| 749 |
|
| 750 |
+
// Download as RTF (simplified RTF string)
|
| 751 |
+
downloadRTFBtn.addEventListener('click', () => {
|
| 752 |
+
let rtfContent = '{\\rtf1\\ansi\\deff0{\\fonttbl{\\f0 Times New Roman;}}\n';
|
| 753 |
+
rtfContent += '\\viewkind4\\uc1\\pard\\f0\\fs24\\b Rig Operation Procedure\\b0\\par\n';
|
| 754 |
+
rtfContent += '\\par\n';
|
| 755 |
+
|
| 756 |
+
document.querySelectorAll('.procedure-block').forEach(block => {
|
| 757 |
+
const stepNum = block.querySelector('.step-number').textContent;
|
| 758 |
+
const type = block.querySelector('.text-sm').textContent.trim();
|
| 759 |
+
rtfContent += `\\b Step ${stepNum}: ${type}\\b0\\par\n`;
|
| 760 |
+
|
| 761 |
+
const textarea = block.querySelector('textarea');
|
| 762 |
+
if (textarea && textarea.value) {
|
| 763 |
+
rtfContent += textarea.value.replace(/\n/g, '\\line ') + '\\par\n';
|
| 764 |
+
}
|
| 765 |
+
|
| 766 |
+
const timeInputs = block.querySelectorAll('input[type="time"]');
|
| 767 |
+
if (timeInputs.length === 2) {
|
| 768 |
+
const start = timeInputs[0].value;
|
| 769 |
+
const end = timeInputs[1].value;
|
| 770 |
+
rtfContent += `Duration: ${start} - ${end}\\par\n`;
|
| 771 |
+
}
|
| 772 |
+
|
| 773 |
+
block.querySelectorAll('.material-list > div').forEach(item => {
|
| 774 |
+
const name = item.querySelector('input[type="text"]')?.value || '';
|
| 775 |
+
const qty = item.querySelector('input[type="number"]')?.value || '';
|
| 776 |
+
const unit = item.querySelector('select')?.options?.[item.querySelector('select')?.selectedIndex]?.text || '';
|
| 777 |
+
if (name) rtfContent += `\\bullet\\t${qty} ${unit} of ${name}\\par\n`;
|
| 778 |
+
});
|
| 779 |
+
|
| 780 |
+
block.querySelectorAll('.personnel-list > div').forEach(item => {
|
| 781 |
+
const name = item.querySelector('input[type="text"]')?.value || '';
|
| 782 |
+
const role = item.querySelector('select')?.options?.[item.querySelector('select')?.selectedIndex]?.text || '';
|
| 783 |
+
if (name) rtfContent += `\\bullet\\t${name} (${role})\\par\n`;
|
| 784 |
+
});
|
| 785 |
+
|
| 786 |
+
block.querySelectorAll('.checklist-items > div').forEach(item => {
|
| 787 |
+
const label = item.querySelector('input[type="text"]')?.value || '';
|
| 788 |
+
const checked = item.querySelector('input[type="checkbox"]')?.checked ? 'x' : ' ';
|
| 789 |
+
rtfContent += `\\bullet\\t[${checked}] ${label}\\par\n`;
|
| 790 |
+
});
|
| 791 |
+
|
| 792 |
+
rtfContent += '\\par\n';
|
| 793 |
+
});
|
| 794 |
+
|
| 795 |
+
rtfContent += '}';
|
| 796 |
+
|
| 797 |
+
const blob = new Blob([rtfContent], { type: 'application/rtf' });
|
| 798 |
+
saveAs(blob, 'Rig_Operation_Procedure.rtf');
|
| 799 |
+
});
|
| 800 |
+
|
| 801 |
approvalBtn.addEventListener('click', () => {
|
| 802 |
if (blocks.length === 0) {
|
| 803 |
alert('Please add at least one block before submitting.');
|
|
|
|
| 805 |
}
|
| 806 |
if (confirm('Submit this procedure for approval? It will be reviewed by the safety team.')) {
|
| 807 |
const procedures = JSON.parse(localStorage.getItem('rigProcedures') || '[]');
|
|
|
|
| 808 |
if (procedures.length > 0) {
|
| 809 |
procedures[procedures.length - 1].status = 'Pending';
|
| 810 |
localStorage.setItem('rigProcedures', JSON.stringify(procedures));
|
|
|
|
| 828 |
}, { offset: Number.NEGATIVE_INFINITY }).element;
|
| 829 |
}
|
| 830 |
|
|
|
|
| 831 |
function loadProcedures() {
|
| 832 |
const tableBody = document.getElementById('procedureTableBody');
|
| 833 |
tableBody.innerHTML = '';
|
|
|
|
| 853 |
tableBody.appendChild(tr);
|
| 854 |
});
|
| 855 |
|
|
|
|
| 856 |
document.querySelectorAll('.edit-proc').forEach(btn => {
|
| 857 |
btn.addEventListener('click', (e) => {
|
| 858 |
const id = e.target.dataset.id;
|
|
|
|
| 873 |
const procedure = procedures.find(p => p.id === id);
|
| 874 |
if (!procedure) return;
|
| 875 |
|
|
|
|
| 876 |
builder.innerHTML = '';
|
| 877 |
blocks = [];
|
| 878 |
procedureCounter = 1;
|
| 879 |
|
|
|
|
| 880 |
procedure.blocks.forEach(blockObj => {
|
| 881 |
const div = document.createElement('div');
|
| 882 |
div.innerHTML = blockObj.html;
|
| 883 |
const block = div.firstElementChild;
|
| 884 |
block.style.display = 'block';
|
| 885 |
|
|
|
|
| 886 |
if (readOnly) {
|
| 887 |
block.querySelectorAll('input, textarea, button').forEach(el => {
|
| 888 |
if (el.type !== 'checkbox') {
|
|
|
|
| 894 |
});
|
| 895 |
block.removeAttribute('draggable');
|
| 896 |
} else {
|
|
|
|
| 897 |
block.querySelector('.remove-block').addEventListener('click', () => {
|
| 898 |
if (confirm('Remove this block?')) {
|
| 899 |
block.remove();
|
|
|
|
| 913 |
updateStepNumbers();
|
| 914 |
}
|
| 915 |
|
|
|
|
| 916 |
window.addEventListener('DOMContentLoaded', init);
|
| 917 |
</script>
|
| 918 |
|
|
|
|
| 919 |
<style media="print">
|
| 920 |
.toolbox, .toolbox-item, #placeholder, .edit-block, .remove-block {
|
| 921 |
display: none !important;
|
prompts.txt
CHANGED
|
@@ -1 +1,2 @@
|
|
| 1 |
-
buatkan rig operation procedure seperti lego dimana di dalamnya terdapat inputing procedure dimana didalamnya terdapat upload gambar , waktu procedure, material yang di butuhkan , safety concern , personel dan informasi tambahan lainnya. penyusunan procedure ada juga tambahan editing untuk memastikan beberapa variable dapat di edit untuk nilai nilai tertentu , database procedure , approval review, document download ke word atau print
|
|
|
|
|
|
| 1 |
+
buatkan rig operation procedure seperti lego dimana di dalamnya terdapat inputing procedure dimana didalamnya terdapat upload gambar , waktu procedure, material yang di butuhkan , safety concern , personel dan informasi tambahan lainnya. penyusunan procedure ada juga tambahan editing untuk memastikan beberapa variable dapat di edit untuk nilai nilai tertentu , database procedure , approval review, document download ke word atau print
|
| 2 |
+
saat download word , pastikan format document dalam doc atau rtf
|