Compare commits

...

16 Commits

Author SHA1 Message Date
c1b1a00c90 Merge remote-tracking branch 'main/master'
# Conflicts:
#	composer.json
#	routes/web.php
2025-04-30 17:04:14 +07:00
d8c9939bf1 feat(helpers): tambahkan helper untuk format tanggal dan waktu
- Menambahkan file `app/Helpers/helpers.php` untuk mendefinisikan fungsi `formatTanggalWaktu`.
- Fungsi `formatTanggalWaktu` digunakan untuk memformat tanggal dan waktu dengan dukungan opsi:
  - Menampilkan nama hari (`showDay`).
  - Menampilkan waktu (`time`).
  - Mengatur lokal (`locale`), default `id_ID`.
- Melakukan perubahan pada `composer.json` untuk menambahkan autoload file helper.
2025-04-27 19:00:48 +07:00
1875fae490 feat(migration): tambah migrasi untuk tabel notifications
Menambahkan migrasi baru untuk tabel `notifications` dengan rincian:
- Membuat tabel `notifications` dengan kolom:
  - `id` (UUID, primary key).
  - `type` (string).
  - `notifiable` (morphs).
  - `data` (text).
  - `read_at` (timestamp, nullable).
  - `timestamps` (created_at dan updated_at).
- Menyediakan fungsi `up` untuk membuat tabel.
- Menyediakan fungsi `down` untuk menghapus tabel.
2025-04-27 15:48:46 +07:00
6c5a986458 feat(composer): perbarui versi PHP dan paket
- Mengubah versi PHP dari ^8.2 menjadi ^8.3
- Memperbarui versi diglactic/laravel-breadcrumbs menjadi ^10.0
- Menambahkan jackiedo/log-reader sebagai dependensi baru
2025-04-27 09:17:27 +07:00
c80ecb04db Merge branch 'lpj'
# Conflicts:
#	composer.json
2025-04-26 17:36:25 +07:00
54ada63ff4 feat(sidebar): tambahkan pemformatan judul menu
- Menambahkan logika untuk memformat judul menu yang lebih panjang.
- Judul yang melebihi 30 karakter akan dipisahkan menjadi beberapa baris.
- Menggunakan elemen <br> untuk pemisahan baris pada judul menu.
2025-04-23 13:05:05 +07:00
8fdba24920 feat(header): perbarui logika notifikasi dan suara
- Tambahkan penundaan 5 detik sebelum memuat ulang halaman setelah suara notifikasi diputar.
- Ubah interval pemeriksaan notifikasi dari 1 ms menjadi 5000 ms untuk efisiensi.
2025-04-23 10:44:27 +07:00
b7c3c18900 feat(header): tambahkan fitur notifikasi dengan suara
- Menambahkan logika untuk menghitung jumlah notifikasi yang belum dibaca saat halaman dimuat.
- Memperbarui suara notifikasi untuk diputar jika ada notifikasi baru.
- Menambahkan fungsi untuk memeriksa notifikasi baru setiap 30 detik.
- Memperbarui UI notifikasi jika ada notifikasi baru yang diterima.
2025-04-22 14:32:26 +07:00
34aea1b015 feat(header): tambahkan suara notifikasi pada header
- Menambahkan elemen audio untuk suara notifikasi.
- Memainkan suara notifikasi jika ada notifikasi yang belum dibaca.
- Menggunakan localStorage untuk menghindari pemutaran suara berulang dalam sesi yang sama.
- Menghapus flag setelah 5 menit untuk memungkinkan pemutaran suara lagi jika pengguna menyegarkan halaman.
2025-04-22 14:22:08 +07:00
b3ce06e03c feat(header): tambahkan fitur notifikasi pada header
- Menambahkan dropdown untuk menampilkan notifikasi yang belum dibaca.
- Menampilkan informasi notifikasi termasuk judul dan pesan.
- Menyediakan tombol untuk menandai semua notifikasi sebagai dibaca.
2025-04-22 13:57:05 +07:00
99f8aeffc3 feat(sidebar): tambahkan section 'laporan' pada menu sidebar
- Menambahkan 'laporan' ke dalam urutan section menu sidebar.
- Memperbarui judul section untuk mencakup 'Laporan'.
2025-04-14 10:35:37 +07:00
179dab656c feat(provider): aktifkan pemuatan relasi otomatis pada model
- Menambahkan pemanggilan Model::automaticallyEagerLoadRelationships() di metode boot.
- Meningkatkan performa dengan mengoptimalkan pemuatan relasi model secara otomatis.
2025-04-11 08:48:46 +07:00
61f065ac8c feat(changes): tambahkan fitur baru untuk meningkatkan performa
- Optimasi algoritma untuk efisiensi yang lebih baik
- Perbaikan bug yang mengganggu pengalaman pengguna
- Pembaruan dokumentasi untuk mencerminkan perubahan terbaru
2025-03-25 09:21:37 +07:00
bbfa14a987 feat(signature): tambahkan file signature.pad.js
- Menambahkan file baru untuk mendukung fungsionalitas tanda tangan.
- Memungkinkan pengguna untuk menggambar tanda tangan secara langsung di aplikasi.
2025-03-25 09:02:12 +07:00
78c1afb0a8 feat(menu): update logic sidebar menu 2025-03-20 14:13:42 +07:00
ab25380df9 set dafult composer 2024-10-29 13:32:15 +07:00
11 changed files with 1225 additions and 1333 deletions

22
app/Helpers/helpers.php Normal file
View File

@ -0,0 +1,22 @@
<?php
use Carbon\Carbon;
if(!function_exists('formatTanggalWaktu')){
function formatTanggalWaktu($tanggal, $time=false, $showDay=false, $locale = 'id_ID')
{
// Parse tanggal dan waktu
$datetime = $time ? $tanggal . ' ' . $time : $tanggal;
$carbon = Carbon::parse($datetime)->locale($locale);
// Tentukan format berdasarkan parameter
if ($showDay && $time) {
return $carbon->isoFormat('dddd, LL HH:mm:ss');
} elseif ($showDay) {
return $carbon->isoFormat('dddd, LL');
} elseif ($time) {
return $carbon->isoFormat('LL HH:mm:ss');
} else {
return $carbon->isoFormat('LL');
}
}
}

View File

@ -2,6 +2,7 @@
namespace App\Providers;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
@ -19,6 +20,6 @@ class AppServiceProvider extends ServiceProvider
*/
public function boot(): void
{
//
Model::automaticallyEagerLoadRelationships();
}
}

View File

@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('notifications', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->string('type');
$table->morphs('notifiable');
$table->text('data');
$table->timestamp('read_at')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('notifications');
}
};

Binary file not shown.

Binary file not shown.

Binary file not shown.

314
public/vendor/pdfobject.min.js vendored Normal file
View File

@ -0,0 +1,314 @@
/**
* PDFObject v2.3.0
* https://github.com/pipwerks/PDFObject
* @license
* Copyright (c) 2008-2024 Philip Hutchison
* MIT-style license: http://pipwerks.mit-license.org/
* UMD module pattern from https://github.com/umdjs/umd/blob/master/templates/returnExports.js
*/
!(function (root, factory) {
"function" == typeof define && define.amd
? define([], factory)
: "object" == typeof module && module.exports
? (module.exports = factory())
: (root.PDFObject = factory());
})(this, function () {
"use strict";
if (
"undefined" == typeof window ||
void 0 === window.navigator ||
void 0 === window.navigator.userAgent
)
return !1;
let win = window,
nav = win.navigator,
ua = nav.userAgent,
suppressConsole = !1,
validateAX = function (type) {
var ax = null;
try {
ax = new ActiveXObject(type);
} catch (e) {
ax = null;
}
return !!ax;
},
supportsPDFs = (function () {
if (
(void 0 !== nav.platform &&
"MacIntel" === nav.platform &&
void 0 !== nav.maxTouchPoints &&
nav.maxTouchPoints > 1) ||
/Mobi|Tablet|Android|iPad|iPhone/.test(ua)
)
return !1;
let supportsPDFVE = "boolean" == typeof nav.pdfViewerEnabled;
return (
!(supportsPDFVE && !nav.pdfViewerEnabled) &&
((supportsPDFVE && nav.pdfViewerEnabled) ||
(function () {
let isChromium = void 0 !== win.chrome,
isSafari =
void 0 !== win.safari ||
(void 0 !== nav.vendor &&
/Apple/.test(nav.vendor) &&
/Safari/.test(ua)),
isFirefox =
void 0 !== win.Mozilla || /irefox/.test(ua);
return isChromium || isSafari || isFirefox;
})() ||
("ActiveXObject" in win &&
(validateAX("AcroPDF.PDF") ||
validateAX("PDF.PdfCtrl"))))
);
})(),
embedError = function (msg) {
return suppressConsole || console.log("[PDFObject]", msg), !1;
},
generatePDFObjectMarkup = function (
embedType,
targetNode,
url,
pdfOpenFragment,
width,
height,
id,
title,
omitInlineStyles,
customAttribute,
PDFJS_URL,
) {
!(function (node) {
for (; node.firstChild; ) node.removeChild(node.firstChild);
})(targetNode);
let source = url;
if ("pdfjs" === embedType) {
source =
PDFJS_URL +
(-1 !== PDFJS_URL.indexOf("?") ? "&" : "?") +
"file=" +
encodeURIComponent(url) +
pdfOpenFragment;
} else source += pdfOpenFragment;
let el = document.createElement("iframe");
if (
((el.className = "pdfobject"),
(el.type = "application/pdf"),
(el.title = title),
(el.src = source),
(el.allow = "fullscreen"),
(el.frameborder = "0"),
id && (el.id = id),
!omitInlineStyles)
) {
let style = "border: none;";
targetNode !== document.body
? (style += "width: " + width + "; height: " + height + ";")
: (style +=
"position: absolute; top: 0; right: 0; bottom: 0; left: 0; width: 100%; height: 100%;"),
(el.style.cssText = style);
}
return (
customAttribute &&
customAttribute.key &&
-1 ===
[
"className",
"type",
"title",
"src",
"style",
"id",
"allow",
"frameborder",
].indexOf(customAttribute.key) &&
el.setAttribute(
customAttribute.key,
void 0 !== customAttribute.value
? customAttribute.value
: "",
),
targetNode.classList.add("pdfobject-container"),
targetNode.appendChild(el),
targetNode.getElementsByTagName("iframe")[0]
);
},
embed = function (url, targetSelector, options) {
let selector = targetSelector || !1,
opt = options || {};
suppressConsole =
"boolean" == typeof opt.suppressConsole && opt.suppressConsole;
let id = "string" == typeof opt.id ? opt.id : "",
page = opt.page || !1,
pdfOpenParams = opt.pdfOpenParams || {},
fallbackLink =
("string" != typeof opt.fallbackLink &&
"boolean" != typeof opt.fallbackLink) ||
opt.fallbackLink,
width = opt.width || "100%",
height = opt.height || "100%",
title = opt.title || "Embedded PDF",
forcePDFJS =
"boolean" == typeof opt.forcePDFJS && opt.forcePDFJS,
omitInlineStyles =
"boolean" == typeof opt.omitInlineStyles &&
opt.omitInlineStyles,
PDFJS_URL = opt.PDFJS_URL || !1,
targetNode = (function (targetSelector) {
let targetNode = document.body;
return (
"string" == typeof targetSelector
? (targetNode =
document.querySelector(targetSelector))
: void 0 !== win.jQuery &&
targetSelector instanceof jQuery &&
targetSelector.length
? (targetNode = targetSelector.get(0))
: void 0 !== targetSelector.nodeType &&
1 === targetSelector.nodeType &&
(targetNode = targetSelector),
targetNode
);
})(selector),
pdfOpenFragment = "",
customAttribute = opt.customAttribute || {},
fallbackHTML_default =
"<p>This browser does not support inline PDFs. Please download the PDF to view it: [pdflink]</p>";
if ("string" != typeof url) return embedError("URL is not valid");
if (!targetNode)
return embedError("Target element cannot be determined");
if (
(page && (pdfOpenParams.page = page),
(pdfOpenFragment = (function (pdfParams) {
let prop,
string = "",
paramArray = [],
fdf = "";
if (
((pdfParams.comment ||
pdfParams.viewrect ||
pdfParams.highlight) &&
(pdfParams.page ||
((pdfParams.page = 1),
embedError(
"The comment, viewrect, and highlight parameters require a page parameter, but none was specified. Defaulting to page 1.",
))),
pdfParams.page &&
(paramArray.push(
"page=" + encodeURIComponent(pdfParams.page),
),
delete pdfParams.page),
pdfParams.fdf &&
((fdf = pdfParams.fdf), delete pdfParams.fdf),
pdfParams)
) {
for (prop in pdfParams)
pdfParams.hasOwnProperty(prop) &&
paramArray.push(
encodeURIComponent(prop) +
"=" +
encodeURIComponent(pdfParams[prop]),
);
fdf &&
paramArray.push("fdf=" + encodeURIComponent(fdf)),
(string = paramArray.join("&")) &&
(string = "#" + string);
}
return string;
})(pdfOpenParams)),
forcePDFJS && PDFJS_URL)
)
return generatePDFObjectMarkup(
"pdfjs",
targetNode,
url,
pdfOpenFragment,
width,
height,
id,
title,
omitInlineStyles,
customAttribute,
PDFJS_URL,
);
if (supportsPDFs)
return generatePDFObjectMarkup(
"iframe",
targetNode,
url,
pdfOpenFragment,
width,
height,
id,
title,
omitInlineStyles,
customAttribute,
);
if (PDFJS_URL)
return generatePDFObjectMarkup(
"pdfjs",
targetNode,
url,
pdfOpenFragment,
width,
height,
id,
title,
omitInlineStyles,
customAttribute,
PDFJS_URL,
);
if (fallbackLink)
if ("string" == typeof fallbackLink)
targetNode.innerHTML = fallbackLink.replace(
/\[url\]/g,
url,
);
else if (-1 !== url.indexOf("data:application/pdf;base64"))
!(function (b64, filename, targetNode, fallbackHTML) {
if (
window.Blob &&
window.URL &&
window.URL.createObjectURL
) {
var xhr = new XMLHttpRequest();
xhr.open("GET", b64, !0),
(xhr.responseType = "blob"),
(xhr.onload = function () {
if (200 === xhr.status) {
var blob = xhr.response,
link = document.createElement("a");
(link.innerText = "Download PDF"),
(link.href =
URL.createObjectURL(blob)),
link.setAttribute(
"download",
filename,
),
(targetNode.innerHTML =
fallbackHTML.replace(
/\[pdflink\]/g,
link.outerHTML,
));
}
}),
xhr.send();
}
})(url, "file.pdf", targetNode, fallbackHTML_default);
else {
let link = "<a href='" + url + "'>Download PDF</a>";
targetNode.innerHTML = fallbackHTML_default.replace(
/\[pdflink\]/g,
link,
);
}
return embedError("This browser does not support embedded PDFs");
};
return {
embed: function (a, b, c) {
return embed(a, b, c);
},
pdfobjectversion: "2.3.0",
supportsPDFs: supportsPDFs,
};
});

595
public/vendor/signature.pad.js vendored Normal file
View File

@ -0,0 +1,595 @@
/*!
* Signature Pad v4.1.7 | https://github.com/szimek/signature_pad
* (c) 2023 Szymon Nowak | Released under the MIT license
*/
!(function (t, e) {
"object" == typeof exports && "undefined" != typeof module
? (module.exports = e())
: "function" == typeof define && define.amd
? define(e)
: ((t =
"undefined" != typeof globalThis
? globalThis
: t || self).SignaturePad = e());
})(this, function () {
"use strict";
class t {
constructor(t, e, i, s) {
if (isNaN(t) || isNaN(e))
throw new Error(`Point is invalid: (${t}, ${e})`);
(this.x = +t),
(this.y = +e),
(this.pressure = i || 0),
(this.time = s || Date.now());
}
distanceTo(t) {
return Math.sqrt(
Math.pow(this.x - t.x, 2) + Math.pow(this.y - t.y, 2),
);
}
equals(t) {
return (
this.x === t.x &&
this.y === t.y &&
this.pressure === t.pressure &&
this.time === t.time
);
}
velocityFrom(t) {
return this.time !== t.time
? this.distanceTo(t) / (this.time - t.time)
: 0;
}
}
class e {
static fromPoints(t, i) {
const s = this.calculateControlPoints(t[0], t[1], t[2]).c2,
n = this.calculateControlPoints(t[1], t[2], t[3]).c1;
return new e(t[1], s, n, t[2], i.start, i.end);
}
static calculateControlPoints(e, i, s) {
const n = e.x - i.x,
o = e.y - i.y,
h = i.x - s.x,
r = i.y - s.y,
a = (e.x + i.x) / 2,
c = (e.y + i.y) / 2,
d = (i.x + s.x) / 2,
l = (i.y + s.y) / 2,
u = Math.sqrt(n * n + o * o),
v = Math.sqrt(h * h + r * r),
_ = v / (u + v),
p = d + (a - d) * _,
m = l + (c - l) * _,
g = i.x - p,
w = i.y - m;
return { c1: new t(a + g, c + w), c2: new t(d + g, l + w) };
}
constructor(t, e, i, s, n, o) {
(this.startPoint = t),
(this.control2 = e),
(this.control1 = i),
(this.endPoint = s),
(this.startWidth = n),
(this.endWidth = o);
}
length() {
let t,
e,
i = 0;
for (let s = 0; s <= 10; s += 1) {
const n = s / 10,
o = this.point(
n,
this.startPoint.x,
this.control1.x,
this.control2.x,
this.endPoint.x,
),
h = this.point(
n,
this.startPoint.y,
this.control1.y,
this.control2.y,
this.endPoint.y,
);
if (s > 0) {
const s = o - t,
n = h - e;
i += Math.sqrt(s * s + n * n);
}
(t = o), (e = h);
}
return i;
}
point(t, e, i, s, n) {
return (
e * (1 - t) * (1 - t) * (1 - t) +
3 * i * (1 - t) * (1 - t) * t +
3 * s * (1 - t) * t * t +
n * t * t * t
);
}
}
class i {
constructor() {
try {
this._et = new EventTarget();
} catch (t) {
this._et = document;
}
}
addEventListener(t, e, i) {
this._et.addEventListener(t, e, i);
}
dispatchEvent(t) {
return this._et.dispatchEvent(t);
}
removeEventListener(t, e, i) {
this._et.removeEventListener(t, e, i);
}
}
class s extends i {
constructor(t, e = {}) {
super(),
(this.canvas = t),
(this._drawingStroke = !1),
(this._isEmpty = !0),
(this._lastPoints = []),
(this._data = []),
(this._lastVelocity = 0),
(this._lastWidth = 0),
(this._handleMouseDown = (t) => {
1 === t.buttons && this._strokeBegin(t);
}),
(this._handleMouseMove = (t) => {
this._strokeMoveUpdate(t);
}),
(this._handleMouseUp = (t) => {
1 === t.buttons && this._strokeEnd(t);
}),
(this._handleTouchStart = (t) => {
if (
(t.cancelable && t.preventDefault(),
1 === t.targetTouches.length)
) {
const e = t.changedTouches[0];
this._strokeBegin(e);
}
}),
(this._handleTouchMove = (t) => {
t.cancelable && t.preventDefault();
const e = t.targetTouches[0];
this._strokeMoveUpdate(e);
}),
(this._handleTouchEnd = (t) => {
if (t.target === this.canvas) {
t.cancelable && t.preventDefault();
const e = t.changedTouches[0];
this._strokeEnd(e);
}
}),
(this._handlePointerStart = (t) => {
t.preventDefault(), this._strokeBegin(t);
}),
(this._handlePointerMove = (t) => {
this._strokeMoveUpdate(t);
}),
(this._handlePointerEnd = (t) => {
this._drawingStroke &&
(t.preventDefault(), this._strokeEnd(t));
}),
(this.velocityFilterWeight = e.velocityFilterWeight || 0.7),
(this.minWidth = e.minWidth || 0.5),
(this.maxWidth = e.maxWidth || 2.5),
(this.throttle = "throttle" in e ? e.throttle : 16),
(this.minDistance = "minDistance" in e ? e.minDistance : 5),
(this.dotSize = e.dotSize || 0),
(this.penColor = e.penColor || "black"),
(this.backgroundColor = e.backgroundColor || "rgba(0,0,0,0)"),
(this.compositeOperation =
e.compositeOperation || "source-over"),
(this._strokeMoveUpdate = this.throttle
? (function (t, e = 250) {
let i,
s,
n,
o = 0,
h = null;
const r = () => {
(o = Date.now()),
(h = null),
(i = t.apply(s, n)),
h || ((s = null), (n = []));
};
return function (...a) {
const c = Date.now(),
d = e - (c - o);
return (
(s = this),
(n = a),
d <= 0 || d > e
? (h && (clearTimeout(h), (h = null)),
(o = c),
(i = t.apply(s, n)),
h || ((s = null), (n = [])))
: h || (h = window.setTimeout(r, d)),
i
);
};
})(s.prototype._strokeUpdate, this.throttle)
: s.prototype._strokeUpdate),
(this._ctx = t.getContext("2d")),
this.clear(),
this.on();
}
clear() {
const { _ctx: t, canvas: e } = this;
(t.fillStyle = this.backgroundColor),
t.clearRect(0, 0, e.width, e.height),
t.fillRect(0, 0, e.width, e.height),
(this._data = []),
this._reset(this._getPointGroupOptions()),
(this._isEmpty = !0);
}
fromDataURL(t, e = {}) {
return new Promise((i, s) => {
const n = new Image(),
o = e.ratio || window.devicePixelRatio || 1,
h = e.width || this.canvas.width / o,
r = e.height || this.canvas.height / o,
a = e.xOffset || 0,
c = e.yOffset || 0;
this._reset(this._getPointGroupOptions()),
(n.onload = () => {
this._ctx.drawImage(n, a, c, h, r), i();
}),
(n.onerror = (t) => {
s(t);
}),
(n.crossOrigin = "anonymous"),
(n.src = t),
(this._isEmpty = !1);
});
}
toDataURL(t = "image/png", e) {
return "image/svg+xml" === t
? ("object" != typeof e && (e = void 0),
`data:image/svg+xml;base64,${btoa(this.toSVG(e))}`)
: ("number" != typeof e && (e = void 0),
this.canvas.toDataURL(t, e));
}
on() {
(this.canvas.style.touchAction = "none"),
(this.canvas.style.msTouchAction = "none"),
(this.canvas.style.userSelect = "none");
const t =
/Macintosh/.test(navigator.userAgent) &&
"ontouchstart" in document;
window.PointerEvent && !t
? this._handlePointerEvents()
: (this._handleMouseEvents(),
"ontouchstart" in window && this._handleTouchEvents());
}
off() {
(this.canvas.style.touchAction = "auto"),
(this.canvas.style.msTouchAction = "auto"),
(this.canvas.style.userSelect = "auto"),
this.canvas.removeEventListener(
"pointerdown",
this._handlePointerStart,
),
this.canvas.removeEventListener(
"pointermove",
this._handlePointerMove,
),
this.canvas.ownerDocument.removeEventListener(
"pointerup",
this._handlePointerEnd,
),
this.canvas.removeEventListener(
"mousedown",
this._handleMouseDown,
),
this.canvas.removeEventListener(
"mousemove",
this._handleMouseMove,
),
this.canvas.ownerDocument.removeEventListener(
"mouseup",
this._handleMouseUp,
),
this.canvas.removeEventListener(
"touchstart",
this._handleTouchStart,
),
this.canvas.removeEventListener(
"touchmove",
this._handleTouchMove,
),
this.canvas.removeEventListener(
"touchend",
this._handleTouchEnd,
);
}
isEmpty() {
return this._isEmpty;
}
fromData(t, { clear: e = !0 } = {}) {
e && this.clear(),
this._fromData(
t,
this._drawCurve.bind(this),
this._drawDot.bind(this),
),
(this._data = this._data.concat(t));
}
toData() {
return this._data;
}
_getPointGroupOptions(t) {
return {
penColor: t && "penColor" in t ? t.penColor : this.penColor,
dotSize: t && "dotSize" in t ? t.dotSize : this.dotSize,
minWidth: t && "minWidth" in t ? t.minWidth : this.minWidth,
maxWidth: t && "maxWidth" in t ? t.maxWidth : this.maxWidth,
velocityFilterWeight:
t && "velocityFilterWeight" in t
? t.velocityFilterWeight
: this.velocityFilterWeight,
compositeOperation:
t && "compositeOperation" in t
? t.compositeOperation
: this.compositeOperation,
};
}
_strokeBegin(t) {
if (
!this.dispatchEvent(
new CustomEvent("beginStroke", {
detail: t,
cancelable: !0,
}),
)
)
return;
this._drawingStroke = !0;
const e = this._getPointGroupOptions(),
i = Object.assign(Object.assign({}, e), { points: [] });
this._data.push(i), this._reset(e), this._strokeUpdate(t);
}
_strokeUpdate(t) {
if (!this._drawingStroke) return;
if (0 === this._data.length) return void this._strokeBegin(t);
this.dispatchEvent(
new CustomEvent("beforeUpdateStroke", { detail: t }),
);
const e = t.clientX,
i = t.clientY,
s =
void 0 !== t.pressure
? t.pressure
: void 0 !== t.force
? t.force
: 0,
n = this._createPoint(e, i, s),
o = this._data[this._data.length - 1],
h = o.points,
r = h.length > 0 && h[h.length - 1],
a = !!r && n.distanceTo(r) <= this.minDistance,
c = this._getPointGroupOptions(o);
if (!r || !r || !a) {
const t = this._addPoint(n, c);
r ? t && this._drawCurve(t, c) : this._drawDot(n, c),
h.push({
time: n.time,
x: n.x,
y: n.y,
pressure: n.pressure,
});
}
this.dispatchEvent(
new CustomEvent("afterUpdateStroke", { detail: t }),
);
}
_strokeEnd(t) {
this._drawingStroke &&
(this._strokeUpdate(t),
(this._drawingStroke = !1),
this.dispatchEvent(
new CustomEvent("endStroke", { detail: t }),
));
}
_handlePointerEvents() {
(this._drawingStroke = !1),
this.canvas.addEventListener(
"pointerdown",
this._handlePointerStart,
),
this.canvas.addEventListener(
"pointermove",
this._handlePointerMove,
),
this.canvas.ownerDocument.addEventListener(
"pointerup",
this._handlePointerEnd,
);
}
_handleMouseEvents() {
(this._drawingStroke = !1),
this.canvas.addEventListener(
"mousedown",
this._handleMouseDown,
),
this.canvas.addEventListener(
"mousemove",
this._handleMouseMove,
),
this.canvas.ownerDocument.addEventListener(
"mouseup",
this._handleMouseUp,
);
}
_handleTouchEvents() {
this.canvas.addEventListener("touchstart", this._handleTouchStart),
this.canvas.addEventListener(
"touchmove",
this._handleTouchMove,
),
this.canvas.addEventListener("touchend", this._handleTouchEnd);
}
_reset(t) {
(this._lastPoints = []),
(this._lastVelocity = 0),
(this._lastWidth = (t.minWidth + t.maxWidth) / 2),
(this._ctx.fillStyle = t.penColor),
(this._ctx.globalCompositeOperation = t.compositeOperation);
}
_createPoint(e, i, s) {
const n = this.canvas.getBoundingClientRect();
return new t(e - n.left, i - n.top, s, new Date().getTime());
}
_addPoint(t, i) {
const { _lastPoints: s } = this;
if ((s.push(t), s.length > 2)) {
3 === s.length && s.unshift(s[0]);
const t = this._calculateCurveWidths(s[1], s[2], i),
n = e.fromPoints(s, t);
return s.shift(), n;
}
return null;
}
_calculateCurveWidths(t, e, i) {
const s =
i.velocityFilterWeight * e.velocityFrom(t) +
(1 - i.velocityFilterWeight) * this._lastVelocity,
n = this._strokeWidth(s, i),
o = { end: n, start: this._lastWidth };
return (this._lastVelocity = s), (this._lastWidth = n), o;
}
_strokeWidth(t, e) {
return Math.max(e.maxWidth / (t + 1), e.minWidth);
}
_drawCurveSegment(t, e, i) {
const s = this._ctx;
s.moveTo(t, e),
s.arc(t, e, i, 0, 2 * Math.PI, !1),
(this._isEmpty = !1);
}
_drawCurve(t, e) {
const i = this._ctx,
s = t.endWidth - t.startWidth,
n = 2 * Math.ceil(t.length());
i.beginPath(), (i.fillStyle = e.penColor);
for (let i = 0; i < n; i += 1) {
const o = i / n,
h = o * o,
r = h * o,
a = 1 - o,
c = a * a,
d = c * a;
let l = d * t.startPoint.x;
(l += 3 * c * o * t.control1.x),
(l += 3 * a * h * t.control2.x),
(l += r * t.endPoint.x);
let u = d * t.startPoint.y;
(u += 3 * c * o * t.control1.y),
(u += 3 * a * h * t.control2.y),
(u += r * t.endPoint.y);
const v = Math.min(t.startWidth + r * s, e.maxWidth);
this._drawCurveSegment(l, u, v);
}
i.closePath(), i.fill();
}
_drawDot(t, e) {
const i = this._ctx,
s = e.dotSize > 0 ? e.dotSize : (e.minWidth + e.maxWidth) / 2;
i.beginPath(),
this._drawCurveSegment(t.x, t.y, s),
i.closePath(),
(i.fillStyle = e.penColor),
i.fill();
}
_fromData(e, i, s) {
for (const n of e) {
const { points: e } = n,
o = this._getPointGroupOptions(n);
if (e.length > 1)
for (let s = 0; s < e.length; s += 1) {
const n = e[s],
h = new t(n.x, n.y, n.pressure, n.time);
0 === s && this._reset(o);
const r = this._addPoint(h, o);
r && i(r, o);
}
else this._reset(o), s(e[0], o);
}
}
toSVG({ includeBackgroundColor: t = !1 } = {}) {
const e = this._data,
i = Math.max(window.devicePixelRatio || 1, 1),
s = this.canvas.width / i,
n = this.canvas.height / i,
o = document.createElementNS(
"http://www.w3.org/2000/svg",
"svg",
);
if (
(o.setAttribute("xmlns", "http://www.w3.org/2000/svg"),
o.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink"),
o.setAttribute("viewBox", `0 0 ${s} ${n}`),
o.setAttribute("width", s.toString()),
o.setAttribute("height", n.toString()),
t && this.backgroundColor)
) {
const t = document.createElement("rect");
t.setAttribute("width", "100%"),
t.setAttribute("height", "100%"),
t.setAttribute("fill", this.backgroundColor),
o.appendChild(t);
}
return (
this._fromData(
e,
(t, { penColor: e }) => {
const i = document.createElement("path");
if (
!(
isNaN(t.control1.x) ||
isNaN(t.control1.y) ||
isNaN(t.control2.x) ||
isNaN(t.control2.y)
)
) {
const s = `M ${t.startPoint.x.toFixed(3)},${t.startPoint.y.toFixed(3)} C ${t.control1.x.toFixed(3)},${t.control1.y.toFixed(3)} ${t.control2.x.toFixed(3)},${t.control2.y.toFixed(3)} ${t.endPoint.x.toFixed(3)},${t.endPoint.y.toFixed(3)}`;
i.setAttribute("d", s),
i.setAttribute(
"stroke-width",
(2.25 * t.endWidth).toFixed(3),
),
i.setAttribute("stroke", e),
i.setAttribute("fill", "none"),
i.setAttribute("stroke-linecap", "round"),
o.appendChild(i);
}
},
(
t,
{ penColor: e, dotSize: i, minWidth: s, maxWidth: n },
) => {
const h = document.createElement("circle"),
r = i > 0 ? i : (s + n) / 2;
h.setAttribute("r", r.toString()),
h.setAttribute("cx", t.x.toString()),
h.setAttribute("cy", t.y.toString()),
h.setAttribute("fill", e),
o.appendChild(h);
},
),
o.outerHTML
);
}
}
return s;
});
//# sourceMappingURL=signature_pad.umd.min.js.map

File diff suppressed because it is too large Load Diff

View File

@ -40,49 +40,38 @@
</div>
</a>
@php
$headingOtorisasi = 0;
$headingMain = 0;
$headingMaster = 0;
$headingSystem = 0;
// Ensure $menus is defined and is an object
$menus = isset($menus) ? json_decode(json_encode($menus)) : new stdClass;
// Define the order of sections
$sectionOrder = ['main','otorisator', 'master', 'system'];
$sectionOrder = ['main', 'otorisator','laporan', 'master', 'system'];
$sectionTitles = [
'main' => 'Apps',
'otorisator' => 'Otorisator',
'laporan' => 'Laporan',
'master' => 'Master Data',
'system' => 'Systems'
];
@endphp
@foreach($sectionOrder as $section)
@if(!empty($menus->$section))
@if($section == 'otorisator' && $headingOtorisasi == 0)
@php
$hasVisibleItems = false;
foreach($menus->$section as $menu) {
if(auth()->user()->hasRole($menu->roles)) {
$hasVisibleItems = true;
break;
}
}
@endphp
@if($hasVisibleItems)
<div class="menu-item pt-2.25 pb-px">
<span class="menu-heading uppercase text-2sm font-semibold text-gray-500 pl-[10px] pr-[10px]">
Otorisator
{{ $sectionTitles[$section] }}
</span>
</div>
@php $headingOtorisasi = 1; @endphp
@elseif($section == 'main' && $headingMain == 0)
<div class="menu-item pt-2.25 pb-px">
<span class="menu-heading uppercase text-2sm font-semibold text-gray-500 pl-[10px] pr-[10px]">
Apps
</span>
</div>
@php $headingMain = 1; @endphp
@elseif($section == 'master' && $headingMaster == 0)
<div class="menu-item pt-2.25 pb-px">
<span class="menu-heading uppercase text-2sm font-semibold text-gray-500 pl-[10px] pr-[10px]">
Master Data
</span>
</div>
@php $headingMaster = 1; @endphp
@elseif($section == 'system' && $headingSystem == 0)
<div class="menu-item pt-2.25 pb-px">
<span class="menu-heading uppercase text-2sm font-semibold text-gray-500 pl-[10px] pr-[10px]">
Systems
</span>
</div>
@php $headingSystem = 1; @endphp
@endif
@foreach($menus->$section as $menu)
@if(auth()->user()->hasRole($menu->roles))
@ -95,7 +84,31 @@
<i class="{{ $menu->icon ?? 'ki-filled ki-element-11 text-lg' }}"></i>
</span>
<span class="menu-title text-sm font-semibold text-gray-700 menu-item-active:text-primary menu-link-hover:!text-primary">
{{ $menu->title }}
@php
$title = $menu->title;
if(strlen($title) > 30) {
$words = explode(' ', $title);
$lines = [];
$currentLine = '';
foreach($words as $word) {
if(strlen($currentLine . ' ' . $word) <= 30 || empty($currentLine)) {
$currentLine = empty($currentLine) ? $word : $currentLine . ' ' . $word;
} else {
$lines[] = $currentLine;
$currentLine = $word;
}
}
if(!empty($currentLine)) {
$lines[] = $currentLine;
}
echo implode('<br>', $lines);
} else {
echo $title;
}
@endphp
</span>
<span class="menu-arrow text-gray-400 w-[20px] shrink-0 justify-end ml-1 mr-[-10px]">
<i class="ki-filled ki-plus text-2xs menu-item-show:hidden">
@ -132,7 +145,31 @@
<i class="{{ $menu->icon ?? 'ki-filled ki-element-11 text-lg' }}"></i>
</span>
<span class="menu-title text-sm font-semibold text-gray-700 menu-item-active:text-primary menu-link-hover:!text-primary">
{{ $menu->title }}
@php
$title = $menu->title;
if(strlen($title) > 30) {
$words = explode(' ', $title);
$lines = [];
$currentLine = '';
foreach($words as $word) {
if(strlen($currentLine . ' ' . $word) <= 30 || empty($currentLine)) {
$currentLine = empty($currentLine) ? $word : $currentLine . ' ' . $word;
} else {
$lines[] = $currentLine;
$currentLine = $word;
}
}
if(!empty($currentLine)) {
$lines[] = $currentLine;
}
echo implode('<br>', $lines);
} else {
echo $title;
}
@endphp
</span>
</a>
</div>
@ -140,6 +177,7 @@
@endif
@endforeach
@endif
@endif
@endforeach
</div>
</div>

View File

@ -3,6 +3,12 @@
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\DashboardController;
Route::get('/notifications/count', function () {
return response()->json([
'count' => auth()->user()->unreadNotifications->count()
]);
})->name('notifications.count')->middleware('auth');
});
Route::middleware(['auth'])->group(function () {
Route::get('/', [DashboardController::class, 'index'])->name('dashboard');
});