Skip to content

Commit d12050d

Browse files
authored
Merge pull request #212 from vuejs-jp/feature/creation-namecard
(PC) Feature/creation namecard
2 parents 2e88b2a + b4f7eba commit d12050d

File tree

21 files changed

+633
-141
lines changed

21 files changed

+633
-141
lines changed

apps/web/app/components/namecard/CreationProcess.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const { color } = useColor()
1818
}"
1919
class="title"
2020
>
21-
{{ t('namecard.creation-process.title') }}
21+
{{ t('namecard.creation_process.title') }}
2222
</h2>
2323
<ol
2424
:style="{

apps/web/app/components/namecard/CreationStatus.vue

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@ const { color } = useColor()
99
export type Status = 'not_created' | 'inquiry_in_progress' | 'inquiry_failed' | 'inquiry_completed'
1010
type Props = {
1111
statusKey: Status
12+
size?: 'small' | 'medium'
1213
}
13-
const props = defineProps<Props>()
14+
const props = withDefaults(defineProps<Props>(), {
15+
size: 'medium',
16+
})
1417
1518
const backgroundColor = computed(() => {
1619
switch (props.statusKey) {
@@ -24,29 +27,46 @@ const backgroundColor = computed(() => {
2427
return color('gray/100')
2528
}
2629
})
30+
31+
const style = computed(() => {
32+
const baseStyle = {
33+
color: color('white'),
34+
backgroundColor: backgroundColor.value,
35+
}
36+
switch (props.size) {
37+
case 'small':
38+
return {
39+
...baseStyle,
40+
height: '24px',
41+
borderRadius: '24px',
42+
padding: '0 20px',
43+
fontWeight: fontWeight('heading/700'),
44+
fontSize: fontSize('body/100'),
45+
}
46+
default:
47+
return {
48+
...baseStyle,
49+
height: '38px',
50+
borderRadius: '38px',
51+
padding: '0 38px',
52+
fontWeight: fontWeight('heading/700'),
53+
fontSize: fontSize('heading/200'),
54+
}
55+
}
56+
})
2757
</script>
2858

2959
<template>
30-
<div
31-
class="creation-status-root"
32-
:style="{
33-
fontWeight: fontWeight('heading/700'),
34-
fontSize: fontSize('heading/200'),
35-
color: color('white'),
36-
backgroundColor: backgroundColor,
37-
}"
38-
>
60+
<div class="creation-status-root" :style="style">
3961
{{ t(`namecard.creation_status.${statusKey}`) }}
4062
</div>
4163
</template>
4264

4365
<style scoped>
4466
.creation-status-root {
45-
display: flex;
67+
display: inline-flex;
4668
justify-content: center;
4769
align-items: center;
48-
width: 166px;
49-
height: 38px;
50-
border-radius: 38px;
70+
line-height: 1;
5171
}
5272
</style>
Lines changed: 202 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,216 @@
11
<script setup lang="ts">
2-
import { useTypography } from '@vuejs-jp/composable'
2+
import { useI18n } from '#i18n'
3+
import { onMounted, ref } from 'vue'
4+
import { useColor, useTypography } from '@vuejs-jp/composable'
35
4-
type Props = {
5-
label: string
6+
interface DragDropProps {
7+
fileAccept: string
68
}
7-
defineProps<Props>()
89
10+
defineProps<DragDropProps>()
11+
const isDragEnter = ref(false)
12+
13+
const emit = defineEmits<{
14+
'check-files': [e: Event, value: File[]]
15+
}>()
16+
17+
const fileName = ref<string | null>()
18+
19+
const { t } = useI18n()
20+
const onDropFile = (e: DragEvent) => {
21+
e.preventDefault()
22+
if (e.dataTransfer?.files) {
23+
fileName.value = e.dataTransfer.files[0].name
24+
emit('check-files', e, Array.from(e.dataTransfer.files))
25+
}
26+
}
27+
28+
function handleCloseButton(e: Event) {
29+
fileName.value = null
30+
emit('check-files', e, [])
31+
}
32+
33+
const onFileInputChange = (event: Event) => {
34+
event.preventDefault()
35+
const { target } = event
36+
if (!(target instanceof HTMLInputElement)) return
37+
38+
if (target.files) {
39+
fileName.value = target.files[0].name
40+
emit('check-files', event, Array.from(target.files))
41+
}
42+
}
43+
44+
const { color: updateColor } = useColor()
945
const { fontWeight, fontSize } = useTypography()
46+
47+
onMounted(() => {
48+
window.ondrop = (e) => {
49+
e.preventDefault()
50+
}
51+
window.ondragover = (e) => {
52+
e.preventDefault()
53+
}
54+
})
1055
</script>
1156

1257
<template>
13-
<!-- TODO 画像アップローダーのブラッシュアップ -->
14-
<VFDragDropArea
58+
<label
59+
for="fileupload"
60+
class="drag-drop-area-root"
1561
:style="{
62+
fontSize: fontSize('body/300'),
1663
fontWeight: fontWeight('heading/100'),
17-
fontSize: fontSize('heading/100'),
64+
color: updateColor('vue-green/200'),
1865
}"
1966
>
20-
{{ label }}
21-
<VFCssResetButton class="select-button" />
22-
</VFDragDropArea>
67+
{{ t('namecard.form.label_avatar') }}
68+
<div v-if="fileName" class="file-name-area">
69+
<p class="file-name">{{ fileName }}</p>
70+
<VFIconButton
71+
class="icon-close"
72+
color="vue-blue"
73+
name="close"
74+
can-hover
75+
@click="handleCloseButton"
76+
/>
77+
</div>
78+
<button
79+
v-else
80+
draggable="true"
81+
class="drag-drop-area"
82+
:class="{ '-isDragEnter': isDragEnter }"
83+
@dragenter="() => (isDragEnter = true)"
84+
@dragleave="() => (isDragEnter = false)"
85+
@dragover.prevent
86+
@drop.prevent="onDropFile"
87+
>
88+
<div class="message-area">
89+
<p
90+
class="label"
91+
:style="{
92+
fontSize: fontSize('body/400'),
93+
fontWeight: fontWeight('heading/200'),
94+
color: updateColor('vue-blue'),
95+
}"
96+
>
97+
{{ t('namecard.form.drag_and_drop') }}
98+
</p>
99+
<p
100+
class="label or"
101+
:style="{
102+
fontSize: fontSize('body/200'),
103+
fontWeight: fontWeight('body/300'),
104+
color: updateColor('vue-blue'),
105+
}"
106+
>
107+
{{ t('namecard.form.or') }}
108+
</p>
109+
</div>
110+
<input
111+
id="fileUpload"
112+
class="select-button"
113+
type="file"
114+
name="avatar"
115+
:accept="fileAccept"
116+
@change="onFileInputChange"
117+
/>
118+
</button>
119+
<p
120+
class="annotation"
121+
:style="{
122+
fontSize: fontSize('body/300'),
123+
fontWeight: fontWeight('body/300'),
124+
color: updateColor('vue-blue'),
125+
}"
126+
>
127+
{{ t('namecard.form.annotation_avatar') }}
128+
</p>
129+
<!-- TODO エラー制御
130+
<Typography v-if="errorMessage" variant="body/200" color="sangosyo/200">{{
131+
errorMessage
132+
}}</Typography> -->
133+
</label>
23134
</template>
24135

25-
<style scoped></style>
136+
<style scoped>
137+
@import url('~/assets/media.css');
138+
139+
.drag-drop-area-root {
140+
display: inline-block;
141+
}
142+
.drag-drop-area {
143+
width: 100%;
144+
margin: calc(var(--unit) * 1.5) auto;
145+
border: 1px solid #e1e1e1;
146+
border-radius: 6px;
147+
background-color: #fff;
148+
color: var(--color-vue-blue);
149+
}
150+
.drag-drop-area:hover {
151+
transition: 0.2s;
152+
box-shadow: 0 2px 10px rgba(53, 73, 94, 14%);
153+
}
154+
.file-name-area {
155+
width: 100%;
156+
margin: calc(var(--unit) * 1.5) auto;
157+
border: 1px solid #e1e1e1;
158+
border-radius: 6px;
159+
background: #c6cacf40;
160+
color: var(--color-vue-blue);
161+
padding: calc(var(--unit) * 3);
162+
display: flex;
163+
justify-content: space-between;
164+
align-items: center;
165+
}
166+
.file-name {
167+
flex-grow: 1;
168+
text-align: center;
169+
}
170+
.icon-close {
171+
display: inline-block;
172+
text-align: right;
173+
height: 22px;
174+
}
175+
.message-area {
176+
display: inline-flex;
177+
flex-direction: column;
178+
justify-content: center;
179+
align-items: center;
180+
height: var(--height-input-area);
181+
width: 100%;
182+
padding: calc(var(--unit) * 3) calc(var(--unit) * 3) 0;
183+
text-align: center;
184+
}
185+
.label {
186+
margin-bottom: calc(var(--unit) * 1.5);
187+
}
188+
#fileUpload {
189+
font-size: 0;
190+
}
191+
::file-selector-button {
192+
--height-button: 50px;
193+
194+
display: inline-flex;
195+
justify-content: center;
196+
align-items: center;
197+
198+
font-size: var(--font-size-body400);
199+
font-weight: 700;
200+
color: var(--color-vue-blue);
201+
background-color: var(--color-white);
202+
203+
height: var(--height-button);
204+
padding: 0 43px;
205+
margin: 0 auto calc(var(--unit) * 3);
206+
border: 2px solid var(--color-vue-blue);
207+
border-radius: var(--height-button);
208+
cursor: pointer;
209+
}
210+
::file-selector-button:hover {
211+
transition: 0.2s;
212+
color: var(--color-white);
213+
background-color: var(--color-vue-blue);
214+
box-shadow: 0 2px 10px rgba(53, 73, 94, 14%);
215+
}
216+
</style>

apps/web/app/composables/useAuth.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,5 @@ export function useAuth() {
3030
return data.user
3131
}
3232

33-
const authUserId = getUser().then(user => {
34-
return user.id
35-
}).catch(()=> null)
36-
37-
return { signIn, signOut, authUserId }
33+
return { signIn, signOut, getUser }
3834
}

apps/web/app/composables/useLocale.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export function useLocale(path: Path) {
3535
.with('namecard_process_3', () => `/${locale.value}/namecard_process_3`)
3636
.with('namecard_process_4', () => `/${locale.value}/namecard_process_4`)
3737
.with('namecard_process_alert', () => `/${locale.value}/namecard_process_alert`)
38+
.with('namecard_annotation_order_number', () => `/${locale.value}/namecard_annotation_order_number`)
3839
.exhaustive(),
3940
)
4041

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { computed } from 'vue'
2+
import { useSupabase, useAsyncData, useAuth } from '#imports'
3+
import type { Status } from '~/components/namecard/CreationStatus.vue'
4+
import type { Attendee } from '@vuejs-jp/model'
5+
6+
7+
export async function useNamecard(userId?:string) {
8+
const { fetchAttendeeDataByUserId } = useSupabase()
9+
const { getUser } = useAuth()
10+
11+
const { data: authUserId } = await useAsyncData('authUserId', async () => {
12+
return (await getUser()).id
13+
})
14+
15+
const { data: attendeeByUserId } = await useAsyncData('attendeeByUserId', async () => {
16+
return await fetchAttendeeDataByUserId('attendees', authUserId.value ?? userId ?? '')
17+
})
18+
19+
const userData = computed(() => {
20+
return attendeeByUserId.value?.data?.[0]
21+
})
22+
23+
const statusKey = computed<Status>(() => {
24+
// TODO テーブルのどの箇所を参照して全ステータスを判定する?
25+
if (userData.value?.activated_at) {
26+
return 'inquiry_in_progress'
27+
} else {
28+
return 'not_created'
29+
}
30+
})
31+
32+
const attendee = computed<Attendee>(() => {
33+
// TODO time 系は空文字じゃない方が良い。supabase側で設定できるのか、フロント側でnew Date使うのか?
34+
return {
35+
id: userData.value?.id ?? '',
36+
user_id: userData.value?.user_id ?? '',
37+
activated_at: userData.value?.activated_at ?? '',
38+
created_at: userData.value?.created_at ?? '',
39+
updated_at: userData.value?.updated_at ?? '',
40+
display_name: userData.value?.display_name ?? '',
41+
email: userData.value?.email ?? '',
42+
provider: userData.value?.provider ?? '',
43+
avatar_url: userData.value?.avatar_url ?? '',
44+
role: userData.value?.role ?? '',
45+
receipt_id: userData.value?.receipt_id ?? '',
46+
}
47+
})
48+
return { authUserId, userData, statusKey, attendee }
49+
}

apps/web/app/composables/useSupabase.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ export function useSupabase() {
2323
return await client.from(table).select().eq('role', role)
2424
}
2525

26+
async function fetchAttendeeDataByUserId(table: Extract<Table, 'attendees'>, userId:string) {
27+
return await client.from(table).select().eq('user_id', userId)
28+
}
29+
2630
async function upsertSpeaker(table: Extract<Table, 'speakers'>, target: FormSpeaker) {
2731
const targetData = { ...target }
2832

@@ -65,6 +69,7 @@ export function useSupabase() {
6569
return {
6670
fetchData,
6771
fetchAttendeeData,
72+
fetchAttendeeDataByUserId,
6873
upsertSpeaker,
6974
upsertSponsor,
7075
upsertJob,

0 commit comments

Comments
 (0)