A lot
This commit is contained in:
5397
KTUSAPS/ClientApp/package-lock.json
generated
5397
KTUSAPS/ClientApp/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -8,27 +8,29 @@
|
|||||||
"lint": "vue-cli-service lint"
|
"lint": "vue-cli-service lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@azure/msal-browser": "^2.18.0",
|
"@azure/msal-browser": "^2.20.0",
|
||||||
"@popperjs/core": "^2.10.1",
|
"@popperjs/core": "^2.11.0",
|
||||||
"axios": "^0.20.0-0",
|
"axios": "^0.20.0-0",
|
||||||
"bootstrap": "^5.1.1",
|
"bootstrap": "^5.1.3",
|
||||||
|
"bootstrap-icons": "^1.7.2",
|
||||||
"cookies-js": "^1.2.3",
|
"cookies-js": "^1.2.3",
|
||||||
"core-js": "^3.7.0",
|
"core-js": "^3.20.0",
|
||||||
|
"joi": "^17.5.0",
|
||||||
"jwt-decode": "^3.1.2",
|
"jwt-decode": "^3.1.2",
|
||||||
"vue": "^3.0.2",
|
"vue": "^3.2.26",
|
||||||
"vue-loader-v16": "npm:vue-loader@^16.0.0-alpha.3",
|
"vue-loader-v16": "npm:vue-loader@^16.8.3",
|
||||||
"vue-router": "^4.0.0-rc.5",
|
"vue-router": "^4.0.12",
|
||||||
"vuex": "^4.0.2"
|
"vuex": "^4.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-babel": "^4.5.9",
|
"@vue/cli-plugin-babel": "^4.5.15",
|
||||||
"@vue/cli-plugin-eslint": "^4.5.9",
|
"@vue/cli-plugin-eslint": "^4.5.15",
|
||||||
"@vue/cli-service": "^4.5.9",
|
"@vue/cli-service": "^4.5.15",
|
||||||
"@vue/compiler-sfc": "^3.0.2",
|
"@vue/compiler-sfc": "^3.2.26",
|
||||||
"babel-eslint": "^10.1.0",
|
"babel-eslint": "^10.1.0",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^6.8.0",
|
||||||
"eslint-plugin-vue": "^7.1.0",
|
"eslint-plugin-vue": "^7.20.0",
|
||||||
"sass": "^1.33.0",
|
"sass": "^1.45.1",
|
||||||
"sass-loader": "^10.2.0"
|
"sass-loader": "^10.2.0"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<nav-menu></nav-menu>
|
<nav-menu />
|
||||||
|
<data-alert />
|
||||||
<div v-if="isLocal" class="container">
|
<div v-if="isLocal" class="container">
|
||||||
<div class="alert alert-danger">
|
<div class="alert alert-danger">
|
||||||
<h4 class="alert-heading">Lokali sistemą aptikta</h4>
|
<h4 class="alert-heading">Lokali sistemą aptikta</h4>
|
||||||
@@ -19,16 +20,20 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<transition name="slide-fade">
|
||||||
<router-view />
|
<router-view />
|
||||||
|
</transition>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import NavMenu from './components/NavMenu.vue'
|
import NavMenu from './components/NavMenu.vue'
|
||||||
|
import DataAlert from './components/DataAlert.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'App',
|
name: 'App',
|
||||||
components: {
|
components: {
|
||||||
NavMenu,
|
NavMenu,
|
||||||
|
DataAlert,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@@ -1 +1,5 @@
|
|||||||
|
$font-family-base: 'Open Sans', sans-serif;
|
||||||
|
|
||||||
|
$primary: #1862a1;
|
||||||
|
$danger: #a1181d;
|
||||||
|
$warning: #eb7d1d;
|
||||||
|
@@ -1,2 +1,33 @@
|
|||||||
@import "custom";
|
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,700;1,300;1,400;1,700&display=swap');
|
||||||
@import "~bootstrap/scss/bootstrap";
|
|
||||||
|
@import 'custom';
|
||||||
|
@import '~bootstrap/scss/bootstrap';
|
||||||
|
@import url('https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.2/font/bootstrap-icons.css');
|
||||||
|
|
||||||
|
/* Enter and leave animations can use different */
|
||||||
|
/* durations and timing functions. */
|
||||||
|
.slide-fade-enter-active {
|
||||||
|
transition: all 0.3s 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-fade-enter-from,
|
||||||
|
.slide-fade-leave-to {
|
||||||
|
transform: translateX(20px);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-fade-enter-to,
|
||||||
|
.slide-fade-leave-from {
|
||||||
|
transform: translateX(0px);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
16
KTUSAPS/ClientApp/src/axios.js
Normal file
16
KTUSAPS/ClientApp/src/axios.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import Axios from 'axios'
|
||||||
|
import store from './store'
|
||||||
|
|
||||||
|
export const authAxios = Axios.create({})
|
||||||
|
export const axios = Axios
|
||||||
|
|
||||||
|
authAxios.interceptors.request.use(
|
||||||
|
async (config) => {
|
||||||
|
await store.dispatch('msalAuth/waitTillReady') // Delay requesrt till we ready
|
||||||
|
config.headers['Authorization'] = `Bearer ${store.state.msalAuth.idToken}`
|
||||||
|
return config
|
||||||
|
},
|
||||||
|
() => {}
|
||||||
|
)
|
||||||
|
|
||||||
|
export default Axios
|
58
KTUSAPS/ClientApp/src/components/DataAlert.vue
Normal file
58
KTUSAPS/ClientApp/src/components/DataAlert.vue
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div ref="modal" class="modal" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Duomenų naudojimo pranešimas</h5>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>
|
||||||
|
Norime jus informuoti, kad sutikdami su šituo duomenų sutikimo jus
|
||||||
|
KTU studentų atstovybei sutinkate perduoti savo duomenis, tokius
|
||||||
|
kaip el. pašto adresas, indentifikacijos kodas, jūsų vardas ir
|
||||||
|
pavardė, ir kitus svarbūs identifikavimo duomenis apie jus.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Norime informuoti, kad ši programa naudoja sausainiukus ir vietinę
|
||||||
|
saugyklą, kad užtikrintų sklandų veikimą.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-primary" @click="confirm">
|
||||||
|
Sutinku
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { Modal } from 'bootstrap'
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
modal: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
confirm() {
|
||||||
|
this.modal.hide()
|
||||||
|
window.localStorage.setItem('conf', 'true')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
var el = this.$refs.modal
|
||||||
|
this.modal = new Modal(el, {
|
||||||
|
keyboard: false,
|
||||||
|
backdrop: 'static',
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!window.localStorage.getItem('conf')) {
|
||||||
|
this.modal.show()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
@@ -32,6 +32,21 @@
|
|||||||
>Pateikti problemą</router-link
|
>Pateikti problemą</router-link
|
||||||
>
|
>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" v-if="$store.state.msalAuth.isLoggedIn">
|
||||||
|
<router-link :to="{ name: 'Problems' }" class="nav-link"
|
||||||
|
>Problemos</router-link
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" v-if="$store.state.msalAuth.isLoggedIn">
|
||||||
|
<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 class="nav-item">
|
<!-- <li class="nav-item">
|
||||||
<a href="/swagger" class="nav-link">Swagger UI</a>
|
<a href="/swagger" class="nav-link">Swagger UI</a>
|
||||||
</li> -->
|
</li> -->
|
||||||
|
34
KTUSAPS/ClientApp/src/pages/Feedbacks.vue
Normal file
34
KTUSAPS/ClientApp/src/pages/Feedbacks.vue
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<div class="card my-4" v-for="f in feedbacks" :key="f.id">
|
||||||
|
<div class="row g-0">
|
||||||
|
<div class="card-body">
|
||||||
|
<h6 class="card-text text-muted">Paskelbta: {{ f.created }}</h6>
|
||||||
|
<h5 class="card-title">Atsiliepimas:</h5>
|
||||||
|
<p class="card-text">{{ f.feedbackLt }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { axios } from '@/axios'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
feedbacks: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async fetchData() {
|
||||||
|
const response = await axios.get('/api/PublishedFeedbacks')
|
||||||
|
this.feedbacks = response.data
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
@@ -12,9 +12,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<template v-if="$store.state.msalAuth.isAdmin">
|
<template v-if="$store.state.msalAuth.isAdmin">
|
||||||
<div class="alert alert-warning">
|
<div class="alert alert-warning">
|
||||||
<span>
|
<span> Tu esi administratorius. </span>
|
||||||
Tu esi administratorius.
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<h3>Prieigos raktas (Access token)</h3>
|
<h3>Prieigos raktas (Access token)</h3>
|
||||||
<a
|
<a
|
||||||
@@ -59,7 +57,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios'
|
import { authAxios } from '@/axios'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
@@ -70,13 +68,9 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
serverVerify() {
|
serverVerify() {
|
||||||
this.verificationResult = null
|
this.verificationResult = null
|
||||||
axios
|
authAxios
|
||||||
.get('/api/AuthMetadata/authed', {
|
.get('/api/AuthMetadata/authed')
|
||||||
headers: {
|
.then((response) => {
|
||||||
Authorization: `Bearer ${this.$store.state.msalAuth.idToken}`,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then(response => {
|
|
||||||
this.verificationResult = response.data
|
this.verificationResult = response.data
|
||||||
})
|
})
|
||||||
.catch(function (error) {
|
.catch(function (error) {
|
||||||
@@ -85,13 +79,9 @@ export default {
|
|||||||
},
|
},
|
||||||
serverAdminVerify() {
|
serverAdminVerify() {
|
||||||
this.verificationResult = null
|
this.verificationResult = null
|
||||||
axios
|
authAxios
|
||||||
.get('/api/AuthMetadata/Admin', {
|
.get('/api/AuthMetadata/Admin')
|
||||||
headers: {
|
.then((response) => {
|
||||||
Authorization: `Bearer ${this.$store.state.msalAuth.idToken}`,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then(response => {
|
|
||||||
this.verificationResult = response.data
|
this.verificationResult = response.data
|
||||||
})
|
})
|
||||||
.catch(function (error) {
|
.catch(function (error) {
|
||||||
|
78
KTUSAPS/ClientApp/src/pages/Issues.vue
Normal file
78
KTUSAPS/ClientApp/src/pages/Issues.vue
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<div class="alert alert-success" v-if="ok">{{ ok }}</div>
|
||||||
|
<div class="alert alert-danger" v-if="error">{{ error }}</div>
|
||||||
|
<div class="card my-4" v-for="i in issues" :key="i.id">
|
||||||
|
<div class="row g-0">
|
||||||
|
<div class="card-body">
|
||||||
|
<h6 class="card-text text-muted">Pateikta: {{ i.created }}</h6>
|
||||||
|
<h6 class="card-text text-muted">El paštas: {{ i.email }}</h6>
|
||||||
|
<h6 class="card-text text-muted">
|
||||||
|
Skelbtinas: {{ i.publishable ? '✔' : '❌' }}
|
||||||
|
</h6>
|
||||||
|
<h6 class="card-text text-muted">
|
||||||
|
Išsprestą: {{ i.solved ? '✔' : '❌' }}
|
||||||
|
</h6>
|
||||||
|
<h6 class="card-text text-muted">
|
||||||
|
Tipas: {{ issueTypes[i.issueTypeId]?.name }}
|
||||||
|
</h6>
|
||||||
|
<h5 class="card-title">Aprašymas:</h5>
|
||||||
|
<p class="card-text">{{ i.description }}</p>
|
||||||
|
<button class="btn btn-danger mx-1" @click="deleteIssue(i.id)">
|
||||||
|
<i class="bi bi-trash-fill"></i>
|
||||||
|
Ištrinti
|
||||||
|
</button>
|
||||||
|
<router-link
|
||||||
|
:to="{ name: 'IssueNewProblem', params: { id: i.id } }"
|
||||||
|
class="btn btn-primary mx-1"
|
||||||
|
>Paskelbti problemą</router-link
|
||||||
|
>
|
||||||
|
<router-link
|
||||||
|
:to="{ name: 'IssueNewFeedback', params: { id: i.id } }"
|
||||||
|
class="btn btn-primary mx-1"
|
||||||
|
>Paskelbti atsiliepimą</router-link
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { axios, authAxios } from '@/axios'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
issues: [],
|
||||||
|
issueTypes: {},
|
||||||
|
ok: null,
|
||||||
|
error: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async fetchData() {
|
||||||
|
const response = await authAxios.get('/api/Issues')
|
||||||
|
this.issues = response.data
|
||||||
|
const response2 = await axios.get('/api/IssueTypes')
|
||||||
|
response2.data.forEach((it) => {
|
||||||
|
this.issueTypes[it.id] = it
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async deleteIssue(id) {
|
||||||
|
this.error = null
|
||||||
|
this.ok = null
|
||||||
|
try {
|
||||||
|
await authAxios.delete(`/api/Issues/${id}`)
|
||||||
|
this.ok = 'Sėkmingai ištrintą'
|
||||||
|
await this.fetchData()
|
||||||
|
} catch (error) {
|
||||||
|
this.error = error
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
102
KTUSAPS/ClientApp/src/pages/NewFeedback.vue
Normal file
102
KTUSAPS/ClientApp/src/pages/NewFeedback.vue
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<div class="alert alert-success" v-if="ok">{{ ok }}</div>
|
||||||
|
<div class="alert alert-danger" v-if="error">{{ error }}</div>
|
||||||
|
<h1>Naujas atsiliepimas</h1>
|
||||||
|
<div class="card my-4">
|
||||||
|
<div class="row g-0">
|
||||||
|
<div class="card-body">
|
||||||
|
<h6 class="card-text text-muted">Pateikta: {{ issue.created }}</h6>
|
||||||
|
<h6 class="card-text text-muted">El paštas: {{ issue.email }}</h6>
|
||||||
|
<h6 class="card-text text-muted">
|
||||||
|
Skelbtinas: {{ issue.publishable ? '✔' : '❌' }}
|
||||||
|
</h6>
|
||||||
|
<h6 class="card-text text-muted">
|
||||||
|
Išsprestą: {{ issue.solved ? '✔' : '❌' }}
|
||||||
|
</h6>
|
||||||
|
<h6 class="card-text text-muted">
|
||||||
|
Tipas: {{ issueTypes[issue.issueTypeId]?.name }}
|
||||||
|
</h6>
|
||||||
|
<h5 class="card-title">Aprašymas:</h5>
|
||||||
|
<p class="card-text">{{ issue.description }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</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>
|
||||||
|
<button @click="create" class="btn btn-primary btn-lg">
|
||||||
|
Sukurti naują atsiliepimą
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { axios, authAxios } from '@/axios'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
issue: null,
|
||||||
|
issueTypes: {},
|
||||||
|
feedback: {
|
||||||
|
feedbackEn: '',
|
||||||
|
feedbackLt: '',
|
||||||
|
issueId: null,
|
||||||
|
},
|
||||||
|
ok: null,
|
||||||
|
error: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async fetchData() {
|
||||||
|
const response = await authAxios.get(
|
||||||
|
`/api/Issues/${this.$route.params.id}`
|
||||||
|
)
|
||||||
|
this.issue = response.data
|
||||||
|
const response2 = await axios.get('/api/IssueTypes')
|
||||||
|
response2.data.forEach((it) => {
|
||||||
|
this.issueTypes[it.id] = it
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async create() {
|
||||||
|
try {
|
||||||
|
this.feedback.issueId = this.issue.id
|
||||||
|
await authAxios.post('/api/PublishedFeedbacks', this.feedback)
|
||||||
|
this.ok = 'Sukurtą.'
|
||||||
|
} catch (error) {
|
||||||
|
this.error = error
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
$route: 'fetchData',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
102
KTUSAPS/ClientApp/src/pages/NewProblem.vue
Normal file
102
KTUSAPS/ClientApp/src/pages/NewProblem.vue
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<div class="alert alert-success" v-if="ok">{{ ok }}</div>
|
||||||
|
<div class="alert alert-danger" v-if="error">{{ error }}</div>
|
||||||
|
<h1>Nauja problema</h1>
|
||||||
|
<div class="card my-4">
|
||||||
|
<div class="row g-0">
|
||||||
|
<div class="card-body">
|
||||||
|
<h6 class="card-text text-muted">Pateikta: {{ issue.created }}</h6>
|
||||||
|
<h6 class="card-text text-muted">El paštas: {{ issue.email }}</h6>
|
||||||
|
<h6 class="card-text text-muted">
|
||||||
|
Skelbtinas: {{ issue.publishable ? '✔' : '❌' }}
|
||||||
|
</h6>
|
||||||
|
<h6 class="card-text text-muted">
|
||||||
|
Išsprestą: {{ issue.solved ? '✔' : '❌' }}
|
||||||
|
</h6>
|
||||||
|
<h6 class="card-text text-muted">
|
||||||
|
Tipas: {{ issueTypes[issue.issueTypeId]?.name }}
|
||||||
|
</h6>
|
||||||
|
<h5 class="card-title">Aprašymas:</h5>
|
||||||
|
<p class="card-text">{{ issue.description }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</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="problem.problemLt"
|
||||||
|
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="problem.problemEn"
|
||||||
|
class="form-control"
|
||||||
|
id="problemEnTextArea"
|
||||||
|
rows="4"
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
<button @click="create" class="btn btn-primary btn-lg">
|
||||||
|
Sukurti naują problemą
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { axios, authAxios } from '@/axios'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
issue: null,
|
||||||
|
issueTypes: {},
|
||||||
|
problem: {
|
||||||
|
problemLt: '',
|
||||||
|
problemEn: '',
|
||||||
|
issueId: null,
|
||||||
|
},
|
||||||
|
ok: null,
|
||||||
|
error: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async fetchData() {
|
||||||
|
const response = await authAxios.get(
|
||||||
|
`/api/Issues/${this.$route.params.id}`
|
||||||
|
)
|
||||||
|
this.issue = response.data
|
||||||
|
const response2 = await axios.get('/api/IssueTypes')
|
||||||
|
response2.data.forEach((it) => {
|
||||||
|
this.issueTypes[it.id] = it
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async create() {
|
||||||
|
try {
|
||||||
|
this.problem.issueId = this.issue.id
|
||||||
|
await authAxios.post('/api/PublishedProblems', this.problem)
|
||||||
|
this.ok = 'Sukurtą.'
|
||||||
|
} catch (error) {
|
||||||
|
this.error = error
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
$route: 'fetchData',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
67
KTUSAPS/ClientApp/src/pages/Problems.vue
Normal file
67
KTUSAPS/ClientApp/src/pages/Problems.vue
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<div class="alert alert-success" v-if="ok">{{ ok }}</div>
|
||||||
|
<div class="alert alert-danger" v-if="error">{{ error }}</div>
|
||||||
|
<div class="card my-4" v-for="p in problems" :key="p.id">
|
||||||
|
<div class="row g-0">
|
||||||
|
<div class="col-lg-2 col-md-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<span class="h3">{{ votes[p.id] }}</span> aktualumo balas
|
||||||
|
<button @click="vote(p.id)" class="btn btn-primary">
|
||||||
|
Balsuoti
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg col-md">
|
||||||
|
<div class="card-body">
|
||||||
|
<h6 class="card-text text-muted">Paskelbta: {{ p.created }}</h6>
|
||||||
|
<h5 class="card-title">Problema:</h5>
|
||||||
|
<p class="card-text">{{ p.problemLt }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { axios, authAxios } from '@/axios'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
problems: [],
|
||||||
|
votes: {},
|
||||||
|
ok: null,
|
||||||
|
error: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async fetchData() {
|
||||||
|
const response = await axios.get('/api/PublishedProblems')
|
||||||
|
this.problems = response.data
|
||||||
|
this.problems.forEach((p) => {
|
||||||
|
this.fetchVoteCount(p.id)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async fetchVoteCount(id) {
|
||||||
|
const response = await axios.get(`/api/PublishedProblems/${id}/Votes`)
|
||||||
|
this.votes[id] = response.data
|
||||||
|
},
|
||||||
|
async vote(id) {
|
||||||
|
this.ok = null
|
||||||
|
this.error = null
|
||||||
|
try {
|
||||||
|
await authAxios.post(`/api/PublishedProblems/${id}/Votes`)
|
||||||
|
this.ok = 'Sekmingai prabalsuotą.'
|
||||||
|
await this.fetchVoteCount(id)
|
||||||
|
} catch (error) {
|
||||||
|
this.error = error
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
@@ -1,5 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container mt-2">
|
<div class="container mt-2">
|
||||||
|
<div class="alert alert-danger" v-if="error">{{ error }}</div>
|
||||||
|
<div class="alert alert-success" v-if="ok">{{ ok }}</div>
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="card col-lg-6 p-5">
|
<div class="card col-lg-6 p-5">
|
||||||
<h1>Pateik savo problemą</h1>
|
<h1>Pateik savo problemą</h1>
|
||||||
@@ -23,9 +25,9 @@
|
|||||||
id="issueTypeSelect"
|
id="issueTypeSelect"
|
||||||
class="form-select"
|
class="form-select"
|
||||||
>
|
>
|
||||||
<option v-for="it in issueTypes" :key="it.id" :value="it.id">{{
|
<option v-for="it in issueTypes" :key="it.id" :value="it.id">
|
||||||
it.name
|
{{ it.name }}
|
||||||
}}</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
@@ -53,15 +55,15 @@
|
|||||||
studentai.
|
studentai.
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-primary">Pateikti</button>
|
<button @click="submit" class="btn btn-primary btn-lg">Pateikti</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<pre>{{ $data }}</pre>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios'
|
import { authAxios, axios } from '@/axios'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -71,6 +73,8 @@ export default {
|
|||||||
issueTypeId: null,
|
issueTypeId: null,
|
||||||
publishable: false,
|
publishable: false,
|
||||||
},
|
},
|
||||||
|
error: null,
|
||||||
|
ok: null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@@ -81,6 +85,20 @@ export default {
|
|||||||
const response = await axios.get('/api/IssueTypes')
|
const response = await axios.get('/api/IssueTypes')
|
||||||
this.issueTypes = response.data
|
this.issueTypes = response.data
|
||||||
},
|
},
|
||||||
|
async submit() {
|
||||||
|
this.error = null
|
||||||
|
this.ok = null
|
||||||
|
try {
|
||||||
|
const response = await authAxios.post('/api/Issues', this.issue)
|
||||||
|
console.log(response)
|
||||||
|
this.ok = 'Problemą sekmingai pateikta.'
|
||||||
|
this.issue.description = ''
|
||||||
|
this.issue.publishable = false
|
||||||
|
this.issue.issueTypeId = null
|
||||||
|
} catch (error) {
|
||||||
|
this.error = error
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@@ -1,6 +1,11 @@
|
|||||||
import { createWebHistory, createRouter } from 'vue-router'
|
import { createWebHistory, createRouter } from 'vue-router'
|
||||||
import Home from '@/pages/Home.vue'
|
import Home from '@/pages/Home.vue'
|
||||||
import Submit from '@/pages/Submit.vue'
|
import Submit from '@/pages/Submit.vue'
|
||||||
|
import Problems from '@/pages/Problems.vue'
|
||||||
|
import Feedbacks from '@/pages/Feedbacks.vue'
|
||||||
|
import Issues from '@/pages/Issues.vue'
|
||||||
|
import NewProblem from '@/pages/NewProblem.vue'
|
||||||
|
import NewFeedback from '@/pages/NewFeedback.vue'
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
@@ -13,6 +18,31 @@ const routes = [
|
|||||||
name: 'Submit',
|
name: 'Submit',
|
||||||
component: Submit,
|
component: Submit,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/problems',
|
||||||
|
name: 'Problems',
|
||||||
|
component: Problems,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/feedbacks',
|
||||||
|
name: 'Feedbacks',
|
||||||
|
component: Feedbacks,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/issues',
|
||||||
|
name: 'Issues',
|
||||||
|
component: Issues,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/issues/:id/newProblem',
|
||||||
|
name: 'IssueNewProblem',
|
||||||
|
component: NewProblem,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/issues/:id/newFeedback',
|
||||||
|
name: 'IssueNewFeedback',
|
||||||
|
component: NewFeedback,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
|
@@ -3,6 +3,7 @@ import axios from 'axios'
|
|||||||
|
|
||||||
// initial state
|
// initial state
|
||||||
const state = () => ({
|
const state = () => ({
|
||||||
|
isReady: false,
|
||||||
isLoggedIn: false,
|
isLoggedIn: false,
|
||||||
isAdmin: false,
|
isAdmin: false,
|
||||||
accessToken: null,
|
accessToken: null,
|
||||||
@@ -11,7 +12,8 @@ const state = () => ({
|
|||||||
displayName: null,
|
displayName: null,
|
||||||
|
|
||||||
debugFullTokenResponse: null,
|
debugFullTokenResponse: null,
|
||||||
debugAccountInfo: null,
|
// debugAccountInfo: null,
|
||||||
|
onReady: [],
|
||||||
})
|
})
|
||||||
|
|
||||||
// getters
|
// getters
|
||||||
@@ -30,12 +32,19 @@ const actions = {
|
|||||||
Authorization: `Bearer ${state.idToken}`,
|
Authorization: `Bearer ${state.idToken}`,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then(res => (isAdmin = res.data))
|
.then((res) => (isAdmin = res.data))
|
||||||
.catch(error => console.error(error))
|
.catch((error) => console.error(error))
|
||||||
}
|
}
|
||||||
commit('setState', { ...state, isAdmin })
|
commit('setState', { ...state, isAdmin })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
async waitTillReady({ commit, state }) {
|
||||||
|
if (!state.isReady) {
|
||||||
|
await new Promise((c) => {
|
||||||
|
commit('addReadyCalback', c)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// mutations
|
// mutations
|
||||||
@@ -46,9 +55,16 @@ const mutations = {
|
|||||||
state.accessToken = msalState.accessToken
|
state.accessToken = msalState.accessToken
|
||||||
state.idToken = msalState.idToken
|
state.idToken = msalState.idToken
|
||||||
state.debugFullTokenResponse = msalState.debugFullTokenResponse
|
state.debugFullTokenResponse = msalState.debugFullTokenResponse
|
||||||
state.debugAccountInfo = msalState.debugAccountInfo
|
// state.debugAccountInfo = msalState.debugAccountInfo
|
||||||
state.email = msalState.email
|
state.email = msalState.email
|
||||||
state.displayName = msalState.displayName
|
state.displayName = msalState.displayName
|
||||||
|
if (!state.isReady && state.idToken) {
|
||||||
|
state.isReady = true
|
||||||
|
state.onReady.forEach((c) => c())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
addReadyCalback(state, callback) {
|
||||||
|
state.onReady.push(callback)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -45,6 +45,11 @@ namespace KTUSAPS.Controllers
|
|||||||
[HttpGet("Authed")]
|
[HttpGet("Authed")]
|
||||||
public bool IsAuthed() => true;
|
public bool IsAuthed() => true;
|
||||||
|
|
||||||
|
[Authorize]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[HttpGet("Claims")]
|
||||||
|
public IEnumerable<object> Claims() => User.Claims.Select((c) => new { c.Type, c.Value });
|
||||||
|
|
||||||
[Authorize("admin")]
|
[Authorize("admin")]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[HttpGet("Admin")]
|
[HttpGet("Admin")]
|
||||||
|
@@ -8,6 +8,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using KTUSAPS.Data;
|
using KTUSAPS.Data;
|
||||||
using KTUSAPS.Data.Model;
|
using KTUSAPS.Data.Model;
|
||||||
using KTUSAPS.Extensions;
|
using KTUSAPS.Extensions;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
|
||||||
namespace KTUSAPS.Controllers
|
namespace KTUSAPS.Controllers
|
||||||
{
|
{
|
||||||
@@ -32,6 +33,7 @@ namespace KTUSAPS.Controllers
|
|||||||
[HttpPost]
|
[HttpPost]
|
||||||
[ProducesResponseType(StatusCodes.Status201Created)]
|
[ProducesResponseType(StatusCodes.Status201Created)]
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||||
|
[Authorize("admin")]
|
||||||
public async Task<ActionResult<IssueType>> CreateIssueType([FromBody] IssueType issueType)
|
public async Task<ActionResult<IssueType>> CreateIssueType([FromBody] IssueType issueType)
|
||||||
{
|
{
|
||||||
if (issueType == null)
|
if (issueType == null)
|
||||||
@@ -64,6 +66,7 @@ namespace KTUSAPS.Controllers
|
|||||||
[HttpPatch("{id}")]
|
[HttpPatch("{id}")]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[Authorize("admin")]
|
||||||
public async Task<IActionResult> UpdateIssueType(int id, IssueType issueType)
|
public async Task<IActionResult> UpdateIssueType(int id, IssueType issueType)
|
||||||
{
|
{
|
||||||
var databaseIssueType = await _context.IssueTypes.FindAsync(id);
|
var databaseIssueType = await _context.IssueTypes.FindAsync(id);
|
||||||
@@ -86,6 +89,7 @@ namespace KTUSAPS.Controllers
|
|||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id}")]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[Authorize("admin")]
|
||||||
public async Task<IActionResult> DeleteIssueType(int id)
|
public async Task<IActionResult> DeleteIssueType(int id)
|
||||||
{
|
{
|
||||||
var issueType = await _context.IssueTypes.FindAsync(id);
|
var issueType = await _context.IssueTypes.FindAsync(id);
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using KTUSAPS.Data.Model;
|
using KTUSAPS.Data.Model;
|
||||||
using KTUSAPS.Extensions;
|
using KTUSAPS.Extensions;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
@@ -23,6 +24,7 @@ namespace KTUSAPS.Controllers
|
|||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[Authorize("admin")]
|
||||||
public async Task<ActionResult<IEnumerable<Issue>>> GetIssues()
|
public async Task<ActionResult<IEnumerable<Issue>>> GetIssues()
|
||||||
{
|
{
|
||||||
return await dataContext.Issues.ToListAsync();
|
return await dataContext.Issues.ToListAsync();
|
||||||
@@ -31,6 +33,7 @@ namespace KTUSAPS.Controllers
|
|||||||
[HttpPost]
|
[HttpPost]
|
||||||
[ProducesResponseType(StatusCodes.Status201Created)]
|
[ProducesResponseType(StatusCodes.Status201Created)]
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||||
|
[Authorize]
|
||||||
public async Task<ActionResult<Issue>> CreateIssueAsync([FromBody] Issue issueToCreate)
|
public async Task<ActionResult<Issue>> CreateIssueAsync([FromBody] Issue issueToCreate)
|
||||||
{
|
{
|
||||||
if (issueToCreate == null)
|
if (issueToCreate == null)
|
||||||
@@ -41,9 +44,11 @@ namespace KTUSAPS.Controllers
|
|||||||
return BadRequest("No typeId has been specified");
|
return BadRequest("No typeId has been specified");
|
||||||
if (issueToCreate.Problem != null && issueToCreate.Feedback != null && issueToCreate.IssueType != null)
|
if (issueToCreate.Problem != null && issueToCreate.Feedback != null && issueToCreate.IssueType != null)
|
||||||
return BadRequest("Do not privide navigation property values.");
|
return BadRequest("Do not privide navigation property values.");
|
||||||
// TODO: Enable next line and make thoes two fields come from user identity
|
if (issueToCreate.UserID != default || issueToCreate.Email != default)
|
||||||
//if (issueToCreate.UserID != default || issueToCreate.Email != default)
|
return BadRequest("Do not provide indentity values.");
|
||||||
// return BadRequest("Do not provide indentity values.");
|
|
||||||
|
issueToCreate.UserID = User.GetUserId();
|
||||||
|
issueToCreate.Email = User.GetEmail();
|
||||||
|
|
||||||
var createdValue = await dataContext.AddAsync(issueToCreate);
|
var createdValue = await dataContext.AddAsync(issueToCreate);
|
||||||
await dataContext.SaveChangesAsync();
|
await dataContext.SaveChangesAsync();
|
||||||
@@ -54,6 +59,7 @@ namespace KTUSAPS.Controllers
|
|||||||
[HttpGet("{id}")]
|
[HttpGet("{id}")]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[Authorize("admin")]
|
||||||
public ActionResult<Issue> GetIssue(int id)
|
public ActionResult<Issue> GetIssue(int id)
|
||||||
{
|
{
|
||||||
var issue = dataContext.Issues.AsQueryable().Where(i => i.Id == id).FirstOrDefault();
|
var issue = dataContext.Issues.AsQueryable().Where(i => i.Id == id).FirstOrDefault();
|
||||||
@@ -65,6 +71,7 @@ namespace KTUSAPS.Controllers
|
|||||||
[HttpPatch("{id}")]
|
[HttpPatch("{id}")]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[Authorize("admin")]
|
||||||
public async Task<ActionResult<Issue>> UpdateIssueAsync(int id, [FromBody] Issue issue)
|
public async Task<ActionResult<Issue>> UpdateIssueAsync(int id, [FromBody] Issue issue)
|
||||||
{
|
{
|
||||||
var databaseIssue = dataContext.Issues.AsQueryable().Where(i => i.Id == id).FirstOrDefault();
|
var databaseIssue = dataContext.Issues.AsQueryable().Where(i => i.Id == id).FirstOrDefault();
|
||||||
@@ -83,6 +90,7 @@ namespace KTUSAPS.Controllers
|
|||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id}")]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[Authorize("admin")]
|
||||||
public async Task<IActionResult> DeleteIssueAsync(int id)
|
public async Task<IActionResult> DeleteIssueAsync(int id)
|
||||||
{
|
{
|
||||||
var issue = dataContext.Issues.AsQueryable().Where(i => i.Id == id).FirstOrDefault();
|
var issue = dataContext.Issues.AsQueryable().Where(i => i.Id == id).FirstOrDefault();
|
||||||
|
@@ -8,6 +8,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using KTUSAPS.Data;
|
using KTUSAPS.Data;
|
||||||
using KTUSAPS.Data.Model;
|
using KTUSAPS.Data.Model;
|
||||||
using KTUSAPS.Extensions;
|
using KTUSAPS.Extensions;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
|
||||||
namespace KTUSAPS.Controllers
|
namespace KTUSAPS.Controllers
|
||||||
{
|
{
|
||||||
@@ -32,6 +33,7 @@ namespace KTUSAPS.Controllers
|
|||||||
[HttpPost]
|
[HttpPost]
|
||||||
[ProducesResponseType(StatusCodes.Status201Created)]
|
[ProducesResponseType(StatusCodes.Status201Created)]
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||||
|
[Authorize("admin")]
|
||||||
public async Task<ActionResult<PublishedFeedback>> PostPublishedFeedback(PublishedFeedback publishedFeedback)
|
public async Task<ActionResult<PublishedFeedback>> PostPublishedFeedback(PublishedFeedback publishedFeedback)
|
||||||
{
|
{
|
||||||
if (publishedFeedback == null)
|
if (publishedFeedback == null)
|
||||||
@@ -63,6 +65,7 @@ namespace KTUSAPS.Controllers
|
|||||||
[HttpPatch("{id}")]
|
[HttpPatch("{id}")]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[Authorize("admin")]
|
||||||
public async Task<ActionResult<PublishedFeedback>> UpdatePublishedFeedback(int id, PublishedFeedback publishedFeedback)
|
public async Task<ActionResult<PublishedFeedback>> UpdatePublishedFeedback(int id, PublishedFeedback publishedFeedback)
|
||||||
{
|
{
|
||||||
var databasePublishedFeedback = await _context.PublishedFeedbacks.FindAsync(id);
|
var databasePublishedFeedback = await _context.PublishedFeedbacks.FindAsync(id);
|
||||||
@@ -88,6 +91,7 @@ namespace KTUSAPS.Controllers
|
|||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id}")]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[Authorize("admin")]
|
||||||
public async Task<IActionResult> DeletePublishedFeedback(int id)
|
public async Task<IActionResult> DeletePublishedFeedback(int id)
|
||||||
{
|
{
|
||||||
var publishedFeedback = await _context.PublishedFeedbacks.FindAsync(id);
|
var publishedFeedback = await _context.PublishedFeedbacks.FindAsync(id);
|
||||||
|
@@ -8,6 +8,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using KTUSAPS.Data;
|
using KTUSAPS.Data;
|
||||||
using KTUSAPS.Data.Model;
|
using KTUSAPS.Data.Model;
|
||||||
using KTUSAPS.Extensions;
|
using KTUSAPS.Extensions;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
|
||||||
namespace KTUSAPS.Controllers
|
namespace KTUSAPS.Controllers
|
||||||
{
|
{
|
||||||
@@ -32,6 +33,7 @@ namespace KTUSAPS.Controllers
|
|||||||
[HttpPost]
|
[HttpPost]
|
||||||
[ProducesResponseType(StatusCodes.Status201Created)]
|
[ProducesResponseType(StatusCodes.Status201Created)]
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||||
|
[Authorize("admin")]
|
||||||
public async Task<ActionResult<PublishedProblem>> CreatePublishedProblem([FromBody] PublishedProblem publishedProblem)
|
public async Task<ActionResult<PublishedProblem>> CreatePublishedProblem([FromBody] PublishedProblem publishedProblem)
|
||||||
{
|
{
|
||||||
if (publishedProblem == null)
|
if (publishedProblem == null)
|
||||||
@@ -63,6 +65,7 @@ namespace KTUSAPS.Controllers
|
|||||||
[HttpPatch("{id}")]
|
[HttpPatch("{id}")]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[Authorize("admin")]
|
||||||
public async Task<ActionResult<PublishedProblem>> UpdatePublishedProblem(int id, PublishedProblem publishedProblem)
|
public async Task<ActionResult<PublishedProblem>> UpdatePublishedProblem(int id, PublishedProblem publishedProblem)
|
||||||
{
|
{
|
||||||
var databasePublishedProblem = await _context.PublishedProblems.FindAsync(id);
|
var databasePublishedProblem = await _context.PublishedProblems.FindAsync(id);
|
||||||
@@ -90,6 +93,7 @@ namespace KTUSAPS.Controllers
|
|||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id}")]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[Authorize("admin")]
|
||||||
public async Task<IActionResult> DeletePublishedProblem(int id)
|
public async Task<IActionResult> DeletePublishedProblem(int id)
|
||||||
{
|
{
|
||||||
var publishedProblem = await _context.PublishedProblems.FindAsync(id);
|
var publishedProblem = await _context.PublishedProblems.FindAsync(id);
|
||||||
@@ -121,17 +125,18 @@ namespace KTUSAPS.Controllers
|
|||||||
[ProducesResponseType(StatusCodes.Status201Created)]
|
[ProducesResponseType(StatusCodes.Status201Created)]
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<ActionResult<Vote>> Vote(int id, Vote vote)
|
[Authorize]
|
||||||
|
public async Task<ActionResult<Vote>> Vote(int id)
|
||||||
{
|
{
|
||||||
var publishedProblem = await _context.PublishedProblems.FindAsync(id);
|
var publishedProblem = await _context.PublishedProblems.FindAsync(id);
|
||||||
if (publishedProblem == null)
|
if (publishedProblem == null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
|
|
||||||
// TODO: Get user id from auth claims
|
var vote = new Vote()
|
||||||
if (vote.UserId == default)
|
{
|
||||||
return BadRequest("Please provide user id");
|
Problem = publishedProblem,
|
||||||
|
UserId = User.GetUserId(),
|
||||||
vote.Problem = publishedProblem;
|
};
|
||||||
|
|
||||||
_context.Votes.Add(vote);
|
_context.Votes.Add(vote);
|
||||||
await _context.SaveChangesAsync();
|
await _context.SaveChangesAsync();
|
||||||
|
41
KTUSAPS/Extensions/ClaimsPrincipalExtensions.cs
Normal file
41
KTUSAPS/Extensions/ClaimsPrincipalExtensions.cs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Security.Claims;
|
||||||
|
|
||||||
|
namespace KTUSAPS.Extensions
|
||||||
|
{
|
||||||
|
public static class ClaimsPrincipalExtensions
|
||||||
|
{
|
||||||
|
private static string getClaimValue(ClaimsPrincipal claimsPrincipal, string claimType)
|
||||||
|
{
|
||||||
|
return claimsPrincipal.Claims.FirstOrDefault(c => c.Type == claimType)?.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetUserId(this ClaimsPrincipal claimsPrincipal)
|
||||||
|
{
|
||||||
|
if (claimsPrincipal == null)
|
||||||
|
return null;
|
||||||
|
return getClaimValue(claimsPrincipal, ClaimTypes.NameIdentifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetName(this ClaimsPrincipal claimsPrincipal)
|
||||||
|
{
|
||||||
|
if (claimsPrincipal == null)
|
||||||
|
return null;
|
||||||
|
return getClaimValue(claimsPrincipal, "name");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetEmail(this ClaimsPrincipal claimsPrincipal)
|
||||||
|
{
|
||||||
|
if (claimsPrincipal == null)
|
||||||
|
return null;
|
||||||
|
return getClaimValue(claimsPrincipal, ClaimTypes.Email);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetObjectId(this ClaimsPrincipal claimsPrincipal)
|
||||||
|
{
|
||||||
|
if (claimsPrincipal == null)
|
||||||
|
return null;
|
||||||
|
return getClaimValue(claimsPrincipal, "http://schemas.microsoft.com/identity/claims/objectidentifier");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user