Skip to content
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
26 changes: 18 additions & 8 deletions apps/client/src/components/account/ProfileCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
translateGrade(formData.grade)
}}</span>
<span class="inset-0 right-0 pr-2 pointer-events-none absolute flex items-center justify-end">
<ChevronsUpDownIcon class="w-4 h-4 text-subtext" aria-hidden="true" />
<div class="i-lucide:lucide:chevrons-up-down w-4 h-4 text-subtext" aria-hidden="true" />
</span>
</ListboxButton>
<transition
Expand All @@ -77,13 +77,18 @@
:key="gradeOption.grade"
v-slot="{ active, selected }"
:value="gradeOption.grade">
<li :class="['py-2 px-4 pr-10 text relative cursor-default select-none', active ? 'bg-overlay' : '']">
<li
:class="[
'py-2 px-4 pr-10 text inline-flex items-center cursor-default select-none',
active ? 'bg-overlay' : '',
selected ? 'text-blue-500' : '',
]">
<span
:class="['block overflow-hidden text-ellipsis whitespace-nowrap', selected ? 'font-medium' : '']">
{{ gradeOption.name }}
</span>
<span v-if="selected" class="inset-0 left-0 pl-3 text-blue-500 absolute flex items-center">
<CheckIcon class="w-4 h-4" aria-hidden="true" />
<span v-if="selected" class="right-0 pr-6 absolute">
<div class="i-lucide:check h-4 w-4 text-blue-500" aria-hidden="true" />
</span>
</li>
</ListboxOption>
Expand All @@ -107,7 +112,7 @@
translateYear(formData.year)
}}</span>
<span class="inset-0 right-0 pr-2 pointer-events-none absolute flex items-center justify-end">
<ChevronsUpDownIcon class="w-4 h-4 text-subtext" aria-hidden="true" />
<div class="i-lucide:lucide:chevrons-up-down w-4 h-4 text-subtext" aria-hidden="true" />
</span>
</ListboxButton>
<transition
Expand All @@ -121,13 +126,18 @@
:key="yearOption.year"
v-slot="{ active, selected }"
:value="yearOption.year">
<li :class="['py-2 px-4 pr-10 text relative cursor-default select-none', active ? 'bg-overlay' : '']">
<li
:class="[
'py-2 px-4 pr-10 text inline-flex items-center cursor-default select-none',
active ? 'bg-overlay' : '',
selected ? 'text-blue-500' : '',
]">
<span
:class="['block overflow-hidden text-ellipsis whitespace-nowrap', selected ? 'font-medium' : '']">
{{ yearOption.name }}
</span>
<span v-if="selected" class="inset-0 left-0 pl-3 text-blue-500 absolute flex items-center">
<CheckIcon class="w-4 h-4" aria-hidden="true" />
<span v-if="selected" class="right-0 pr-6 absolute">
<div class="i-lucide:check h-4 w-4 text-blue-500" aria-hidden="true" />
</span>
</li>
</ListboxOption>
Expand Down
2 changes: 1 addition & 1 deletion apps/client/src/components/account/UserHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
</div>
<div class="min-w-0">
<h2 class="text-lg font-bold text my-0 truncate">{{ user?.lastName }} {{ user?.firstName }}</h2>
<p class="text-base text-subtext my-0 truncate">{{ user?.username }}</p>
<p class="text-base text-subtext my-0 truncate">{{ user?.username ? '@' + user?.username : '' }}</p>
</div>
</div>
<button type="button" class="btn-secondary px-3 py-1.5 text-sm" @click="isEditing = true">編集</button>
Expand Down
22 changes: 22 additions & 0 deletions apps/client/src/components/signin/LoadingView.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<template>
<div class="h-50dvh w-full flex flex-col items-center justify-center">
<div class="i-lucide:loader-circle animate-spin text-2xl" aria-hidden />
</div>
<p class="text-sub text-center">
画面が切り替わらない場合は
<RouterLink :to="redirectUrl" class="text-blue-500 hover:text-blue-600 underline underline-offset-4">
こちら
</RouterLink>
</p>
</template>

<script setup lang="ts">
import { RouterLink } from 'vue-router';

const props = defineProps({
redirectUrl: {
type: String,
default: '/',
},
});
</script>
257 changes: 114 additions & 143 deletions apps/client/src/pages/SignIn.vue
Original file line number Diff line number Diff line change
@@ -1,171 +1,142 @@
<template>
<div class="p-4 flex items-center justify-center">
<Show when="signed-in">
<div class="card h-full w-full">
<h1 class="text-3xl font-bold text tracking-tighter text-center">Welcome Back</h1>
<p class="text-base text-subtext opacity-80">
ログインが完了しました。<br />
ホームへリダイレクトしています。
</p>
<div class="mt-4 mb-2 flex w-full justify-center">
<div class="w-48 h-3 bg-overlay1 overflow-hidden rounded-full">
<div
:style="{ width: gaugePercent + '%' }"
class="bg-blue-500 ease h-full transition-[width] duration-1000" />
</div>
</div>
<p class="text-sub">
画面が切り替わらない場合は
<RouterLink :to="redirectUrl" class="text-blue-500 hover:text-blue-600 underline underline-offset-4">
こちら
</RouterLink>
</p>
<Show when="signed-in">
<LoadingView :redirect-url="redirectUrl" />
</Show>

<Show when="signed-out">
<div class="card max-w-md mx-auto h-full w-full">
<div class="p-2">
<h1 class="heading-1">サインイン</h1>
</div>
</Show>

<Show when="signed-out">
<div class="card max-w-md mx-auto h-full w-full">
<div class="p-2">
<h1 class="heading-1">サインイン</h1>
</div>
<div class="p-2 pt-0">
<div class="gap-6 flex flex-col">
<form v-if="needsVerification" @submit.prevent="handleVerifyCode">
<Input id="code" v-model="code" label="認証コード" name="code" required placeholder="認証コードを入力" />
<p class="text-sub mt-2">{{ email }} に認証コードを送信しました</p>

<div v-if="error" class="mt-4 text-base text-red-500">
<p>{{ error }}</p>
<div class="p-2 pt-0">
<div class="gap-6 flex flex-col">
<form v-if="needsVerification" @submit.prevent="handleVerifyCode">
<Input id="code" v-model="code" label="認証コード" name="code" required placeholder="認証コードを入力" />
<p class="text-sub mt-2">{{ email }} に認証コードを送信しました</p>

<div v-if="error" class="mt-4 text-base text-red-500">
<p>{{ error }}</p>
</div>
<button type="submit" class="btn-primary mt-4 w-full" :disabled="isLoading">
{{ isLoading ? '認証中...' : '認証' }}
</button>
</form>

<form v-else @submit.prevent="handleSignIn">
<Input
id="email"
v-model="email"
type="email"
label="メールアドレス"
name="email"
required
autocomplete="email"
placeholder="example@mail.com" />

<div class="mt-4 user-select-none">
<div class="mb-1.5 text-xs flex items-center">
<label for="password" class="form-label">パスワード</label>
<a
href="https://accounts.omu-aikido.com/sign-in/"
class="text-base text-subtext ml-auto inline-block underline underline-offset-4 hover:underline"
target="_blank"
rel="noopener noreferrer">
パスワードを忘れた
</a>
</div>
<button type="submit" class="btn-primary mt-4 w-full" :disabled="isLoading">
{{ isLoading ? '認証中...' : '認証' }}
</button>
</form>

<form v-else @submit.prevent="handleSignIn">
<Input
id="email"
v-model="email"
type="email"
label="メールアドレス"
name="email"
id="password"
v-model="password"
type="password"
name="password"
required
autocomplete="email"
placeholder="example@mail.com" />

<div class="mt-4 user-select-none">
<!-- added wrapper for layout spacing if needed, though Input handles vertical gap -->
<!-- Password input needs custom label layout for 'Forgot Password' link?
Input component has internal label. If we want the link next to label, we might need a slot or different approach.
The Input component puts label above.
Let's use the Input component but maybe we can't easily put the link *inside* the label line without a slot.
However, the Input component doesn't have a label-right slot.

Workaround: Don't use 'label' prop on Input, render label manually above it.
-->
<div class="mb-1.5 text-xs flex items-center">
<label for="password" class="form-label">パスワード</label>
<a
href="https://accounts.omu-aikido.com/sign-in/"
class="text-base text-subtext ml-auto inline-block underline underline-offset-4 hover:underline"
target="_blank"
rel="noopener noreferrer">
パスワードを忘れた
</a>
</div>
<Input
id="password"
v-model="password"
type="password"
name="password"
required
autocomplete="current-password" />
</div>

<div v-if="error" class="mt-4 text-base text-red-500">
<p>{{ error }}</p>
<div class="mt-4 text-base text-subtext">
サインインに失敗する場合は、
<a
href="https://accounts.omu-aikido.com"
target="_blank"
rel="noopener noreferrer"
class="text-blue-500 hover:text-blue-600 underline underline-offset-4">
こちら
</a>
からサインインをお試しください。
</div>
autocomplete="current-password" />
</div>

<div v-if="error" class="mt-4 text-base text-red-500">
<p>{{ error }}</p>
<div class="mt-4 text-base text-subtext">
サインインに失敗する場合は、
<a
href="https://accounts.omu-aikido.com"
target="_blank"
rel="noopener noreferrer"
class="text-blue-500 hover:text-blue-600 underline underline-offset-4">
こちら
</a>
からサインインをお試しください。
</div>
</div>

<button type="submit" class="btn-primary mt-6 w-full" :disabled="isLoading">
{{ isLoading ? 'サインイン中...' : 'サインイン' }}
</button>
</form>

<button
type="button"
class="btn text-white w-full bg-[#5865f2] hover:bg-[#4752c4]"
:disabled="isLoading"
@click="handleSignInWithDiscord">
<svg
class="sq-5 mr-2"
aria-hidden="true"
focusable="false"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 256 256">
<g>
<circle stroke="none" cx="96" cy="144" r="12" fill="currentColor" />
<circle stroke="none" cx="160" cy="144" r="12" fill="currentColor" />
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="16"
d="M74 80a175 175 0 0 1 54-8 175 175 0 0 1 54 8m0 96a175 175 0 0 1-54 8 175 175 0 0 1-54-8" />
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="16"
d="m155 182 12 24a8 8 0 0 0 9 4c25-6 46-16 61-30a8 8 0 0 0 3-8L206 59a8 8 0 0 0-5-5 176 176 0 0 0-30-9 8 8 0 0 0-9 5l-8 24m-53 108-12 24a8 8 0 0 1-9 4c-25-6-46-16-61-30a8 8 0 0 1-3-8L50 59a8 8 0 0 1 5-5 176 176 0 0 1 30-9 8 8 0 0 1 9 5l8 24" />
</g>
</svg>
Discordで認証
<button type="submit" class="btn-primary mt-6 w-full" :disabled="isLoading">
{{ isLoading ? 'サインイン中...' : 'サインイン' }}
</button>
</div>
<hr class="my-6" />
<div class="mt-4 text-base text-subtext text-center">
まだアカウントがありませんか?
<RouterLink to="/sign-up" class="text-blue-500 hover:text-blue-600 underline underline-offset-4">
サインアップ
</RouterLink>
</div>
</form>

<button
type="button"
class="btn text-white w-full bg-[#5865f2] hover:bg-[#4752c4]"
:disabled="isLoading"
@click="handleSignInWithDiscord">
<svg
class="sq-5 mr-2"
aria-hidden="true"
focusable="false"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 256 256">
<g>
<circle stroke="none" cx="96" cy="144" r="12" fill="currentColor" />
<circle stroke="none" cx="160" cy="144" r="12" fill="currentColor" />
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="16"
d="M74 80a175 175 0 0 1 54-8 175 175 0 0 1 54 8m0 96a175 175 0 0 1-54 8 175 175 0 0 1-54-8" />
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="16"
d="m155 182 12 24a8 8 0 0 0 9 4c25-6 46-16 61-30a8 8 0 0 0 3-8L206 59a8 8 0 0 0-5-5 176 176 0 0 0-30-9 8 8 0 0 0-9 5l-8 24m-53 108-12 24a8 8 0 0 1-9 4c-25-6-46-16-61-30a8 8 0 0 1-3-8L50 59a8 8 0 0 1 5-5 176 176 0 0 1 30-9 8 8 0 0 1 9 5l8 24" />
</g>
</svg>
Discordで認証
</button>
</div>
<hr class="my-6" />
<div class="mt-4 text-base text-subtext text-center">
まだアカウントがありませんか?
<RouterLink to="/sign-up" class="text-blue-500 hover:text-blue-600 underline underline-offset-4">
サインアップ
</RouterLink>
</div>
</div>
</Show>
</div>
</div>
</Show>
</template>

<script setup lang="ts">
import Input from '@/components/ui/UiInput.vue';
import { Show } from '@clerk/vue';
import { useAuth } from '@/composable/useAuth';
import { useSignIn } from '@/composable/useSignIn';
import { onMounted, ref, watch } from 'vue';
import { useRoute, useRouter } from 'vue-router';

import { useAuth } from '@/composable/useAuth';
import { useSignIn } from '@/composable/useSignIn';

import Input from '@/components/ui/UiInput.vue';
import LoadingView from '@/components/signin/LoadingView.vue';

const router = useRouter();
const route = useRoute();
const { email, password, code, isLoading, error, needsVerification, signIn, verifyCode, signInWithDiscord } =
useSignIn();
const { isAuthenticated } = useAuth();
const gaugePercent = ref(0);
const redirectUrl = ref('/');

const handleSuccessfulSignIn = () => {
gaugePercent.value = 100;
setTimeout(() => {
router.push(redirectUrl.value);
}, 1000);
Expand Down
1 change: 1 addition & 0 deletions apps/server/src/app/user/clerk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export const clerk = new Hono<{ Bindings: Env }>() //
const updatePayload: Parameters<typeof clerkClient.users.updateUser>[1] = {};

if (username && typeof username === 'string') updatePayload.username = username;
if (!username) updatePayload.username = '';
if (firstName && typeof firstName === 'string') updatePayload.firstName = firstName;
if (lastName && typeof lastName === 'string') updatePayload.lastName = lastName;
if (Object.keys(updatePayload).length > 0) await clerkClient.users.updateUser(auth.userId, updatePayload);
Expand Down
Loading