mirror of
https://bitbucket.org/studiotem/jandl-taskmanager-app.git
synced 2026-10-21 14:29:34 +00:00
Merge branch 'main' of https://bitbucket.org/studiotem/jandl-taskmanager-app into production
This commit is contained in:
commit
b00b49df6d
5 files changed
+103
-85
No files matched your search
@@ -106,8 +106,11 @@ input[type="time"]::-webkit-calendar-picker-indicator
|
|||||||
height: 40px !important
|
height: 40px !important
|
||||||
|
|
||||||
.server-links
|
.server-links
|
||||||
|
background: rgba(127, 57, 251, 1)
|
||||||
|
padding: 4px 5px
|
||||||
|
|
||||||
a
|
a
|
||||||
text-decoration: underline
|
color: #fff
|
||||||
|
|
||||||
@media (max-width: 534px)
|
@media (max-width: 534px)
|
||||||
.mobile-view
|
.mobile-view
|
||||||
|
|||||||
@@ -1,47 +1,13 @@
|
|||||||
export function getServerLinks(description) {
|
import { useToast } from 'vue-toast-notification';
|
||||||
const os = detectOS()
|
const toast = useToast();
|
||||||
|
|
||||||
// Create a temporary DOM element to manipulate the HTML content
|
declare global {
|
||||||
const tempDiv = document.createElement('div')
|
interface Window {
|
||||||
tempDiv.innerHTML = description
|
toast: typeof toast;
|
||||||
|
|
||||||
const links = new Set()
|
|
||||||
const paragraphs = tempDiv.querySelectorAll('p')
|
|
||||||
|
|
||||||
paragraphs.forEach(p => {
|
|
||||||
const text = p.textContent.trim()
|
|
||||||
|
|
||||||
if (os === 'mac' && text.startsWith('smb://')) {
|
|
||||||
links.add(`<a href="${text}" target="_blank" rel="noopener noreferrer">${text}</a>`)
|
|
||||||
} else if (os === 'win' && text.startsWith('\\\\')) {
|
|
||||||
links.add(`<a href="file://${text.replace(/\\/g, '/')}" target="_blank" rel="noopener noreferrer">${text}</a>`)
|
|
||||||
} else if (os === 'mac' && text.startsWith('\\\\')) {
|
|
||||||
const smbLink = 'smb:' + text.replace(/\\/g, '/')
|
|
||||||
links.add(`<a href="${smbLink}" target="_blank" rel="noopener noreferrer">${smbLink}</a>`)
|
|
||||||
} else if (os === 'win' && text.startsWith('smb://')) {
|
|
||||||
const windowsLink = text.replace(/^smb:/, '').replace(/\//g, '\\')
|
|
||||||
links.add(`<a href="file://${windowsLink}" target="_blank" rel="noopener noreferrer">${windowsLink}</a>`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const anchor = p.querySelector('a')
|
|
||||||
if (anchor) {
|
|
||||||
const href = anchor.getAttribute('href')
|
|
||||||
if (os === 'mac' && href.startsWith('smb://')) {
|
|
||||||
links.add(`<a href="${href}" target="_blank" rel="noopener noreferrer">${href}</a>`)
|
|
||||||
} else if (os === 'win' && href.startsWith('\\\\')) {
|
|
||||||
links.add(`<a href="file://${href.replace(/\\/g, '/')}" target="_blank" rel="noopener noreferrer">${href}</a>`)
|
|
||||||
} else if (os === 'mac' && href.startsWith('\\\\')) {
|
|
||||||
const smbLink = 'smb:' + href.replace(/\\/g, '/')
|
|
||||||
links.add(`<a href="${smbLink}" target="_blank" rel="noopener noreferrer">${smbLink}</a>`)
|
|
||||||
} else if (os === 'win' && href.startsWith('smb://')) {
|
|
||||||
const windowsLink = href.replace(/^smb:/, '').replace(/\//g, '\\')
|
|
||||||
links.add(`<a href="file://${windowsLink}" target="_blank" rel="noopener noreferrer">${windowsLink}</a>`)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
return Array.from(links).join('<br>')
|
window.toast = toast;
|
||||||
}
|
|
||||||
|
|
||||||
export function detectOS() {
|
export function detectOS() {
|
||||||
const platform = window.navigator.platform
|
const platform = window.navigator.platform
|
||||||
@@ -55,3 +21,69 @@ export function detectOS() {
|
|||||||
}
|
}
|
||||||
return 'win'
|
return 'win'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function prepareHTMLDescription(description) {
|
||||||
|
if (!description) return description
|
||||||
|
|
||||||
|
const os = detectOS()
|
||||||
|
|
||||||
|
// make an array from the description of strings from between <p> and </p> tags
|
||||||
|
const paragraphs = description.split('<p>').map(p => p.split('</p>')[0])
|
||||||
|
|
||||||
|
const modifiedParagraphs = paragraphs.map(paragraph => {
|
||||||
|
const words = paragraph.split(' ')
|
||||||
|
|
||||||
|
const modifiedWords = words.map(word => {
|
||||||
|
let link = null
|
||||||
|
|
||||||
|
if (os === 'mac' && word.startsWith('smb://')) {
|
||||||
|
link = word
|
||||||
|
} else if (os === 'win' && word.startsWith('\\\\')) {
|
||||||
|
link = `${word.replace(/\//g, '\\')}`
|
||||||
|
} else if (os === 'mac' && word.startsWith('\\\\')) {
|
||||||
|
const smbLink = 'smb:' + word.replace(/\\/g, '/')
|
||||||
|
link = smbLink
|
||||||
|
} else if (os === 'win' && word.startsWith('smb://')) {
|
||||||
|
const windowsLink = word.replace(/^smb:/, '').replace(/\//g, '\\')
|
||||||
|
link = `${windowsLink}`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!link) return word
|
||||||
|
|
||||||
|
if (os === 'mac') return `<a href="${link}">${link}</a>`
|
||||||
|
|
||||||
|
return `<span style="cursor: pointer; text-decoration: underline" onclick="${getCopyFunction(link)}">${link}</span>`
|
||||||
|
})
|
||||||
|
|
||||||
|
return `<p>${modifiedWords.join(' ')}</p>`
|
||||||
|
})
|
||||||
|
|
||||||
|
description = modifiedParagraphs.join('')
|
||||||
|
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCopyFunction(text) {
|
||||||
|
return `
|
||||||
|
console.log('${text.replace(/\\/g, '\\\\')}')
|
||||||
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||||
|
navigator.clipboard.writeText('${text.replace(/\\/g, '\\\\')}').then(() => {
|
||||||
|
window.toast.success('Link copied to clipboard!')
|
||||||
|
}).catch(err => {
|
||||||
|
window.toast.error('Failed to copy link to clipboard!')
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const input = document.createElement('input');
|
||||||
|
input.style.position = 'fixed';
|
||||||
|
input.style.opacity = 0;
|
||||||
|
input.style.pointerEvents = 'none';
|
||||||
|
input.value = '${text.replace(/\\/g, '\\\\')}';
|
||||||
|
document.body.appendChild(input);
|
||||||
|
input.select();
|
||||||
|
input.focus();
|
||||||
|
document.execCommand('copy');
|
||||||
|
document.body.removeChild(input);
|
||||||
|
window.toast.success('Link copied to clipboard!')
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}
|
||||||
@@ -1,22 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="px-5 py-5 border-bottom" v-if="description" >
|
<div class="px-5 py-5 border-bottom" v-if="description" >
|
||||||
<p>Popis zadania</p>
|
<p>Popis zadania</p>
|
||||||
<section v-html="description" class="description-detail px-2 py-2 w-100 rounded mt-2" />
|
<section v-html="preparedDescription" class="description-detail px-2 py-2 w-100 rounded mt-2" />
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="hasMasterTaskDescLinks" class="px-5">
|
|
||||||
<p class="py-3">Odkazy na server</p>
|
|
||||||
<p class="server-links" v-html="masterTaskDescLinks" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="px-5 py-5 border-bottom" v-if="inheritedDescription">
|
<div class="px-5 py-5 border-bottom" v-if="inheritedDescription">
|
||||||
<p>Popis mastertasku</p>
|
<p>Popis mastertasku</p>
|
||||||
<section v-html="inheritedDescription" class="description-detail px-2 py-2 w-100 rounded mt-2" />
|
<section v-html="preparedInheritedDescription" class="description-detail px-2 py-2 w-100 rounded mt-2" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getServerLinks } from '@/plugins/tm@_utils/server-links.utils'
|
import { prepareHTMLDescription } from '@/plugins/tm@_utils/server-links.utils'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
@@ -31,14 +26,17 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
data() {
|
||||||
masterTaskDescLinks() {
|
return {
|
||||||
return getServerLinks(this.description)
|
copiedIndices: [],
|
||||||
|
preparedDescription: '',
|
||||||
|
preparedInheritedDescription: ''
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
hasMasterTaskDescLinks() {
|
mounted() {
|
||||||
return this.masterTaskDescLinks.trim().length > 0
|
this.preparedDescription = prepareHTMLDescription(this.description)
|
||||||
}
|
this.preparedInheritedDescription = prepareHTMLDescription(this.inheritedDescription)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -298,7 +298,7 @@ export default {
|
|||||||
async save() {
|
async save() {
|
||||||
if (this.mastertask.id) {
|
if (this.mastertask.id) {
|
||||||
this.mastertask.employees = this.mastertask.employees.map(employee => {
|
this.mastertask.employees = this.mastertask.employees.map(employee => {
|
||||||
const { duration } = this.mastertask.subtasks.find(({ employee_id }) => employee_id == employee.id)
|
const { duration } = this.mastertask.subtasks.find(({ employee_id }) => employee_id == employee.id) || {}
|
||||||
employee.duration = duration
|
employee.duration = duration
|
||||||
return employee
|
return employee
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,27 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="px-5 py-5 border-bottom" v-if="description" >
|
<div class="px-5 py-5 border-bottom" v-if="description" >
|
||||||
<p>Popis zadania</p>
|
<p>Popis zadania</p>
|
||||||
<section v-html="description" class="description-detail px-2 py-2 w-100 rounded mt-2" />
|
<section v-html="preparedDescription" class="description-detail px-2 py-2 w-100 rounded mt-2" />
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="hasSubtaskDescLinks" class="px-5">
|
|
||||||
<p class="py-3">Odkazy na server</p>
|
|
||||||
<p class="server-links" v-html="subtaskDescLinks" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="px-5 py-5 border-bottom" v-if="mastertaskDescription">
|
<div class="px-5 py-5 border-bottom" v-if="mastertaskDescription">
|
||||||
<p>Popis mastertasku</p>
|
<p>Popis mastertasku</p>
|
||||||
<section v-html="mastertaskDescription" class="description-detail px-2 py-2 w-100 rounded mt-2" />
|
<section v-html="preparedMastertaskDescription" class="description-detail px-2 py-2 w-100 rounded mt-2" />
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="hasSubtaskMTDescLinks" class="px-5">
|
|
||||||
<p class="py-3">Odkazy na server</p>
|
|
||||||
<p class="server-links" v-html="subtaskMTDecsLinks" />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getServerLinks } from '@/plugins/tm@_utils/server-links.utils'
|
import { prepareHTMLDescription } from '@/plugins/tm@_utils/server-links.utils'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
@@ -36,22 +26,17 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
data() {
|
||||||
subtaskDescLinks() {
|
return {
|
||||||
return getServerLinks(this.description)
|
copiedIndices: [],
|
||||||
},
|
preparedDescription: '',
|
||||||
|
preparedMastertaskDescription: ''
|
||||||
subtaskMTDecsLinks() {
|
|
||||||
return getServerLinks(this.mastertaskDescription)
|
|
||||||
},
|
|
||||||
|
|
||||||
hasSubtaskDescLinks() {
|
|
||||||
return this.subtaskDescLinks.trim().length > 0
|
|
||||||
},
|
|
||||||
|
|
||||||
hasSubtaskMTDescLinks() {
|
|
||||||
return this.subtaskMTDecsLinks.trim().length > 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.preparedDescription = prepareHTMLDescription(this.description)
|
||||||
|
this.preparedMastertaskDescription = prepareHTMLDescription(this.mastertaskDescription)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
Reference in new issue
Block a user