implement admin flags page

This commit is contained in:
MiniDigger 2022-03-19 11:49:40 +01:00 committed by MiniDigger | Martin
parent 1d9492511f
commit 908c3e6944
4 changed files with 82 additions and 5 deletions

View File

@ -15,6 +15,7 @@ Stuff that needs to be done before I consider this a successful POC
- [ ] maybe deployment alongside the existing frontend? (server is working now)
- [ ] figure out why vite isn't serving the manifest
- [ ] cors?
- [ ] i18n + ssr, server needs local for both logged in and anon user, date formatting needs to go thru i18n
- [x] investigate why eslint/prettier don't auto fix
## Big list of pages!
@ -211,9 +212,9 @@ once QA has passed, the checkboxes can be removed and the page can be ~~striked
- [ ] qa
- admin (empty)
- flags
- [ ] fetch
- [ ] layout
- [ ] functionality
- [x] fetch
- [x] layout
- [x] functionality (cors error)
- [ ] design
- [ ] qa
- health

View File

@ -0,0 +1,9 @@
<script setup lang="ts">
const props = defineProps<{
username: string;
}>();
</script>
<template>
<span>{{ username }}'s avatar</span>
</template>

View File

@ -1,7 +1,7 @@
import { useApi, useInternalApi } from "~/composables/useApi";
import { PaginatedResult, Project, User } from "hangar-api";
import { useInitialState } from "~/composables/useInitialState";
import { HangarNotification, Invites } from "hangar-internal";
import { Flag, HangarNotification, Invites } from "hangar-internal";
export async function useProjects(pagination = { limit: 25, offset: 0 }, blocking = true) {
return await useInitialState("useProjects", () => useApi<PaginatedResult<Project>>("projects", false, "get", pagination), blocking);
@ -30,3 +30,7 @@ export async function useInvites(blocking = true) {
export async function useNotifications(blocking = true) {
return await useInitialState("useNotifications", () => useInternalApi<HangarNotification[]>("notifications", false), blocking);
}
export async function useFlags(blocking = true) {
return await useInitialState("useFlags", () => useInternalApi<Flag[]>("flags/", false), blocking);
}

View File

@ -1,5 +1,68 @@
<script lang="ts" setup>
import { useContext } from "vite-ssr/vue";
import { useI18n } from "vue-i18n";
import { useRoute } from "vue-router";
import { useFlags } from "~/composables/useApiHelper";
import { handleRequestError } from "~/composables/useErrorHandling";
import { ref } from "vue";
import { Flag } from "hangar-internal";
import { useInternalApi } from "~/composables/useApi";
import UserAvatar from "~/components/UserAvatar.vue";
import { prettyDateTime } from "~/composables/useDate";
const ctx = useContext();
const i18n = useI18n();
const { params } = useRoute();
const flags = await useFlags().catch((e) => handleRequestError(e, ctx, i18n));
const loading = ref<{ [key: number]: boolean }>({});
if (flags && flags.value) {
for (const flag of flags.value) {
loading.value[flag.id] = false;
}
}
function resolve(flag: Flag) {
loading.value[flag.id] = true;
useInternalApi<Flag[]>(`flags/${flag.id}/resolve/true`, false, "POST")
.catch<any>((e) => handleRequestError(e, ctx, i18n))
.then(async () => {
if (flags && flags.value) {
const newFlags = await useInternalApi<Flag[]>("flags/", false).catch((e) => handleRequestError(e, ctx, i18n));
if (newFlags) {
flags.value = newFlags;
}
}
})
.finally(() => {
loading.value[flag.id] = false;
});
}
</script>
<template>
<h1>flags</h1>
<h1>{{ i18n.t("flagReview.title") }}</h1>
<template v-if="flags.length > 0">
<div v-for="flag in flags" :key="flag.id">
<UserAvatar :username="flag.reportedByName"></UserAvatar>
<h2>
<!-- this is client only, as the date format causes hydration mismatches... -->
<!-- I think the proper fix is using vue-i18n for date format -->
<ClientOnly>{{
i18n.t("flagReview.line1", [flag.reportedByName, `${flag.projectNamespace.owner}/${flag.projectNamespace.slug}`, prettyDateTime(flag.createdAt)])
}}</ClientOnly>
<router-link :to="`/${flag.projectNamespace.owner}/${flag.projectNamespace.slug}`" target="_blank">open in new </router-link>
</h2>
<h3>{{ i18n.t("flagReview.line2", [i18n.t(flag.reason)]) }}</h3>
<h3>{{ i18n.t("flagReview.line3", [flag.comment]) }}</h3>
<a fixHref="$util.forumUrl(flag.reportedByName)">{{ i18n.t("flagReview.msgUser") }}</a>
<a fixHref="$util.forumUrl(flag.projectNamespace.owner)">{{ i18n.t("flagReview.msgProjectOwner") }}</a>
<!-- todo modal for visibility change -->
<button :disabled="loading[flag.id]" @click="resolve(flag)">{{ i18n.t("flagReview.markResolved") }}</button>
</div>
</template>
<div v-else>
{{ i18n.t("flagReview.noFlags") }}
</div>
</template>
<route lang="yaml">