From d0611acea05837d085df1ed564bf99c2409b81a8 Mon Sep 17 00:00:00 2001 From: Junseo Kim Date: Sun, 20 Jul 2025 23:30:23 +0900 Subject: [PATCH 01/12] =?UTF-8?q?refactor:=20=EC=88=98=EC=A0=95=EB=90=9C?= =?UTF-8?q?=20=EC=B5=9C=EC=86=8C=20=EB=84=88=EB=B9=84=EB=A5=BC=20300px?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/common/InputField/InputField.styles.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/common/InputField/InputField.styles.ts b/frontend/src/components/common/InputField/InputField.styles.ts index 60bcccefb..a81ad59bb 100644 --- a/frontend/src/components/common/InputField/InputField.styles.ts +++ b/frontend/src/components/common/InputField/InputField.styles.ts @@ -2,7 +2,7 @@ import styled from 'styled-components'; export const InputContainer = styled.div<{ width: string; readOnly?: boolean }>` width: ${(props) => props.width}; - min-width: 385px; + min-width: 300px; display: flex; flex-direction: column; From 9b689813e298252671509dcc9773413491952e67 Mon Sep 17 00:00:00 2001 From: Junseo Kim Date: Sun, 20 Jul 2025 23:30:41 +0900 Subject: [PATCH 02/12] =?UTF-8?q?refactor:=20=EC=B5=9C=EC=86=8C=20?= =?UTF-8?q?=EB=84=88=EB=B9=84=EB=A5=BC=20385px=EC=97=90=EC=84=9C=20300px?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/common/CustomTextArea/CustomTextArea.styles.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/common/CustomTextArea/CustomTextArea.styles.ts b/frontend/src/components/common/CustomTextArea/CustomTextArea.styles.ts index 70ddbeb39..aae92cf83 100644 --- a/frontend/src/components/common/CustomTextArea/CustomTextArea.styles.ts +++ b/frontend/src/components/common/CustomTextArea/CustomTextArea.styles.ts @@ -4,7 +4,7 @@ import styled from 'styled-components'; export const TextAreaContainer = styled.div<{ width: string }>` width: ${(props) => props.width}; - min-width: 385px; + min-width: 300px; display: flex; flex-direction: column; `; @@ -38,7 +38,7 @@ export const TextArea = styled.textarea<{ hasError?: boolean }>` border-color: ${({ hasError }) => (hasError ? 'red' : '#007bff')}; box-shadow: 0 0 3px ${({ hasError }) => - hasError ? 'rgba(255, 0, 0, 0.5)' : 'rgba(0, 123, 255, 0.5)'}; + hasError ? 'rgba(255, 0, 0, 0.5)' : 'rgba(0, 123, 255, 0.5)'}; } &:disabled { From 270e878a1ba2320ad0fd01b685a6803c5770a690 Mon Sep 17 00:00:00 2001 From: Junseo Kim Date: Sun, 20 Jul 2025 23:31:27 +0900 Subject: [PATCH 03/12] =?UTF-8?q?refactor:=20=EC=A7=80=EC=9B=90=EC=84=9C?= =?UTF-8?q?=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit margin-top 및 gap 값 수정 --- .../pages/ApplicationFormPage/ApplicationFormPage.styles.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.styles.ts b/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.styles.ts index b691152ce..fe24d737c 100644 --- a/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.styles.ts +++ b/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.styles.ts @@ -5,7 +5,7 @@ export const FormTitle = styled.h1` font-weight: 700; border: none; outline: none; - margin-top: 20px; + margin-top: 30px; margin-bottom: 46px; `; @@ -15,7 +15,7 @@ export const QuestionsWrapper = styled.div` gap: 50px; @media (max-width: 500px) { - gap: 30px; + gap: 10px; } `; From f791496af733fef4cb9f73bb9c9c0f1587f7a3a2 Mon Sep 17 00:00:00 2001 From: Junseo Kim Date: Sun, 20 Jul 2025 23:31:36 +0900 Subject: [PATCH 04/12] =?UTF-8?q?refactor:=20QuestionContainer=EC=9D=98=20?= =?UTF-8?q?=ED=8C=A8=EB=94=A9=20=EA=B0=92=EC=9D=84=2024px=EC=97=90?= =?UTF-8?q?=EC=84=9C=2026px=2015px=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/QuestionContainer/QuestionContainer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/pages/ApplicationFormPage/components/QuestionContainer/QuestionContainer.tsx b/frontend/src/pages/ApplicationFormPage/components/QuestionContainer/QuestionContainer.tsx index d252dad39..9f5955e83 100644 --- a/frontend/src/pages/ApplicationFormPage/components/QuestionContainer/QuestionContainer.tsx +++ b/frontend/src/pages/ApplicationFormPage/components/QuestionContainer/QuestionContainer.tsx @@ -4,7 +4,7 @@ import { forwardRef } from 'react'; const Container = styled.div<{ hasError?: boolean }>` border: ${({ hasError }) => (hasError ? '1px solid #FF5414' : 'transparent')}; border-radius: 16px; - padding: 24px; + padding: 26px 15px; position: relative; scroll-margin-top: 120px; transition: border 0.2s ease; From e0d3a25bc56ee425daaa6d7ba8027c12e7c94054 Mon Sep 17 00:00:00 2001 From: Junseo Kim Date: Sun, 20 Jul 2025 23:31:43 +0900 Subject: [PATCH 05/12] =?UTF-8?q?refactor:=20PageContainer=EC=9D=98=20padd?= =?UTF-8?q?ingTop=20=EA=B0=92=EC=9D=84=20172px=EC=97=90=EC=84=9C=20100px?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx b/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx index 86c1f6bee..eb4969054 100644 --- a/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx +++ b/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx @@ -94,7 +94,7 @@ const AnswerApplicationForm = () => { return ( <>
- + Date: Sun, 20 Jul 2025 23:52:55 +0900 Subject: [PATCH 06/12] =?UTF-8?q?refactor:=20FormTitle=20=EB=B0=8F=20Quest?= =?UTF-8?q?ionsWrapper=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ApplicationFormPage.styles.ts | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.styles.ts b/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.styles.ts index fe24d737c..47360191c 100644 --- a/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.styles.ts +++ b/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.styles.ts @@ -1,18 +1,41 @@ import styled from 'styled-components'; export const FormTitle = styled.h1` - font-size: 2.5rem; + font-size: 2.2rem; font-weight: 700; border: none; outline: none; margin-top: 30px; - margin-bottom: 46px; + margin-bottom: 30px; + padding: 0 15px; `; +export const FormDescription = styled.div` + white-space: pre-line; + font-size: 1rem; + line-height: 1.6; + color: #444; + margin-top: -20px; + margin-bottom: 48px; + padding: 0 15px; + + a { + color: #0077cc; + text-decoration: underline; + word-break: break-all; + } + + @media (max-width: 500px) { + font-size: 0.95rem; + line-height: 1.5; + } +`; + + export const QuestionsWrapper = styled.div` display: flex; flex-direction: column; - gap: 50px; + gap: 20px; @media (max-width: 500px) { gap: 10px; From a68347de5b2c1c3683ae35a421464c090117f191 Mon Sep 17 00:00:00 2001 From: Junseo Kim Date: Sun, 20 Jul 2025 23:53:23 +0900 Subject: [PATCH 07/12] =?UTF-8?q?feat:=20=EC=A7=80=EC=9B=90=EC=84=9C=20?= =?UTF-8?q?=EC=84=A4=EB=AA=85=20=EB=B6=80=EB=B6=84=20UI=20=EB=B0=8F=20?= =?UTF-8?q?=EC=9C=A0=ED=8B=B8=20=ED=95=A8=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ApplicationFormPage.tsx | 40 +++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx b/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx index eb4969054..b400d9d47 100644 --- a/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx +++ b/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx @@ -1,4 +1,4 @@ -import { useState, useRef } from 'react'; +import React, { useState, useRef, Fragment } from 'react'; import { PageContainer } from '@/styles/PageContainer.styles'; import Header from '@/components/common/Header/Header'; import { useNavigate, useParams } from 'react-router-dom'; @@ -13,6 +13,32 @@ import applyToClub from '@/apis/application/applyToClub'; import QuestionContainer from '@/pages/ApplicationFormPage/components/QuestionContainer/QuestionContainer'; import * as Styled from './ApplicationFormPage.styles'; +const parseDescriptionWithLinks = (text: string): React.ReactNode => { + const urlRegex = /(https?:\/\/[^\s]+)/g; + + const parts = text.split(urlRegex); + + return parts.map((part, index) => { + if (urlRegex.test(part)) { + return ( + + {part} + + ); + } else { + return {part}; + } + }); +} + + + const AnswerApplicationForm = () => { const { clubId } = useParams<{ clubId: string }>(); const navigate = useNavigate(); @@ -95,14 +121,22 @@ const AnswerApplicationForm = () => { <>
- + /> */} {formData.title} + {/* + 지원서 설명 받았다고 치고 + {formData.description && ( + + {parseDescriptionWithLinks(formData.description)} + + + )} */} {formData.questions.map((q: Question, i: number) => ( Date: Mon, 21 Jul 2025 00:03:01 +0900 Subject: [PATCH 08/12] =?UTF-8?q?feat:=20=EB=A7=81=ED=81=AC=EA=B0=80=20?= =?UTF-8?q?=ED=8F=AC=ED=95=A8=EB=90=9C=20=EC=84=A4=EB=AA=85=EC=9D=84=20?= =?UTF-8?q?=ED=8C=8C=EC=8B=B1=ED=95=98=EB=8A=94=20=EC=9C=A0=ED=8B=B8=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/utils/parseDescriptionWithLinks.tsx | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 frontend/src/utils/parseDescriptionWithLinks.tsx diff --git a/frontend/src/utils/parseDescriptionWithLinks.tsx b/frontend/src/utils/parseDescriptionWithLinks.tsx new file mode 100644 index 000000000..6e3ee0d3e --- /dev/null +++ b/frontend/src/utils/parseDescriptionWithLinks.tsx @@ -0,0 +1,22 @@ +import { Fragment } from 'react'; + +export const parseDescriptionWithLinks = (text: string): React.ReactNode => { + const urlRegex = /(https?:\/\/[^\s]+)/g; + + return text.split(urlRegex).map((part, index) => { + const isUrl = /^https?:\/\/[^\s]+$/.test(part); + return isUrl ? ( + + {part} + + ) : ( + {part} + ); + }); +}; From 05444a08a55b4dc32d33bde39b8677eb05aa3bfb Mon Sep 17 00:00:00 2001 From: Junseo Kim Date: Mon, 21 Jul 2025 00:03:14 +0900 Subject: [PATCH 09/12] =?UTF-8?q?feat:=20=EC=A7=80=EC=9B=90=EC=84=9C=20?= =?UTF-8?q?=EB=8B=B5=EB=B3=80=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80?= =?UTF-8?q?=EC=82=AC=EB=A5=BC=20=EC=9C=84=ED=95=9C=20validateAnswers=20?= =?UTF-8?q?=ED=9B=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/hooks/useValidateAnswers.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 frontend/src/hooks/useValidateAnswers.ts diff --git a/frontend/src/hooks/useValidateAnswers.ts b/frontend/src/hooks/useValidateAnswers.ts new file mode 100644 index 000000000..2cbba07fd --- /dev/null +++ b/frontend/src/hooks/useValidateAnswers.ts @@ -0,0 +1,14 @@ +import { Question } from '@/types/application'; + +export const validateAnswers = ( + questions: Question[], + getAnswersById: (id: number) => string[] +): number[] => { + return questions + .filter(q => q.options.required) + .filter(q => { + const answers = getAnswersById(q.id); + return answers.length === 0 || answers.every(s => s.trim() === ''); + }) + .map(q => q.id); +}; From 547436b61baf47de13d7b3d4a2dff854198ee8e0 Mon Sep 17 00:00:00 2001 From: Junseo Kim Date: Mon, 21 Jul 2025 00:03:21 +0900 Subject: [PATCH 10/12] =?UTF-8?q?refactor:=20media=20=EC=BF=BC=EB=A6=AC?= =?UTF-8?q?=EB=A5=BC=20=EC=82=AC=EC=9A=A9=ED=95=98=EC=97=AC=20=EC=8A=A4?= =?UTF-8?q?=ED=83=80=EC=9D=BC=EC=9D=84=20=EA=B0=9C=EC=84=A0=ED=95=98?= =?UTF-8?q?=EA=B3=A0=20=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20CSS=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ApplicationFormPage.styles.ts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.styles.ts b/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.styles.ts index 47360191c..5b0899e1a 100644 --- a/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.styles.ts +++ b/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.styles.ts @@ -1,4 +1,5 @@ import styled from 'styled-components'; +import { media } from '@/styles/mediaQuery'; export const FormTitle = styled.h1` font-size: 2.2rem; @@ -19,14 +20,8 @@ export const FormDescription = styled.div` margin-bottom: 48px; padding: 0 15px; - a { - color: #0077cc; - text-decoration: underline; - word-break: break-all; - } - - @media (max-width: 500px) { - font-size: 0.95rem; + ${media.mobile} { + font-size: 0.95rem; line-height: 1.5; } `; @@ -37,7 +32,7 @@ export const QuestionsWrapper = styled.div` flex-direction: column; gap: 20px; - @media (max-width: 500px) { + ${media.mobile} { gap: 10px; } From e5a3b23978963372a7639f3b384929c6ba89377b Mon Sep 17 00:00:00 2001 From: Junseo Kim Date: Mon, 21 Jul 2025 00:03:26 +0900 Subject: [PATCH 11/12] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0=20=EB=B0=8F?= =?UTF-8?q?=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ApplicationFormPage.tsx | 86 ++++++------------- 1 file changed, 28 insertions(+), 58 deletions(-) diff --git a/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx b/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx index b400d9d47..f203ec85d 100644 --- a/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx +++ b/frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx @@ -1,9 +1,9 @@ -import React, { useState, useRef, Fragment } from 'react'; +import { useState, useRef } from 'react'; import { PageContainer } from '@/styles/PageContainer.styles'; import Header from '@/components/common/Header/Header'; import { useNavigate, useParams } from 'react-router-dom'; import { useGetClubDetail } from '@/hooks/queries/club/useGetClubDetail'; -import ClubProfile from '@/pages/ClubDetailPage/components/ClubProfile/ClubProfile'; +//import ClubProfile from '@/pages/ClubDetailPage/components/ClubProfile/ClubProfile'; import { useAnswers } from '@/hooks/useAnswers'; import QuestionAnswerer from '@/pages/ApplicationFormPage/components/QuestionAnswerer/QuestionAnswerer'; import { useGetApplication } from '@/hooks/queries/application/useGetApplication'; @@ -11,32 +11,10 @@ import { Question } from '@/types/application'; import Spinner from '@/components/common/Spinner/Spinner'; import applyToClub from '@/apis/application/applyToClub'; import QuestionContainer from '@/pages/ApplicationFormPage/components/QuestionContainer/QuestionContainer'; +import { parseDescriptionWithLinks } from '@/utils/parseDescriptionWithLinks'; +import { validateAnswers } from '@/hooks/useValidateAnswers'; import * as Styled from './ApplicationFormPage.styles'; -const parseDescriptionWithLinks = (text: string): React.ReactNode => { - const urlRegex = /(https?:\/\/[^\s]+)/g; - - const parts = text.split(urlRegex); - - return parts.map((part, index) => { - if (urlRegex.test(part)) { - return ( - - {part} - - ); - } else { - return {part}; - } - }); -} - const AnswerApplicationForm = () => { @@ -74,53 +52,47 @@ const AnswerApplicationForm = () => { } }; + const handleScrollToInvalid = (invalidIds: number[]) => { + const firstInvalidIndex = formData?.questions.findIndex((q: Question) => invalidIds.includes(q.id)); + const targetEl = firstInvalidIndex !== undefined ? questionRefs.current[firstInvalidIndex] : null; + targetEl?.scrollIntoView({ behavior: 'smooth', block: 'start' }); + }; - if (isError || clubError) { - alert(applicationError?.message || '문제가 발생했어요.'); - navigate(`/club/${clubId}`); - return
문제가 발생했어요. 잠시 후 다시 시도해 주세요.
; - } - - if (!formData || !clubDetail) { - return ( -
- 지원서 정보를 불러오지 못했어요. 새로고침하거나 잠시 후 다시 시도해 주세요. -
- ); - } const handleSubmit = async () => { - const invalidIds: number[] = formData.questions - .filter((q: Question) => q.options.required) - .filter((q: Question) => { - const a = getAnswersById(q.id); - return a.length === 0 || a.every((s) => s.trim() === ''); - }) - .map((q: Question) => q.id); + if (!formData) return; + + const invalidIds = validateAnswers(formData.questions, getAnswersById); if (invalidIds.length > 0) { setInvalidQuestionIds(invalidIds); - - const firstInvalidIndex = formData.questions.findIndex((q: Question) => - invalidIds.includes(q.id), - ); - const targetEl = questionRefs.current[firstInvalidIndex]; - targetEl?.scrollIntoView({ behavior: 'smooth', block: 'start' }); + handleScrollToInvalid(invalidIds); return; } try { - await applyToClub(clubId, answers); + await applyToClub(clubId!, answers); alert('답변이 성공적으로 제출되었습니다.'); - } catch (e) { + } catch { alert('답변 제출에 실패했습니다. 잠시 후 다시 시도해 주세요.'); } }; + if (!clubId) return null; + if (isLoading) return ; + if (isError || clubError) { + alert(applicationError?.message || '문제가 발생했어요.'); + navigate(`/club/${clubId}`); + return
문제가 발생했어요. 잠시 후 다시 시도해 주세요.
; + } + if (!formData || !clubDetail) { + return
지원서 정보를 불러오지 못했어요. 새로고침하거나 잠시 후 다시 시도해 주세요.
; + } + return ( <>
- + {/* { tags={clubDetail.tags} /> */} {formData.title} - {/* - 지원서 설명 받았다고 치고 {formData.description && ( {parseDescriptionWithLinks(formData.description)} - )} */} + )} {formData.questions.map((q: Question, i: number) => ( Date: Mon, 21 Jul 2025 14:21:04 +0900 Subject: [PATCH 12/12] =?UTF-8?q?fix:=20=EA=B3=A0=EC=9C=A0=20=ED=82=A4=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EB=B0=A9=EC=8B=9D=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/utils/parseDescriptionWithLinks.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/utils/parseDescriptionWithLinks.tsx b/frontend/src/utils/parseDescriptionWithLinks.tsx index 6e3ee0d3e..a2dbbd969 100644 --- a/frontend/src/utils/parseDescriptionWithLinks.tsx +++ b/frontend/src/utils/parseDescriptionWithLinks.tsx @@ -7,7 +7,7 @@ export const parseDescriptionWithLinks = (text: string): React.ReactNode => { const isUrl = /^https?:\/\/[^\s]+$/.test(part); return isUrl ? ( { {part} ) : ( - {part} + {part} ); }); };