Skip to content

Commit

Permalink
Hande availability change; add mutations to store; split view and edi…
Browse files Browse the repository at this point in the history
…t mode
  • Loading branch information
Bettelstab committed Aug 9, 2024
1 parent fe7815b commit 638c4dc
Show file tree
Hide file tree
Showing 14 changed files with 464 additions and 191 deletions.
3 changes: 3 additions & 0 deletions frontend/src/assets/icons/back.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions frontend/src/assets/icons/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Back from './back.svg?component'
import Bell from './bell.svg?component'
import Camera from './camera.svg?component'
import Cockpit from './cockpit.svg?component'
Expand All @@ -13,6 +14,7 @@ import Message from './message.svg?component'
import Pinterest from './pinterest.svg?component'
import Place from './place.svg?component'
import Share from './share.svg?component'
import Triangle from './triangle.svg?component'
import Website from './website.svg?component'
import Working from './working.svg?component'
import WorldCafe from './worldCafe.svg?component'
Expand All @@ -23,6 +25,7 @@ import Youtube from './youtube.svg?component'
import type { IconAliases } from 'vuetify'

const aliases: Partial<IconAliases> = {
back: Back,
bell: Bell,
camera: Camera,
cockpit: Cockpit,
Expand All @@ -38,6 +41,7 @@ const aliases: Partial<IconAliases> = {
pinterest: Pinterest,
place: Place,
share: Share,
triange: Triangle,
website: Website,
working: Working,
'world-cafe': WorldCafe,
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/assets/icons/triangle.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 1 addition & 19 deletions frontend/src/components/cockpit/about-me/AboutMe.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,7 @@ const meta = {
component: AboutMe as SBComp,
tags: ['autodocs'],
argTypes: {},
args: {
name: 'John Doe',
initials: 'JD',
introduction: 'Pushing pixels and experiences',
availability: 'available',
details: [
{ category: 'place', text: 'Berlin, Germany' },
{ category: 'place', text: 'Madrid, Spain' },
{ category: 'work', text: 'Frontend Developer at Company XY' },
{ category: 'language', text: 'English, German, Spanish' },
{ category: 'education', text: 'B.Sc. Computer Science' },
{ category: 'feeling', text: 'Happy like a really happy honey cake horse' },
],
social: {
linkedin: 'https://www.linkedin.com/in/johndoe',
xing: 'https://www.xing.com/profile/John_Doe',
x: 'https://www.x.com/johndoe',
},
},
args: {},
} satisfies Meta<typeof AboutMe>

export default meta
Expand Down
224 changes: 55 additions & 169 deletions frontend/src/components/cockpit/about-me/AboutMe.vue
Original file line number Diff line number Diff line change
@@ -1,187 +1,73 @@
<template>
<CockpitCard narrow>
<template #default>
<div class="header">
<v-avatar class="avatar d-flex align-center text-font bg-primary" size="75">
<v-img v-if="props.userImage" :src="props.userImage" />
<span v-else>{{ props.initials }}</span>
</v-avatar>
<select v-model="availability" class="availability">
<option v-for="item in availabilityOptions" :key="item.value" :value="item.value">
{{ item.circle }} {{ item.text }} <v-icon icon="$chevronDown"></v-icon>
</option>
</select>
<div class="name">{{ props.name }}</div>
<div class="introduction">{{ props.introduction }}</div>
</div>
<ul class="details">
<li v-for="(detail, index) in props.details" :key="index">
<v-chip :prepend-icon="getIcon(detail.category)" class="detail">{{ detail.text }}</v-chip>
</li>
</ul>
<ul class="social">
<li v-if="props.social.facebook">
<a :href="props.social.facebook" target="_blank" rel="noopener noreferrer">
<v-icon icon="$facebook"></v-icon>
</a>
</li>
<li v-if="props.social.linkedin">
<a :href="props.social.linkedin" target="_blank" rel="noopener noreferrer">
<v-icon icon="$linkedIn"></v-icon>
</a>
</li>
<li v-if="props.social.xing">
<a :href="props.social.xing" target="_blank" rel="noopener noreferrer">
<v-icon icon="$xing"></v-icon>
</a>
</li>
<li v-if="props.social.x">
<a :href="props.social.x" target="_blank" rel="noopener noreferrer">
<v-icon icon="$x"></v-icon>
</a>
</li>
</ul>
</template>
</CockpitCard>
<ClientOnly>
<AboutMeView
v-if="user && mode === 'view'"
:username="user.username"
:name="user.name"
:availability="user.availability"
:introduction="user.introduction"
:details="user.details"
:social="user.social"
:user-image="avatar"
:initials="initials"
@edit="() => setMode('edit')"
@update-availability="updateAvailability"
></AboutMeView>
<AboutMeEdit
v-if="user && mode === 'edit'"
:username="user.username"
:name="user.name"
:availability="user.availability"
:introduction="user.introduction"
:details="user.details"
:social="user.social"
@back="() => setMode('view')"
></AboutMeEdit>
</ClientOnly>
</template>

<script lang="ts" setup>
import { ref } from 'vue'
import CockpitCard from '#components/cockpit/cockpitCard/CockpitCard.vue'
import ClientOnly from '#components/ClientOnly.vue'
import globalErrorHandler from '#plugins/globalErrorHandler'
import { useUserStore, UserAvailability, UserDetail } from '#stores/userStore'
type DetailCategory = 'place' | 'work' | 'language' | 'education' | 'feeling'
import AboutMeEdit from './AboutMeEdit.vue'
import AboutMeView from './AboutMeView.vue'
type Detail = {
category: DetailCategory
text: string
}
type Mode = 'view' | 'edit'
const mode = ref<Mode>('view')
const props = defineProps<{
userName: string
name: string
userImage?: string
initials?: string
introduction?: string
availability?: 'available' | 'partly_available' | 'busy'
details: Detail[]
social: {
discord?: string
telegram?: string
facebook?: string
tiktok?: string
snapchat?: string
reddit?: string
wechat?: string
instagram?: string
pintarest?: string
linkedin?: string
youtube?: string
whatsapp?: string
xing?: string
x?: string
}
}>()
const setMode = (newMode: Mode) => (mode.value = newMode)
const availability = ref(props.availability)
const userStore = useUserStore()
const availabilityOptions = [
{ circle: '🟢', value: 'available', text: 'Available to work' },
{ circle: '🟡', value: 'partly_available', text: 'Busy but have time' },
{ circle: '🔴', value: 'busy', text: 'Busy' },
]
const {
getCurrentUser: user,
getCurrentUserAvatar: avatar,
getCurrentUserInitials: initials,
} = userStore
function getIcon(category: DetailCategory) {
switch (category) {
case 'place':
return '$place'
case 'work':
return '$working'
case 'language':
return '$world'
case 'education':
return '$education'
case 'feeling':
return '$feeling'
default:
throw new Error(`Unknown category: ${category}`)
const updateAvailability = async (newAvailability: UserAvailability) => {
try {
await userStore.updateUser({
name: user!.name,
availability: newAvailability,
})
} catch (error) {
globalErrorHandler.error(`Could not update user: ${(error as Error).message}`, error)
}
}
</script>

<style scoped>
.header {
display: grid;
grid-template-columns: 75px 1fr;
grid-template-rows: 1 fr 1fr 1fr;
gap: 10px;
}
.avatar {
grid-row: 1 / 3;
border-radius: 15px;
}
.availability select {
display: none;
}
.availability::after {
grid-column: 2;
grid-row: 1;
content: '';
font-size: 10px;
color: white;
display: flex;
height: 24px;
padding: 0px 12px 0px 4px;
justify-content: center;
align-items: center;
gap: 6px;
border-radius: 9999px;
border: 1px solid rgba(214, 223, 233, 0.4);
background: #5d6670;
}
.name {
grid-column: 2;
grid-row: 2;
}
.introduction {
grid-column: 2;
grid-row: 3;
font-size: 12px;
}
.details,
.social {
padding: 0;
list-style: none;
}
.details {
display: flex;
flex-flow: row wrap;
gap: 10px;
width: 300px;
padding: 10px;
border-radius: 15px;
background: #f3f3f3;
}
.detail {
font-size: 10px !important;
const updateIntroduction = async (newIntroduction: string) => {

Check failure on line 63 in frontend/src/components/cockpit/about-me/AboutMe.vue

View workflow job for this annotation

GitHub Actions / Lint - Frontend

'updateIntroduction' is assigned a value but never used
await userStore.updateUser({
name: user!.name,
introduction: newIntroduction,
})
}
.social {
display: flex;
const addDetail = async (detail: UserDetail) => {

Check failure on line 70 in frontend/src/components/cockpit/about-me/AboutMe.vue

View workflow job for this annotation

GitHub Actions / Lint - Frontend

'addDetail' is assigned a value but never used
await userStore.addUserDetail(detail)
}
hr {
margin-block: 10px;
border-color: white;
}
</style>
</script>
82 changes: 82 additions & 0 deletions frontend/src/components/cockpit/about-me/AboutMeEdit.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<template>
<CockpitCard narrow>
<template #header>
<div class="header">
<button class="back" @click="$emit('back')"><v-icon icon="$back"></v-icon></button>
<h2>{{ $t('cockpit.about-me.edit.title') }}</h2>
</div>
</template>
<template #default>
<v-form class="new-detail" @submit.prevent="addDetail">
<v-select
v-model="newDetail.category"
:items="detailTypes"
label="Type"
name="type"
></v-select>
<v-text-field v-model="newDetail.text" name="text"></v-text-field>
<v-button type="submit">{{ $t('cockpit.about-me.edit.add') }}</v-button>
</v-form>
<ul class="details">
<li v-for="item in props.details" :key="item.id">
<v-icon icon="$close" @click="() => removeDetail(item.id)"></v-icon>
<span>{{ item.text }}</span>
</li>
</ul>
</template>
</CockpitCard>
</template>

<script lang="ts" setup>
import { reactive } from 'vue'
import CockpitCard from '#components/cockpit/cockpitCard/CockpitCard.vue'
import type { UserDetail, SocialMedia, UserDetailCategory } from '#stores/userStore'
const props = defineProps<{
username: string
name: string
userImage?: string
initials?: string
introduction?: string
details: UserDetail[]
social: SocialMedia[]
}>()
const emit = defineEmits<{
(e: 'back'): void
(e: 'add-detail', detail: UserDetail): void
(e: 'remove-detail', id: number): void
}>()
const newDetail = reactive<UserDetail>({
id: 1,
category: 'place',
text: '',
})
const detailTypes: UserDetailCategory[] = ['place', 'work', 'education', 'feeling', 'language']
const addDetail = () => {
emit('add-detail', newDetail)
}
const removeDetail = (id: number) => {
emit('remove-detail', id)
}
</script>

<style scoped>
.back {
width: 40px;
}
.header {
display: flex;
gap: 20px;
}
.details {
min-height: 50px;
}
</style>
Loading

0 comments on commit 638c4dc

Please sign in to comment.