Global toast notifications; replace all alert() calls
Add a useToast() composable + a single ToastHost mounted in AppLayout. Convert every alert() across views/components (29 call sites, all error paths) to toast.error, and use toast.success to confirm a warranty refresh. Kills the native "localhost says" dialog and gives consistent, dismissable feedback. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -177,6 +177,8 @@ import { Cog, Monitor, Printer, Globe, Package } from 'lucide-vue-next'
|
|||||||
import { assetsApi, relationshipTypesApi } from '../api'
|
import { assetsApi, relationshipTypesApi } from '../api'
|
||||||
import { colorStyle } from '@/utils/colorStyle'
|
import { colorStyle } from '@/utils/colorStyle'
|
||||||
import { useAuthStore } from '../stores/auth'
|
import { useAuthStore } from '../stores/auth'
|
||||||
|
import { useToast } from '../composables/toast'
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
assetId: {
|
assetId: {
|
||||||
@@ -349,7 +351,7 @@ async function saveRelationship() {
|
|||||||
emit('updated')
|
emit('updated')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to create relationship:', error)
|
console.error('Failed to create relationship:', error)
|
||||||
alert('Failed to create relationship: ' + (error.response?.data?.message || error.message))
|
toast.error('Failed to create relationship: ' + (error.response?.data?.message || error.message))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,7 +364,7 @@ async function deleteRelationship(relationshipId) {
|
|||||||
emit('updated')
|
emit('updated')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to delete relationship:', error)
|
console.error('Failed to delete relationship:', error)
|
||||||
alert('Failed to delete relationship')
|
toast.error('Failed to delete relationship')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
75
frontend/src/components/ToastHost.vue
Normal file
75
frontend/src/components/ToastHost.vue
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
<template>
|
||||||
|
<div class="toast-host">
|
||||||
|
<transition-group name="toast">
|
||||||
|
<div
|
||||||
|
v-for="t in toast.items"
|
||||||
|
:key="t.id"
|
||||||
|
class="toast"
|
||||||
|
:class="`toast-${t.type}`"
|
||||||
|
@click="toast.dismiss(t.id)"
|
||||||
|
>
|
||||||
|
<span class="toast-message">{{ t.message }}</span>
|
||||||
|
<button class="toast-close" @click.stop="toast.dismiss(t.id)" aria-label="Dismiss">×</button>
|
||||||
|
</div>
|
||||||
|
</transition-group>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useToast } from '../composables/toast'
|
||||||
|
|
||||||
|
const toast = useToast()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.toast-host {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 1.25rem;
|
||||||
|
right: 1.25rem;
|
||||||
|
z-index: 3000;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.6rem;
|
||||||
|
max-width: min(420px, calc(100vw - 2.5rem));
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 0.75rem;
|
||||||
|
padding: 0.75rem 0.9rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: var(--bg-card);
|
||||||
|
color: var(--text);
|
||||||
|
border-left: 4px solid var(--secondary);
|
||||||
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
line-height: 1.35;
|
||||||
|
}
|
||||||
|
.toast-success { border-left-color: var(--success); }
|
||||||
|
.toast-error { border-left-color: var(--danger); }
|
||||||
|
.toast-info { border-left-color: var(--primary); }
|
||||||
|
|
||||||
|
.toast-message { flex: 1; }
|
||||||
|
|
||||||
|
.toast-close {
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
color: var(--text-light);
|
||||||
|
font-size: 1.1rem;
|
||||||
|
line-height: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-enter-active,
|
||||||
|
.toast-leave-active {
|
||||||
|
transition: opacity 0.2s ease, transform 0.2s ease;
|
||||||
|
}
|
||||||
|
.toast-enter-from,
|
||||||
|
.toast-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(20px);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
35
frontend/src/composables/toast.js
Normal file
35
frontend/src/composables/toast.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
// Global toast notifications. Import useToast() anywhere and call
|
||||||
|
// toast.success('...') / toast.error('...') / toast.info('...'). A single
|
||||||
|
// <ToastHost /> mounted in AppLayout renders the stack.
|
||||||
|
import { reactive } from 'vue'
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
items: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
let nextId = 1
|
||||||
|
|
||||||
|
function dismiss(id) {
|
||||||
|
const index = state.items.findIndex(t => t.id === id)
|
||||||
|
if (index !== -1) state.items.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
function push(message, type = 'info', duration = 5000) {
|
||||||
|
if (!message) return
|
||||||
|
const id = nextId++
|
||||||
|
state.items.push({ id, message, type })
|
||||||
|
if (duration > 0) {
|
||||||
|
setTimeout(() => dismiss(id), duration)
|
||||||
|
}
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useToast() {
|
||||||
|
return {
|
||||||
|
items: state.items,
|
||||||
|
dismiss,
|
||||||
|
success: (message, duration) => push(message, 'success', duration),
|
||||||
|
error: (message, duration) => push(message, 'error', duration ?? 8000),
|
||||||
|
info: (message, duration) => push(message, 'info', duration),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -69,12 +69,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<router-view />
|
<router-view />
|
||||||
</main>
|
</main>
|
||||||
|
<ToastHost />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
import ToastHost from '../components/ToastHost.vue'
|
||||||
import {
|
import {
|
||||||
Sun, Moon, LayoutDashboard, Calendar, Map, Cog, Monitor,
|
Sun, Moon, LayoutDashboard, Calendar, Map, Cog, Monitor,
|
||||||
Printer, Globe, Usb, AppWindow, BookOpen, BarChart3, Bell, Image, ShieldCheck
|
Printer, Globe, Usb, AppWindow, BookOpen, BarChart3, Bell, Image, ShieldCheck
|
||||||
|
|||||||
@@ -109,6 +109,8 @@ import { Cog, Monitor, Printer, Globe, Package, MapPin } from 'lucide-vue-next'
|
|||||||
import ShopFloorMap from '../components/ShopFloorMap.vue'
|
import ShopFloorMap from '../components/ShopFloorMap.vue'
|
||||||
import { assetsApi } from '../api'
|
import { assetsApi } from '../api'
|
||||||
import { currentTheme } from '../stores/theme'
|
import { currentTheme } from '../stores/theme'
|
||||||
|
import { useToast } from '../composables/toast'
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const assets = ref([])
|
const assets = ref([])
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
@@ -196,7 +198,7 @@ async function savePosition() {
|
|||||||
pickedPosition.value = null
|
pickedPosition.value = null
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to save position:', error)
|
console.error('Failed to save position:', error)
|
||||||
alert('Failed to save position')
|
toast.error('Failed to save position')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,7 +224,7 @@ async function clearPosition() {
|
|||||||
pickedPosition.value = null
|
pickedPosition.value = null
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to clear position:', error)
|
console.error('Failed to clear position:', error)
|
||||||
alert('Failed to clear position')
|
toast.error('Failed to clear position')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,6 +85,8 @@ import { useAuthStore } from '../stores/auth'
|
|||||||
import { loadMapConfig, state as mapConfig } from '../composables/mapConfig'
|
import { loadMapConfig, state as mapConfig } from '../composables/mapConfig'
|
||||||
import { exportMapPdf } from '../utils/mapPdf'
|
import { exportMapPdf } from '../utils/mapPdf'
|
||||||
import { assetTypeLabel, assetDetailRoute } from '../utils/assetTypes'
|
import { assetTypeLabel, assetDetailRoute } from '../utils/assetTypes'
|
||||||
|
import { useToast } from '../composables/toast'
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
@@ -254,7 +256,7 @@ async function exportPdf() {
|
|||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Map PDF export failed:', e)
|
console.error('Map PDF export failed:', e)
|
||||||
alert('Failed to export map PDF. See console for details.')
|
toast.error('Failed to export map PDF. See console for details.')
|
||||||
} finally {
|
} finally {
|
||||||
exporting.value = false
|
exporting.value = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,6 +142,8 @@
|
|||||||
import { ref, computed, onMounted } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { employeesApi, usbApi, notificationsApi } from '@/api'
|
import { employeesApi, usbApi, notificationsApi } from '@/api'
|
||||||
|
import { useToast } from '../../composables/toast'
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
@@ -244,7 +246,7 @@ async function checkinDevice(device) {
|
|||||||
await loadCheckoutHistory()
|
await loadCheckoutHistory()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error checking in device:', err)
|
console.error('Error checking in device:', err)
|
||||||
alert('Failed to check in device')
|
toast.error('Failed to check in device')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -199,6 +199,8 @@ import AssetRelationships from '../../components/AssetRelationships.vue'
|
|||||||
import CustomFieldsSection from '../../components/CustomFieldsSection.vue'
|
import CustomFieldsSection from '../../components/CustomFieldsSection.vue'
|
||||||
import WarrantyPanel from '../../components/WarrantyPanel.vue'
|
import WarrantyPanel from '../../components/WarrantyPanel.vue'
|
||||||
import { useIdentifierFlags } from '../../composables/identifierSettings'
|
import { useIdentifierFlags } from '../../composables/identifierSettings'
|
||||||
|
import { useToast } from '../../composables/toast'
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const { isEnabled } = useIdentifierFlags()
|
const { isEnabled } = useIdentifierFlags()
|
||||||
|
|
||||||
@@ -267,7 +269,7 @@ async function confirmDelete() {
|
|||||||
router.push('/network')
|
router.push('/network')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error deleting device:', error)
|
console.error('Error deleting device:', error)
|
||||||
alert('Failed to delete device')
|
toast.error('Failed to delete device')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,6 +101,8 @@
|
|||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { businessunitsApi } from '../../api'
|
import { businessunitsApi } from '../../api'
|
||||||
import PaginationBar from '../../components/PaginationBar.vue'
|
import PaginationBar from '../../components/PaginationBar.vue'
|
||||||
|
import { useToast } from '../../composables/toast'
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const items = ref([])
|
const items = ref([])
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
@@ -181,7 +183,7 @@ async function deleteItem() {
|
|||||||
toDelete.value = null
|
toDelete.value = null
|
||||||
loadData()
|
loadData()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert('Failed to delete')
|
toast.error('Failed to delete')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -114,6 +114,8 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
import { assetsApi, customFieldsApi } from '../../api'
|
import { assetsApi, customFieldsApi } from '../../api'
|
||||||
|
import { useToast } from '../../composables/toast'
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const assetTypes = ref([])
|
const assetTypes = ref([])
|
||||||
const assettypeid = ref(null)
|
const assettypeid = ref(null)
|
||||||
@@ -206,7 +208,7 @@ async function deleteField(f) {
|
|||||||
await customFieldsApi.remove(f.fieldid)
|
await customFieldsApi.remove(f.fieldid)
|
||||||
loadFields()
|
loadFields()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert(err.response?.data?.error?.message || err.response?.data?.message || 'Failed to delete')
|
toast.error(err.response?.data?.error?.message || err.response?.data?.message || 'Failed to delete')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -104,6 +104,8 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { dashboardDefaultsApi, businessUnitsApi } from '../../api'
|
import { dashboardDefaultsApi, businessUnitsApi } from '../../api'
|
||||||
|
import { useToast } from '../../composables/toast'
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const items = ref([])
|
const items = ref([])
|
||||||
const businessUnits = ref([])
|
const businessUnits = ref([])
|
||||||
@@ -181,7 +183,7 @@ async function deleteItem() {
|
|||||||
toDelete.value = null
|
toDelete.value = null
|
||||||
loadData()
|
loadData()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert('Failed to delete')
|
toast.error('Failed to delete')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ import { ref, computed, onMounted } from 'vue'
|
|||||||
import { equipmentApi } from '../../api'
|
import { equipmentApi } from '../../api'
|
||||||
import ColorSwatchPicker from '@/components/ColorSwatchPicker.vue'
|
import ColorSwatchPicker from '@/components/ColorSwatchPicker.vue'
|
||||||
import { colorStyle } from '@/utils/colorStyle'
|
import { colorStyle } from '@/utils/colorStyle'
|
||||||
|
import { useToast } from '../../composables/toast'
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const items = ref([])
|
const items = ref([])
|
||||||
const showInactive = ref(false)
|
const showInactive = ref(false)
|
||||||
@@ -136,7 +138,7 @@ async function deleteType(t) {
|
|||||||
await equipmentApi.types.remove(t.equipmenttypeid)
|
await equipmentApi.types.remove(t.equipmenttypeid)
|
||||||
loadData()
|
loadData()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert(err.response?.data?.error?.message || err.response?.data?.message || 'Failed to delete')
|
toast.error(err.response?.data?.error?.message || err.response?.data?.message || 'Failed to delete')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ import { ref, computed, onMounted } from 'vue'
|
|||||||
import { locationsApi } from '../../api'
|
import { locationsApi } from '../../api'
|
||||||
import ColorSwatchPicker from '@/components/ColorSwatchPicker.vue'
|
import ColorSwatchPicker from '@/components/ColorSwatchPicker.vue'
|
||||||
import { colorStyle } from '@/utils/colorStyle'
|
import { colorStyle } from '@/utils/colorStyle'
|
||||||
|
import { useToast } from '../../composables/toast'
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const items = ref([])
|
const items = ref([])
|
||||||
const showInactive = ref(false)
|
const showInactive = ref(false)
|
||||||
@@ -136,7 +138,7 @@ async function deleteType(t) {
|
|||||||
await locationsApi.types.remove(t.locationtypeid)
|
await locationsApi.types.remove(t.locationtypeid)
|
||||||
loadData()
|
loadData()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert(err.response?.data?.error?.message || err.response?.data?.message || 'Failed to delete')
|
toast.error(err.response?.data?.error?.message || err.response?.data?.message || 'Failed to delete')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -211,6 +211,8 @@
|
|||||||
import { ref, computed, onMounted } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
import { locationsApi } from '../../api'
|
import { locationsApi } from '../../api'
|
||||||
import PaginationBar from '../../components/PaginationBar.vue'
|
import PaginationBar from '../../components/PaginationBar.vue'
|
||||||
|
import { useToast } from '../../composables/toast'
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const locations = ref([])
|
const locations = ref([])
|
||||||
const locationTypes = ref([])
|
const locationTypes = ref([])
|
||||||
@@ -371,7 +373,7 @@ async function deleteLocation() {
|
|||||||
loadLocations()
|
loadLocations()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error deleting location:', err)
|
console.error('Error deleting location:', err)
|
||||||
alert('Failed to delete location')
|
toast.error('Failed to delete location')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -145,6 +145,8 @@
|
|||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { machinetypesApi } from '../../api'
|
import { machinetypesApi } from '../../api'
|
||||||
import PaginationBar from '../../components/PaginationBar.vue'
|
import PaginationBar from '../../components/PaginationBar.vue'
|
||||||
|
import { useToast } from '../../composables/toast'
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const machineTypes = ref([])
|
const machineTypes = ref([])
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
@@ -256,7 +258,7 @@ async function deleteType() {
|
|||||||
loadTypes()
|
loadTypes()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error deleting machine type:', err)
|
console.error('Error deleting machine type:', err)
|
||||||
alert('Failed to delete machine type')
|
toast.error('Failed to delete machine type')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -183,6 +183,8 @@
|
|||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { modelsApi, vendorsApi, machinetypesApi } from '../../api'
|
import { modelsApi, vendorsApi, machinetypesApi } from '../../api'
|
||||||
import PaginationBar from '../../components/PaginationBar.vue'
|
import PaginationBar from '../../components/PaginationBar.vue'
|
||||||
|
import { useToast } from '../../composables/toast'
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const models = ref([])
|
const models = ref([])
|
||||||
const vendors = ref([])
|
const vendors = ref([])
|
||||||
@@ -345,7 +347,7 @@ async function deleteModel() {
|
|||||||
loadModels()
|
loadModels()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error deleting model:', err)
|
console.error('Error deleting model:', err)
|
||||||
alert('Failed to delete model')
|
toast.error('Failed to delete model')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ import { ref, computed, onMounted } from 'vue'
|
|||||||
import { networkApi } from '../../api'
|
import { networkApi } from '../../api'
|
||||||
import ColorSwatchPicker from '@/components/ColorSwatchPicker.vue'
|
import ColorSwatchPicker from '@/components/ColorSwatchPicker.vue'
|
||||||
import { colorStyle } from '@/utils/colorStyle'
|
import { colorStyle } from '@/utils/colorStyle'
|
||||||
|
import { useToast } from '../../composables/toast'
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const items = ref([])
|
const items = ref([])
|
||||||
const showInactive = ref(false)
|
const showInactive = ref(false)
|
||||||
@@ -136,7 +138,7 @@ async function deleteType(t) {
|
|||||||
await networkApi.types.remove(t.networkdevicetypeid)
|
await networkApi.types.remove(t.networkdevicetypeid)
|
||||||
loadData()
|
loadData()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert(err.response?.data?.error?.message || err.response?.data?.message || 'Failed to delete')
|
toast.error(err.response?.data?.error?.message || err.response?.data?.message || 'Failed to delete')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -119,6 +119,8 @@
|
|||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { operatingsystemsApi } from '../../api'
|
import { operatingsystemsApi } from '../../api'
|
||||||
import PaginationBar from '../../components/PaginationBar.vue'
|
import PaginationBar from '../../components/PaginationBar.vue'
|
||||||
|
import { useToast } from '../../composables/toast'
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const items = ref([])
|
const items = ref([])
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
@@ -206,7 +208,7 @@ async function deleteItem() {
|
|||||||
toDelete.value = null
|
toDelete.value = null
|
||||||
loadData()
|
loadData()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert('Failed to delete')
|
toast.error('Failed to delete')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -86,6 +86,8 @@ import { ref, computed, onMounted } from 'vue'
|
|||||||
import { computersApi } from '../../api'
|
import { computersApi } from '../../api'
|
||||||
import ColorSwatchPicker from '@/components/ColorSwatchPicker.vue'
|
import ColorSwatchPicker from '@/components/ColorSwatchPicker.vue'
|
||||||
import { colorStyle } from '@/utils/colorStyle'
|
import { colorStyle } from '@/utils/colorStyle'
|
||||||
|
import { useToast } from '../../composables/toast'
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const pcTypes = ref([])
|
const pcTypes = ref([])
|
||||||
const showInactive = ref(false)
|
const showInactive = ref(false)
|
||||||
@@ -130,7 +132,7 @@ async function deleteType(pt) {
|
|||||||
await computersApi.types.remove(pt.computertypeid)
|
await computersApi.types.remove(pt.computertypeid)
|
||||||
loadData()
|
loadData()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert(err.response?.data?.error?.message || err.response?.data?.message || 'Failed to delete')
|
toast.error(err.response?.data?.error?.message || err.response?.data?.message || 'Failed to delete')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -97,6 +97,8 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
import { printersApi } from '../../api'
|
import { printersApi } from '../../api'
|
||||||
|
import { useToast } from '../../composables/toast'
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const items = ref([])
|
const items = ref([])
|
||||||
const models = ref([])
|
const models = ref([])
|
||||||
@@ -171,7 +173,7 @@ async function deleteDriver(d) {
|
|||||||
await printersApi.drivers.delete(d.driverid)
|
await printersApi.drivers.delete(d.driverid)
|
||||||
loadData()
|
loadData()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert(err.response?.data?.error?.message || err.response?.data?.message || 'Failed to delete')
|
toast.error(err.response?.data?.error?.message || err.response?.data?.message || 'Failed to delete')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ import { ref, computed, onMounted } from 'vue'
|
|||||||
import { printersApi } from '../../api'
|
import { printersApi } from '../../api'
|
||||||
import ColorSwatchPicker from '@/components/ColorSwatchPicker.vue'
|
import ColorSwatchPicker from '@/components/ColorSwatchPicker.vue'
|
||||||
import { colorStyle } from '@/utils/colorStyle'
|
import { colorStyle } from '@/utils/colorStyle'
|
||||||
|
import { useToast } from '../../composables/toast'
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const items = ref([])
|
const items = ref([])
|
||||||
const showInactive = ref(false)
|
const showInactive = ref(false)
|
||||||
@@ -136,7 +138,7 @@ async function deleteType(t) {
|
|||||||
await printersApi.types.remove(t.printertypeid)
|
await printersApi.types.remove(t.printertypeid)
|
||||||
loadData()
|
loadData()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert(err.response?.data?.error?.message || err.response?.data?.message || 'Failed to delete')
|
toast.error(err.response?.data?.error?.message || err.response?.data?.message || 'Failed to delete')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ import { ref, computed, onMounted } from 'vue'
|
|||||||
import { relationshipTypesApi } from '../../api'
|
import { relationshipTypesApi } from '../../api'
|
||||||
import ColorSwatchPicker from '@/components/ColorSwatchPicker.vue'
|
import ColorSwatchPicker from '@/components/ColorSwatchPicker.vue'
|
||||||
import { colorStyle } from '@/utils/colorStyle'
|
import { colorStyle } from '@/utils/colorStyle'
|
||||||
|
import { useToast } from '../../composables/toast'
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const items = ref([])
|
const items = ref([])
|
||||||
const showInactive = ref(false)
|
const showInactive = ref(false)
|
||||||
@@ -136,7 +138,7 @@ async function deleteType(t) {
|
|||||||
await relationshipTypesApi.remove(t.relationshiptypeid)
|
await relationshipTypesApi.remove(t.relationshiptypeid)
|
||||||
loadData()
|
loadData()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert(err.response?.data?.error?.message || err.response?.data?.message || 'Failed to delete')
|
toast.error(err.response?.data?.error?.message || err.response?.data?.message || 'Failed to delete')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -139,6 +139,8 @@ import { ref, onMounted } from 'vue'
|
|||||||
import { assetsApi } from '../../api'
|
import { assetsApi } from '../../api'
|
||||||
import PaginationBar from '../../components/PaginationBar.vue'
|
import PaginationBar from '../../components/PaginationBar.vue'
|
||||||
import ColorSwatchPicker from '@/components/ColorSwatchPicker.vue'
|
import ColorSwatchPicker from '@/components/ColorSwatchPicker.vue'
|
||||||
|
import { useToast } from '../../composables/toast'
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const statuses = ref([])
|
const statuses = ref([])
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
@@ -251,7 +253,7 @@ async function deleteStatus() {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error deleting status:', err)
|
console.error('Error deleting status:', err)
|
||||||
error.value = err.response?.data?.message || 'Failed to delete status'
|
error.value = err.response?.data?.message || 'Failed to delete status'
|
||||||
alert(error.value)
|
toast.error(error.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -285,6 +285,8 @@ import { ref, onMounted } from 'vue'
|
|||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { networkApi, locationsApi } from '../../api'
|
import { networkApi, locationsApi } from '../../api'
|
||||||
import PaginationBar from '../../components/PaginationBar.vue'
|
import PaginationBar from '../../components/PaginationBar.vue'
|
||||||
|
import { useToast } from '../../composables/toast'
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
@@ -489,7 +491,7 @@ async function deleteSubnet() {
|
|||||||
loadSubnets()
|
loadSubnets()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error deleting subnet:', err)
|
console.error('Error deleting subnet:', err)
|
||||||
alert(err.response?.data?.message || 'Failed to delete subnet')
|
toast.error(err.response?.data?.message || 'Failed to delete subnet')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -187,6 +187,8 @@
|
|||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { networkApi } from '../../api'
|
import { networkApi } from '../../api'
|
||||||
import PaginationBar from '../../components/PaginationBar.vue'
|
import PaginationBar from '../../components/PaginationBar.vue'
|
||||||
|
import { useToast } from '../../composables/toast'
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const vlans = ref([])
|
const vlans = ref([])
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
@@ -326,7 +328,7 @@ async function deleteVLAN() {
|
|||||||
loadVLANs()
|
loadVLANs()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error deleting VLAN:', err)
|
console.error('Error deleting VLAN:', err)
|
||||||
alert(err.response?.data?.message || 'Failed to delete VLAN')
|
toast.error(err.response?.data?.message || 'Failed to delete VLAN')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -168,6 +168,8 @@ import { ref, onMounted } from 'vue'
|
|||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { usbApi } from '../../api'
|
import { usbApi } from '../../api'
|
||||||
import Modal from '../../components/Modal.vue'
|
import Modal from '../../components/Modal.vue'
|
||||||
|
import { useToast } from '../../composables/toast'
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
@@ -217,7 +219,7 @@ async function doCheckout() {
|
|||||||
await loadDevice()
|
await loadDevice()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Checkout error:', error)
|
console.error('Checkout error:', error)
|
||||||
alert(error.response?.data?.message || 'Checkout failed')
|
toast.error(error.response?.data?.message || 'Checkout failed')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,7 +230,7 @@ async function doCheckin() {
|
|||||||
await loadDevice()
|
await loadDevice()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Checkin error:', error)
|
console.error('Checkin error:', error)
|
||||||
alert(error.response?.data?.message || 'Check in failed')
|
toast.error(error.response?.data?.message || 'Check in failed')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -147,6 +147,8 @@ import { usbApi } from '../../api'
|
|||||||
import Modal from '../../components/Modal.vue'
|
import Modal from '../../components/Modal.vue'
|
||||||
import EmployeeSearch from '../../components/EmployeeSearch.vue'
|
import EmployeeSearch from '../../components/EmployeeSearch.vue'
|
||||||
import PaginationBar from '../../components/PaginationBar.vue'
|
import PaginationBar from '../../components/PaginationBar.vue'
|
||||||
|
import { useToast } from '../../composables/toast'
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const devices = ref([])
|
const devices = ref([])
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
@@ -241,7 +243,7 @@ async function doCheckout() {
|
|||||||
loadDevices()
|
loadDevices()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Checkout error:', error)
|
console.error('Checkout error:', error)
|
||||||
alert(error.response?.data?.message || 'Checkout failed')
|
toast.error(error.response?.data?.message || 'Checkout failed')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,7 +254,7 @@ async function doCheckin() {
|
|||||||
loadDevices()
|
loadDevices()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Checkin error:', error)
|
console.error('Checkin error:', error)
|
||||||
alert(error.response?.data?.message || 'Check in failed')
|
toast.error(error.response?.data?.message || 'Check in failed')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
4
frontend/src/views/vendors/VendorsList.vue
vendored
4
frontend/src/views/vendors/VendorsList.vue
vendored
@@ -186,6 +186,8 @@
|
|||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { vendorsApi } from '../../api'
|
import { vendorsApi } from '../../api'
|
||||||
import PaginationBar from '../../components/PaginationBar.vue'
|
import PaginationBar from '../../components/PaginationBar.vue'
|
||||||
|
import { useToast } from '../../composables/toast'
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const vendors = ref([])
|
const vendors = ref([])
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
@@ -321,7 +323,7 @@ async function deleteVendor() {
|
|||||||
loadVendors()
|
loadVendors()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error deleting vendor:', err)
|
console.error('Error deleting vendor:', err)
|
||||||
alert('Failed to delete vendor')
|
toast.error('Failed to delete vendor')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
<button class="btn btn-primary" @click="openModal()">+ Add Warranty</button>
|
<button class="btn btn-primary" @click="openModal()">+ Add Warranty</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="filters">
|
<div class="filters">
|
||||||
<label>Status
|
<label>Status
|
||||||
<select v-model="statusFilter" class="form-control" @change="loadData">
|
<select v-model="statusFilter" class="form-control" @change="loadData">
|
||||||
@@ -138,6 +139,9 @@
|
|||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { colorStyle } from '@/utils/colorStyle'
|
import { colorStyle } from '@/utils/colorStyle'
|
||||||
import { warrantyApi, assetsApi } from '../../api'
|
import { warrantyApi, assetsApi } from '../../api'
|
||||||
|
import { useToast } from '../../composables/toast'
|
||||||
|
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
const items = ref([])
|
const items = ref([])
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
@@ -261,7 +265,7 @@ async function deleteWarranty(w) {
|
|||||||
await warrantyApi.remove(w.warrantyid)
|
await warrantyApi.remove(w.warrantyid)
|
||||||
loadData()
|
loadData()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert(apiError(err, 'Failed to delete'))
|
toast.error(apiError(err, 'Failed to delete'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,9 +274,11 @@ async function refresh(w) {
|
|||||||
const response = await warrantyApi.refresh(w.warrantyid)
|
const response = await warrantyApi.refresh(w.warrantyid)
|
||||||
loadData()
|
loadData()
|
||||||
const updated = response.data?.data
|
const updated = response.data?.data
|
||||||
if (updated) alert(`Refreshed: ${updated.vendor} - ${updated.servicelevel || 'coverage'} ends ${updated.enddate || 'unknown'}`)
|
if (updated) {
|
||||||
|
toast.success(`${updated.vendor}: ${updated.servicelevel || 'coverage'} ends ${updated.enddate || 'unknown'}`)
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert(apiError(err, 'Refresh failed'))
|
toast.error(apiError(err, 'Refresh failed'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user