Made things do things.
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
"axios": "^0.25.0",
|
||||
"bootstrap": "^5.1.3",
|
||||
"bootstrap-icons": "^1.7.2",
|
||||
"lodash": "^4.17.21",
|
||||
"vue": "^3.2.25",
|
||||
"vue-router": "^4.0.12",
|
||||
"vuex": "^4.0.2"
|
||||
|
@@ -1,6 +1,11 @@
|
||||
<template>
|
||||
<nav-menu />
|
||||
<header>
|
||||
<nav-menu />
|
||||
<admin-menu />
|
||||
</header>
|
||||
<data-alert />
|
||||
<confirm-alert ref="ca" />
|
||||
<div class="mb-3"></div>
|
||||
<div v-if="isLocal" class="container">
|
||||
<div class="alert alert-danger">
|
||||
<h4 class="alert-heading">Lokali sistemą aptikta</h4>
|
||||
@@ -20,20 +25,26 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<transition name="slide-fade">
|
||||
<router-view />
|
||||
</transition>
|
||||
<router-view v-slot="{ Component }">
|
||||
<transition name="slide-fade" mode="out-in">
|
||||
<component :is="Component" />
|
||||
</transition>
|
||||
</router-view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import NavMenu from './components/NavMenu.vue'
|
||||
import DataAlert from './components/DataAlert.vue'
|
||||
import ConfirmAlert from './components/ConfirmAlert.vue'
|
||||
import AdminMenu from './components/AdminMenu.vue'
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
components: {
|
||||
NavMenu,
|
||||
DataAlert,
|
||||
ConfirmAlert,
|
||||
AdminMenu,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -54,6 +65,36 @@ export default {
|
||||
return location.protocol !== 'https:'
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async confirm(text, title, options = {}) {
|
||||
const passedOptions = Object.assign(
|
||||
{ text, title, affirmitive: {}, negative: {} },
|
||||
options
|
||||
)
|
||||
await new Promise((res, rej) => {
|
||||
passedOptions.affirmitive.action = res
|
||||
passedOptions.negative.action = () =>
|
||||
rej(new Error('User rejected confirmation.'))
|
||||
this.$refs.ca.configure(passedOptions)
|
||||
this.$refs.ca.show()
|
||||
})
|
||||
},
|
||||
async deleteConfirmation() {
|
||||
await this.confirm(
|
||||
'Ar tikrai norite ištrinti šį elementą?',
|
||||
'Ištrinimo patvirtinimas',
|
||||
{
|
||||
affirmitive: {
|
||||
text: 'Ištrinti',
|
||||
type: 'danger',
|
||||
},
|
||||
negative: {
|
||||
text: 'Atšaukti',
|
||||
},
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@@ -7,17 +7,11 @@
|
||||
/* Enter and leave animations can use different */
|
||||
/* durations and timing functions. */
|
||||
.slide-fade-enter-active {
|
||||
transition: all 0.3s ease-out;
|
||||
transition: all 0.15s ease-out;
|
||||
}
|
||||
|
||||
.slide-fade-leave-active {
|
||||
transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
|
||||
}
|
||||
|
||||
.slide-fade-leave-active,
|
||||
.slide-fade-enter-active {
|
||||
position: relative;
|
||||
top: 0;
|
||||
transition: all 0.15s cubic-bezier(1, 0.5, 0.8, 1);
|
||||
}
|
||||
|
||||
.slide-fade-enter-from,
|
||||
@@ -31,3 +25,23 @@
|
||||
transform: translateX(0px);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.fade-enter-active {
|
||||
transition: all 0.3s ease-out;
|
||||
}
|
||||
|
||||
.fade-leave-active {
|
||||
transition: all 0.3s cubic-bezier(1, 0.5, 0.8, 1);
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
transform: scale(0);
|
||||
}
|
||||
|
||||
.fade-enter-to,
|
||||
.fade-leave-from {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
58
KTUSAPS/ClientApp/src/components/AdminMenu.vue
Normal file
58
KTUSAPS/ClientApp/src/components/AdminMenu.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<nav
|
||||
v-if="$store.state.msalAuth.isAdmin"
|
||||
class="navbar navbar-expand-lg navbar-dark bg-black"
|
||||
>
|
||||
<div class="container-fluid">
|
||||
<button
|
||||
class="navbar-toggler"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#adminbarNav"
|
||||
aria-controls="navbarNav"
|
||||
aria-expanded="false"
|
||||
aria-label="Toggle navigation"
|
||||
>
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div
|
||||
class="collapse navbar-collapse"
|
||||
:class="{ show: isExpanded }"
|
||||
id="adminbarNav"
|
||||
>
|
||||
<ul class="navbar-nav me-auto">
|
||||
<li class="nav-item">
|
||||
<router-link :to="{ name: 'Issues' }" class="nav-link"
|
||||
>Pateiktos problemos</router-link
|
||||
>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<router-link :to="{ name: 'AdminIssueTypes' }" class="nav-link"
|
||||
>Problemų tipai</router-link
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AdminMenu',
|
||||
data() {
|
||||
return {
|
||||
isExpanded: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
collapse() {
|
||||
this.isExpanded = false
|
||||
},
|
||||
|
||||
toggle() {
|
||||
this.isExpanded = !this.isExpanded
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
67
KTUSAPS/ClientApp/src/components/Alerter.vue
Normal file
67
KTUSAPS/ClientApp/src/components/Alerter.vue
Normal file
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<transition name="fade">
|
||||
<div
|
||||
v-if="shown"
|
||||
class="alert"
|
||||
:class="{ ['alert-' + type]: true }"
|
||||
role="alert"
|
||||
>
|
||||
<h4 v-if="title" class="alert-heading">{{ title }}</h4>
|
||||
{{ text }}
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import defaults from 'lodash/defaults'
|
||||
|
||||
const defaultOptions = {
|
||||
title: null,
|
||||
text: 'You have not configured alerter.',
|
||||
type: 'danger',
|
||||
}
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
shown: false,
|
||||
text: 'A simple alert',
|
||||
title: null,
|
||||
type: 'primary',
|
||||
dismisstimer: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
show() {
|
||||
this.shown = true
|
||||
},
|
||||
hide() {
|
||||
this.shown = false
|
||||
this.cancelAutoDismiss()
|
||||
},
|
||||
cancelAutoDismiss() {
|
||||
if (this.dismisstimer) clearTimeout(this.dismisstimer)
|
||||
this.dismisstimer = null
|
||||
},
|
||||
autoDismiss(timeout) {
|
||||
this.cancelAutoDismiss()
|
||||
this.dismisstimer = setTimeout(() => this.hide(), timeout)
|
||||
},
|
||||
configure(options) {
|
||||
const final = defaults(options, defaultOptions)
|
||||
this.text = final.text
|
||||
this.title = final.title
|
||||
this.type = final.type
|
||||
},
|
||||
alert(options) {
|
||||
this.configure(options)
|
||||
this.cancelAutoDismiss()
|
||||
this.show()
|
||||
},
|
||||
alertTimed(options, timeout = 4000) {
|
||||
this.alert(options)
|
||||
this.autoDismiss(timeout)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
98
KTUSAPS/ClientApp/src/components/ConfirmAlert.vue
Normal file
98
KTUSAPS/ClientApp/src/components/ConfirmAlert.vue
Normal file
@@ -0,0 +1,98 @@
|
||||
<template>
|
||||
<div ref="modal" class="modal fade" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">{{ options.title }}</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>
|
||||
{{ options.text }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="btn"
|
||||
:class="'btn-' + options.affirmitive.type"
|
||||
@click="accept"
|
||||
>
|
||||
{{ options.affirmitive.text }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn"
|
||||
:class="'btn-' + options.negative.type"
|
||||
@click="reject"
|
||||
>
|
||||
{{ options.negative.text }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import defaultsDeep from 'lodash/defaultsDeep'
|
||||
import { Modal } from 'bootstrap'
|
||||
|
||||
const defaultValues = {
|
||||
title: 'Confirmation',
|
||||
text: 'Do you confirm this action?',
|
||||
affirmitive: {
|
||||
hide: false,
|
||||
text: 'Yes',
|
||||
type: 'primary',
|
||||
action: () => null,
|
||||
},
|
||||
negative: {
|
||||
hide: false,
|
||||
text: 'No',
|
||||
type: 'secondary',
|
||||
action: () => null,
|
||||
},
|
||||
}
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
modal: null,
|
||||
beenUsed: false,
|
||||
options: Object.assign({}, defaultValues),
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
var el = this.$refs.modal
|
||||
el.addEventListener('hide.bs.modal', () => this.handleHideEvent())
|
||||
this.modal = new Modal(el, {})
|
||||
},
|
||||
methods: {
|
||||
handleHideEvent() {
|
||||
if (!this.beenUsed) {
|
||||
this.options.negative.action()
|
||||
}
|
||||
},
|
||||
show() {
|
||||
this.beenUsed = false
|
||||
this.modal.show()
|
||||
},
|
||||
hide() {
|
||||
this.modal.hide()
|
||||
},
|
||||
accept() {
|
||||
this.beenUsed = true
|
||||
this.options.affirmitive.action()
|
||||
this.hide()
|
||||
},
|
||||
reject() {
|
||||
this.beenUsed = true
|
||||
this.options.negative.action()
|
||||
this.hide()
|
||||
},
|
||||
configure(options) {
|
||||
this.options = defaultsDeep({}, options || {}, defaultValues)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
@@ -1,73 +1,68 @@
|
||||
<template>
|
||||
<header class="mb-3">
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
||||
<div class="container-fluid">
|
||||
<router-link :to="{ name: 'Home' }" class="navbar-brand"
|
||||
>KTU SA Problemų sprendimo sistema</router-link
|
||||
>
|
||||
<button
|
||||
class="navbar-toggler"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#navbarNav"
|
||||
aria-controls="navbarNav"
|
||||
aria-expanded="false"
|
||||
aria-label="Toggle navigation"
|
||||
>
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div
|
||||
class="collapse navbar-collapse"
|
||||
:class="{ show: isExpanded }"
|
||||
id="navbarNav"
|
||||
>
|
||||
<ul class="navbar-nav me-auto">
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
||||
<div class="container-fluid">
|
||||
<router-link :to="{ name: 'Home' }" class="navbar-brand"
|
||||
>KTU SA Problemų sprendimo sistema</router-link
|
||||
>
|
||||
<button
|
||||
class="navbar-toggler"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#navbarNav"
|
||||
aria-controls="navbarNav"
|
||||
aria-expanded="false"
|
||||
aria-label="Toggle navigation"
|
||||
>
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div
|
||||
class="collapse navbar-collapse"
|
||||
:class="{ show: isExpanded }"
|
||||
id="navbarNav"
|
||||
>
|
||||
<ul class="navbar-nav me-auto">
|
||||
<li class="nav-item">
|
||||
<router-link :to="{ name: 'Home' }" class="nav-link"
|
||||
>Pagrindinis</router-link
|
||||
>
|
||||
</li>
|
||||
<template v-if="$store.state.msalAuth.isLoggedIn">
|
||||
<li class="nav-item">
|
||||
<router-link :to="{ name: 'Home' }" class="nav-link"
|
||||
>Pagrindinis</router-link
|
||||
>
|
||||
</li>
|
||||
<li class="nav-item" v-if="$store.state.msalAuth.isLoggedIn">
|
||||
<router-link :to="{ name: 'Submit' }" class="nav-link"
|
||||
>Pateikti problemą</router-link
|
||||
>
|
||||
</li>
|
||||
<li class="nav-item" v-if="$store.state.msalAuth.isLoggedIn">
|
||||
<li class="nav-item">
|
||||
<router-link :to="{ name: 'Problems' }" class="nav-link"
|
||||
>Problemos</router-link
|
||||
>
|
||||
</li>
|
||||
<li class="nav-item" v-if="$store.state.msalAuth.isLoggedIn">
|
||||
<li class="nav-item">
|
||||
<router-link :to="{ name: 'Feedbacks' }" class="nav-link"
|
||||
>Atsiliepimai</router-link
|
||||
>
|
||||
</li>
|
||||
<li class="nav-item" v-if="$store.state.msalAuth.isAdmin">
|
||||
<router-link :to="{ name: 'Issues' }" class="nav-link"
|
||||
>Pateiktos problemos</router-link
|
||||
>
|
||||
</li>
|
||||
<li v-if="$root.isLocal" class="nav-item">
|
||||
<a href="/swagger" class="nav-link">Swagger UI</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="navbar-nav">
|
||||
<template v-if="$store.state.msalAuth.isLoggedIn">
|
||||
<span class="navbar-text"
|
||||
>Prisijungta kaip {{ $store.state.msalAuth.displayName }}
|
||||
</span>
|
||||
<div class="nav-item">
|
||||
<a href="#" @click="LogoutMsal" class="nav-link">Atsijungti</a>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else class="nav-item">
|
||||
<a href="#" @click="LoginMsal" class="nav-link">Prisijungti</a>
|
||||
</template>
|
||||
<li v-if="$root.isLocal" class="nav-item">
|
||||
<a href="/swagger" class="nav-link">SUI</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="navbar-nav">
|
||||
<template v-if="$store.state.msalAuth.isLoggedIn">
|
||||
<span class="navbar-text"
|
||||
>Prisijungta kaip {{ $store.state.msalAuth.displayName }}
|
||||
</span>
|
||||
<div class="nav-item">
|
||||
<a href="#" @click="LogoutMsal" class="nav-link">Atsijungti</a>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else class="nav-item">
|
||||
<a href="#" @click="LoginMsal" class="nav-link">Prisijungti</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
</div>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
40
KTUSAPS/ClientApp/src/components/forms/FeedbackForm.vue
Normal file
40
KTUSAPS/ClientApp/src/components/forms/FeedbackForm.vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<div class="mb-3">
|
||||
<label for="feedbackLtTextArea" class="form-label"
|
||||
>Lietuviškas Aprašymas</label
|
||||
>
|
||||
<textarea
|
||||
v-model="feedbackLt"
|
||||
class="form-control"
|
||||
id="feedbackLtTextArea"
|
||||
rows="4"
|
||||
></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="feedbackEnTextArea" class="form-label"
|
||||
>Angliškas Aprašymas</label
|
||||
>
|
||||
<textarea
|
||||
v-model="feedbackEn"
|
||||
class="form-control"
|
||||
id="feedbackEnTextArea"
|
||||
rows="4"
|
||||
></textarea>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapModel } from './formHelpers'
|
||||
export default {
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
computed: {
|
||||
...mapModel(['feedbackLt', 'feedbackEn']),
|
||||
},
|
||||
}
|
||||
</script>
|
27
KTUSAPS/ClientApp/src/components/forms/IssueTypeForm.vue
Normal file
27
KTUSAPS/ClientApp/src/components/forms/IssueTypeForm.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<div class="mb-3">
|
||||
<label for="nameInput" class="form-label">Lietuviškas pavadinimas</label>
|
||||
<input v-model="name" type="text" class="form-control" id="nameInput" />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="nameEnInput" class="form-label">Angliškas pavadinimas</label>
|
||||
<input v-model="nameEn" type="text" class="form-control" id="nameEnInput" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapModel } from './formHelpers'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
computed: {
|
||||
...mapModel(['name', 'nameEn']),
|
||||
},
|
||||
}
|
||||
</script>
|
27
KTUSAPS/ClientApp/src/components/forms/formHelpers.js
Normal file
27
KTUSAPS/ClientApp/src/components/forms/formHelpers.js
Normal file
@@ -0,0 +1,27 @@
|
||||
export function autoComputed(propName, defaultValue) {
|
||||
return {
|
||||
get() {
|
||||
return this.modelValue[propName] || defaultValue
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('update:modelValue', {
|
||||
...this.modelValue,
|
||||
[propName]: value,
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function mapModel(props) {
|
||||
const computedValues = {}
|
||||
if (Array.isArray(props)) {
|
||||
props.forEach((propName) => {
|
||||
computedValues[propName] = autoComputed(propName, '')
|
||||
})
|
||||
} else {
|
||||
Object.entries(props).forEach(([propName, defaultValue]) => {
|
||||
computedValues[propName] = autoComputed(propName, defaultValue)
|
||||
})
|
||||
}
|
||||
return computedValues
|
||||
}
|
@@ -11,4 +11,4 @@ const app = createApp(App)
|
||||
app.use(router)
|
||||
app.use(store)
|
||||
|
||||
app.mount('#app')
|
||||
app.mount('#app')
|
||||
|
@@ -21,6 +21,7 @@ const msalState = {
|
||||
displayName: null,
|
||||
|
||||
debugFullTokenResponse: null,
|
||||
msalRefreshTimer: null,
|
||||
}
|
||||
|
||||
async function initializeMSAL() {
|
||||
@@ -35,6 +36,7 @@ async function initializeMSAL() {
|
||||
redirectUri: window.location.protocol + '//' + window.location.host + '/',
|
||||
},
|
||||
}
|
||||
msalState.msalRefreshTimer = setInterval(__refreshToken, 10 * 60 * 1000)
|
||||
|
||||
msalState.msal = new msal.PublicClientApplication(msalConfig)
|
||||
|
||||
@@ -70,6 +72,22 @@ export function LogoutMsal() {
|
||||
msalState.msal.logout()
|
||||
}
|
||||
|
||||
async function __refreshToken() {
|
||||
if (!msalState.isLoggedIn) return
|
||||
msalState.debugFullTokenResponse = await msalState.msal
|
||||
.acquireTokenSilent({ scopes: RequestedScopes })
|
||||
.catch((error) => {
|
||||
if (error instanceof msal.InteractionRequiredAuthError) {
|
||||
// fallback to interaction when silent call fails
|
||||
return msalState.msal.acquireTokenRedirect({
|
||||
scopes: RequestedScopes,
|
||||
})
|
||||
}
|
||||
})
|
||||
__responseObjectToMsalState()
|
||||
__stateChanged()
|
||||
}
|
||||
|
||||
async function __handleResponse(response) {
|
||||
if (response !== null) {
|
||||
if (__isAccountAceptable(response.account)) {
|
||||
@@ -82,7 +100,7 @@ async function __handleResponse(response) {
|
||||
msalState.msal
|
||||
.getAllAccounts()
|
||||
.filter(__isAccountAceptable)
|
||||
.forEach(account => {
|
||||
.forEach((account) => {
|
||||
msalState.msal.setActiveAccount(account)
|
||||
})
|
||||
|
||||
@@ -90,7 +108,7 @@ async function __handleResponse(response) {
|
||||
if (account != null) {
|
||||
msalState.debugFullTokenResponse = await msalState.msal
|
||||
.acquireTokenSilent({ scopes: RequestedScopes })
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
if (error instanceof msal.InteractionRequiredAuthError) {
|
||||
// fallback to interaction when silent call fails
|
||||
return msalState.msal.acquireTokenRedirect({
|
||||
@@ -118,7 +136,7 @@ function __isAccountAceptable(account) {
|
||||
}
|
||||
|
||||
function __stateChanged() {
|
||||
msalState.stateChangeCallbacks.forEach(cb => cb())
|
||||
msalState.stateChangeCallbacks.forEach((cb) => cb())
|
||||
}
|
||||
|
||||
async function __loadAuthParameters() {
|
||||
|
@@ -66,6 +66,7 @@ export default {
|
||||
this.error = null
|
||||
this.ok = null
|
||||
try {
|
||||
await this.$root.deleteConfirmation()
|
||||
await authAxios.delete(`/api/Issues/${id}`)
|
||||
this.ok = 'Sėkmingai ištrintą'
|
||||
await this.fetchData()
|
||||
|
@@ -24,28 +24,7 @@
|
||||
</div>
|
||||
<div class="row justify-content-center">
|
||||
<div class="card col-lg-6 p-5">
|
||||
<div class="mb-3">
|
||||
<label for="problemLtTextArea" class="form-label"
|
||||
>Lietuviškas Aprašymas</label
|
||||
>
|
||||
<textarea
|
||||
v-model="feedback.feedbackLt"
|
||||
class="form-control"
|
||||
id="problemLtTextArea"
|
||||
rows="4"
|
||||
></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="problemEnTextArea" class="form-label"
|
||||
>Angliškas Aprašymas</label
|
||||
>
|
||||
<textarea
|
||||
v-model="feedback.feedbackEn"
|
||||
class="form-control"
|
||||
id="problemEnTextArea"
|
||||
rows="4"
|
||||
></textarea>
|
||||
</div>
|
||||
<feedback-form v-model="feedback" />
|
||||
<button @click="create" class="btn btn-primary btn-lg">
|
||||
Sukurti naują atsiliepimą
|
||||
</button>
|
||||
@@ -56,8 +35,12 @@
|
||||
|
||||
<script>
|
||||
import { axios, authAxios } from '@/axios'
|
||||
import FeedbackForm from '@/components/forms/FeedbackForm.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FeedbackForm,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
issue: null,
|
||||
|
15
KTUSAPS/ClientApp/src/pages/admin/Admin.vue
Normal file
15
KTUSAPS/ClientApp/src/pages/admin/Admin.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="container">
|
||||
<div class="alert alert-warning">
|
||||
<b>Dėmesio!</b> Tu esi administracinėje skiltyje, visą informacija
|
||||
pateikiama čia yra galimai konfidianciali.
|
||||
</div>
|
||||
</div>
|
||||
<router-view v-slot="{ Component }">
|
||||
<transition name="slide-fade" mode="out-in">
|
||||
<component :is="Component" />
|
||||
</transition>
|
||||
</router-view>
|
||||
</div>
|
||||
</template>
|
62
KTUSAPS/ClientApp/src/pages/admin/IssueTypeEdit.vue
Normal file
62
KTUSAPS/ClientApp/src/pages/admin/IssueTypeEdit.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<h1>Redaguoti problemos tipą</h1>
|
||||
<div class="row justify-content-center">
|
||||
<div class="card col-lg-6 p-5">
|
||||
<issue-type-form v-model="issueType" />
|
||||
<alerter ref="submitAlerter" />
|
||||
<button @click="update" class="btn btn-primary btn-lg">
|
||||
Išsaugoti
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { axios, authAxios } from '@/axios'
|
||||
import IssueTypeForm from '@/components/forms/IssueTypeForm.vue'
|
||||
import Alerter from '@/components/Alerter.vue'
|
||||
export default {
|
||||
components: {
|
||||
IssueTypeForm,
|
||||
Alerter,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
issueType: { name: '', nameEn: '' },
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
async fetchData() {
|
||||
const response = await axios.get(
|
||||
`/api/IssueTypes/${this.$route.params.id}`
|
||||
)
|
||||
this.issueType = response.data
|
||||
},
|
||||
async update() {
|
||||
try {
|
||||
if (!this.issueType.name || !this.issueType.nameEn) {
|
||||
this.$refs.submitAlerter.alertTimed({
|
||||
text: 'Problemos tipas turi turėti pavadinimus!!!',
|
||||
})
|
||||
return
|
||||
}
|
||||
await authAxios.patch(
|
||||
`/api/IssueTypes/${this.$route.params.id}`,
|
||||
this.issueType
|
||||
)
|
||||
this.$router.push({ name: 'AdminIssueTypes' })
|
||||
} catch (error) {
|
||||
this.$refs.submitAlerter.alert({
|
||||
title: 'Įvyko klaidą sukuriant tipą.',
|
||||
text: error,
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
54
KTUSAPS/ClientApp/src/pages/admin/IssueTypeNew.vue
Normal file
54
KTUSAPS/ClientApp/src/pages/admin/IssueTypeNew.vue
Normal file
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<h1>Naujas problemos tipas</h1>
|
||||
<div class="row justify-content-center">
|
||||
<div class="card col-lg-6 p-5">
|
||||
<issue-type-form v-model="issueType" />
|
||||
<alerter ref="submitAlerter" />
|
||||
<button @click="create" class="btn btn-primary btn-lg">
|
||||
Sukurti naują problemos tipą
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { authAxios } from '@/axios'
|
||||
import IssueTypeForm from '@/components/forms/IssueTypeForm.vue'
|
||||
import Alerter from '@/components/Alerter.vue'
|
||||
export default {
|
||||
components: {
|
||||
IssueTypeForm,
|
||||
Alerter,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
issueType: { name: '', nameEn: '' },
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async create() {
|
||||
try {
|
||||
if (!this.issueType.name || !this.issueType.nameEn) {
|
||||
this.$refs.submitAlerter.alertTimed({
|
||||
text: 'Problemos tipas turi turėti pavadinimus!!!',
|
||||
})
|
||||
return
|
||||
}
|
||||
await authAxios.post('/api/IssueTypes', this.issueType)
|
||||
this.$refs.submitAlerter.alertTimed({
|
||||
text: 'Sėkmingai sukurtas problemos tipas.',
|
||||
type: 'success',
|
||||
})
|
||||
this.issueType = {}
|
||||
} catch (error) {
|
||||
this.$refs.submitAlerter.alert({
|
||||
title: 'Įvyko klaidą sukuriant tipą.',
|
||||
text: error,
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
57
KTUSAPS/ClientApp/src/pages/admin/IssueTypes.vue
Normal file
57
KTUSAPS/ClientApp/src/pages/admin/IssueTypes.vue
Normal file
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<router-link
|
||||
:to="{ name: 'AdminIssueTypeNew' }"
|
||||
class="btn btn-primary mx-1"
|
||||
>Sukurti naują problemos tipą</router-link
|
||||
>
|
||||
<div class="card my-4" v-for="it in issueTypes" :key="it.id">
|
||||
<div class="row g-0">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ it.name }}</h5>
|
||||
<h5 class="card-title text-muted">{{ it.nameEn }}</h5>
|
||||
<button class="btn btn-danger mx-1" @click="deleteIssueType(it.id)">
|
||||
<i class="bi bi-trash-fill"></i>
|
||||
Ištrinti
|
||||
</button>
|
||||
<router-link
|
||||
:to="{ name: 'AdminIssueTypeEdit', params: { id: it.id } }"
|
||||
class="btn btn-primary mx-1"
|
||||
>Redaguoti</router-link
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { axios, authAxios } from '@/axios'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
issueTypes: [],
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
async fetchData() {
|
||||
const response = await axios.get('/api/IssueTypes')
|
||||
this.issueTypes = response.data
|
||||
},
|
||||
async deleteIssueType(id) {
|
||||
try {
|
||||
await this.$root.deleteConfirmation()
|
||||
await authAxios.delete(`/api/IssueTypes/${id}`)
|
||||
// TODO: Display some success info
|
||||
await this.fetchData()
|
||||
} catch (error) {
|
||||
// TODO: Display error.
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
28
KTUSAPS/ClientApp/src/router/admin.js
Normal file
28
KTUSAPS/ClientApp/src/router/admin.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import IssueTypes from '@/pages/admin/IssueTypes.vue'
|
||||
import IssueTypeNew from '@/pages/admin/IssueTypeNew.vue'
|
||||
import IssueTypeEdit from '@/pages/admin/IssueTypeEdit.vue'
|
||||
import Admin from '@/pages/admin/Admin.vue'
|
||||
|
||||
export default [
|
||||
{
|
||||
path: '/admin',
|
||||
component: Admin,
|
||||
children: [
|
||||
{
|
||||
path: 'issuetypes',
|
||||
name: 'AdminIssueTypes',
|
||||
component: IssueTypes,
|
||||
},
|
||||
{
|
||||
path: 'issuetypes/new',
|
||||
name: 'AdminIssueTypeNew',
|
||||
component: IssueTypeNew,
|
||||
},
|
||||
{
|
||||
path: 'issuetypes/:id/edit',
|
||||
name: 'AdminIssueTypeEdit',
|
||||
component: IssueTypeEdit,
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
@@ -6,6 +6,7 @@ import Feedbacks from '@/pages/Feedbacks.vue'
|
||||
import Issues from '@/pages/Issues.vue'
|
||||
import NewProblem from '@/pages/NewProblem.vue'
|
||||
import NewFeedback from '@/pages/NewFeedback.vue'
|
||||
import AdminRoutes from './admin'
|
||||
|
||||
const routes = [
|
||||
{
|
||||
@@ -43,6 +44,7 @@ const routes = [
|
||||
name: 'IssueNewFeedback',
|
||||
component: NewFeedback,
|
||||
},
|
||||
...AdminRoutes,
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
|
@@ -375,6 +375,11 @@ is-number@^7.0.0:
|
||||
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
|
||||
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
|
||||
|
||||
lodash@^4.17.21:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||
|
||||
magic-string@^0.25.7:
|
||||
version "0.25.7"
|
||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051"
|
||||
|
Reference in New Issue
Block a user