mirror of
https://github.com/Drop-OSS/drop.git
synced 2026-01-31 15:37:09 +01:00
Add user profile page (#302)
* Add user page and API endpoint * add: /user/[id] page * add: /api/v1/user/[id] API endpoint * Change loading message in user profile page * Fix build errors, prettier code
This commit is contained in:
@@ -629,6 +629,14 @@
|
|||||||
"type": "Type",
|
"type": "Type",
|
||||||
"upload": "Upload",
|
"upload": "Upload",
|
||||||
"uploadFile": "Upload file",
|
"uploadFile": "Upload file",
|
||||||
|
"user": {
|
||||||
|
"unknown": "Unknown user",
|
||||||
|
"editProfile": "Edit profile",
|
||||||
|
"recent": "Recent activity (TODO)",
|
||||||
|
"recentSub": "Recent activity by this user",
|
||||||
|
"notFound": "User not found",
|
||||||
|
"noActivity": "No recent activity"
|
||||||
|
},
|
||||||
"userHeader": {
|
"userHeader": {
|
||||||
"closeSidebar": "Close sidebar",
|
"closeSidebar": "Close sidebar",
|
||||||
"links": {
|
"links": {
|
||||||
|
|||||||
75
pages/user/[id]/index.vue
Normal file
75
pages/user/[id]/index.vue
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
<template>
|
||||||
|
<div class="max-w-6xl mx-auto px-4 py-10">
|
||||||
|
<div class="flex items-center gap-x-6">
|
||||||
|
<img
|
||||||
|
v-if="profile?.profilePictureObjectId"
|
||||||
|
:src="useObject(profile.profilePictureObjectId)"
|
||||||
|
class="w-24 h-24 rounded-md object-cover"
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<h1 class="text-2xl font-bold font-display text-zinc-100">
|
||||||
|
{{ profile?.displayName ?? profile?.username ?? $t("user.unknown") }}
|
||||||
|
</h1>
|
||||||
|
<!-- eslint-disable-next-line @intlify/vue-i18n/no-raw-text -->
|
||||||
|
<div class="text-zinc-400 mt-1">@{{ profile?.username }}</div>
|
||||||
|
<div class="mt-3">
|
||||||
|
<NuxtLink
|
||||||
|
v-if="isCurrentUser"
|
||||||
|
to="/account"
|
||||||
|
class="px-3 py-2 bg-zinc-800 rounded text-sm text-zinc-200 hover:bg-zinc-700"
|
||||||
|
>
|
||||||
|
{{ $t("user.editProfile") }}
|
||||||
|
</NuxtLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-10">
|
||||||
|
<h2 class="text-xl font-semibold font-display text-zinc-100">
|
||||||
|
{{ $t("user.recent") }}
|
||||||
|
</h2>
|
||||||
|
<p class="mt-2 text-zinc-400">{{ $t("user.recentSub") }}</p>
|
||||||
|
|
||||||
|
<div class="mt-6">
|
||||||
|
<div v-if="loading" class="text-zinc-500">
|
||||||
|
{{ $t("common.srLoading") }}
|
||||||
|
</div>
|
||||||
|
<div v-else-if="!profile">
|
||||||
|
<div class="text-zinc-400">{{ $t("user.notFound") }}</div>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<div class="mt-4 text-zinc-400">{{ $t("user.noActivity") }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useObject } from "~/composables/objects";
|
||||||
|
import { useUser } from "~/composables/user";
|
||||||
|
import type { UserModel } from "~/prisma/client/models";
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const id = (route.params.id ?? "") as string;
|
||||||
|
|
||||||
|
const loading = ref(true);
|
||||||
|
let profile: UserModel | null = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
profile = await $dropFetch(`/api/v1/user/${id}`);
|
||||||
|
} catch {
|
||||||
|
profile = null;
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const current = useUser();
|
||||||
|
const isCurrentUser = computed(
|
||||||
|
() => !!current.value && current.value.id === profile?.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
useHead({
|
||||||
|
title: profile?.displayName ?? profile?.username ?? "User",
|
||||||
|
});
|
||||||
|
</script>
|
||||||
29
server/api/v1/user/[id]/index.get.ts
Normal file
29
server/api/v1/user/[id]/index.get.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import aclManager from "~/server/internal/acls";
|
||||||
|
import prisma from "~/server/internal/db/database";
|
||||||
|
|
||||||
|
export default defineEventHandler(async (h3) => {
|
||||||
|
const requestingUser = await aclManager.getUserACL(h3, ["read"]);
|
||||||
|
if (!requestingUser) throw createError({ statusCode: 403 });
|
||||||
|
|
||||||
|
const userId = getRouterParam(h3, "id");
|
||||||
|
if (!userId)
|
||||||
|
throw createError({
|
||||||
|
statusCode: 400,
|
||||||
|
statusMessage: "No userId in route.",
|
||||||
|
});
|
||||||
|
|
||||||
|
const user = await prisma.user.findUnique({
|
||||||
|
where: { id: userId },
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
username: true,
|
||||||
|
displayName: true,
|
||||||
|
profilePictureObjectId: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!user)
|
||||||
|
throw createError({ statusCode: 404, statusMessage: "User not found." });
|
||||||
|
|
||||||
|
return user;
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user