mirror of
https://github.com/Troplo/Colubrina.git
synced 2024-12-24 23:45:15 +11:00
1.0.26
This commit is contained in:
parent
686f602b56
commit
7125909046
10 changed files with 404 additions and 20 deletions
|
@ -24,6 +24,7 @@ app.use("/api/v1/usercontent", require("./routes/usercontent.js"))
|
|||
app.use("/api/v1/mediaproxy", require("./routes/mediaproxy.js"))
|
||||
app.use("/api/v1/associations", require("./routes/associations.js"))
|
||||
app.use("/api/v1/polls", require("./routes/polls.js"))
|
||||
app.use("/api/v1/feedback", require("./routes/feedback.js"))
|
||||
app.get("/api/v1/state", async (req, res) => {
|
||||
res.json({
|
||||
release: req.app.locals.config.release,
|
||||
|
|
42
backend/migrations/20220831101545-feedback.js
Normal file
42
backend/migrations/20220831101545-feedback.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
"use strict"
|
||||
module.exports = {
|
||||
up: async (queryInterface, Sequelize) => {
|
||||
await queryInterface.createTable("Feedbacks", {
|
||||
id: {
|
||||
allowNull: false,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
type: Sequelize.BIGINT
|
||||
},
|
||||
feedbackText: {
|
||||
type: Sequelize.TEXT
|
||||
},
|
||||
starRating: {
|
||||
type: Sequelize.INTEGER
|
||||
},
|
||||
route: {
|
||||
type: Sequelize.STRING
|
||||
},
|
||||
debug: {
|
||||
type: Sequelize.JSON
|
||||
},
|
||||
userId: {
|
||||
type: Sequelize.BIGINT
|
||||
},
|
||||
tenant: {
|
||||
type: Sequelize.STRING
|
||||
},
|
||||
createdAt: {
|
||||
allowNull: false,
|
||||
type: Sequelize.DATE
|
||||
},
|
||||
updatedAt: {
|
||||
allowNull: false,
|
||||
type: Sequelize.DATE
|
||||
}
|
||||
})
|
||||
},
|
||||
down: async (queryInterface, Sequelize) => {
|
||||
await queryInterface.dropTable("Feedbacks")
|
||||
}
|
||||
}
|
|
@ -20,7 +20,7 @@ module.exports = (sequelize, DataTypes) => {
|
|||
feedbackText: DataTypes.TEXT,
|
||||
starRating: DataTypes.BIGINT,
|
||||
route: DataTypes.STRING,
|
||||
userId: DataTypes.STRING,
|
||||
userId: DataTypes.BIGINT,
|
||||
tenant: DataTypes.STRING,
|
||||
debug: DataTypes.JSON
|
||||
},
|
||||
|
|
|
@ -2,7 +2,7 @@ const express = require("express")
|
|||
const router = express.Router()
|
||||
const Errors = require("../lib/errors.js")
|
||||
const auth = require("../lib/authorize.js")
|
||||
const { User, Theme, Message } = require("../models")
|
||||
const { User, Theme, Message, Feedback } = require("../models")
|
||||
const { Op } = require("sequelize")
|
||||
const dayjs = require("dayjs")
|
||||
const fs = require("fs")
|
||||
|
@ -33,6 +33,23 @@ router.all("*", auth, async (req, res, next) => {
|
|||
}
|
||||
})
|
||||
|
||||
router.get("/feedback", auth, async (req, res, next) => {
|
||||
try {
|
||||
const feedback = await Feedback.findAndCountAll({
|
||||
order: [["createdAt", "DESC"]],
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: "user"
|
||||
}
|
||||
]
|
||||
})
|
||||
res.json(feedback)
|
||||
} catch (err) {
|
||||
return next(err)
|
||||
}
|
||||
})
|
||||
|
||||
router.get("/", auth, async (req, res, next) => {
|
||||
try {
|
||||
res.json({
|
||||
|
|
25
backend/routes/feedback.js
Normal file
25
backend/routes/feedback.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
const express = require("express")
|
||||
const router = express.Router()
|
||||
const Errors = require("../lib/errors.js")
|
||||
const auth = require("../lib/authorize.js")
|
||||
const { Feedback } = require("../models")
|
||||
|
||||
router.post("/", auth, async (req, res, next) => {
|
||||
try {
|
||||
await Feedback.create({
|
||||
feedbackText: req.body.text,
|
||||
starRating: req.body.starRating,
|
||||
debug: {
|
||||
client: req.body.debug
|
||||
},
|
||||
route: req.body.route,
|
||||
userId: req.user.id,
|
||||
tenant: "colubrina"
|
||||
})
|
||||
res.sendStatus(204)
|
||||
} catch (e) {
|
||||
next(e)
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = router
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "colubrina",
|
||||
"version": "1.0.25",
|
||||
"version": "1.0.26",
|
||||
"description": "Simple instant communication.",
|
||||
"private": true,
|
||||
"author": "Troplo <troplo@troplo.com>",
|
||||
|
|
|
@ -357,6 +357,139 @@
|
|||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
<v-dialog v-model="shortcuts" width="700">
|
||||
<v-card color="card" elevation="7">
|
||||
<v-toolbar color="toolbar">
|
||||
<v-toolbar-title> Shortcuts </v-toolbar-title>
|
||||
</v-toolbar>
|
||||
<v-card-text>
|
||||
<v-container>
|
||||
<v-layout row wrap>
|
||||
<v-card class="mx-2 mt-2">
|
||||
<v-card-title> QuickSwitcher </v-card-title>
|
||||
<v-card-text class="text-center">
|
||||
<v-btn text outlined> CTRL </v-btn>
|
||||
<v-btn text outlined class="ml-2"> K </v-btn>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
<v-card class="mx-2 mt-2">
|
||||
<v-card-title> RouteSwitcher </v-card-title>
|
||||
<v-card-text class="text-center">
|
||||
<v-btn text outlined> CTRL </v-btn>
|
||||
<v-btn text outlined class="ml-2"> B </v-btn>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
<v-card class="mx-2 mt-2">
|
||||
<v-card-title> Shortcuts </v-card-title>
|
||||
<v-card-text class="text-center">
|
||||
<v-btn text outlined class="ml-2"> CTRL </v-btn>
|
||||
<v-btn text outlined class="ml-2"> / </v-btn>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
<v-card class="mx-2 mt-2">
|
||||
<v-card-title> Toggle CSS </v-card-title>
|
||||
<v-card-text class="text-center">
|
||||
<v-btn text outlined> F9 </v-btn>
|
||||
<span class="ml-2">or</span>
|
||||
<v-btn text outlined class="ml-2"> CTRL </v-btn>
|
||||
<v-btn text outlined class="ml-2"> ALT </v-btn>
|
||||
<v-btn text outlined class="ml-2"> D </v-btn>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-layout>
|
||||
</v-container>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
<v-dialog v-model="feedback.modal" width="700">
|
||||
<v-card color="card" elevation="7">
|
||||
<v-toolbar color="toolbar">
|
||||
<v-toolbar-title> Provide Feedback </v-toolbar-title>
|
||||
</v-toolbar>
|
||||
<v-card-text>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col cols="12" md="4" sm="6">
|
||||
Rating:
|
||||
<v-rating
|
||||
v-model="feedback.rating"
|
||||
background-color="grey darken-1"
|
||||
color="yellow darken-3"
|
||||
empty-icon="$ratingFull"
|
||||
hover
|
||||
></v-rating>
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<v-textarea
|
||||
class="rounded-xl"
|
||||
v-model="feedback.text"
|
||||
label="Enter your Feedback"
|
||||
required
|
||||
autofocus
|
||||
placeholder="Enter your Feedback"
|
||||
></v-textarea>
|
||||
</v-col>
|
||||
<small
|
||||
>Your feedback will be used to make
|
||||
{{ $store.state.site.name }} even better.</small
|
||||
>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
class="rounded-xl"
|
||||
color="blue darken-1"
|
||||
text
|
||||
@click="feedback.modal = false"
|
||||
>
|
||||
Close
|
||||
</v-btn>
|
||||
<v-btn
|
||||
class="rounded-xl"
|
||||
color="blue darken-1"
|
||||
text
|
||||
@click="submitFeedback()"
|
||||
>
|
||||
Submit
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
<v-dialog v-model="route.modal" width="700">
|
||||
<v-card color="card" elevation="7">
|
||||
<v-toolbar color="toolbar">
|
||||
<v-toolbar-title> Go to Route </v-toolbar-title>
|
||||
</v-toolbar>
|
||||
<v-card-text>
|
||||
<v-container>
|
||||
<v-text-field
|
||||
class="rounded-xl"
|
||||
v-model="route.value"
|
||||
autofocus
|
||||
@keyup.enter="goToRoute()"
|
||||
label="Route"
|
||||
required
|
||||
></v-text-field>
|
||||
</v-container>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn class="rounded-xl" text @click="route.modal = false">
|
||||
Close
|
||||
</v-btn>
|
||||
<v-btn
|
||||
class="rounded-xl"
|
||||
color="blue darken-1"
|
||||
text
|
||||
@click="goToRoute()"
|
||||
>
|
||||
Go
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
<v-app-bar app color="dark" elevation="5" style="z-index: 15">
|
||||
<v-app-bar-nav-icon
|
||||
@click.stop="$store.state.drawer = !$store.state.drawer"
|
||||
|
@ -397,6 +530,20 @@
|
|||
>
|
||||
Debug
|
||||
</button>
|
||||
<button
|
||||
style="display: none"
|
||||
v-shortkey="['ctrl', 'b']"
|
||||
@shortkey="route.modal = true"
|
||||
>
|
||||
Debug
|
||||
</button>
|
||||
<button
|
||||
style="display: none"
|
||||
v-shortkey="['ctrl', '/']"
|
||||
@shortkey="shortcuts = true"
|
||||
>
|
||||
Debug
|
||||
</button>
|
||||
<template v-if="$route.name === 'Communications'">
|
||||
<v-toolbar-title v-if="$store.state.selectedChat?.chat?.type">
|
||||
{{
|
||||
|
@ -406,28 +553,66 @@
|
|||
}}
|
||||
</v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
icon
|
||||
v-model="$store.state.context.pins.value"
|
||||
@click="show($event, 'pins', null, null, true)"
|
||||
id="pin-button"
|
||||
>
|
||||
<v-icon>mdi-pin-outline</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
icon
|
||||
@click="$store.state.searchPanel = !$store.state.searchPanel"
|
||||
>
|
||||
<v-icon>mdi-magnify</v-icon>
|
||||
</v-btn>
|
||||
<v-btn icon @click="$store.state.userPanel = !$store.state.userPanel">
|
||||
<v-icon>mdi-account-group-outline</v-icon>
|
||||
</v-btn>
|
||||
<v-tooltip bottom>
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-btn v-on="on" icon @click="feedback.modal = true">
|
||||
<v-icon>mdi-bug</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<span>Provide Feedback</span>
|
||||
</v-tooltip>
|
||||
<v-tooltip bottom>
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-btn
|
||||
icon
|
||||
v-model="$store.state.context.pins.value"
|
||||
@click="show($event, 'pins', null, null, true)"
|
||||
id="pin-button"
|
||||
v-on="on"
|
||||
>
|
||||
<v-icon>mdi-pin-outline</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<span>Chat Pins</span>
|
||||
</v-tooltip>
|
||||
<v-tooltip bottom>
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-btn
|
||||
icon
|
||||
@click="$store.state.searchPanel = !$store.state.searchPanel"
|
||||
v-on="on"
|
||||
>
|
||||
<v-icon>mdi-magnify</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<span>Search Messages</span>
|
||||
</v-tooltip>
|
||||
<v-tooltip bottom>
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-btn
|
||||
v-on="on"
|
||||
icon
|
||||
@click="$store.state.userPanel = !$store.state.userPanel"
|
||||
>
|
||||
<v-icon>mdi-account-group-outline</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<span>Toggle member list</span>
|
||||
</v-tooltip>
|
||||
</template>
|
||||
<template v-else>
|
||||
<v-toolbar-title>
|
||||
{{ $route.name }}
|
||||
</v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
<v-tooltip bottom>
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-btn v-on="on" icon @click="feedback.modal = true">
|
||||
<v-icon>mdi-bug</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<span>Provide Feedback</span>
|
||||
</v-tooltip>
|
||||
</template>
|
||||
</v-app-bar>
|
||||
<v-navigation-drawer
|
||||
|
@ -682,6 +867,17 @@ export default {
|
|||
components: { NicknameDialog },
|
||||
data() {
|
||||
return {
|
||||
shortcuts: false,
|
||||
route: {
|
||||
modal: false,
|
||||
value: ""
|
||||
},
|
||||
feedback: {
|
||||
modal: false,
|
||||
route: "",
|
||||
rating: 0,
|
||||
text: ""
|
||||
},
|
||||
search: "",
|
||||
nickname: {
|
||||
dialog: false,
|
||||
|
@ -757,6 +953,30 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
goToRoute() {
|
||||
this.$router.push(this.route.value)
|
||||
this.route.modal = false
|
||||
this.route.value = ""
|
||||
},
|
||||
submitFeedback() {
|
||||
this.axios
|
||||
.post("/api/v1/feedback", {
|
||||
text: this.feedback.text,
|
||||
starRating: this.feedback.rating,
|
||||
route: this.feedback.route
|
||||
})
|
||||
.then(() => {
|
||||
this.feedback.text = ""
|
||||
this.feedback.rating = 0
|
||||
this.feedback.modal = false
|
||||
this.$toast.success("Thank you for making a better Colubrina.")
|
||||
})
|
||||
.catch(() => {
|
||||
this.$toast.error(
|
||||
"Something went wrong while submitting feedback, you should submit feedback about this."
|
||||
)
|
||||
})
|
||||
},
|
||||
setNotifications(value) {
|
||||
this.axios
|
||||
.put("/api/v1/communications/settings/" + this.context.user.raw.id, {
|
||||
|
@ -1049,6 +1269,7 @@ export default {
|
|||
}
|
||||
},
|
||||
mounted() {
|
||||
this.feedback.route = this.$route.path
|
||||
Vue.axios.defaults.headers.common["Authorization"] =
|
||||
localStorage.getItem("token")
|
||||
this.searchUsers()
|
||||
|
@ -1108,6 +1329,11 @@ export default {
|
|||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
watch: {
|
||||
$route(to) {
|
||||
this.feedback.route = to.path
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -139,6 +139,14 @@ const routes = [
|
|||
import(
|
||||
/* webpackChunkName: "adminLogs" */ "../views/Admin/AdminLogs.vue"
|
||||
)
|
||||
},
|
||||
{
|
||||
path: "feedback",
|
||||
name: "Feedback",
|
||||
component: () =>
|
||||
import(
|
||||
/* webpackChunkName: "adminFeedback" */ "../views/Admin/AdminFeedback.vue"
|
||||
)
|
||||
}
|
||||
],
|
||||
component: () =>
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
<v-icon>mdi-account-multiple</v-icon>
|
||||
<span>Users</span>
|
||||
</v-tab>
|
||||
<v-tab to="/admin/feedback">
|
||||
<v-icon>mdi-bug</v-icon>
|
||||
<span>Feedback</span>
|
||||
</v-tab>
|
||||
<v-tab to="/admin/themes">
|
||||
<v-icon>mdi-brush</v-icon>
|
||||
<span>Themes</span>
|
||||
|
|
61
frontend/src/views/Admin/AdminFeedback.vue
Normal file
61
frontend/src/views/Admin/AdminFeedback.vue
Normal file
|
@ -0,0 +1,61 @@
|
|||
<template>
|
||||
<div id="admin-feedback">
|
||||
<v-toolbar color="toolbar">
|
||||
<v-toolbar-title>Feedback ({{ feedback.count }})</v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn @click="getFeedback" icon>
|
||||
<v-icon>mdi-refresh</v-icon>
|
||||
</v-btn>
|
||||
</v-toolbar>
|
||||
<v-container :fluid="$vuetify.breakpoint.lgAndDown">
|
||||
<v-card
|
||||
v-for="item in feedback.rows"
|
||||
:key="item.id"
|
||||
class="rounded-xl mb-2"
|
||||
color="card"
|
||||
>
|
||||
<v-toolbar color="toolbar">
|
||||
<v-toolbar-title>
|
||||
Feedback -
|
||||
{{ item.user.username }} ({{ item.user.id }})
|
||||
</v-toolbar-title>
|
||||
</v-toolbar>
|
||||
<v-container :fluid="$vuetify.breakpoint.lgAndDown">
|
||||
{{ item.feedbackText.substring(0, 1000) }}
|
||||
<br />
|
||||
<small v-if="item.user">
|
||||
Route: {{ item.route }}<br />
|
||||
Rating: {{ item.starRating }}<br />
|
||||
Created At:
|
||||
{{ $date(item.createdAt).format("YYYY-MM-DD hh:mm A") }}</small
|
||||
>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "AdminFeedback",
|
||||
data() {
|
||||
return {
|
||||
feedback: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getFeedback() {
|
||||
this.axios
|
||||
.get(process.env.VUE_APP_BASE_URL + "/api/v1/admin/feedback")
|
||||
.then((res) => {
|
||||
this.feedback = res.data
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getFeedback()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
Loading…
Reference in a new issue