jws-frontend/src/components/home/PersonCard.vue
2024-04-23 11:13:57 +00:00

356 lines
9.1 KiB
Vue

<script lang="ts" setup>
import { Icon } from '@iconify/vue';
import AppBox from 'components/app/AppBox.vue';
import AppCircle from 'components/app/AppCircle.vue';
defineProps<{
list: {
id: string;
name: string;
detail?: { label: string; value: string }[];
male?: boolean;
female?: boolean;
disabled?: boolean;
badge?: string;
img?: string;
}[];
gridColumns?: number;
noHover?: boolean;
noAction?: boolean;
noDetail?: boolean;
noBg?: boolean;
detailColumnCount?: number;
}>();
defineEmits<{
(e: 'deleteCard', id: string): void;
(e: 'updateCard', action: 'FORM' | 'INFO', id: string): void;
(e: 'enterCard', action: 'FORM' | 'INFO', id: string): void;
(e: 'toggleStatus', id: string): void;
}>();
</script>
<template>
<div
class="person-container"
:style="`grid-template-columns: repeat( ${
gridColumns
? `${gridColumns}, 1fr`
: $q.screen.gt.sm
? '4, 1fr'
: $q.screen.gt.xs
? '2, 1fr'
: '1, 1fr'
})`"
>
<AppBox
bordered
class="column person-box"
:class="{
'person-box__disabled': v.disabled,
'person-box__no-hover': noHover,
'person-box__no-detail': noDetail,
'person-box__no-bg': noBg,
}"
@click="$emit('enterCard', 'INFO', v.id)"
v-for="(v, i) in list"
:key="i"
>
<div class="q-pa-sm column items-center">
<!-- kebab menu -->
<div class="full-width text-right" v-if="!noAction">
<q-btn
flat
round
padding="sm"
icon="mdi-dots-vertical"
size="sm"
@click.stop=""
>
<q-menu class="bordered">
<q-list v-close-popup>
<q-item
clickable
dense
class="row q-py-sm"
style="white-space: nowrap"
@click="$emit('enterCard', 'INFO', v.id)"
>
<q-icon
name="mdi-eye-outline"
class="col-3"
size="xs"
style="color: hsl(var(--green-6-hsl))"
/>
<span class="col-9 q-px-md flex items-center">
{{ $t('viewDetail') }}
</span>
</q-item>
<q-item
dense
clickable
class="row q-py-sm"
style="white-space: nowrap"
@click="$emit('updateCard', 'FORM', v.id)"
v-close-popup
>
<q-icon
name="mdi-pencil-outline"
class="col-3"
size="xs"
style="color: hsl(var(--cyan-6-hsl))"
/>
<span class="col-9 q-px-md flex items-center">
{{ $t('edit') }}
</span>
</q-item>
<q-item
dense
clickable
@click="$emit('deleteCard', v.id)"
v-close-popup
>
<q-icon
name="mdi-trash-can-outline"
size="xs"
class="col-3 app-text-negative"
/>
<span class="col-9 q-px-md flex items-center">
{{ $t('delete') }}
</span>
</q-item>
<q-item dense>
<q-item-section class="q-py-sm">
<div class="q-pa-sm surface-2 rounded">
<q-toggle
dense
size="sm"
@click="$emit('toggleStatus', v.id)"
:model-value="!v.disabled"
val="xs"
padding="none"
>
<div class="q-ml-xs">
{{
v.disabled
? $t('switchOnLabel')
: $t('switchOffLabel')
}}
</div>
</q-toggle>
</div>
</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</div>
<!-- profile -->
<div style="position: relative">
<AppCircle
bordered
class="avatar"
style="border: 2px solid var(--border-color)"
>
<q-img
:src="v.img"
style="object-fit: cover; width: 100%; height: 100%"
/>
<div class="status-circle">
<q-icon
:name="`mdi-${v.disabled ? 'close' : 'check'}`"
:style="`color:${v.disabled ? 'var(--gray-6)' : 'white'}`"
/>
</div>
</AppCircle>
</div>
<!-- name symbol -->
<span class="items-center row q-my-sm">
{{ v.name }}
<Icon
class="q-pl-sm"
:class="{
'symbol-gender': v.male || v.female,
'symbol-gender__male': !v.disabled && v.male,
'symbol-gender__female': !v.disabled && v.female,
'symbol-gender__disable': v.disabled,
}"
:icon="`material-symbols:${v.male ? 'male' : 'female'}`"
width="24px"
/>
<!-- <Icon class="locale q-pl-sm" icon="circle-flags:th" width="24px" /> -->
</span>
<span
v-if="v.badge"
class="badge q-px-sm"
:class="{
'bg-gender': v.male || v.female,
'bg-gender__male': !v.disabled && v.male,
'bg-gender__female': !v.disabled && v.female,
'bg-gender__disable': v.disabled,
}"
>
{{ v.badge }}
</span>
</div>
<!-- detail -->
<q-separator v-if="!noDetail" />
<div
v-if="!noDetail"
class="q-pt-sm q-px-sm q-pb-md person-detail rounded-b full-width"
:class="{
'bg-gender': v.male || v.female,
'bg-gender__light':
(!v.disabled && v.male) || (!v.disabled && v.female),
'bg-gender__male': !v.disabled && v.male,
'bg-gender__female': !v.disabled && v.female,
'bg-gender__disable': v.disabled,
}"
:style="
(detailColumnCount &&
`grid-template-columns: repeat(${detailColumnCount}, 1fr);`) ||
''
"
>
<div v-for="(d, j) in v.detail" :key="j">
<span class="app-text-muted-2">
{{ d.label }}
</span>
<span>{{ d.value || '-' }}</span>
</div>
</div>
</AppBox>
</div>
</template>
<style scoped>
.person-box {
background-color: var(--surface-2);
border-radius: var(--radius-2) !important;
transition: 100ms ease-in-out;
padding: 0;
&.person-box__disabled {
opacity: 0.4;
.status-circle {
background-color: var(--surface-1);
}
.locale {
filter: grayscale();
}
}
&.person-box__no-detail {
padding-block: 2rem;
}
&.person-box__no-bg {
background-color: transparent;
}
& .bg-gender {
color: hsla(var(--_fg));
background-color: hsl(var(--_bg));
&.bg-gender__disable {
--_bg: var(--stone-6-hsl);
background-color: hsla(var(--_bg) / 0.3);
}
&.bg-gender__light {
color: unset;
background-color: hsla(var(--_bg) / 0.1);
}
&.bg-gender__male {
--_fg: 0 100 100%;
--_bg: var(--gender-male);
}
&.bg-gender__female {
--_fg: 0 100 100%;
--_bg: var(--gender-female);
}
}
& .symbol-gender {
color: hsla(var(--_fg));
&.symbol-gender__disable {
--_fg: var(--stone-6-hsl);
}
&.symbol-gender__male {
--_fg: var(--gender-male);
}
&.symbol-gender__female {
--_fg: var(--gender-female);
}
}
& .person-detail {
display: grid;
flex-grow: 1;
grid-template-columns: repeat(2, 1fr);
gap: var(--size-2);
overflow-x: scroll;
& > * {
display: flex;
flex-direction: column;
& > :first-child {
font-size: var(--font-size-0);
}
}
}
& .status-circle {
width: 18px;
height: 18px;
border-radius: 50%;
background-color: var(--teal-6);
border: 2px solid var(--border-color);
bottom: 0.6rem;
right: 0.6rem;
position: absolute;
display: inline-flex;
justify-content: center;
align-items: center;
}
&:not(.person-box__disabled):not(.person-box__no-hover):hover {
--_hover: hsl(var(--gender-male));
cursor: pointer;
box-shadow: var(--shadow-2);
& .person-detail {
--_hover: hsl(var(--_bg));
box-shadow: inset 0em -5px hsl(var(--_bg));
}
}
}
.person-container {
display: grid;
gap: var(--size-6);
}
.avatar {
block-size: 7rem;
}
.badge {
display: inline-block;
border-radius: var(--radius-6);
background-color: var(--surface-2);
text-wrap: nowrap;
}
</style>