generated from VLADIMIR/template_frontend
up
This commit is contained in:
@@ -228,6 +228,8 @@ export type Scenario = {
|
||||
updatedAt: wellKnownTimestamp | undefined;
|
||||
createdAt: wellKnownTimestamp | undefined;
|
||||
publishedAt: wellKnownTimestamp | undefined;
|
||||
averageRating: number | undefined;
|
||||
reviewsCount: number | undefined;
|
||||
};
|
||||
|
||||
export type Story = {
|
||||
@@ -718,6 +720,51 @@ export type GetGameResultsRsp = {
|
||||
teams: TeamResult[] | undefined;
|
||||
};
|
||||
|
||||
export type GameReview = {
|
||||
id: number | undefined;
|
||||
gameId: number | undefined;
|
||||
teamId: number | undefined;
|
||||
teamName: string | undefined;
|
||||
rating: number | undefined;
|
||||
text: string | undefined;
|
||||
status: string | undefined;
|
||||
createdAt: wellKnownTimestamp | undefined;
|
||||
};
|
||||
|
||||
export type CreateGameReviewReq = {
|
||||
gameId: number | undefined;
|
||||
teamId: number | undefined;
|
||||
password: string | undefined;
|
||||
rating: number | undefined;
|
||||
text: string | undefined;
|
||||
};
|
||||
|
||||
export type CreateGameReviewRsp = {
|
||||
error: string | undefined;
|
||||
review: GameReview | undefined;
|
||||
};
|
||||
|
||||
export type GetGameReviewsReq = {
|
||||
gameId: number | undefined;
|
||||
};
|
||||
|
||||
export type GetGameReviewsRsp = {
|
||||
error: string | undefined;
|
||||
averageRating: number | undefined;
|
||||
reviewsCount: number | undefined;
|
||||
reviews: GameReview[] | undefined;
|
||||
};
|
||||
|
||||
export type ModerateGameReviewReq = {
|
||||
id: number | undefined;
|
||||
status: string | undefined;
|
||||
};
|
||||
|
||||
export type ModerateGameReviewRsp = {
|
||||
error: string | undefined;
|
||||
review: GameReview | undefined;
|
||||
};
|
||||
|
||||
export type UpdateGameReq = {
|
||||
id: number | undefined;
|
||||
name: string | undefined;
|
||||
@@ -961,6 +1008,10 @@ export interface EveningDetectiveServer {
|
||||
DownloadTeamQR(request: GetGameReq): Promise<googleapi_HttpBody>;
|
||||
GetGameAnswers(request: GetGameAnswersReq): Promise<GetGameAnswersRsp>;
|
||||
GetGameResults(request: GetGameResultsReq): Promise<GetGameResultsRsp>;
|
||||
CreateGameReview(request: CreateGameReviewReq): Promise<CreateGameReviewRsp>;
|
||||
GetGameReviews(request: GetGameReviewsReq): Promise<GetGameReviewsRsp>;
|
||||
GetGameReviewsForModeration(request: GetGameReviewsReq): Promise<GetGameReviewsRsp>;
|
||||
ModerateGameReview(request: ModerateGameReviewReq): Promise<ModerateGameReviewRsp>;
|
||||
UpdateGame(request: UpdateGameReq): Promise<UpdateGameRsp>;
|
||||
StartGame(request: StartGameReq): Promise<StartGameRsp>;
|
||||
PauseGame(request: PauseGameReq): Promise<PauseGameRsp>;
|
||||
@@ -2098,6 +2149,86 @@ export function createEveningDetectiveServerClient(
|
||||
method: "GetGameResults",
|
||||
}) as Promise<GetGameResultsRsp>;
|
||||
},
|
||||
CreateGameReview(request) { // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
if (!request.gameId) {
|
||||
throw new Error("missing required field request.gameId");
|
||||
}
|
||||
const path = `api/games/${request.gameId}/reviews`; // eslint-disable-line quotes
|
||||
const body = JSON.stringify(request);
|
||||
const queryParams: string[] = [];
|
||||
let uri = path;
|
||||
if (queryParams.length > 0) {
|
||||
uri += `?${queryParams.join("&")}`
|
||||
}
|
||||
return handler({
|
||||
path: uri,
|
||||
method: "POST",
|
||||
body,
|
||||
}, {
|
||||
service: "EveningDetectiveServer",
|
||||
method: "CreateGameReview",
|
||||
}) as Promise<CreateGameReviewRsp>;
|
||||
},
|
||||
GetGameReviews(request) { // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
if (!request.gameId) {
|
||||
throw new Error("missing required field request.gameId");
|
||||
}
|
||||
const path = `api/games/${request.gameId}/reviews`; // eslint-disable-line quotes
|
||||
const body = null;
|
||||
const queryParams: string[] = [];
|
||||
let uri = path;
|
||||
if (queryParams.length > 0) {
|
||||
uri += `?${queryParams.join("&")}`
|
||||
}
|
||||
return handler({
|
||||
path: uri,
|
||||
method: "GET",
|
||||
body,
|
||||
}, {
|
||||
service: "EveningDetectiveServer",
|
||||
method: "GetGameReviews",
|
||||
}) as Promise<GetGameReviewsRsp>;
|
||||
},
|
||||
GetGameReviewsForModeration(request) { // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
if (!request.gameId) {
|
||||
throw new Error("missing required field request.gameId");
|
||||
}
|
||||
const path = `api/admin/games/${request.gameId}/reviews`; // eslint-disable-line quotes
|
||||
const body = null;
|
||||
const queryParams: string[] = [];
|
||||
let uri = path;
|
||||
if (queryParams.length > 0) {
|
||||
uri += `?${queryParams.join("&")}`
|
||||
}
|
||||
return handler({
|
||||
path: uri,
|
||||
method: "GET",
|
||||
body,
|
||||
}, {
|
||||
service: "EveningDetectiveServer",
|
||||
method: "GetGameReviewsForModeration",
|
||||
}) as Promise<GetGameReviewsRsp>;
|
||||
},
|
||||
ModerateGameReview(request) { // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
if (!request.id) {
|
||||
throw new Error("missing required field request.id");
|
||||
}
|
||||
const path = `api/admin/game-reviews/${request.id}`; // eslint-disable-line quotes
|
||||
const body = JSON.stringify(request);
|
||||
const queryParams: string[] = [];
|
||||
let uri = path;
|
||||
if (queryParams.length > 0) {
|
||||
uri += `?${queryParams.join("&")}`
|
||||
}
|
||||
return handler({
|
||||
path: uri,
|
||||
method: "PUT",
|
||||
body,
|
||||
}, {
|
||||
service: "EveningDetectiveServer",
|
||||
method: "ModerateGameReview",
|
||||
}) as Promise<ModerateGameReviewRsp>;
|
||||
},
|
||||
UpdateGame(request) { // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
if (!request.id) {
|
||||
throw new Error("missing required field request.id");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { NTag } from 'naive-ui'
|
||||
import { NRate, NTag } from 'naive-ui'
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
@@ -45,6 +45,12 @@ getScenarios()
|
||||
|
||||
<p class="scenario-description">{{ scenario.description }}</p>
|
||||
|
||||
<div class="scenario-rating">
|
||||
<n-rate :value="scenario.averageRating" readonly allow-half size="small" />
|
||||
<span v-if="scenario.reviewsCount">{{ scenario.averageRating?.toFixed(1) }} · {{ scenario.reviewsCount }}</span>
|
||||
<span v-else>Пока нет оценок</span>
|
||||
</div>
|
||||
|
||||
<p class="scenario-label">
|
||||
<n-tag :bordered="false" type="success" class="status-block"
|
||||
>Автор: {{ scenario.author?.username }}</n-tag
|
||||
@@ -124,6 +130,14 @@ getScenarios()
|
||||
padding-top: 12px;
|
||||
}
|
||||
|
||||
.scenario-rating {
|
||||
align-items: center;
|
||||
color: #aaa;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.status-block {
|
||||
height: auto;
|
||||
line-height: 1.5;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { useMessage } from 'naive-ui'
|
||||
import { NTag } from 'naive-ui'
|
||||
import { NRate, NTag, useMessage } from 'naive-ui'
|
||||
import { ref } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
@@ -23,6 +22,8 @@ const scenario = ref<Scenario>({
|
||||
updatedAt: undefined,
|
||||
createdAt: undefined,
|
||||
publishedAt: undefined,
|
||||
averageRating: undefined,
|
||||
reviewsCount: undefined,
|
||||
})
|
||||
|
||||
async function getScenario(id: number) {
|
||||
@@ -49,6 +50,11 @@ getScenario(Number(scenarioId))
|
||||
</div>
|
||||
<div class="scenario-details">
|
||||
<p class="scenario-description">{{ scenario.description }}</p>
|
||||
<div class="scenario-rating">
|
||||
<n-rate :value="scenario.averageRating" readonly allow-half size="small" />
|
||||
<span v-if="scenario.reviewsCount">{{ scenario.averageRating?.toFixed(1) }} · {{ scenario.reviewsCount }} отзывов</span>
|
||||
<span v-else>Пока нет оценок</span>
|
||||
</div>
|
||||
<div class="scenario-author">
|
||||
<n-tag :bordered="false" type="success" class="status-block">
|
||||
Автор: {{ scenario.author?.username }}
|
||||
@@ -124,6 +130,14 @@ getScenario(Number(scenarioId))
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.scenario-rating {
|
||||
align-items: center;
|
||||
color: #aaa;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.status-block {
|
||||
height: auto;
|
||||
line-height: 1.5;
|
||||
|
||||
@@ -58,6 +58,8 @@ const scenario = ref<Scenario>({
|
||||
updatedAt: undefined,
|
||||
createdAt: undefined,
|
||||
publishedAt: undefined,
|
||||
averageRating: undefined,
|
||||
reviewsCount: undefined,
|
||||
})
|
||||
|
||||
const backgroundFiles = ref<UploadFileInfo[]>([])
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { NButton, NConfigProvider, NFlex, NInput, NQrCode } from 'naive-ui'
|
||||
import { NButton, NConfigProvider, NFlex, NInput, NQrCode, NRate } from 'naive-ui'
|
||||
import { useMessage } from 'naive-ui'
|
||||
import { computed, nextTick, ref } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
@@ -51,6 +51,12 @@ const game = ref<Game | undefined>({
|
||||
|
||||
const code = ref('')
|
||||
|
||||
const reviewKey = `game-review-${teamId}`
|
||||
const reviewSubmitted = ref(localStorage.getItem(reviewKey) === '1')
|
||||
const reviewRating = ref(0)
|
||||
const reviewText = ref('')
|
||||
const reviewLoading = ref(false)
|
||||
|
||||
const url = window.location.href
|
||||
|
||||
const isAtBottom = () => {
|
||||
@@ -212,6 +218,29 @@ const paperControlsOverrides = {
|
||||
},
|
||||
}
|
||||
|
||||
async function submitReview() {
|
||||
if (!reviewRating.value || reviewText.value.trim() === '') {
|
||||
message.error('Поставьте оценку и напишите отзыв')
|
||||
return
|
||||
}
|
||||
reviewLoading.value = true
|
||||
const res = await client.CreateGameReview({
|
||||
gameId: Number(game.value?.id),
|
||||
teamId: Number(teamId),
|
||||
password: password as string,
|
||||
rating: reviewRating.value,
|
||||
text: reviewText.value.trim(),
|
||||
})
|
||||
reviewLoading.value = false
|
||||
if (res.error != '') {
|
||||
message.error(res.error!)
|
||||
return
|
||||
}
|
||||
reviewSubmitted.value = true
|
||||
localStorage.setItem(reviewKey, '1')
|
||||
message.success('Спасибо за отзыв')
|
||||
}
|
||||
|
||||
async function deleteLast() {
|
||||
const res = await client.DeleteLastTeamAction({
|
||||
id: Number(teamId),
|
||||
@@ -245,6 +274,24 @@ useSimplePolling(() => {
|
||||
<MessagePaper role="status">
|
||||
<div class="font">Игра завершена. Спасибо за участие!</div>
|
||||
</MessagePaper>
|
||||
<MessagePaper v-if="!reviewSubmitted" class="review-block">
|
||||
<div class="font review-title">Оцените игру</div>
|
||||
<hr class="review-hr" />
|
||||
<div class="font review-label">Оценка</div>
|
||||
<n-rate v-model:value="reviewRating" :count="5" />
|
||||
<n-input
|
||||
v-model:value="reviewText"
|
||||
class="review-input"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 4, maxRows: 8 }"
|
||||
placeholder="Расскажите, как прошла игра"
|
||||
/>
|
||||
<n-flex justify="end">
|
||||
<button type="button" class="paper-button" :disabled="reviewLoading" @click="submitReview">
|
||||
Отправить отзыв
|
||||
</button>
|
||||
</n-flex>
|
||||
</MessagePaper>
|
||||
</div>
|
||||
<div v-else class="width700">
|
||||
<GameInputForm
|
||||
@@ -347,6 +394,29 @@ useSimplePolling(() => {
|
||||
height: 100dvh;
|
||||
}
|
||||
|
||||
.review-block {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.review-title {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.review-hr {
|
||||
border: dashed 1px;
|
||||
border-color: black;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.review-label {
|
||||
color: black;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.review-input {
|
||||
margin: 14px 0;
|
||||
}
|
||||
|
||||
.smooth-scroll {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user