-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Hande availability change; add mutations to store; split view and edi…
…t mode
- Loading branch information
1 parent
fe7815b
commit 638c4dc
Showing
14 changed files
with
464 additions
and
191 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) => { | ||
await userStore.updateUser({ | ||
name: user!.name, | ||
introduction: newIntroduction, | ||
}) | ||
} | ||
.social { | ||
display: flex; | ||
const addDetail = async (detail: UserDetail) => { | ||
await userStore.addUserDetail(detail) | ||
} | ||
hr { | ||
margin-block: 10px; | ||
border-color: white; | ||
} | ||
</style> | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
Oops, something went wrong.