traxxx-web/pages/users/@username/+Page.vue

236 lines
4.0 KiB
Vue

<template>
<div class="page">
<div class="profile">
<div class="profile-header">
<div class="user">
<img
v-if="profile.avatar"
:src="profile.avatar"
class="avatar"
>
<h2 class="username ellipsis">{{ profile.username }}</h2>
</div>
<span class="age">{{ formatDistanceStrict(Date.now(), profile.createdAt) }}</span>
</div>
<section class="profile-section">
<div class="section-header">
<h3 class="heading">Stashes</h3>
<button
class="button"
@click="showStashDialog = true"
>
<Icon icon="plus3" />
<span class="button-label">New stash</span>
</button>
</div>
<Dialog
v-if="showStashDialog"
title="New stash"
@close="showStashDialog = false"
@open="stashNameInput?.focus()"
>
<form
class="dialog-body"
@submit.prevent="createStash"
>
<input
ref="stashNameInput"
v-model="stashName"
maxlength="24"
placeholder="Stash name"
class="input"
>
<button
class="button button-submit"
>Create stash</button>
</form>
</Dialog>
<ul class="stashes nolist">
<li
v-for="stash in profile.stashes"
:key="`stash-${stash.id}`"
>
<StashTile
:stash="stash"
:profile="profile"
@reload="reloadProfile"
/>
</li>
</ul>
</section>
</div>
</div>
</template>
<script setup>
import { ref, inject } from 'vue';
import { formatDistanceStrict } from 'date-fns';
import { get, post } from '#/src/api.js';
import StashTile from '#/components/stashes/tile.vue';
import Dialog from '#/components/dialog/dialog.vue';
const pageContext = inject('pageContext');
const profile = ref(pageContext.pageProps.profile);
const stashName = ref(null);
const stashNameInput = ref(null);
const done = ref(true);
const showStashDialog = ref(false);
async function reloadProfile() {
profile.value = await get(`/users/${profile.value.id}`);
}
async function createStash() {
if (done.value === false) {
return;
}
done.value = false;
await post('/stashes', {
name: stashName.value,
public: false,
}, {
successFeedback: `Created stash '${stashName.value}'`,
errorFeedback: `Failed to create stash '${stashName.value}'`,
appendErrorMessage: true,
}).finally(() => { done.value = true; });
showStashDialog.value = false;
stashName.value = null;
await reloadProfile();
}
</script>
<style scoped>
.page {
display: flex;
flex-grow: 1;
justify-content: center;
background: var(--background-base-10);
}
.profile {
width: 1200px;
max-width: 100%;
}
.profile-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: .5rem 1rem;
color: var(--highlight-strong-30);
background: var(--grey-dark-40);
border-radius: 0 0 .5rem .5rem;
}
.user {
display: flex;
overflow: hidden;
}
.username {
margin: 0;
font-size: 1.25rem;
}
.age {
display: flex;
flex-shrink: 0;
font-size: .9rem;
.icon {
width: .9rem;
fill: var(--highlight-strong-20);
margin-right: .75rem;
transform: translateY(-1px);
}
}
.avatar {
width: 1.5rem;
height: 1.5rem;
border-radius: .25rem;
margin-right: 1rem;
}
.section-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: .5rem .5rem .5rem .5rem;
.button {
margin-left: 1rem;
}
}
.heading {
margin: 0;
font-size: 1.1rem;
color: var(--primary);
}
.stashes {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
gap: 1rem;
padding: 0 .5rem 1rem .5rem;
}
.dialog-body {
padding: 1rem;
.input {
margin-bottom: .5rem;
}
}
@media(--compact) {
.profile-header {
border-radius: 0;
}
.section-header {
padding: .5rem 1rem .5rem 1rem;
}
.stashes {
padding: 0 1rem 1rem 1rem;
}
}
@media(--small-30) {
.section-header {
padding: .5rem .5rem .5rem .5rem;
}
.stashes {
padding: 0 .5rem 1rem .5rem;
}
.age .icon {
display: none;
}
}
@media(--small-50) {
.age {
display: none;
}
}
</style>