Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Events page #140

Merged
merged 29 commits into from
Jan 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
30e77a9
概ね構成要素の配置は完了 / 公開設定ポップアップとプルダウンメニュー未実装
Pugma Dec 2, 2023
5b421cf
Eventsパージのアイコン修正 / トロフィーからカレンダーに
Pugma Dec 3, 2023
c819f3c
公開範囲設定更新のプルダウンも要素の配置は完了 / あとは細かい座標や変数の調整
Pugma Dec 3, 2023
ebb3581
ポップアップの位置修正に成功
Pugma Dec 3, 2023
727e84f
不要なCSSの削除 / importの統合 / プルダウン絞り込みでHomepageを初期選択に
Pugma Dec 3, 2023
e122dcd
公開範囲をローカルで反映できるように / APIは次のコミットで
Pugma Dec 4, 2023
ed3ed80
ポップアップから公開範囲設定の変更ができるように
Pugma Dec 4, 2023
9279977
ポップアップを範囲外クリックで閉じるところまで / chevronでも範囲外判定となるのは解消予定
Pugma Dec 4, 2023
4ce6f46
型推論に頼る
Pugma Dec 5, 2023
f819c30
配置するaccordionの種類を変更
Pugma Dec 5, 2023
3d41f71
accordionの中身の要素を入れた / 適切なArrayがなかったのでベタ打ち
Pugma Dec 5, 2023
eea8d4a
型推論に頼る / chevronのアニメーションを付ける / 空白8pxを入れる
Pugma Dec 9, 2023
e6513c7
onBeforeUnmount -> onUnmounted
Pugma Dec 19, 2023
c438333
8pxの間隔の確保をmarginで行うように
Pugma Dec 19, 2023
59fdbcd
リンク範囲拡大/関数内での変数定義/絞り込み機能の実装/公開範囲表示機構修正
Pugma Dec 27, 2023
5410bc3
リンク範囲拡大/関数内での変数定義/絞り込み機能の実装/公開範囲表示機構修正2
Pugma Dec 27, 2023
9754dba
プルダウンメニューの中身の修正
Pugma Dec 27, 2023
f7cb963
アイコンミスの修正
Pugma Dec 27, 2023
d0a9780
router-linkを使いながらリンク範囲を拡大することに成功!
Pugma Jan 1, 2024
e368c8b
公開範囲絞り込みの標準値を公開に/buttonタグ内のブロクオブジェクトをインラインに
Pugma Jan 1, 2024
cc262e9
アコーディオン内順序変更 / デフォルト値の変更
Pugma Jan 2, 2024
cd4b3cd
イベント公開範囲の絞り込みに「すべて」を追加
Pugma Jan 3, 2024
bb35409
mutateの導入 / computedに変えてみたけど:wakarazu:
Pugma Jan 3, 2024
fde4f1a
eventLevelValueをrefで再度実装
Pugma Jan 3, 2024
7661e23
APIの名前変更に対応 / 不要なDetail取得の廃止
Pugma Jan 18, 2024
4ef6d89
eventの絞り込みをcomputedで実装
Pugma Jan 18, 2024
0db90c3
ホバー時の角丸不具合修正 / その他CSSの整理 / lint
Pugma Jan 18, 2024
2a9cbc0
union型でaccordionとのprops, emitsのやり取りを行うように
Pugma Jan 19, 2024
55d78c5
union型の宣言箇所をtsファイルに移動
Pugma Jan 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 130 additions & 0 deletions src/components/Events/EventItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
<script lang="ts" setup>
import { ref, onMounted, onUnmounted } from 'vue'

import apis, { Event, EditEventRequest, EventLevel } from '/@/lib/apis'
import Icon from '/@/components/UI/Icon.vue'
import { getFullDayString } from '/@/lib/date'
import EventLevelMenu from '/@/components/Events/EventLevelMenu.vue'
import {
eventLevelValueMap,
getEventLevelFromValue,
eventLevels,
type EventLevelValue
} from '/@/consts/eventLevel'
import { useEventStore } from '/@/store/event'

interface Props {
event: Event
}

const props = defineProps<Props>()

const displayMenu = ref(false)

const eventLevelValue = ref<EventLevelValue>(
eventLevelValueMap[props.event.level]
)

const updateEventLevel = async (v: EventLevelValue) => {
eventLevelValue.value = v
const currentEventLevel: EventLevel = getEventLevelFromValue(v)
const editReq: EditEventRequest = { level: currentEventLevel }
await apis.editEvent(props.event.id, editReq)
useEventStore().mutate()
}

const element = ref<HTMLDivElement | null>(null)

const clickOutside = (e: MouseEvent) => {
// [対象の要素]が[クリックされた要素]を含まない場合
if (e.target instanceof Node && !element.value?.contains(e.target)) {
displayMenu.value = false
}
}

// windowにセットしたイベントはremoveするのを忘れずに
onMounted(() => {
addEventListener('click', clickOutside)
})
onUnmounted(() => {
removeEventListener('click', clickOutside)
})
</script>

<template>
<div :class="$style.container">
<router-link :to="`events/${event.id}`" :class="$style.link">
<p :class="$style.name">{{ event.name }}</p>
<p :class="$style.duration">
<icon name="mdi:calendar" />
{{ getFullDayString(new Date(event.duration.since)) }}
</p>
</router-link>
<div :class="$style.displayMenu">
<button
ref="element"
:class="$style.opener"
@click="displayMenu = !displayMenu"
>
<span>
{{ eventLevels[eventLevelValue].label }}
</span>
<span ref="element" :class="$style.icon" :is-menu-open="displayMenu">
<icon name="mdi:chevron-down" />
</span>
</button>
<event-level-menu
v-if="displayMenu"
:event-level="eventLevelValue"
:class="$style.menu"
@update-event-level="updateEventLevel"
/>
</div>
</div>
</template>

<style lang="scss" module>
.link {
flex-grow: 1;
color: inherit;
text-decoration: none;
}
.container {
padding: 0.5rem;
border: 1px solid $color-primary-text;
border-radius: 8px;
display: flex;
&:hover {
background-color: $color-background-dim;
}
}

.name {
color: $color-primary;
font-size: 1.125rem;
}

.duration {
display: flex;
align-items: center;
gap: 0.5rem;
margin-top: 1rem;
}

.opener {
display: flex;
}
Pugma marked this conversation as resolved.
Show resolved Hide resolved

.displayMenu {
position: relative;
margin-top: auto;
margin-bottom: auto;
}
.icon {
margin-left: 8px;
&[is-menu-open='true'] {
transform: rotate(-0.5turn);
}
transition: 0.5s;
Pugma marked this conversation as resolved.
Show resolved Hide resolved
}
</style>
11 changes: 7 additions & 4 deletions src/components/Events/EventLevelMenu.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
<script lang="ts" setup>
import {
EventLevelValue,
eventLevels
} from '/@/consts/eventLevel'
import { EventLevelValue, eventLevels } from '/@/consts/eventLevel'

interface Props {
eventLevel: EventLevelValue
Expand Down Expand Up @@ -35,13 +32,19 @@ const emit = defineEmits<{
<style lang="scss" module>
.eventLevelMenu {
width: max-content;
position: absolute;
display: flex;
flex-direction: column;
padding: 1rem;
gap: 1rem;
border: 1px solid $color-secondary;
border-radius: 0.375rem;
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
top: 1.75rem;
right: 0px;
opacity: 1;
background-color: $color-background;
z-index: 1;
}
.eventLevelMenuButton {
text-align: left;
Expand Down
44 changes: 44 additions & 0 deletions src/components/UI/EventTypeAccordion.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<script lang="ts" setup>
import { computed } from 'vue'
import BaseSelect from '/@/components/UI/BaseSelect.vue'
import type { EventLevelValueWithAll } from '/@/consts/eventLevel'

interface Props {
modelValue: EventLevelValueWithAll
}
const props = defineProps<Props>()

const emit = defineEmits<{
(e: 'update:modelValue', value: EventLevelValueWithAll): void
}>()

const value = computed({
get: () => props.modelValue,
set(v: EventLevelValueWithAll) {
emit('update:modelValue', v)
}
})

const options = [
{
label: 'すべて',
value: 'all'
},
{
label: '公開',
value: 'public'
},
{
label: '匿名公開',
value: 'anonymous'
},
{
label: '非公開',
value: 'private'
}
]
</script>

<template>
<base-select v-model="value" :options="options" searchable />
</template>
2 changes: 2 additions & 0 deletions src/consts/eventLevel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ type EventLevelValueMap = typeof eventLevelValueMap

export type EventLevelValue = EventLevelValueMap[keyof EventLevelValueMap]

export type EventLevelValueWithAll = EventLevelValue | 'all'

interface EventLevelDetail<Level extends EventLevel = EventLevel> {
label: string
value: Level
Expand Down
4 changes: 2 additions & 2 deletions src/pages/Event.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ const { mutate } = useEventStore()
const eventId = useParam('id')
const event: EventDetail = (await apis.getEvent(eventId.value)).data

const eventLevel = ref<EventLevelValue>(eventLevelValueMap[event.eventLevel])
const eventLevel = ref<EventLevelValue>(eventLevelValueMap[event.level])

const isSending = ref(false)
const updateEvent = async () => {
isSending.value = true
try {
const requestData: EditEventRequest = {
eventLevel: getEventLevelFromValue(eventLevel.value)
level: getEventLevelFromValue(eventLevel.value)
}
await apis.editEvent(eventId.value, requestData)
mutate()
Expand Down
98 changes: 88 additions & 10 deletions src/pages/Events.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,93 @@
<template>
<div>Events</div>
</template>
<script lang="ts" setup>
import { computed, ref } from 'vue'

import ContentHeader from '/@/components/Layout/ContentHeader.vue'
import PageContainer from '/@/components/Layout/PageContainer.vue'
import EventTypeAccordion from '/@/components/UI/EventTypeAccordion.vue'
import EventItem from '/@/components/Events/EventItem.vue'
import FormInput from '/@/components/UI/FormInput.vue'
import { useEventStore } from '/@/store/event'
import {
type EventLevelValueWithAll,
eventLevelValueMap
} from '/@/consts/eventLevel'

const eventStore = useEventStore()
const events = await eventStore.fetchEvents()
const eventType = ref<EventLevelValueWithAll>('all')

<script lang="ts">
import { defineComponent } from 'vue'
const searchQuery = ref('')

export default defineComponent({
name: 'Events',
components: {},
setup() {
return {}
const filteredEvents = computed(() => {
if (eventType.value === 'all') {
return events
} else {
return events.filter(
event => eventLevelValueMap[event.level] === eventType.value
)
}
})
</script>

<template>
<page-container>
<content-header
icon-name="mdi:calendar"
:header-texts="[{ title: 'Events', url: '/events' }]"
detail="イベントの公開設定を変更します"
:class="$style.header"
/>
<div :class="$style.searchFormContainer">
<div :class="$style.searchForm">
<p :class="$style.body2">検索</p>
<form-input
v-model="searchQuery"
placeholder="イベント名"
icon="magnify"
/>
</div>
<div>
<p :class="$style.body2">公開設定で絞り込み</p>
<event-type-accordion v-model="eventType" />
</div>
</div>
<ul :class="$style.eventList">
<li v-for="event in filteredEvents" :key="event.id">
<event-item :event="event" />
</li>
</ul>
</page-container>
</template>

<style lang="scss" module>
.header {
margin: 4rem 0 2rem;
}

.searchFormContainer {
display: flex;
align-items: center;
margin-bottom: 1rem;
gap: 0.5rem;
}

.searchForm {
flex-grow: 1;
}

.body2 {
font-size: 0.875rem;
color: $color-secondary;
}

.eventList {
list-style: none;
li {
margin-bottom: 0.5rem;

&:last-child {
margin-bottom: 0;
}
}
}
</style>