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
17 changes: 14 additions & 3 deletions src/components/common/modals/edit-homework-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,17 @@ import { Modal } from '@/components/common/ui/modal';

import { useEditHomework } from '@/hooks/queries/group-study-homework-api';
import { useToastStore } from '@/stores/use-toast-store';
import { formatExternalLink } from '@/utils/format';
import { isValidUrl } from '@/utils/validation';

const EditHomeworkFormSchema = z.object({
textContent: z.string().min(1, '과제 상세 내용을 입력해주세요.').max(1000),
attachmentLink: z.string().optional(),
attachmentLink: z
.string()
.optional()
.refine((val) => !val || isValidUrl(val), {
message: '올바른 URL 형식을 입력해주세요. (예: https://example.com)',
}),
});

type EditHomeworkFormValues = z.infer<typeof EditHomeworkFormSchema>;
Expand Down Expand Up @@ -98,7 +105,11 @@ function EditHomeworkForm({
homeworkId,
request: {
textContent: values.textContent,
optionalSubmission: { link: values.attachmentLink },
optionalSubmission: {
link: values.attachmentLink
? formatExternalLink(values.attachmentLink)
: values.attachmentLink,
},
},
},
{
Expand Down Expand Up @@ -127,7 +138,7 @@ function EditHomeworkForm({
label="과제 상세 내용"
direction="vertical"
required
maxCharCount={5000}
maxCharCount={1000}
showCounterRight={false}
>
<TextAreaInput
Expand Down
15 changes: 13 additions & 2 deletions src/components/common/modals/submit-homework-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,17 @@ import { Modal } from '@/components/common/ui/modal';

import { useSubmitHomework } from '@/hooks/queries/group-study-homework-api';
import { useToastStore } from '@/stores/use-toast-store';
import { formatExternalLink } from '@/utils/format';
import { isValidUrl } from '@/utils/validation';

const SubmitHomeworkFormSchema = z.object({
textContent: z.string().min(1, '과제 상세 내용을 입력해주세요.').max(1000),
attachmentLink: z.string().optional(),
attachmentLink: z
.string()
.optional()
.refine((val) => !val || isValidUrl(val), {
message: '올바른 URL 형식을 입력해주세요. (예: https://example.com)',
}),
});

type SubmitHomeworkFormValues = z.infer<typeof SubmitHomeworkFormSchema>;
Expand Down Expand Up @@ -98,7 +105,11 @@ function SubmitHomeworkForm({
missionId,
request: {
textContent: values.textContent,
optionalSubmission: { link: values.attachmentLink },
optionalSubmission: {
link: values.attachmentLink
? formatExternalLink(values.attachmentLink)
: values.attachmentLink,
},
},
},
{
Expand Down
7 changes: 5 additions & 2 deletions src/components/contents/homework-detail-content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
} from '@/hooks/queries/peer-review-api';

import { useUserStore } from '@/stores/useUserStore';
import { formatExternalLink } from '@/utils/format';

const ConfirmDeleteModal = dynamic(
() => import('@/components/common/modals/confirm-delete-modal'),
Expand Down Expand Up @@ -132,7 +133,9 @@ export default function HomeworkDetailContent({
제출한 과제
</span>
<a
href={homework.homeworkContent.optionalContent.link}
href={formatExternalLink(
homework.homeworkContent.optionalContent.link,
)}
target="_blank"
rel="noopener noreferrer"
className="text-text-brand font-designer-14r hover:underline"
Expand All @@ -146,7 +149,7 @@ export default function HomeworkDetailContent({
{/* 피어 리뷰 */}
<PeerReviewSection
homeworkId={homeworkId}
peerReviews={peerReviews ?? []}
peerReviews={peerReviews}
isMyHomework={isMyHomework}
/>
</div>
Expand Down
9 changes: 5 additions & 4 deletions src/components/hall-of-fame/mvp-team-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { cn } from '@/components/common/ui/(shadcn)/lib/utils';
import UserAvatar from '@/components/common/ui/avatar';
import Tooltip from '@/components/common/ui/tooltip';
import type { MVPTeam } from '@/types/one-to-one-study/hall-of-fame';
import { formatExternalLink } from '@/utils/format';

interface MVPTeamCardProps {
team: MVPTeam;
Expand All @@ -19,12 +20,12 @@ export default function MVPTeamCard({ team, className }: MVPTeamCardProps) {
return (
<div
className={cn(
'rounded-200 shadow-2 relative overflow-hidden border border-[#FFEBA4] bg-gradient-to-br from-[#FFF8E7] to-[#FFF] p-500',
'rounded-200 shadow-2 relative overflow-hidden border border-yellow-200 bg-gradient-to-br from-yellow-50 to-gray-0 p-500',
className,
)}
>
<div className="absolute top-0 left-0 p-300 opacity-10">
<Trophy className="text-text-warning h-[120px] w-[120px]" />
<Trophy className="text-text-warning h-1500 w-1500" />
</div>
<div className="absolute top-400 right-400 z-20">
<Tooltip
Expand Down Expand Up @@ -104,12 +105,12 @@ export default function MVPTeamCard({ team, className }: MVPTeamCardProps) {
{team.sharedLinks.map((link, i) => (
<a
key={link.id}
href={link.url}
href={formatExternalLink(link.url)}
target="_blank"
rel="noopener noreferrer"
className="font-designer-13r text-text-subtle hover:text-text-information flex items-center gap-100 truncate transition-all hover:underline"
>
<span className="bg-fill-neutral-subtle-default text-text-subtle flex h-4 w-4 shrink-0 items-center justify-center rounded-full text-[10px]">
<span className="bg-fill-neutral-subtle-default text-text-subtle font-designer-10r flex h-4 w-4 shrink-0 items-center justify-center rounded-full">
{i + 1}
</span>
{link.title}
Expand Down
3 changes: 2 additions & 1 deletion src/components/lists/study-list-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
type DailyStudy,
type GetDailyStudiesResponse,
} from '@/types/api/schedule.types';
import { formatExternalLink } from '@/utils/format';
import LinkIcon from 'public/icons/Link.svg';

const TABLE_HEADERS = [
Expand Down Expand Up @@ -61,7 +62,7 @@ function mapDailyStudyToDisplayData(
'진행 상태': getStatusBadge(row.progressStatus),
'참고 자료': row.link ? (
<a
href={row.link}
href={formatExternalLink(row.link)}
target="_blank"
rel="noopener noreferrer"
onClick={() => {
Expand Down
14 changes: 9 additions & 5 deletions src/components/schedule/today-study-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { TUTORIAL_DAILY_STUDY_MOCK } from '@/config/tutorial-mock';
import { useAuthReady } from '@/features/auth/model/use-auth';
import { useDailyStudyDetailQuery } from '@/hooks/queries/use-interview-query';
import { DailyStudyDetail } from '@/types/api/interview.types';
import { formatExternalLink } from '@/utils/format';

const StudyDoneModal = dynamic(
() => import('@/components/common/modals/study-done-modal'),
Expand Down Expand Up @@ -95,7 +96,7 @@ export default function TodayStudyCard({
description={
isInterviewee ? '당신은 지원자입니다.' : '당신은 면접관입니다.'
}
className="mb-4"
className="mb-200"
titleClassName="font-bold-h5"
descriptionClassName="font-designer-14r"
/>
Expand Down Expand Up @@ -158,7 +159,7 @@ const renderFeedback = (
<div className="bg-background-alternative rounded-50 col-span-1 flex flex-col gap-100 px-300 py-250">
<h3 className="font-designer-14m text-text-subtle">피드백</h3>

<div className="flex flex-col items-center justify-center gap-[10px]">
<div className="flex flex-col items-center justify-center gap-125">
<Image
src="/icons/feedback.svg"
width={65}
Expand Down Expand Up @@ -308,8 +309,9 @@ function PartnerInfo({
realName={realName ?? name}
trigger={
<button
type="button"
data-tutorial="study-contact-button"
className="border-r-border-subtle rounded-l-75 hover:bg-fill-neutral-subtle-hover flex flex-1 items-center gap-75 border-r py-75 pr-[10px] pl-150 transition"
className="border-r-border-subtle rounded-l-75 hover:bg-fill-neutral-subtle-hover flex flex-1 items-center gap-75 border-r py-75 pr-125 pl-150 transition"
>
<Image
src="/icons/phone.svg"
Expand Down Expand Up @@ -364,11 +366,13 @@ function StudySubject({ subject }: Pick<DailyStudyDetail, 'subject'>) {
}

function StudyLink({ link }: Pick<DailyStudyDetail, 'link'>) {
if (!link) return null;

return (
<div className="bg-background-alternative rounded-50 col-span-2 flex items-center gap-300 px-300 py-250">
<span className="text-sm text-gray-600">스터디 링크</span>
<span className="font-designer-14r text-text-subtle">스터디 링크</span>

<Link href={link} target="_blank" rel="noreferrer">
<Link href={formatExternalLink(link)} target="_blank" rel="noreferrer">
<div className="flex cursor-pointer gap-75">
<Image
src="icons/Link.svg"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import dayjs from 'dayjs';
import { MessageCircle, Paperclip, Search, SendHorizontal } from 'lucide-react';
import Link from 'next/link';
import { cn } from '@/components/common/ui/(shadcn)/lib/utils';
import Badge from '@/components/common/ui/badge';
import Button from '@/components/common/ui/button';
import { MENTORING_REQUEST_STATUS_META } from '@/features/mentoring/model/management-status-meta';
Expand All @@ -20,6 +21,7 @@ import type {
NoteConsultationChannel,
NoteConsultationListItem,
} from '@/types/mentoring/note-consultation-view';
import { formatExternalLink } from '@/utils/format';

function UserAvatar({
name,
Expand All @@ -32,11 +34,12 @@ function UserAvatar({

return (
<div
className={`flex h-44 w-44 shrink-0 items-center justify-center rounded-full ${
className={cn(
'flex h-44 w-44 shrink-0 items-center justify-center rounded-full',
color === 'brand'
? 'bg-fill-brand-subtle-default text-text-brand'
: 'bg-fill-neutral-default-default text-text-subtle'
}`}
: 'bg-fill-neutral-default-default text-text-subtle',
)}
>
<span className="font-designer-16b">{initial}</span>
</div>
Expand All @@ -58,11 +61,12 @@ function RequestListCard({
<button
type="button"
onClick={onClick}
className={`rounded-150 w-full px-175 py-175 text-left transition-colors ${
className={cn(
'rounded-150 w-full px-175 py-175 text-left transition-colors',
selected
? 'bg-fill-brand-subtle-default'
: 'hover:bg-background-alternative'
}`}
: 'hover:bg-background-alternative',
)}
>
<div className="flex items-start gap-150">
<UserAvatar name={item.displayName} />
Expand All @@ -85,9 +89,6 @@ function RequestListCard({
{isUnreadLike && (
<span className="bg-fill-brand-default-default text-text-inverse font-designer-11m inline-flex h-20 min-w-[20px] shrink-0 items-center justify-center rounded-full px-50">
{item.mentorReplyCount}
<p className="font-designer-11m text-text-subtlest mt-25 truncate">
{item.displayRole || '상담 참여자'}
</p>
</span>
)}
</div>
Expand Down Expand Up @@ -141,7 +142,7 @@ function QuestionCard({ request }: { request: MentoringRequest }) {
{request.referenceLinks?.map((link) => (
<a
key={link}
href={link}
href={formatExternalLink(link)}
target="_blank"
rel="noopener noreferrer"
className="font-designer-11m text-text-brand border-border-subtle inline-flex items-center gap-50 rounded-full border px-100 py-50 underline"
Expand Down Expand Up @@ -383,22 +384,24 @@ export function NoteConsultationFilters({
<button
type="button"
onClick={() => onActiveChannelChange('sent')}
className={`font-designer-13m rounded-75 h-36 flex-1 ${
className={cn(
'font-designer-13m rounded-75 h-36 flex-1',
activeChannel === 'sent'
? 'bg-fill-brand-subtle-default text-text-brand'
: 'text-text-subtle'
}`}
: 'text-text-subtle',
)}
>
내가 신청한 상담
</button>
<button
type="button"
onClick={() => onActiveChannelChange('received')}
className={`font-designer-13m rounded-75 h-36 flex-1 ${
className={cn(
'font-designer-13m rounded-75 h-36 flex-1',
activeChannel === 'received'
? 'bg-fill-brand-subtle-default text-text-brand'
: 'text-text-subtle'
}`}
: 'text-text-subtle',
)}
>
받은 쪽지 신청
</button>
Expand Down
19 changes: 10 additions & 9 deletions src/features/study/one-to-one/history/ui/study-history-row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import dynamic from 'next/dynamic';
import UserAvatar from '@/components/common/ui/avatar';
import { StudyHistoryItem } from '@/types/one-to-one-study/study-history';
import { formatExternalLink } from '@/utils/format';

const UserProfileModal = dynamic(
() => import('@/components/common/modals/user-profile-modal'),
Expand Down Expand Up @@ -51,7 +52,7 @@ export const StudyHistoryRow = ({ item }: { item: StudyHistoryItem }) => {
image={partner.profileImage || undefined}
alt={partner.name}
size={32}
className="h-8 w-8"
className="h-400 w-400"
/>
<span className="text-text-default truncate font-medium">
{partner.name}
Expand All @@ -65,7 +66,7 @@ export const StudyHistoryRow = ({ item }: { item: StudyHistoryItem }) => {
image={undefined}
alt="상대방 정보 없음"
size={32}
className="h-8 w-8 opacity-60"
className="h-400 w-400 opacity-60"
/>
<span className="truncate font-medium">상대방 정보 없음</span>
</div>
Expand All @@ -75,12 +76,12 @@ export const StudyHistoryRow = ({ item }: { item: StudyHistoryItem }) => {
{/* 역할 */}
<div className="flex w-[120px] shrink-0 items-center justify-center gap-50">
{item.role === 'INTERVIEWER' ? (
<span className="rounded-50 bg-fill-brand-subtle-default text-text-brand inline-flex items-center gap-50 px-150 py-25 text-[12px] font-medium">
<span className="rounded-50 bg-fill-brand-subtle-default text-text-brand font-designer-12m inline-flex items-center gap-50 px-150 py-25">
<Mic className="h-3 w-3" />
면접자
</span>
) : (
<span className="rounded-50 bg-fill-information-subtle-default text-text-information inline-flex items-center gap-50 px-150 py-25 text-[12px] font-medium">
<span className="rounded-50 bg-fill-information-subtle-default text-text-information font-designer-12m inline-flex items-center gap-50 px-150 py-25">
<User className="h-3 w-3" />
지원자
</span>
Expand All @@ -92,12 +93,12 @@ export const StudyHistoryRow = ({ item }: { item: StudyHistoryItem }) => {
{item.attendance === 'ATTENDED' ? (
<span className="text-text-success flex flex-col items-center gap-25">
<CheckCircle className="h-4 w-4" />
<span className="text-[10px] font-bold">역할수행</span>
<span className="font-designer-10b">역할수행</span>
</span>
) : (
<span className="text-text-warning flex flex-col items-center gap-25">
<Clock className="h-4 w-4" />
<span className="text-[10px] font-bold">미진행</span>
<span className="font-designer-10b">미진행</span>
</span>
)}
</div>
Expand All @@ -107,12 +108,12 @@ export const StudyHistoryRow = ({ item }: { item: StudyHistoryItem }) => {
{item.status === 'COMPLETED' ? (
<span className="text-text-success flex flex-col items-center gap-25">
<CheckCircle2 className="h-4 w-4" />
<span className="text-[10px] font-bold">완료</span>
<span className="font-designer-10b">완료</span>
</span>
) : (
<span className="text-text-warning flex flex-col items-center gap-25">
<Clock className="h-4 w-4" />
<span className="text-[10px] font-bold">진행중</span>
<span className="font-designer-10b">진행중</span>
</span>
)}
</div>
Expand All @@ -121,7 +122,7 @@ export const StudyHistoryRow = ({ item }: { item: StudyHistoryItem }) => {
<div className="flex w-[80px] shrink-0 justify-center">
{item.link ? (
<a
href={item.link}
href={formatExternalLink(item.link)}
target="_blank"
rel="noopener noreferrer"
className="text-text-subtle hover:text-text-strong group flex items-center justify-center p-100 transition-all"
Expand Down
Loading
Loading