diff --git a/.codegraph/.gitignore b/.codegraph/.gitignore new file mode 100644 index 00000000..d20c0fe4 --- /dev/null +++ b/.codegraph/.gitignore @@ -0,0 +1,5 @@ +# CodeGraph data files — local to each machine, not for committing. +# Ignore everything in .codegraph/ except this file itself, so transient +# files (the database, daemon.pid, sockets, logs) never show up in git. +* +!.gitignore diff --git a/index.html b/index.html index d3bf425d..3f4a30a6 100644 --- a/index.html +++ b/index.html @@ -1,4 +1,4 @@ - + - - - - - - CoreUI Vue.js Admin Template + + + + + + ManjaPro 2 - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + +
diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json index 42696123..145dc62e 100644 --- a/node_modules/.package-lock.json +++ b/node_modules/.package-lock.json @@ -975,6 +975,22 @@ "@popperjs/core": "^2.11.8" } }, + "node_modules/bootstrap-icons": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.13.1.tgz", + "integrity": "sha512-ijombt4v6bv5CLeXvRWKy7CuM3TRTuPEuGaGKvTV5cz65rQSY8RQ2JcHt6b90cBBAC7s8fsf2EkQDldzCoXUjw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", @@ -2770,6 +2786,16 @@ "node": ">=8" } }, + "node_modules/sweetalert2": { + "version": "11.26.25", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.26.25.tgz", + "integrity": "sha512-+hunCOJdJ6FLj04T9YSLvvZXRjsvIkTeTKP2e4VF8CaBias961BTnWiSFAy7F/CM5eq3QK2Rraoc5Gzftslvkg==", + "license": "MIT", + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/limonte" + } + }, "node_modules/tinyglobby": { "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", diff --git a/node_modules/.vite/deps/@coreui_icons.js b/node_modules/.vite/deps/@coreui_icons.js index d6f7cb6d..116012b4 100644 --- a/node_modules/.vite/deps/@coreui_icons.js +++ b/node_modules/.vite/deps/@coreui_icons.js @@ -1,4 +1,4 @@ -import { t as __exportAll } from "./chunk-D7D4PA-g.js"; +import { n as __exportAll } from "./chunk-EAsCxrDo.js"; //#region node_modules/@coreui/icons/dist/esm/brand/cib-500px-5.js var cib500px5 = ["32 32", ""]; //#endregion diff --git a/node_modules/.vite/deps/@coreui_vue.js b/node_modules/.vite/deps/@coreui_vue.js index 5c35e20d..8248f122 100644 --- a/node_modules/.vite/deps/@coreui_vue.js +++ b/node_modules/.vite/deps/@coreui_vue.js @@ -1,4 +1,4 @@ -import { t as __exportAll } from "./chunk-D7D4PA-g.js"; +import { n as __exportAll } from "./chunk-EAsCxrDo.js"; import { $n as toRefs, At as onBeforeMount, C as vShow, Cn as withDirectives, Ft as onMounted, I as Teleport, Kn as ref, Ot as nextTick, U as computed, Ut as provide, V as cloneVNode, Yt as resolveComponent, cn as useId, gn as watch, jt as onBeforeUnmount, n as Transition, nt as defineComponent, pt as h, ut as getCurrentInstance, xt as inject, zt as onUnmounted } from "./vue.runtime.esm-bundler-BbnC8aGa.js"; //#region node_modules/@coreui/vue/dist/esm/components/accordion/CAccordion.js var CAccordion = defineComponent({ diff --git a/node_modules/.vite/deps/_metadata.json b/node_modules/.vite/deps/_metadata.json index 9f23a742..b5dbd054 100644 --- a/node_modules/.vite/deps/_metadata.json +++ b/node_modules/.vite/deps/_metadata.json @@ -1,73 +1,79 @@ { - "hash": "4d35c1d8", + "hash": "2e340ed7", "configHash": "10ab3426", - "lockfileHash": "02073072", - "browserHash": "4f5afd98", + "lockfileHash": "70e9b55b", + "browserHash": "3bb1ff32", "optimized": { "@coreui/icons-vue": { "src": "../../@coreui/icons-vue/dist/index.es.js", "file": "@coreui_icons-vue.js", - "fileHash": "01b1000b", + "fileHash": "4d6686d2", "needsInterop": false }, "@coreui/icons": { "src": "../../@coreui/icons/dist/esm/index.js", "file": "@coreui_icons.js", - "fileHash": "11116c1c", + "fileHash": "907f9c3e", "needsInterop": false }, "@coreui/utils": { "src": "../../@coreui/utils/dist/esm/index.js", "file": "@coreui_utils.js", - "fileHash": "a7904746", + "fileHash": "6e72ff9c", "needsInterop": false }, "@coreui/vue-chartjs": { "src": "../../@coreui/vue-chartjs/dist/esm/index.js", "file": "@coreui_vue-chartjs.js", - "fileHash": "ff7ea82c", + "fileHash": "50ed4496", "needsInterop": false }, "@coreui/vue": { "src": "../../@coreui/vue/dist/esm/index.js", "file": "@coreui_vue.js", - "fileHash": "f875f908", + "fileHash": "3ebf09b5", "needsInterop": false }, "axios": { "src": "../../axios/index.js", "file": "axios.js", - "fileHash": "aab15b87", + "fileHash": "184e9400", "needsInterop": false }, "pinia": { "src": "../../pinia/dist/pinia.mjs", "file": "pinia.js", - "fileHash": "620e0ade", + "fileHash": "46bbb8ba", "needsInterop": false }, "simplebar-vue": { "src": "../../simplebar-vue/dist/simplebar-vue.esm.js", "file": "simplebar-vue.js", - "fileHash": "dc00c9a8", + "fileHash": "0567ff81", "needsInterop": false }, + "sweetalert2": { + "src": "../../sweetalert2/dist/sweetalert2.all.js", + "file": "sweetalert2.js", + "fileHash": "aaaec58d", + "needsInterop": true + }, "vue-router": { "src": "../../vue-router/dist/vue-router.js", "file": "vue-router.js", - "fileHash": "71557f71", + "fileHash": "7ea651fe", "needsInterop": false }, "vue": { "src": "../../vue/dist/vue.runtime.esm-bundler.js", "file": "vue.js", - "fileHash": "a24d774e", + "fileHash": "50e5209b", "needsInterop": false } }, "chunks": { - "chunk-D7D4PA-g": { - "file": "chunk-D7D4PA-g.js", + "chunk-EAsCxrDo": { + "file": "chunk-EAsCxrDo.js", "isDynamicEntry": false }, "dist-CY5vkPpf": { diff --git a/node_modules/.vite/deps/chunk-D7D4PA-g.js b/node_modules/.vite/deps/chunk-D7D4PA-g.js deleted file mode 100644 index a621e83b..00000000 --- a/node_modules/.vite/deps/chunk-D7D4PA-g.js +++ /dev/null @@ -1,13 +0,0 @@ -//#region \0rolldown/runtime.js -var __defProp = Object.defineProperty; -var __exportAll = (all, no_symbols) => { - let target = {}; - for (var name in all) __defProp(target, name, { - get: all[name], - enumerable: true - }); - if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" }); - return target; -}; -//#endregion -export { __exportAll as t }; diff --git a/package-lock.json b/package-lock.json index 102665b8..e9cb8441 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,9 +19,11 @@ "@popperjs/core": "^2.11.8", "axios": "^1.18.1", "bootstrap": "^5.3.8", + "bootstrap-icons": "^1.13.1", "chart.js": "^4.5.1", "pinia": "^3.0.4", "simplebar-vue": "^2.4.2", + "sweetalert2": "^11.26.25", "vue": "^3.5.31", "vue-router": "^5.0.4" }, @@ -1568,6 +1570,22 @@ "@popperjs/core": "^2.11.8" } }, + "node_modules/bootstrap-icons": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.13.1.tgz", + "integrity": "sha512-ijombt4v6bv5CLeXvRWKy7CuM3TRTuPEuGaGKvTV5cz65rQSY8RQ2JcHt6b90cBBAC7s8fsf2EkQDldzCoXUjw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", @@ -3589,6 +3607,16 @@ "node": ">=8" } }, + "node_modules/sweetalert2": { + "version": "11.26.25", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.26.25.tgz", + "integrity": "sha512-+hunCOJdJ6FLj04T9YSLvvZXRjsvIkTeTKP2e4VF8CaBias961BTnWiSFAy7F/CM5eq3QK2Rraoc5Gzftslvkg==", + "license": "MIT", + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/limonte" + } + }, "node_modules/tinyglobby": { "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", diff --git a/package.json b/package.json index eb02ea73..d1239c83 100644 --- a/package.json +++ b/package.json @@ -28,9 +28,11 @@ "@popperjs/core": "^2.11.8", "axios": "^1.18.1", "bootstrap": "^5.3.8", + "bootstrap-icons": "^1.13.1", "chart.js": "^4.5.1", "pinia": "^3.0.4", "simplebar-vue": "^2.4.2", + "sweetalert2": "^11.26.25", "vue": "^3.5.31", "vue-router": "^5.0.4" }, diff --git a/src/_nav.js b/src/_nav.js index 388618b1..2466e512 100644 --- a/src/_nav.js +++ b/src/_nav.js @@ -21,6 +21,7 @@ * * @type {Array} */ + export default [ { component: 'CNavItem', @@ -88,7 +89,7 @@ export default [ component: 'CNavItem', name: 'Colors', to: '/theme/colors', - icon: 'cil-drop', + icon: 'cil-square', }, { component: 'CNavItem', diff --git a/src/api/axios.js b/src/api/axios.js new file mode 100644 index 00000000..2ecdb3db --- /dev/null +++ b/src/api/axios.js @@ -0,0 +1,22 @@ +import axios from 'axios' +import { getToken } from '@/utils/session' + +const api = axios.create({ + baseURL: 'https://api.radiq.my.id/api', + headers: { + 'Content-Type': 'application/json', + }, +}) + +api.interceptors.request.use( + (config) => { + const token = getToken() + if (token) { + config.headers.Authorization = `Bearer ${token}` + } + return config + }, + (error) => Promise.reject(error), +) + +export default api diff --git a/src/assets/icons/index.js b/src/assets/icons/index.js index cc20da72..97be2201 100644 --- a/src/assets/icons/index.js +++ b/src/assets/icons/index.js @@ -80,6 +80,13 @@ import { cilUserFemale, cilUserFollow, cilXCircle, + cilSquare, // new add + cilBarcode, + cilBookmark, + cilAperture, + cilCamera, + cilDescription, + cilCog, } from '@coreui/icons' export const iconsSet = Object.assign( @@ -139,6 +146,13 @@ export const iconsSet = Object.assign( cilUserFemale, cilUserFollow, cilXCircle, + cilSquare, // new add + cilBarcode, + cilBookmark, + cilAperture, + cilCamera, + cilDescription, + cilCog, }, { cifUs, diff --git a/src/components/AppFooter.vue b/src/components/AppFooter.vue index b62df395..2c3c217e 100644 --- a/src/components/AppFooter.vue +++ b/src/components/AppFooter.vue @@ -1,14 +1,12 @@ diff --git a/src/components/AppSidebarNav.js b/src/components/AppSidebarNav.js index 1006a9c3..49889eb3 100644 --- a/src/components/AppSidebarNav.js +++ b/src/components/AppSidebarNav.js @@ -96,11 +96,12 @@ const AppSidebarNav = defineComponent({ }) : h('span', { class: 'nav-icon' }, h('span', { class: 'nav-icon-bullet' })), item.name, - item.external && h(resolveComponent('CIcon'), { - class: 'ms-2', - name: 'cil-external-link', - size: 'sm' - }), + item.external && + h(resolveComponent('CIcon'), { + class: 'ms-2', + name: 'cil-external-link', + size: 'sm', + }), item.badge && h( CBadge, diff --git a/src/components/materials/AssignMaterialModal.vue b/src/components/materials/AssignMaterialModal.vue new file mode 100644 index 00000000..4e5bcef8 --- /dev/null +++ b/src/components/materials/AssignMaterialModal.vue @@ -0,0 +1,247 @@ + + + + + diff --git a/src/components/materials/MaterialDetailModal.vue b/src/components/materials/MaterialDetailModal.vue new file mode 100644 index 00000000..075a1be6 --- /dev/null +++ b/src/components/materials/MaterialDetailModal.vue @@ -0,0 +1,103 @@ + + + + + diff --git a/src/components/materials/MaterialHistory.vue b/src/components/materials/MaterialHistory.vue new file mode 100644 index 00000000..cd071b0f --- /dev/null +++ b/src/components/materials/MaterialHistory.vue @@ -0,0 +1,86 @@ + + + + diff --git a/src/components/materials/MaterialTable.vue b/src/components/materials/MaterialTable.vue new file mode 100644 index 00000000..26daed5c --- /dev/null +++ b/src/components/materials/MaterialTable.vue @@ -0,0 +1,99 @@ + + + diff --git a/src/components/materials/ReturnMaterialModal.vue b/src/components/materials/ReturnMaterialModal.vue new file mode 100644 index 00000000..f7d0abbb --- /dev/null +++ b/src/components/materials/ReturnMaterialModal.vue @@ -0,0 +1,36 @@ + + + diff --git a/src/components/materials/TransferMaterialModal.vue b/src/components/materials/TransferMaterialModal.vue new file mode 100644 index 00000000..b9e5f12a --- /dev/null +++ b/src/components/materials/TransferMaterialModal.vue @@ -0,0 +1,41 @@ + + + diff --git a/src/main.js b/src/main.js index b4e39eac..c0f2a071 100644 --- a/src/main.js +++ b/src/main.js @@ -17,6 +17,7 @@ import { createApp } from 'vue' import { createPinia } from 'pinia' +import { setToken, hasToken } from '@/utils/session' import App from './App.vue' import router from './router' @@ -31,6 +32,9 @@ import DocsComponents from '@/components/DocsComponents' import DocsExample from '@/components/DocsExample' import DocsIcons from '@/components/DocsIcons' +import { CButton } from '@coreui/vue' +import { CBadge } from '@coreui/vue' + // Create Vue application instance const app = createApp(App) @@ -39,6 +43,8 @@ app.use(createPinia()) // State management app.use(router) // Router for SPA navigation app.use(CoreuiVue) // CoreUI component library +app.component('CButton', CButton) + // Provide icons globally app.provide('icons', icons) @@ -48,5 +54,10 @@ app.component('DocsComponents', DocsComponents) app.component('DocsExample', DocsExample) app.component('DocsIcons', DocsIcons) +// TODO: Hapus setelah fitur Login tersedia — token sementara untuk development +if (!hasToken()) { + setToken('UU7HVn7ECHe7MvqMTeTeEpoFdBA2wc0NHQX29WyG91fc6982') +} + // Mount application to DOM app.mount('#app') diff --git a/src/services/materialService.js b/src/services/materialService.js new file mode 100644 index 00000000..3c2f1dcc --- /dev/null +++ b/src/services/materialService.js @@ -0,0 +1,22 @@ +import api from '@/api/axios' + +export const materialService = { + fetchAll(params = {}) { + return api.get('/materials', { params }) + }, + fetchByUser(userId) { + return api.get(`/materials/user/${userId}`) + }, + fetchHistory(barcodeId) { + return api.get(`/materials/history/${barcodeId}`) + }, + assign(payload) { + return api.post('/materials/assign', payload) + }, + transfer(payload) { + return api.post('/materials/transfer', payload) + }, + returnMaterial(payload) { + return api.post('/materials/return', payload) + }, +} diff --git a/src/stores/materialStore.js b/src/stores/materialStore.js new file mode 100644 index 00000000..3f71d526 --- /dev/null +++ b/src/stores/materialStore.js @@ -0,0 +1,62 @@ +import { defineStore } from 'pinia' +import { materialService } from '@/services/materialService' + +export const useMaterialStore = defineStore('material', { + state: () => ({ + materials: [], + histories: [], + loading: false, + error: '', + }), + + actions: { + async fetchAll(params = {}) { + this.loading = true + this.error = '' + try { + const res = await materialService.fetchAll({ per_page: 1000, ...params }) + const resData = res.data + + if (Array.isArray(resData)) { + this.materials = resData + } else if (resData && Array.isArray(resData.data)) { + this.materials = resData.data + } else { + console.warn('fetchAll: unexpected response', resData) + this.materials = [] + } + } catch (err) { + this.error = err.response?.data?.message || 'Gagal mengambil data material.' + } finally { + this.loading = false + } + }, + async fetchByUser(userId) { + if (!userId) return + this.loading = true + this.error = '' + try { + const res = await materialService.fetchByUser(userId) + this.materials = res.data.data || [] + } catch (err) { + this.error = err.response?.data?.message || 'Gagal mengambil data material.' + } finally { + this.loading = false + } + }, + + async fetchHistory(barcodeId) { + if (!barcodeId) return + this.loading = true + this.error = '' + try { + const res = await materialService.fetchHistory(barcodeId) + this.histories = res.data.data || [] + } catch (err) { + this.error = err.response?.data?.message || 'Gagal mengambil history material.' + } finally { + this.loading = false + } + }, + }, +}) diff --git a/src/styles/style.scss b/src/styles/style.scss index c5891ac1..c30adb7b 100644 --- a/src/styles/style.scss +++ b/src/styles/style.scss @@ -1,5 +1,9 @@ @use '@coreui/coreui/scss/coreui' as * with ( - $enable-deprecation-messages: false + $enable-deprecation-messages: false, + $primary: #f28500, + $primary-text-emphasis: #9e5500, + $primary-bg-subtle: #fde6cc, + $primary-border-subtle: #f9b364 ); @use '@coreui/chartjs/scss/coreui-chartjs'; @use 'vendors/simplebar'; @@ -8,6 +12,13 @@ body { background-color: var(--cui-tertiary-bg); } +:root { + --cui-primary: #f28500; + --cui-primary-rgb: 242, 133, 0; + --cui-link-color: #f28500; + --cui-link-color-rgb: 242, 133, 0; +} + .wrapper { width: 100%; padding-inline: var(--cui-sidebar-occupy-start, 0) var(--cui-sidebar-occupy-end, 0); @@ -129,3 +140,65 @@ body { --cui-tertiary-bg: #16171d; --cui-border-color: #22242d; } + +// font bold untuk toast edit +.swal-title-bold { + font-weight: 700 !important; /* Bold */ + font-size: 16px; +} + +//materials +.timeline { + position: relative; + margin-left: 20px; +} + +.timeline::before { + content: ''; + position: absolute; + left: 14px; + top: 0; + bottom: 0; + width: 2px; + background: #d8dbe0; +} + +.timeline-item { + position: relative; + display: flex; + margin-bottom: 20px; +} + +.timeline-icon { + width: 30px; + height: 30px; + border-radius: 50%; + background: #ffffff; + border: 2px solid #d8dbe0; + display: flex; + align-items: center; + justify-content: center; + z-index: 1; +} + +.timeline-content { + flex: 1; + margin-left: 15px; + background: #f8f9fa; + border-radius: 8px; + padding: 12px; +} + +.card { + transition: 0.3s; +} + +.table tbody tr { + transition: 0.2s; +} + +.table tbody tr:hover { + transform: scale(1.01); + + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); +} diff --git a/src/utils/session.js b/src/utils/session.js new file mode 100644 index 00000000..1b076023 --- /dev/null +++ b/src/utils/session.js @@ -0,0 +1,21 @@ +const TOKEN_KEY = 'UU7HVn7ECHe7MvqMTeTeEpoFdBA2wc0NHQX29WyG91fc6982' + +export function setToken(token) { + sessionStorage.setItem(TOKEN_KEY, token) +} + +export function getToken() { + return sessionStorage.getItem(TOKEN_KEY) +} + +export function removeToken() { + sessionStorage.removeItem(TOKEN_KEY) +} + +export function hasToken() { + return !!sessionStorage.getItem(TOKEN_KEY) +} + +export function clearSession() { + sessionStorage.clear() +} diff --git a/src/utils/swal.js b/src/utils/swal.js new file mode 100644 index 00000000..1b9554c3 --- /dev/null +++ b/src/utils/swal.js @@ -0,0 +1,53 @@ +import Swal from 'sweetalert2' + +export function showConfirm(title = 'Konfirmasi', text = 'Apakah Anda yakin?') { + return Swal.fire({ + title, + text, + icon: 'question', + showCancelButton: true, + confirmButtonColor: '#dc3545', + confirmButtonText: 'Ya, hapus!', + cancelButtonText: 'Batal', + theme: 'auto', + }) +} + +export function showSuccess(message = 'Data berhasil disimpan') { + Swal.fire({ + icon: 'success', + title: 'Berhasil', + text: message, + showConfirmButton: false, + timer: 2000, + timerProgressBar: true, + theme: 'auto', + }) +} + +export function showSuccessr() { + Swal.fire({ + position: 'top-end', + icon: 'success', + title: 'Data Berhasil Di Simpan', + showConfirmButton: false, + timer: 1500, + theme: 'auto', + toast: true, + width: '350px', + padding: '10px 18px', + customClass: { + title: 'swal-title-bold', + }, + }) +} + +export function showError() { + Swal.fire({ + icon: 'error', + title: 'Gagal', + text: 'Terjadi Kesalahan', + confirmButtonText: 'Tutup', + theme: 'auto', + }) +} diff --git a/src/views/tickets/Ticket.vue b/src/views/tickets/Ticket.vue index 770c5046..9e16c3d0 100644 --- a/src/views/tickets/Ticket.vue +++ b/src/views/tickets/Ticket.vue @@ -142,14 +142,11 @@ async function saveTicket() { } try { if (isEdit.value && editingIndex.value >= 0) { - const res = await fetch( - `https://api.radiq.my.id/api/tickets/${form.ticket_no}`, - { - method: 'PUT', - headers: { Authorization: token, 'Content-Type': 'application/json' }, - body: JSON.stringify(body), - }, - ) + const res = await fetch(`https://api.radiq.my.id/api/tickets/${form.ticket_no}`, { + method: 'PUT', + headers: { Authorization: token, 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }) if (res.ok) store.updateTicket(editingIndex.value, form) } else { const res = await fetch('https://api.radiq.my.id/api/tickets', { @@ -227,7 +224,7 @@ function isPending(item) { Ticket - + + Tambah Ticket diff --git a/src/views/tickets/TicketIncident.vue b/src/views/tickets/TicketIncident.vue index 0b2b3f07..aac1192f 100644 --- a/src/views/tickets/TicketIncident.vue +++ b/src/views/tickets/TicketIncident.vue @@ -1,32 +1,21 @@