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

Guild Member Setting 페이지 #275

Merged
merged 32 commits into from
Jan 11, 2025
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
61d829f
refactor: guild dialog를 Intercepting Routes을 이용해 수정
sumi-0011 Jan 5, 2025
fc9c338
refactor: components로 파일 정리
sumi-0011 Jan 5, 2025
3d8d475
refactor: guild detail page refactor
sumi-0011 Jan 5, 2025
27068ff
feat: guild create init
sumi-0011 Jan 5, 2025
7f2a686
feat: get guild icons API
sumi-0011 Jan 5, 2025
c67f5a6
feat: get background API
sumi-0011 Jan 5, 2025
205cdab
feat: 내가 가입한 길드 페이지 추가
sumi-0011 Jan 7, 2025
ee1e625
feat: guild create 후 페이지 이동
sumi-0011 Jan 7, 2025
90ee5ba
feat: guild type img 추가
sumi-0011 Jan 7, 2025
76fddb8
feat: dropdown menu 커스텀
sumi-0011 Jan 7, 2025
0aa3543
feat: guild 더보기 버튼 추가
sumi-0011 Jan 7, 2025
cb9b5dd
Merge remote-tracking branch 'origin/feat/guild-create' into feat/gui…
sumi-0011 Jan 8, 2025
5e2799e
refactor: state -> form 방식으로 변경 진행중
sumi-0011 Jan 8, 2025
9bb8712
feat: 선택 가능한 폼 데이터 추가
sumi-0011 Jan 8, 2025
40a80f6
feat: guild form 제작
sumi-0011 Jan 8, 2025
9a3cfb9
feat: update guild 추가
sumi-0011 Jan 8, 2025
315b3fe
feat: 길드 설정 메뉴 접근 제어
sumi-0011 Jan 9, 2025
7c7c3e1
feat: setting 접근 제어
sumi-0011 Jan 9, 2025
b2fd77d
feat: Intercepting용 Dialog 추가
sumi-0011 Jan 9, 2025
f47351f
feat: update setting dialog 추가
sumi-0011 Jan 9, 2025
caa8ca0
feat: component 구현중
sumi-0011 Jan 9, 2025
f98c850
fix: member setting ssr error fix
sumi-0011 Jan 10, 2025
c6fe182
feat: kick member form 추가
sumi-0011 Jan 10, 2025
724160f
feat: 길드 가입 수락
sumi-0011 Jan 10, 2025
9ad6531
feat: 길드 가입, 거절 API 연결
sumi-0011 Jan 10, 2025
104b2b0
refactor: member card 파일 이동
sumi-0011 Jan 10, 2025
f205dbf
feat: 가입 수락, 제거
sumi-0011 Jan 10, 2025
4749ba6
feat: member page min height
sumi-0011 Jan 10, 2025
7cecdf2
refactor: layout 하나로 정의
sumi-0011 Jan 10, 2025
0cac774
feat: back trigger 추가
sumi-0011 Jan 10, 2025
91ca9e0
Merge branch 'guild' into feat/guild-member
sumi-0011 Jan 11, 2025
911b1b8
fix: max length 추가
sumi-0011 Jan 11, 2025
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
1 change: 1 addition & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"@gitanimals/util-common": "workspace:*",
"@next/third-parties": "^14.2.5",
"@octokit/core": "^6.1.2",
"@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-slot": "^1.1.0",
"@shadow-panda/style-context": "^0.7.1",
"@suspensive/react": "^2.17.1",
Expand Down
4 changes: 1 addition & 3 deletions apps/web/src/app/[locale]/error.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
'use client';

import { useEffect } from 'react';
import { signOut } from 'next-auth/react';

import { sendMessageToErrorChannel } from '@/apis/slack/sendMessage';
import { ErrorPage } from '@/components/Error/ErrorPage';
Expand Down Expand Up @@ -35,9 +34,8 @@ Error Stack: ${error.stack}
const router = useRouter();

const onClickRetry = () => {
signOut();
reset();
router.push('/');
router.refresh();
};

return (
Expand Down
41 changes: 6 additions & 35 deletions apps/web/src/app/[locale]/guild/(list)/GuildCard.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,24 @@
'use client';

import { useState } from 'react';
import Image from 'next/image';
import { Flex } from '_panda/jsx';
import { flex } from '_panda/patterns';
import { type Guild } from '@gitanimals/api';
import { inboxQueries } from '@gitanimals/react-query';
import { useQueryClient } from '@tanstack/react-query';
import { UsersRoundIcon } from 'lucide-react';

import { joinGuildAction } from '@/serverActions/guild';

import { GuildDetail } from './GuildDetail';
import { GuildJoinPetSelectDialog } from './GuildPetSelectDialog';
import { Link } from '@/i18n/routing';

interface GuildCardProps {
guild: Guild;
}

export function GuildCard({ guild }: GuildCardProps) {
const queryClient = useQueryClient();
const [isJoinPetSelectOpen, setIsJoinPetSelectOpen] = useState(false);
const [isDetailOpen, setIsDetailOpen] = useState(false);

const submitJoinGuild = async (selectPersona: string) => {
try {
await joinGuildAction({
guildId: guild.id,
personaId: selectPersona,
});

queryClient.invalidateQueries({ queryKey: inboxQueries.allKey() });
} catch (error) {
console.error(error);
}
};

return (
<>
<button type="button" className={cardStyle} onClick={() => setIsDetailOpen(true)}>
<Link href={`/guild/detail/${guild.id}`} className={cardStyle}>
<div className="card-top">
<Image src={guild.guildIcon} alt={guild.title} width={40} height={40} className="card-guild-icon" />
<Flex gap="6px" alignItems="center">
Expand All @@ -59,26 +39,17 @@ export function GuildCard({ guild }: GuildCardProps) {
<span>{guild.totalContributions}</span>
</li>
</ul>
</button>
{isDetailOpen && (
<GuildDetail
guildId={guild.id}
onClose={() => setIsDetailOpen(false)}
onJoin={() => {
setIsDetailOpen(false);
setIsJoinPetSelectOpen(true);
}}
/>
)}
{isJoinPetSelectOpen && (
</Link>

{/* {isJoinPetSelectOpen && (
<GuildJoinPetSelectDialog
onSubmit={(selectPersona) => {
submitJoinGuild(selectPersona);
setIsJoinPetSelectOpen(false);
}}
onClose={() => setIsJoinPetSelectOpen(false)}
/>
)}
)} */}
</>
);
}
Expand Down
126 changes: 0 additions & 126 deletions apps/web/src/app/[locale]/guild/(list)/GuildDetail.tsx

This file was deleted.

10 changes: 10 additions & 0 deletions apps/web/src/app/[locale]/guild/@modal/(.)create/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import GuildCreate from '../../_components/GuildCreate';
import GuildModal from '../GuildModal';

export default function GuildCreateModal() {
return (
<GuildModal title="Create Guild">
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

제목을 국제화(i18n)하는 것이 좋겠습니다.

"Create Guild"와 같은 문자열은 국제화가 필요합니다. 다른 파일들에서 사용된 것처럼 메시지 파일을 통해 다국어를 지원하는 것이 좋겠습니다.

예시 구현:

-    <GuildModal title="Create Guild">
+    <GuildModal title={t('guild.create.title')}>

그리고 메시지 파일에 다음 항목을 추가해주세요:

{
  "guild": {
    "create": {
      "title": "길드 생성"
    }
  }
}

<GuildCreate />
</GuildModal>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
'use client';

import { css } from '_panda/css';
import { inboxQueries } from '@gitanimals/react-query';
import { Dialog } from '@gitanimals/ui-panda';
import { useQueryClient } from '@tanstack/react-query';

import { useRouter } from '@/i18n/routing';
import { joinGuildAction } from '@/serverActions/guild';

import { GuildJoinPetSelectDialog } from '../../../../_components/GuildPetSelectDialog';
import GuildModal from '../../../GuildModal';

export default function GuildJoinModal({ params }: { params: { id: string } }) {
const queryClient = useQueryClient();
const router = useRouter();

const submitJoinGuild = async (selectPersona: string) => {
try {
await joinGuildAction({
guildId: params.id,
personaId: selectPersona,
});

queryClient.invalidateQueries({ queryKey: inboxQueries.allKey() });
router.replace(`/guild`);
} catch (error) {
console.error(error);
}
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

에러 처리 및 사용자 피드백 개선 필요

현재 에러가 발생했을 때 콘솔에만 출력되고 있습니다. 사용자에게 적절한 피드백을 제공해야 합니다.

+ import { toast } from '@gitanimals/ui-panda';

  const submitJoinGuild = async (selectPersona: string) => {
    try {
      await joinGuildAction({
        guildId: params.id,
        personaId: selectPersona,
      });

      queryClient.invalidateQueries({ queryKey: inboxQueries.allKey() });
+     toast.success('길드 가입이 완료되었습니다.');
      router.replace(`/guild`);
    } catch (error) {
-     console.error(error);
+     toast.error('길드 가입 중 오류가 발생했습니다. 다시 시도해 주세요.');
+     throw error;
    }
  };

Committable suggestion skipped: line range outside the PR's diff.


return (
<GuildModal>
<div>
<Dialog.Title className={dialogTitleStyle}>Choose your pet</Dialog.Title>
<p className={dialogDescriptionStyle}>If you choose a pet, it will be shown in the guild image.</p>
</div>
<GuildJoinPetSelectDialog onSubmit={submitJoinGuild} />
</GuildModal>
);
}

const dialogTitleStyle = css({});

const dialogDescriptionStyle = css({
textStyle: 'glyph20.regular',
color: 'white.white_50',
mt: 3,
textAlign: 'center',
});
17 changes: 17 additions & 0 deletions apps/web/src/app/[locale]/guild/@modal/(.)detail/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Button } from '@gitanimals/ui-panda';

import { Link } from '@/i18n/routing';

import { GuildDetail } from '../../../_components/GuildDetail';
import GuildModal from '../../GuildModal';

export default function GuildDetailModal({ params }: { params: { id: string } }) {
return (
<GuildModal>
<GuildDetail guildId={params.id} />
<Link href={`/guild/detail/${params.id}/join`} style={{ margin: 'auto' }}>
<Button w="100px">Join</Button>
</Link>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Join 버튼 UX 개선 필요

  1. 버튼의 고정 너비(100px)가 다국어 지원시 문제될 수 있습니다.
  2. 로딩 상태와 에러 상태 처리가 필요합니다.
  3. 버튼 클릭 시 사용자 피드백이 없습니다.

다음과 같은 개선을 제안합니다:

-      <Link href={`/guild/detail/${params.id}/join`} style={{ margin: 'auto' }}>
-        <Button w="100px">Join</Button>
+      <Link href={`/guild/detail/${params.id}/join`} style={{ margin: 'auto', width: 'fit-content' }}>
+        <Button
+          minW="100px"
+          isLoading={isLoading}
+          loadingText="Joining..."
+        >
+          {t('join')}
+        </Button>
       </Link>

Committable suggestion skipped: line range outside the PR's diff.

</GuildModal>
);
}
46 changes: 46 additions & 0 deletions apps/web/src/app/[locale]/guild/@modal/GuildModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use client';

import { type PropsWithChildren, useEffect, useState } from 'react';
import { css, cx } from '_panda/css';
import { Dialog } from '@gitanimals/ui-panda';

import { usePathname, useRouter } from '@/i18n/routing';
import { customScrollHorizontalStyle } from '@/styles/scrollStyle';

export default function GuildModal({ children, title }: PropsWithChildren<{ title?: string }>) {
const router = useRouter();
const pathname = usePathname();

const [isOpen, setIsOpen] = useState(false);

const onClose = () => {
router.back();
};

useEffect(() => {
if (!isOpen) {
setIsOpen(true);
} else {
setIsOpen(false);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [pathname]);

return (
<Dialog open={isOpen} onOpenChange={onClose}>
<Dialog.Content size="large" className={dialogContentStyle}>
{title && <Dialog.Title>{title}</Dialog.Title>}
{children}
</Dialog.Content>
</Dialog>
);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

베이스 모달 컴포넌트 추출 필요

InterceptingDialog와 중복되는 코드가 많습니다. 공통 로직을 분리하여 재사용성을 높일 필요가 있습니다.

베이스 모달 컴포넌트를 만들고 이를 상속받아 사용하는 구조로 변경을 제안드립니다.


const dialogContentStyle = cx(
css({
height: 'fit-content',
gap: 8,
overflowY: 'auto',
}),
customScrollHorizontalStyle,
);
3 changes: 3 additions & 0 deletions apps/web/src/app/[locale]/guild/@modal/default.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function GuildModal() {
return null;
}
26 changes: 26 additions & 0 deletions apps/web/src/app/[locale]/guild/[id]/@modal/(.)setting/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { redirect } from 'next/navigation';
import { checkIsLeader, getGuildBackgrounds, getGuildById, getGuildIcons } from '@gitanimals/api';
import { DialogTitle } from '@gitanimals/ui-panda';

import { InterceptingDialog } from '@/components/InterceptingDialog';

import { GuildSetting } from '../../setting/GuildSetting';

export default async function GuildSettingModal({ params }: { params: { id: string } }) {
const isLeader = await checkIsLeader(params.id);

const icons = await getGuildIcons();
const backgrounds = await getGuildBackgrounds();
const data = await getGuildById({ guildId: params.id });

if (!isLeader) {
redirect(`/guild/${params.id}`);
}

return (
<InterceptingDialog>
<DialogTitle>Guild Setting</DialogTitle>
<GuildSetting icons={icons} backgrounds={backgrounds} guildId={params.id} initialData={data} />
</InterceptingDialog>
);
}
3 changes: 3 additions & 0 deletions apps/web/src/app/[locale]/guild/[id]/@modal/default.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function GuildModal() {
return null;
}
Loading
Loading