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
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ import { ERROR_CODE, ERROR_MESSAGE_MAP } from '@piki/core';
import { useEffect, useMemo, useState } from 'react';
import { toast } from 'sonner';

import { CheckIconFill, StopwatchIconFill } from '@/assets/icons/fill';
import { CheckIconFill, TimerIconFill } from '@/assets/icons/fill';
import Button from '@/components/button';
import { Drawer, DrawerContent, DrawerDescription, DrawerTitle } from '@/components/drawer';
import { ANALYTICS_EVENT } from '@/consts/analytics';
import { logAnalyticsEvent } from '@/utils/analytics';
import { parseServerLocalDateTime } from '@/utils/formatDate';
import { share } from '@/utils/share';
import { markInviteSent } from '@/utils/inviteSentSession';
import { copyToClipboard, share } from '@/utils/share';

import { usePatchInviteExpiry } from '../../_hooks/usePatchInviteExpiry';
import { markInviteSent } from '@/utils/inviteSentSession';
import InviteExpiresPicker from './InviteExpiresPicker';

type InviteFriendsDialogProps = {
Expand All @@ -22,6 +22,8 @@ type InviteFriendsDialogProps = {
/** 마감 시각 변경 API 호출용 */
tournamentId: number;
inviteUrl?: string;
/** 초대 코드 — 링크 대신 코드로 참여할 때 쓴다 */
inviteCode?: string;
/** ISO 8601 — 초대 코드 만료 시각 */
inviteExpiresAt?: string;
};
Expand Down Expand Up @@ -68,6 +70,7 @@ function InviteFriendsDialog({
onOpenChange,
tournamentId,
inviteUrl,
inviteCode,
inviteExpiresAt,
}: InviteFriendsDialogProps) {
const [isPickerOpen, setIsPickerOpen] = useState(false);
Expand All @@ -91,6 +94,17 @@ function InviteFriendsDialog({

const handleOpenPicker = () => setIsPickerOpen(true);

const handleCopyInviteCode = async () => {
if (!inviteCode) return;

try {
await copyToClipboard(inviteCode);
toast.success('초대 코드를 복사했어요.');
} catch {
toast.warning('이 환경에서는 복사를 지원하지 않아요.');
}
};

const handleConfirmExpires = (newExpiresAt: string) => {
patchInviteExpiryMutation(
{ newExpiresAt },
Expand Down Expand Up @@ -133,49 +147,72 @@ function InviteFriendsDialog({
<DrawerContent>
<div className="flex flex-col items-center gap-6">
<div className="flex flex-col items-center gap-1">
<DrawerTitle className="heading-1-bold text-text-neutral-primary">
<DrawerTitle className="heading-2-semibold text-text-neutral-primary">
친구 초대하기
</DrawerTitle>
<DrawerDescription className="body-1-medium text-text-neutral-tertiary">
초대 링크를 보내 친구와 함께 담을 수 있어요.
<DrawerDescription className="body-2-regular text-text-neutral-secondary">
링크를 보내 친구와 함께 담을 수 있어요.
</DrawerDescription>
</div>

{expiresInfo && (
<div className="flex w-full items-center justify-between rounded-xl border border-border-neutral-muted bg-bg-layer-default px-4 py-5">
<div className="flex items-center gap-4">
<div className="flex size-11 items-center justify-center rounded-3xl bg-sky-blue-50">
<StopwatchIconFill className="size-6 text-text-accent" />
<div className="flex w-full flex-col gap-5">
{expiresInfo && (
<div className="flex flex-col gap-1">
<p className="body-2-medium text-text-neutral-primary">담기 기한</p>
<div className="flex w-full items-center justify-between rounded-xl border border-gray-75 p-4">
<div className="flex items-center gap-3">
<div className="flex size-11 items-center justify-center rounded-full bg-sky-blue-50">
<TimerIconFill className="size-6 text-sky-blue-300" />
</div>
<div className="flex flex-col gap-0.5">
<p className="caption-1-semibold text-sky-blue-500">
{expiresInfo.remainingLabel}
</p>
<p className="heading-2-semibold text-text-neutral-primary">
{expiresInfo.absoluteLabel}
</p>
</div>
</div>
<button
type="button"
className="cursor-pointer body-2-medium text-text-neutral-tertiary"
onClick={handleOpenPicker}
>
변경
</button>
</div>
<div className="flex flex-col gap-0.5">
<p className="body-2-semibold text-text-accent">{expiresInfo.remainingLabel}</p>
<p className="heading-1-bold text-text-neutral-primary">
{expiresInfo.absoluteLabel}
</p>
</div>
)}

{inviteCode && (
<div className="flex flex-col gap-2">
<p className="body-2-medium text-text-neutral-primary">초대 코드</p>
<div className="flex w-full items-center justify-between rounded-xl border border-gray-75 p-4">
<p className="heading-2-semibold text-text-neutral-primary">{inviteCode}</p>
<button
type="button"
className="cursor-pointer body-2-medium text-text-neutral-tertiary"
onClick={handleCopyInviteCode}
>
복사
</button>
</div>
</div>
<button
type="button"
className="cursor-pointer body-2-medium text-text-neutral-tertiary underline"
onClick={handleOpenPicker}
>
변경
</button>
</div>
)}

<div className="flex w-full flex-col gap-2 rounded-xl bg-gray-50 p-4">
<div className="flex items-center gap-2">
<CheckIconFill className="size-4.5 text-text-neutral-secondary" />
<p className="body-2-medium text-text-neutral-secondary">
최대 7명까지 초대할 수 있어요.
</p>
</div>
<div className="flex items-center gap-2">
<CheckIconFill className="size-4.5 text-text-neutral-secondary" />
<p className="body-2-medium text-text-neutral-secondary">
설정한 기한이 지나면 후보를 담을 수 없어요.
</p>
)}

<div className="flex w-full flex-col gap-1 rounded-xl bg-gray-50 px-4 py-3">
<div className="flex items-center gap-1">
<CheckIconFill className="size-4.5 text-icon-neutral-secondary" />
<p className="caption-1-regular text-text-neutral-secondary">
최대 7명까지 초대할 수 있어요.
</p>
</div>
<div className="flex items-center gap-1">
<CheckIconFill className="size-4.5 text-icon-neutral-secondary" />
<p className="caption-1-regular text-text-neutral-secondary">
설정한 기한이 지나면 후보를 담을 수 없어요.
</p>
</div>
</div>
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ function ParticipantPanel({
onOpenChange={setIsInviteDialogOpen}
tournamentId={Number(tournamentId)}
inviteUrl={inviteUrl}
inviteCode={inviteCode}
inviteExpiresAt={inviteExpiresAt}
/>
</>
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/utils/share.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const canUseWebShare = (data: ShareDataT) => {
return true;
};

const copyToClipboard = async (text: string) => {
export const copyToClipboard = async (text: string) => {
if (typeof navigator !== 'undefined' && navigator.clipboard?.writeText) {
await navigator.clipboard.writeText(text);
return;
Expand Down
Loading