Skip to content
2 changes: 2 additions & 0 deletions src/entities/chat/model/types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import type { ImageSource } from "expo-image";

export interface ChatRoomItemProps {
id: string;
profileImage: ImageSource;
roomName: string;
lastMessage: string;
unreadCount?: number;
onPress?: () => void;
}

export interface Message {
Expand Down
63 changes: 37 additions & 26 deletions src/entities/chat/ui/ChatRoomItem.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Text, View } from "react-native";
import { Pressable, Text, View } from "react-native";

import { colorTokens } from "@/shared/styles/tokens";
import { ProfileAvatar } from "@/shared/ui/profile";

import type { ChatRoomItemProps } from "../model/types";
Expand All @@ -9,35 +10,45 @@ export function ChatRoomItem({
roomName,
lastMessage,
unreadCount = 0,
onPress,
}: ChatRoomItemProps) {
const displayCount = unreadCount > 99 ? "99+" : unreadCount; // 100 이상은 "99+"로 표시
return (
<View className="w-full flex-row items-center justify-between px-[10px] gap-[27px]">
{/* Left: profile + texts */}
<View className="flex-row shrink items-center gap-[27px]">
<ProfileAvatar source={profileImage} size={48} />
<Pressable onPress={onPress}>
{({ pressed }) => (
<View
className="w-full h-[70px] justify-center"
style={{ backgroundColor: pressed ? colorTokens.neutral : undefined }}
>
Comment thread
eunhyekimyeah marked this conversation as resolved.
Outdated
<View className="h-[48px] px-6 flex-row items-center justify-between gap-[27px]">
{/* 프로필 + 텍스트 영역 */}
<View className="ml-2 flex-row shrink items-center gap-[27px]">
<ProfileAvatar source={profileImage} size={48} />
{/* 텍스트 */}
<View className="shrink">
<Text className="font-bold text-[16px] leading-[22px] text-content-primary">
{roomName}
</Text>
<Text
className="font-regular text-[13px] leading-[22px] tracking-[-0.41px] text-content-secondary"
numberOfLines={1}
>
{lastMessage}
</Text>
</View>
</View>

{/* Text area */}
<View className="shrink">
<Text className="font-bold text-[16px] leading-[22px] text-content-primary">
{roomName}
</Text>
<Text
className="font-regular text-[13px] leading-[22px] tracking-[-0.41px] text-content-secondary"
numberOfLines={1}
>
{lastMessage}
</Text>
</View>
</View>

{/* Right: unread count badge */}
{unreadCount > 0 && (
<View className="rounded-full bg-primary justify-center px-[8px]">
<Text className="font-regular text-[12px] leading-[22px] tracking-[-0.41px] text-white">
{unreadCount}
</Text>
{/* 안읽은 메세지 카운트 뱃지 */}
{unreadCount > 0 && (
<View className="rounded-full bg-primary justify-center px-[8px]">
<Text className="font-regular text-[12px] leading-[22px] tracking-[-0.41px] text-white">
{displayCount}
</Text>
</View>
)}
</View>
</View>
)}
</View>
</Pressable>
);
}
25 changes: 25 additions & 0 deletions src/pages/admin/chat/model/mockChatRooms.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { ChatRoomItemProps } from "@/entities/chat";

export const MOCK_ADMIN_CHAT_ROOMS: ChatRoomItemProps[] = [
{
id: "1",
profileImage: { uri: "https://picsum.photos/seed/store1/48" },
roomName: "인쌩맥주 숭실대점",
lastMessage: "제휴 협력 가능할까요?",
unreadCount: 1,
},
{
id: "2",
profileImage: { uri: "https://picsum.photos/seed/store2/48" },
roomName: "역전할머니맥주 숭실대점",
lastMessage: "감사합니다..!",
unreadCount: 200,
},
{
id: "3",
profileImage: { uri: "https://picsum.photos/seed/store3/48" },
roomName: "리얼후라이",
lastMessage: "잘 부탁드립니다",
unreadCount: 0,
},
];
45 changes: 41 additions & 4 deletions src/pages/admin/chat/ui/AdminChatPage.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,46 @@
import { Text, View } from "react-native";
import { FlatList, View } from "react-native";

import { ChatRoomItem, type ChatRoomItemProps } from "@/entities/chat";
import { EmptyState } from "@/shared/ui/empty-state";
import { PageLayout } from "@/shared/ui/layout";
import { PageTitle } from "@/shared/ui/page-title";

import { MOCK_ADMIN_CHAT_ROOMS } from "../model/mockChatRooms"; //목데이터

// 채팅방 리스트 로컬 컴포넌트
function ChatRoomList({ rooms }: { rooms: ChatRoomItemProps[] }) {
return (
<FlatList
data={rooms}
keyExtractor={(item) => item.id}
getItemLayout={(_, index) => ({
length: 70,
offset: 70 * index,
index,
})}
renderItem={({ item }) => <ChatRoomItem {...item} />}
ListEmptyComponent={
<EmptyState
title="아직 채팅 내역이 없어요"
description={"제휴 협력을 원하는 매장에 채팅을\n시도 할 수 있어요!"}
/>
}
/>
);
}
// PageLayout에 px-6을 적용하지 않은 이유: 디자인 상 채팅방 아이템이 화면 양 끝까지 닿아야 하기 때문
export function AdminChatPage() {
return (
<View className="flex-1 items-center justify-center bg-canvas">
<Text className="text-content-primary font-medium">관리자 채팅</Text>
</View>
<PageLayout
withTopInset={true}
withBottomInset={false}
className="flex-1 bg-canvas"
contentContainerClassName="flex-1 pt-6"
>
<View className="px-6">
<PageTitle title="채팅 내역" />
</View>
<ChatRoomList rooms={MOCK_ADMIN_CHAT_ROOMS} />
</PageLayout>
);
}
2 changes: 1 addition & 1 deletion src/pages/admin/dashboard/ui/AdminDashboardPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export function AdminDashboardPage() {
activeOpacity={0.8}
className="bg-primary items-center justify-center rounded-[8px] h-[41px]"
onPress={() => {
router.push("/(protected)/(admin)/partner-ship-suggestion-box");
router.push("/(protected)/admin/partner-ship-suggestion-box");
}}
>
<Text className="text-[11px] font-semibold text-content-inverse leading-caption tracking-caption">
Expand Down
2 changes: 1 addition & 1 deletion src/pages/admin/home/ui/AdminHomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export function AdminHomePage() {
<PartnershipListWidget
partnerships={MOCK_PARTNERSHIPS}
onViewAll={() =>
router.push("/(protected)/(admin)/admin-partnership-list")
router.push("/(protected)/admin/admin-partnership-list")
}
/>

Expand Down
12 changes: 6 additions & 6 deletions src/pages/customer-service/ui/CustomerServicePage.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { useRef, useState } from "react";
import type { SubmitHandler, UseFormHandleSubmit } from "react-hook-form";
import { ActivityIndicator, Pressable, Text, View } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import type { InquiryFormData } from "@/features/inquiry-form";
import { InquiryForm } from "@/features/inquiry-form";
import { InquiryList } from "./InquiryList";
import { colorTokens } from "@/shared/styles/tokens";
import { AppTopBar } from "@/shared/ui/app-top-bar";
import { PageLayout } from "@/shared/ui/layout";
import { TabBar } from "@/shared/ui/TabBar";
import { colorTokens } from "@/shared/styles/tokens";
import { useRef, useState } from "react";
import { ActivityIndicator, Pressable, Text, View } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import type { InquiryFormData } from "@/features/inquiry-form";
import { InquiryList } from "./InquiryList";

// Mock user data - 실제 auth 시스템 연결은 나중에
const MOCK_USER = {
Expand Down
2 changes: 1 addition & 1 deletion src/pages/customer-service/ui/InquiryItem.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useRouter } from "expo-router";
import { Pressable, Text, View } from "react-native";
import { colorTokens } from "@/shared/styles/tokens";
import type { Inquiry } from "@/entities/inquiry";
import { colorTokens } from "@/shared/styles/tokens";

interface InquiryItemProps {
inquiry: Inquiry;
Expand Down
6 changes: 3 additions & 3 deletions src/pages/dev-hub/ui/DevHubPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export function DevHubPage() {

<Pressable
className="mb-3 items-center rounded-lg bg-primary py-4"
onPress={() => router.push("/(protected)/(student)/(tabs)/home")}
onPress={() => router.push("/(protected)/student/(tabs)/home")}
>
<Text className="text-sm font-semibold text-white">
학생 홈으로 이동
Expand All @@ -22,7 +22,7 @@ export function DevHubPage() {

<Pressable
className="mb-3 items-center rounded-lg bg-primary py-4"
onPress={() => router.push("/(protected)/(admin)/(tabs)/home")}
onPress={() => router.push("/(protected)/admin/(tabs)/home")}
>
<Text className="text-sm font-semibold text-white">
관리자 홈으로 이동
Expand All @@ -31,7 +31,7 @@ export function DevHubPage() {

<Pressable
className="mb-6 items-center rounded-lg bg-primary py-4"
onPress={() => router.push("/(protected)/(partner)/(tabs)/home")}
onPress={() => router.push("/(protected)/partner/(tabs)/home")}
>
<Text className="text-sm font-semibold text-white">
제휴업체 홈으로 이동
Expand Down
16 changes: 10 additions & 6 deletions src/pages/inquiry-detail/ui/InquiryDetailPage.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { BackArrowIcon } from "@/shared/assets/icons";
import { colorTokens } from "@/shared/styles/tokens";
import { useLocalSearchParams, useRouter } from "expo-router";
import { ActivityIndicator, Pressable, ScrollView, Text, View } from "react-native";
import {
ActivityIndicator,
Pressable,
ScrollView,
Text,
View,
} from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import { BackArrowIcon } from "@/shared/assets/icons";
import { colorTokens } from "@/shared/styles/tokens";
import { useInquiryDetail } from "../api/useInquiryDetail";

export function InquiryDetailPage() {
Expand Down Expand Up @@ -43,9 +49,7 @@ export function InquiryDetailPage() {
)}

{data && (
<ScrollView
contentContainerClassName="px-6 pt-7 pb-10"
>
<ScrollView contentContainerClassName="px-6 pt-7 pb-10">
{/* 제목 + 날짜 */}
<View className="items-center gap-0.5 mb-6">
<Text className="text-[18px] font-semibold text-content-primary tracking-[0.25px] text-center">
Expand Down
18 changes: 18 additions & 0 deletions src/pages/partner/chat/model/mockChatRooms.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { ChatRoomItemProps } from "@/entities/chat";

export const MOCK_PARTNER_CHAT_ROOMS: ChatRoomItemProps[] = [
{
id: "1",
profileImage: { uri: "https://picsum.photos/seed/admin1/48" },
roomName: "ASSU 운영진",
lastMessage: "제휴 조건 검토 중입니다.",
unreadCount: 2,
},
{
id: "2",
profileImage: { uri: "https://picsum.photos/seed/admin2/48" },
roomName: "ASSU 관리자",
lastMessage: "확인했습니다. 감사합니다!",
unreadCount: 0,
},
];
47 changes: 43 additions & 4 deletions src/pages/partner/chat/ui/PartnerChatPage.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,48 @@
import { Text, View } from "react-native";
import { FlatList, View } from "react-native";

import { ChatRoomItem, type ChatRoomItemProps } from "@/entities/chat";
import { EmptyState } from "@/shared/ui/empty-state";
import { PageLayout } from "@/shared/ui/layout";
import { PageTitle } from "@/shared/ui/page-title";

import { MOCK_PARTNER_CHAT_ROOMS } from "../model/mockChatRooms"; // 목데이터

// 채팅방 리스트 로컬 컴포넌트
function ChatRoomList({ rooms }: { rooms: ChatRoomItemProps[] }) {
return (
<FlatList
data={rooms}
keyExtractor={(item) => item.id}
getItemLayout={(_, index) => ({
length: 70,
offset: 70 * index,
index,
})}
renderItem={({ item }) => <ChatRoomItem {...item} />}
ListEmptyComponent={
<EmptyState
title="아직 채팅 내역이 없어요"
description={"제휴 협력을 원하는 매장에 채팅을\n시도 할 수 있어요!"}
Comment thread
eunhyekimyeah marked this conversation as resolved.
Outdated
/>
}
contentContainerClassName="pt-2"
/>
);
}

// PageLayout에 px-6을 적용하지 않은 이유: 디자인 상 채팅방 아이템이 화면 양 끝까지 닿아야 하기 때문
export function PartnerChatPage() {
return (
<View className="flex-1 items-center justify-center bg-canvas">
<Text className="text-content-primary font-medium">업체 채팅</Text>
</View>
<PageLayout
withTopInset={true}
withBottomInset={false}
className="flex-1 bg-canvas"
contentContainerClassName="flex-1 pt-6"
>
<View className="px-6">
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

큰 문제는 아니고 궁금한건데 contentContainerClassName="flex-1 px-6 pt-6" 에서 패딩이 적용되는데 내부 className에 패딩을 또 적용하신 이유가 있을까요!? 패딩이 중복되나 싶어서 여쭤봅니다!

Copy link
Copy Markdown
Contributor Author

@eunhyekimyeah eunhyekimyeah Apr 27, 2026

Choose a reason for hiding this comment

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

첨에 작성하고 나중에 더 구현하다가 놓친거같습니다!ㅎㅎ 수정했어용

<PageTitle title="채팅 내역" />
</View>
<ChatRoomList rooms={MOCK_PARTNER_CHAT_ROOMS} />
</PageLayout>
);
}
2 changes: 1 addition & 1 deletion src/pages/partner/dashboard/ui/PartnerDashboardPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export function PartnerDashboardPage() {
<View className="flex-1 items-center justify-center bg-canvas gap-gutter">
<Text className="text-content-primary font-medium">업체 대시보드</Text>
<Pressable
onPress={() => router.push("/(protected)/(partner)/review")}
onPress={() => router.push("/(protected)/partner/review")}
className="bg-primary px-screen-m py-card-p rounded-md items-center"
>
<Text className="text-content-inverse font-semibold text-md">
Expand Down
2 changes: 1 addition & 1 deletion src/pages/partner/home/ui/PartnerHomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export function PartnerHomePage() {
partnerships={MOCK_PARTNERSHIPS}
title="제휴단체 목록"
onViewAll={() =>
router.push("/(protected)/(partner)/partner-partnership-list")
router.push("/(protected)/partner/partner-partnership-list")
}
/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import {
ProposalInfoStep,
proposalSchema,
} from "@/features/partnership-proposal";
import { PageLayout } from "@/shared/ui/layout";
import { AppTopBar } from "@/shared/ui/app-top-bar";
import { PageLayout } from "@/shared/ui/layout";

type Step = "step1" | "step2" | "complete";

Expand Down
3 changes: 1 addition & 2 deletions src/pages/student/suggestion-form/model/useSuggestionForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ export function useSuggestionForm() {

const { mutate } = useMutation({
mutationFn: postSuggestion,
onSuccess: () =>
router.replace("/(protected)/(student)/suggestion-complete"),
onSuccess: () => router.replace("/(protected)/student/suggestion-complete"),
// TODO: API 연동 시 에러 핸들링 추가 (예: 토스트 메시지)
});

Expand Down
4 changes: 1 addition & 3 deletions src/pages/student/suggestion/ui/StudentSuggestionPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,7 @@ export function StudentSuggestionPage() {
{count > COLLAPSED_LIMIT && (
<Pressable
onPress={() =>
router.push(
`/(protected)/(student)/benefit-all?month=${month}`,
)
router.push(`/(protected)/student/benefit-all?month=${month}`)
}
>
<Text className="font-regular text-sm text-content-secondary leading-caption tracking-caption">
Expand Down
Loading
Loading