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

eventページの作成 #76

Merged
merged 3 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
33 changes: 33 additions & 0 deletions src/components/Event/EventHostItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<script lang="ts" setup>
import { User } from '/@/lib/apis'
import UserIcon from '/@/components/UI/UserIcon.vue'

interface Props {
host: User
}

defineProps<Props>()
</script>

<template>
<div :class="$style.container">
<user-icon :user-id="host.name" :size="48" />
<p :class="$style.name">{{ host.name }}</p>
</div>
</template>

<style lang="scss" module>
.container {
padding: 0.5rem;
display: flex;
align-items: center;
gap: 1rem;

border: 1px solid $color-primary-text;
border-radius: 8px;
}

.name {
font-size: 1.125rem;
}
</style>
89 changes: 89 additions & 0 deletions src/components/UI/RadioButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<script lang="ts" setup>
import { computed } from 'vue'

interface Props {
modelValue: string
label: string
value: string
description?: string
}

const props = defineProps<Props>()

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

const vModelValue = computed({
get: () => props.modelValue,
set: v => emit('update:modelValue', v)
})
</script>

<template>
<div :class="$style.container">
<input
:id="value"
v-model="vModelValue"
:value="value"
type="radio"
:class="$style.input"
/>
<label :for="value" :class="$style.label">{{ label }}</label>
<p v-if="description" :class="$style.description">{{ description }}</p>
</div>
</template>

<style lang="scss" module>
.container {
display: grid;
align-items: center;
gap: 12px;
grid-template-columns: auto minmax(0, 0.5fr) 1fr;
}
.input {
appearance: none;
position: relative;
width: 30px;
height: 30px;
border-radius: 50%;
transition: all 0.1s ease-in-out;
border: 2px solid $color-secondary;

&:checked {
border: 2px solid $color-primary;
}

&::after {
content: '';
display: block;
width: 20px;
height: 20px;
border-radius: 50%;
position: absolute;
margin: auto;
inset: 0;
background-color: $color-primary;
}

&:not(:checked)::after {
display: none;
}
}
.label {
color: $color-primary;
font-weight: bold;

.input:not(:checked) ~ & {
color: $color-secondary;
}
}
.description {
color: $color-text;
font-size: 12px;

.input:not(:checked) ~ & {
color: $color-secondary;
}
}
</style>
45 changes: 45 additions & 0 deletions src/consts/eventLevel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { EventLevel } from '/@/lib/apis'

interface EventLevelDetail {
label: string
value: string
description: string
}

type EventLevelMap = Map<EventLevel, EventLevelDetail>

export const eventLevels: EventLevelMap = new Map([
[
EventLevel.Public,
{
label: '公開',
value: 'public',
description: 'ポートフォリオにて公開します'
}
],
[
EventLevel.Anonymous,
{
label: '匿名公開',
value: 'anonymous',
description: '企画者の名前を伏せて、ポートフォリオにて公開します'
}
],
[
EventLevel.Private,
{
label: '非公開',
value: 'private',
description: 'ポートフォリオにて公開しません'
}
]
])

export const getEventLevelFromValue = (
value: string
): EventLevel | undefined => {
const entry = Array.from(eventLevels).find(
([, service]) => service.value === value
)
return entry?.[0]
}
188 changes: 177 additions & 11 deletions src/pages/Event.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,181 @@
<template>
<div>EventDetail</div>
</template>
<script lang="ts" setup>
import ContentHeader from '/@/components/Layout/ContentHeader.vue'
import PageContainer from '/@/components/Layout/PageContainer.vue'
import BaseButton from '/@/components/UI/BaseButton.vue'
import EventHostItem from '/@/components/Event/EventHostItem.vue'

import apis, { EditEventRequest, EventDetail } from '/@/lib/apis'
import { RouterLink, useRouter } from 'vue-router'
import { getDisplayDuration } from '/@/lib/date'
import useParam from '/@/use/param'
import { useDataFetcher } from '/@/use/fetcher'
import { ref } from 'vue'
import { useToast } from 'vue-toastification'
import RadioButton from '/@/components/UI/RadioButton.vue'
import { eventLevels, getEventLevelFromValue } from '/@/consts/eventLevel'

<script lang="ts">
import { defineComponent } from 'vue'
const router = useRouter()
const toast = useToast()

export default defineComponent({
name: 'Event',
components: {},
setup() {
return {}
const eventId = useParam('id')
const { data: event } = useDataFetcher<EventDetail>(() =>
apis.getEvent(eventId.value)
)

const eventLevel = ref(
eventLevels.get(event.value?.eventLevel ?? 0)?.value ?? 'public'
)

const isSending = ref(false)
const updateEvent = async () => {
isSending.value = true
try {
const requestData: EditEventRequest = {
eventLevel: getEventLevelFromValue(eventLevel.value)
}
await apis.editEvent(eventId.value, requestData)
toast.success('イベント情報を更新しました')
router.push(`/events/${eventId.value}`)
} catch {
toast.error('イベント情報の更新に失敗しました')
}
})
isSending.value = false
}
</script>

<template>
<page-container>
<div :class="$style.headerContainer">
<content-header
icon-name="mdi:calendar"
:header-texts="[
{ title: 'Events', url: '/events' },
{ title: event?.name ?? '', url: `/events/${eventId}` }
]"
detail="イベントの詳細を確認します。"
:class="$style.header"
/>
</div>
<div v-if="event !== undefined">
<section :class="$style.section">
<h2 :class="$style.h2">イベント名</h2>
<p :class="$style.content">{{ event.name }}</p>
</section>
<section :class="$style.section">
<h2 :class="$style.h2">日時</h2>
<p :class="$style.content">
{{ getDisplayDuration(event.duration) }}
</p>
</section>
<section :class="$style.section">
<h2 :class="$style.h2">場所</h2>
<p :class="$style.content">
{{ event.place }}
</p>
</section>
<section :class="$style.section">
<h2 :class="$style.h2">説明</h2>
<p :class="$style.content">{{ event.description }}</p>
</section>
<section :class="$style.section">
<h2 :class="$style.h2">主催者</h2>
<event-host-item
v-for="host in event.hostname"
:key="host.id"
:class="$style.content"
:host="host"
/>
</section>

<section :class="$style.section">
<h2 :class="$style.h2">公開設定</h2>
<div :class="[$style.content, $style.radioButtons]">
<radio-button
v-model="eventLevel"
label="公開"
value="public"
description="ポートフォリオにて公開します"
/>
<radio-button
v-model="eventLevel"
label="匿名公開"
value="anonymous"
description="企画者の名前を伏せて、ポートフォリオにて公開します"
/>
<radio-button
v-model="eventLevel"
label="非公開"
value="private"
description="ポートフォリオにて公開しません"
/>
</div>
</section>
</div>

<div :class="$style.buttonContainer">
<router-link to="/events" :class="$style.link">
<base-button
:class="$style.backButton"
type="secondary"
icon="mdi:arrow-left"
>
Back
</base-button>
</router-link>
<base-button
:is-disabled="isSending"
type="primary"
icon="mdi:update"
@click="updateEvent"
>
Update
</base-button>
</div>
</page-container>
</template>

<style lang="scss" module>
.headerContainer {
display: flex;
justify-content: space-between;
align-items: center;
}
.header {
margin: 4rem 0 2rem;
}
.link {
text-decoration: none;
color: inherit;
}
.section {
margin-bottom: 2rem;
}
.h2 {
font-weight: bold;
font-size: 1.25rem;
}
.content {
margin-top: 0.5rem;
padding-left: 0.5rem;
}
.contestLinkContainer {
display: flex;
align-items: center;
gap: 0.25rem;
}
.contestLink {
color: $color-text;
}
.radioButtons {
display: flex;
flex-direction: column;
padding: 0.5rem;
gap: 1.25rem;
width: 60%;
}
.buttonContainer {
display: flex;
justify-content: flex-end;
gap: 1rem;
}
</style>
Loading