mirror of
https://github.com/CompassConnections/Compass.git
synced 2026-05-08 23:25:01 -04:00
Major re-design of the profile pages
This commit is contained in:
@@ -11,8 +11,8 @@ android {
|
||||
applicationId "com.compassconnections.app"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 101
|
||||
versionName "1.22.0"
|
||||
versionCode 102
|
||||
versionName "1.23.0"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
aaptOptions {
|
||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||
|
||||
@@ -58,3 +58,9 @@ export function getLocationText(
|
||||
|
||||
return `${city}${stateOrCountry && ', '}${stateOrCountry}`
|
||||
}
|
||||
|
||||
export function getGoogleMapsUrl(locationText: string) {
|
||||
let text = locationText.split(', ').join('+')
|
||||
text = text.replace(' ', '+')
|
||||
return `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(text)}`
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export class SettingsPage {
|
||||
this.themeToggle = page.getByTestId('settings-dark-light-toggle')
|
||||
this.fontPicker = page.getByTestId('settings-font-picker')
|
||||
this.downloadProfileJSONDataButton = page.getByRole('button', {
|
||||
name: 'Download all my data (JSON)',
|
||||
name: 'Download all my data',
|
||||
})
|
||||
this.manageHiddenProfilesButton = page.getByRole('button', {name: 'Manage hidden profiles'})
|
||||
this.hiddenProfilesSection = page.getByTestId('hidden-profiles')
|
||||
|
||||
@@ -42,7 +42,7 @@ export function AnswerCompatibilityQuestionButton(props: {
|
||||
onClick={() => setOpen(true)}
|
||||
color="none"
|
||||
className={
|
||||
'px-3 py-2 rounded-md border border-primary-600 text-ink-700 hover:bg-primary-50 hover:text-ink-900'
|
||||
'px-3 py-2 rounded-md border border-primary-600 text-primary-800 hover:bg-primary-50'
|
||||
}
|
||||
>
|
||||
{t('answers.answer.cta', 'Answer{core} Questions', {
|
||||
@@ -78,7 +78,7 @@ export function CompatibilityPageButton() {
|
||||
return (
|
||||
<Link
|
||||
href="/compatibility"
|
||||
className="px-3 py-2 rounded-md border border-primary-600 text-ink-700 hover:bg-primary-50 flex items-center justify-center text-center"
|
||||
className="px-3 py-2 rounded-md border border-primary-600 text-primary-800 hover:bg-primary-50 flex items-center justify-center text-center text-sm"
|
||||
>
|
||||
{t('answers.answer.view_list', 'View List of Questions')}
|
||||
</Link>
|
||||
|
||||
@@ -26,7 +26,6 @@ import {
|
||||
import {Col} from 'web/components/layout/col'
|
||||
import {Modal, MODAL_CLASS, SCROLLABLE_MODAL_CLASS} from 'web/components/layout/modal'
|
||||
import {Row} from 'web/components/layout/row'
|
||||
import {CompatibleBadge} from 'web/components/widgets/compatible-badge'
|
||||
import {Input} from 'web/components/widgets/input'
|
||||
import {Linkify} from 'web/components/widgets/linkify'
|
||||
import {Pagination} from 'web/components/widgets/pagination'
|
||||
@@ -45,6 +44,7 @@ import {useUser} from 'web/hooks/use-user'
|
||||
import {useT} from 'web/lib/locale'
|
||||
import {db} from 'web/lib/supabase/db'
|
||||
|
||||
import {CompatibleBadge} from '../widgets/compatible-badge'
|
||||
import {Subtitle} from '../widgets/profile-subtitle'
|
||||
import {
|
||||
AnswerCompatibilityQuestionButton,
|
||||
@@ -57,13 +57,12 @@ import {
|
||||
deleteCompatibilityAnswer,
|
||||
getEmptyAnswer,
|
||||
IMPORTANCE_CHOICES,
|
||||
IMPORTANCE_DISPLAY_COLORS,
|
||||
submitCompatibilityAnswer,
|
||||
} from './answer-compatibility-question-content'
|
||||
import {PreferredList, PreferredListNoComparison} from './compatibility-question-preferred-list'
|
||||
import {PinQuestionButton} from './pin-question-button'
|
||||
|
||||
const NUM_QUESTIONS_TO_SHOW = 4
|
||||
const NUM_QUESTIONS_TO_SHOW = 8
|
||||
const NUM_PINNED_QUESTIONS_TO_SHOW = 4
|
||||
|
||||
export function separateQuestionsArray(
|
||||
@@ -213,18 +212,6 @@ export function CompatibilityQuestionsDisplay(props: {
|
||||
|
||||
return (
|
||||
<Col className="gap-4">
|
||||
<Row className={'gap-8'}>
|
||||
<Subtitle>
|
||||
{isCurrentUser
|
||||
? t('answers.display.your_prompts', 'Compatibility Prompts')
|
||||
: t('answers.display.user_prompts', 'Compatibility Prompts', {
|
||||
name: shortenName(user.name),
|
||||
})}
|
||||
</Subtitle>
|
||||
{compatibilityScore && (
|
||||
<CompatibleBadge compatibility={compatibilityScore} className={'mt-7 mr-4'} />
|
||||
)}
|
||||
</Row>
|
||||
{pinnedAnswers.length > 0 && (
|
||||
<Col className="gap-3">
|
||||
<PinIcon />
|
||||
@@ -254,7 +241,7 @@ export function CompatibilityQuestionsDisplay(props: {
|
||||
)}
|
||||
<Row className="flex-wrap items-center justify-between gap-x-6 gap-y-4">
|
||||
{answeredQuestions.length > 0 && (
|
||||
<div className="relative mt-3 w-full max-w-[50%] xl:max-w-[600px]">
|
||||
<div className="relative mt-3 w-full max-w-[50%] xl:max-w-[400px]">
|
||||
{/*<input*/}
|
||||
{/* type="text"*/}
|
||||
{/* placeholder={t('answers.search_placeholder', 'Search prompts...')}*/}
|
||||
@@ -283,6 +270,9 @@ export function CompatibilityQuestionsDisplay(props: {
|
||||
user={user}
|
||||
profile={profile}
|
||||
/>
|
||||
{compatibilityScore && (
|
||||
<CompatibleBadge compatibility={compatibilityScore} className={'mt-5 mr-4'} />
|
||||
)}
|
||||
</Row>
|
||||
{answeredQuestions.length <= 0 ? (
|
||||
<span className="text-ink-600 text-sm">
|
||||
@@ -391,6 +381,7 @@ export function CompatibilityAnswerBlock(props: {
|
||||
refreshCompatibilityAll: () => void
|
||||
fromProfilePage?: Profile
|
||||
showCommunityInfo?: boolean
|
||||
className?: string
|
||||
}) {
|
||||
const {
|
||||
answer,
|
||||
@@ -400,6 +391,7 @@ export function CompatibilityAnswerBlock(props: {
|
||||
isCurrentUser,
|
||||
refreshCompatibilityAll,
|
||||
fromProfilePage,
|
||||
className,
|
||||
} = props
|
||||
|
||||
const showCommunityInfo = props.showCommunityInfo === undefined ? true : props.showCommunityInfo
|
||||
@@ -452,12 +444,13 @@ export function CompatibilityAnswerBlock(props: {
|
||||
return (
|
||||
<Col
|
||||
data-testid="profile-compatibility-section"
|
||||
className={
|
||||
'bg-canvas-50 border border-canvas-200 flex-grow gap-2 whitespace-pre-line rounded-xl p-4 leading-relaxed'
|
||||
}
|
||||
className={clsx(
|
||||
'bg-canvas-200/20 border border-canvas-200 flex-grow gap-2 whitespace-pre-line rounded-xl p-4 leading-relaxed',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<Row
|
||||
className="justify-between gap-1 font-semibold"
|
||||
className="justify-between gap-1 font-medium"
|
||||
data-testid="profile-compatibility-question"
|
||||
>
|
||||
{question.question}
|
||||
@@ -535,18 +528,21 @@ export function CompatibilityAnswerBlock(props: {
|
||||
</Row>
|
||||
{answerText && (
|
||||
<Row
|
||||
className="bg-canvas-200 w-fit gap-1 rounded-xl px-2 py-1 text-sm"
|
||||
className="border border-primary-200 bg-primary-50 text-primary-700 w-fit gap-1 rounded-full px-3 py-1 text-sm"
|
||||
data-testid="profile-compatibility-question-answer"
|
||||
>
|
||||
{answerText}
|
||||
</Row>
|
||||
)}
|
||||
<Row className="px-2" data-testid="profile-compatibility-question-answer-explanation">
|
||||
<Row
|
||||
className="px-2 text-sm text-ink-500 leading-relaxed pl-2 border-l-2 border-canvas-300 ml-1"
|
||||
data-testid="profile-compatibility-question-answer-explanation"
|
||||
>
|
||||
{answer?.explanation && <Linkify className="" text={`"${answer.explanation}"`} />}
|
||||
</Row>
|
||||
{distinctPreferredAnswersText.length > 0 && (
|
||||
<Col className="gap-2">
|
||||
<div className="text-sm">
|
||||
<div className="text-xs font-semibold uppercase tracking-wider text-ink-300">
|
||||
{preferredDoesNotIncludeAnswerText
|
||||
? t('answers.display.acceptable', 'Acceptable')
|
||||
: t('answers.display.also_acceptable', 'Also acceptable')}
|
||||
@@ -556,7 +552,10 @@ export function CompatibilityAnswerBlock(props: {
|
||||
data-testid="profile-compatibility-question-acceptable-answer"
|
||||
>
|
||||
{distinctPreferredAnswersText.map((text) => (
|
||||
<Row key={text} className="bg-canvas-200 w-fit gap-1 rounded-xl px-2 py-1 text-sm">
|
||||
<Row
|
||||
key={text}
|
||||
className="border border-canvas-200 bg-canvas-100 text-ink-500 w-fit gap-1 rounded-full px-3 py-1 text-sm"
|
||||
>
|
||||
{text}
|
||||
</Row>
|
||||
))}
|
||||
@@ -716,10 +715,10 @@ function CompatibilityDisplay(props: {
|
||||
<button
|
||||
onClick={() => setOpen(true)}
|
||||
className={clsx(
|
||||
'text-ink-1000 h-fit w-28 rounded-full px-2 py-0.5 text-xs transition-colors',
|
||||
'border text-ink-1000 h-fit w-28 rounded-full px-2 py-0.5 text-xs transition-colors',
|
||||
answerCompatibility
|
||||
? 'bg-green-500/20 hover:bg-green-500/30'
|
||||
: 'bg-red-500/20 hover:bg-red-500/30',
|
||||
? 'bg-green-500/10 text-green-800 border-green-500/25 hover:bg-green-500/30'
|
||||
: 'bg-red-500/20 text-red-800 border-red-500/25 hover:bg-red-500/30',
|
||||
)}
|
||||
>
|
||||
{answerCompatibility
|
||||
@@ -803,16 +802,51 @@ function ImportanceDisplay(props: {importance: number}) {
|
||||
|
||||
function ImportanceButton(props: {importance: number; onClick: () => void; className?: string}) {
|
||||
const {importance, onClick, className} = props
|
||||
|
||||
// Color scheme based on importance level
|
||||
const importanceColors = {
|
||||
3: {
|
||||
// Very Important — full primary amber
|
||||
background: 'rgb(var(--color-primary-50))',
|
||||
color: 'rgb(var(--color-primary-700))',
|
||||
border: 'rgb(var(--color-primary-200))',
|
||||
},
|
||||
2: {
|
||||
// Important — softer amber, slightly stepped back
|
||||
background: 'rgb(var(--color-primary-50))',
|
||||
color: 'rgb(var(--color-primary-600))',
|
||||
border: 'rgb(var(--color-primary-100))',
|
||||
},
|
||||
1: {
|
||||
// Somewhat Important — warm neutral
|
||||
background: 'rgb(var(--color-canvas-100))',
|
||||
color: 'rgb(var(--color-ink-500))',
|
||||
border: 'rgb(var(--color-canvas-300))',
|
||||
},
|
||||
0: {
|
||||
// Not Important — near-invisible
|
||||
background: 'rgb(var(--color-canvas-50))',
|
||||
color: 'rgb(var(--color-ink-300))',
|
||||
border: 'rgb(var(--color-canvas-200))',
|
||||
},
|
||||
}
|
||||
|
||||
const colors =
|
||||
importanceColors[importance as keyof typeof importanceColors] || importanceColors[3]
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={onClick}
|
||||
className={clsx(
|
||||
'text-ink-1000 h-fit rounded-full px-2 py-0.5 text-xs transition-colors',
|
||||
// Longer width for "Somewhat important"
|
||||
'h-fit rounded-full px-2 py-0.5 text-xs font-medium transition-colors',
|
||||
importance === 1 ? 'w-36' : 'w-28',
|
||||
IMPORTANCE_DISPLAY_COLORS[importance],
|
||||
className,
|
||||
)}
|
||||
style={{
|
||||
background: colors.background,
|
||||
color: colors.color,
|
||||
border: `1px solid ${colors.border}`,
|
||||
}}
|
||||
>
|
||||
<ImportanceDisplay importance={importance} />
|
||||
</button>
|
||||
|
||||
@@ -17,7 +17,7 @@ export function BackButton(props: {className?: string}) {
|
||||
<button
|
||||
type="button"
|
||||
className={clsx(
|
||||
'text-ink-500 hover:text-ink-900 inline-flex items-center gap-2 text-sm',
|
||||
'text-ink-500 hover:text-primary-700 inline-flex items-center gap-2 text-sm transition-all',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -27,7 +27,7 @@ export function BioBlock(props: {
|
||||
<Col
|
||||
className={clsx(
|
||||
'flex-grow whitespace-pre-line rounded-md leading-relaxed',
|
||||
!edit && 'px-3 py-2',
|
||||
!edit && 'px-0 py-2',
|
||||
)}
|
||||
>
|
||||
<Row className="w-full">
|
||||
@@ -51,6 +51,7 @@ export function BioBlock(props: {
|
||||
text={t('more_options_user.edit_bio', 'Bio options')}
|
||||
noTap
|
||||
testId="profile-bio-options"
|
||||
className={'h-fit'}
|
||||
>
|
||||
<DropdownMenu
|
||||
items={[
|
||||
|
||||
@@ -8,7 +8,6 @@ import {useTextEditor} from 'web/components/widgets/editor'
|
||||
import {Tooltip} from 'web/components/widgets/tooltip'
|
||||
import {useT} from 'web/lib/locale'
|
||||
|
||||
import {Subtitle} from '../widgets/profile-subtitle'
|
||||
import {BioBlock} from './profile-bio-block'
|
||||
|
||||
export default function TooShortBio() {
|
||||
@@ -39,7 +38,6 @@ export function ProfileBio(props: {
|
||||
const [edit, setEdit] = useState(false)
|
||||
const editor = useTextEditor({defaultValue: ''})
|
||||
const [textLength, setTextLength] = useState(MAX_INT)
|
||||
const t = useT()
|
||||
|
||||
useEffect(() => {
|
||||
if (!editor) return
|
||||
@@ -53,7 +51,6 @@ export function ProfileBio(props: {
|
||||
return (
|
||||
<Col>
|
||||
{textLength < MIN_BIO_LENGTH && !edit && isCurrentUser && <TooShortBio />}
|
||||
<Subtitle className="mb-4">{t('profile.bio.about_me', 'About Me')}</Subtitle>
|
||||
<BioBlock
|
||||
isCurrentUser={isCurrentUser}
|
||||
profile={profile}
|
||||
|
||||
@@ -21,6 +21,7 @@ export type ColorType =
|
||||
| 'yellow-outline'
|
||||
| 'gold'
|
||||
| 'none'
|
||||
| 'primary'
|
||||
|
||||
const sizeClasses = {
|
||||
'2xs': 'px-2 py-1 text-xs',
|
||||
@@ -65,6 +66,8 @@ export function buttonClass(size: SizeType, color: ColorType) {
|
||||
gradient,
|
||||
'enabled:!bg-gradient-to-br from-yellow-400 via-yellow-100 to-yellow-300 dark:from-yellow-600 dark:via-yellow-200 dark:to-yellow-400 !text-gray-900',
|
||||
],
|
||||
color === 'primary' &&
|
||||
'w-fit border-canvas-300 text-primary-700 bg-canvas-200 hover:bg-canvas-300 mb-4 !rounded-full border px-4 py-2 text-sm transition-colors',
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ export function CopyLinkOrShareButton(props: {
|
||||
return (
|
||||
<ToolTipOrDiv
|
||||
hasChildren={!!children}
|
||||
text={tooltip ?? t('copy_link_button.copy_link', 'Copy link')}
|
||||
text={tooltip ?? t('copy_link_button.copy_link', 'Copy Link')}
|
||||
noTap
|
||||
placement="bottom"
|
||||
>
|
||||
@@ -49,7 +49,7 @@ export function CopyLinkOrShareButton(props: {
|
||||
onClick={onClick}
|
||||
className={clsx(
|
||||
className,
|
||||
'text-primary-700 hover:text-primary-800 gap-2',
|
||||
'text-ink-500 hover:text-primary-800 gap-2',
|
||||
isSuccess && 'duration-[25ms]',
|
||||
)}
|
||||
disabled={!url}
|
||||
@@ -59,13 +59,13 @@ export function CopyLinkOrShareButton(props: {
|
||||
{isSuccess ? (
|
||||
<CheckIcon
|
||||
strokeWidth={'3'}
|
||||
className={clsx(iconClassName ?? 'h-[1.1rem]')}
|
||||
className={clsx(iconClassName, 'h-[1.1rem]')}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
) : (
|
||||
<LinkIcon
|
||||
strokeWidth={'2.5'}
|
||||
className={clsx(iconClassName ?? 'h-[1.1rem]')}
|
||||
className={clsx(iconClassName, 'h-[1.1rem]')}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
@@ -157,7 +157,7 @@ export function SimpleCopyTextButton(props: {
|
||||
return (
|
||||
<IconButton onClick={onClick} className={className} disabled={!text}>
|
||||
<Tooltip
|
||||
text={tooltip ?? t('copy_link_button.copy_link', 'Copy link')}
|
||||
text={tooltip ?? t('copy_link_button.copy_link', 'Copy Link')}
|
||||
noTap
|
||||
placement="bottom"
|
||||
>
|
||||
|
||||
@@ -44,7 +44,7 @@ export function MoreOptionsUserButton(props: {user: User}) {
|
||||
<Tooltip text={t('more_options_user.more_options', 'More Options')} noTap>
|
||||
<Button
|
||||
color={'gray-white'}
|
||||
className="rounded-none px-6"
|
||||
className="border-canvas-300 flex items-center gap-1.5 rounded-lg border px-[8px] py-2 text-sm text-primary-700 transition-colors hover:border-primary-400 hover:bg-primary-50"
|
||||
onClick={() => setIsModalOpen(true)}
|
||||
>
|
||||
<EllipsisHorizontalIcon className={clsx('h-5 w-5 flex-shrink-0')} aria-hidden="true" />
|
||||
|
||||
@@ -34,7 +34,7 @@ export function EmailVerificationButton() {
|
||||
color={'gray-outline'}
|
||||
onClick={() => sendVerificationEmail(firebaseUser, t)}
|
||||
disabled={isEmailVerified}
|
||||
className={'w-fit'}
|
||||
className={'w-fit !text-ink-500'}
|
||||
>
|
||||
{isEmailVerified
|
||||
? t('settings.email.verified', 'Email Verified ✔️')
|
||||
|
||||
@@ -267,9 +267,9 @@ function Filters(props: {
|
||||
selection={
|
||||
<RelationshipFilterText
|
||||
relationship={filters.pref_relation_styles as RelationshipType[]}
|
||||
highlightedClass={
|
||||
hasAny(filters.pref_relation_styles) ? 'text-primary-600' : 'text-ink-900'
|
||||
}
|
||||
// highlightedClass={
|
||||
// hasAny(filters.pref_relation_styles) ? 'text-primary-600' : 'text-ink-900'
|
||||
// }
|
||||
/>
|
||||
}
|
||||
>
|
||||
@@ -287,7 +287,7 @@ function Filters(props: {
|
||||
location={locationFilterProps.location}
|
||||
radius={locationFilterProps.radius}
|
||||
youProfile={youProfile}
|
||||
highlightedClass={!locationFilterProps.location ? 'text-ink-900' : 'text-primary-600'}
|
||||
// highlightedClass={!locationFilterProps.location ? 'text-ink-900' : 'text-primary-600'}
|
||||
/>
|
||||
}
|
||||
>
|
||||
@@ -305,7 +305,7 @@ function Filters(props: {
|
||||
<AgeFilterText
|
||||
pref_age_min={filters.pref_age_min}
|
||||
pref_age_max={filters.pref_age_max}
|
||||
highlightedClass={noMinAge && noMaxAge ? 'text-ink-900' : 'text-primary-600'}
|
||||
// highlightedClass={noMinAge && noMaxAge ? 'text-ink-900' : 'text-primary-600'}
|
||||
/>
|
||||
}
|
||||
>
|
||||
@@ -321,7 +321,7 @@ function Filters(props: {
|
||||
selection={
|
||||
<GenderFilterText
|
||||
gender={filters.genders as Gender[]}
|
||||
highlightedClass={hasAny(filters.genders) ? 'text-primary-600' : 'text-ink-900'}
|
||||
// highlightedClass={hasAny(filters.genders) ? 'text-primary-600' : 'text-ink-900'}
|
||||
/>
|
||||
}
|
||||
>
|
||||
@@ -348,11 +348,11 @@ function Filters(props: {
|
||||
<RelationshipStatusFilterText
|
||||
options={filters.relationship_status as string[]}
|
||||
defaultLabel={t('filter.relationship_status.any', 'Any')}
|
||||
highlightedClass={
|
||||
hasAny(filters.relationship_status || undefined)
|
||||
? 'text-primary-600'
|
||||
: 'text-ink-900'
|
||||
}
|
||||
// highlightedClass={
|
||||
// hasAny(filters.relationship_status || undefined)
|
||||
// ? 'text-primary-600'
|
||||
// : 'text-ink-900'
|
||||
// }
|
||||
/>
|
||||
}
|
||||
>
|
||||
@@ -368,11 +368,11 @@ function Filters(props: {
|
||||
selection={
|
||||
<RomanticFilterText
|
||||
relationship={filters.pref_romantic_styles as RomanticType[]}
|
||||
highlightedClass={
|
||||
hasAny(filters.pref_romantic_styles || undefined)
|
||||
? 'text-primary-600'
|
||||
: 'text-ink-900'
|
||||
}
|
||||
// highlightedClass={
|
||||
// hasAny(filters.pref_romantic_styles || undefined)
|
||||
// ? 'text-primary-600'
|
||||
// : 'text-ink-900'
|
||||
// }
|
||||
/>
|
||||
}
|
||||
>
|
||||
@@ -388,11 +388,11 @@ function Filters(props: {
|
||||
selection={
|
||||
<HasKidsLabel
|
||||
has_kids={filters.has_kids ?? -1}
|
||||
highlightedClass={
|
||||
filters.has_kids != null && filters.has_kids !== -1
|
||||
? 'text-primary-600'
|
||||
: 'text-ink-900'
|
||||
}
|
||||
// highlightedClass={
|
||||
// filters.has_kids != null && filters.has_kids !== -1
|
||||
// ? 'text-primary-600'
|
||||
// : 'text-ink-900'
|
||||
// }
|
||||
/>
|
||||
}
|
||||
>
|
||||
@@ -408,11 +408,11 @@ function Filters(props: {
|
||||
selection={
|
||||
<KidsLabel
|
||||
strength={filters.wants_kids_strength ?? -1}
|
||||
highlightedClass={
|
||||
filters.wants_kids_strength != null && filters.wants_kids_strength !== -1
|
||||
? 'text-primary-600'
|
||||
: 'text-ink-900'
|
||||
}
|
||||
// highlightedClass={
|
||||
// filters.wants_kids_strength != null && filters.wants_kids_strength !== -1
|
||||
// ? 'text-primary-600'
|
||||
// : 'text-ink-900'
|
||||
// }
|
||||
/>
|
||||
}
|
||||
>
|
||||
@@ -439,9 +439,9 @@ function Filters(props: {
|
||||
radius={raisedInLocationFilterProps.radius}
|
||||
labelPrefix={t('filter.raised_in', 'Grew up')}
|
||||
youProfile={youProfile}
|
||||
highlightedClass={
|
||||
!raisedInLocationFilterProps.location ? 'text-ink-900' : 'text-primary-600'
|
||||
}
|
||||
// highlightedClass={
|
||||
// !raisedInLocationFilterProps.location ? 'text-ink-900' : 'text-primary-600'
|
||||
// }
|
||||
/>
|
||||
}
|
||||
>
|
||||
@@ -459,9 +459,9 @@ function Filters(props: {
|
||||
selection={
|
||||
<EducationFilterText
|
||||
options={filters.education_levels as string[]}
|
||||
highlightedClass={
|
||||
hasAny(filters.education_levels) ? 'text-primary-600' : 'text-ink-900'
|
||||
}
|
||||
// highlightedClass={
|
||||
// hasAny(filters.education_levels) ? 'text-primary-600' : 'text-ink-900'
|
||||
// }
|
||||
/>
|
||||
}
|
||||
>
|
||||
@@ -477,9 +477,9 @@ function Filters(props: {
|
||||
<InterestFilterText
|
||||
options={filters.work as string[] | undefined}
|
||||
label={'work'}
|
||||
highlightedClass={
|
||||
hasAny(filters.work || undefined) ? 'text-primary-600' : 'text-ink-900'
|
||||
}
|
||||
// highlightedClass={
|
||||
// hasAny(filters.work || undefined) ? 'text-primary-600' : 'text-ink-900'
|
||||
// }
|
||||
/>
|
||||
}
|
||||
>
|
||||
@@ -508,9 +508,9 @@ function Filters(props: {
|
||||
<InterestFilterText
|
||||
options={filters.interests as string[] | undefined}
|
||||
label={'interests'}
|
||||
highlightedClass={
|
||||
hasAny(filters.interests || undefined) ? 'text-primary-600' : 'text-ink-900'
|
||||
}
|
||||
// highlightedClass={
|
||||
// hasAny(filters.interests || undefined) ? 'text-primary-600' : 'text-ink-900'
|
||||
// }
|
||||
/>
|
||||
}
|
||||
>
|
||||
@@ -531,9 +531,9 @@ function Filters(props: {
|
||||
<InterestFilterText
|
||||
options={filters.causes as string[] | undefined}
|
||||
label={'causes'}
|
||||
highlightedClass={
|
||||
hasAny(filters.causes || undefined) ? 'text-primary-600' : 'text-ink-900'
|
||||
}
|
||||
// highlightedClass={
|
||||
// hasAny(filters.causes || undefined) ? 'text-primary-600' : 'text-ink-900'
|
||||
// }
|
||||
/>
|
||||
}
|
||||
>
|
||||
@@ -553,9 +553,9 @@ function Filters(props: {
|
||||
selection={
|
||||
<DietFilterText
|
||||
options={filters.diet as DietType[] | undefined}
|
||||
highlightedClass={
|
||||
hasAny(filters.diet || undefined) ? 'text-primary-600' : 'text-ink-900'
|
||||
}
|
||||
// highlightedClass={
|
||||
// hasAny(filters.diet || undefined) ? 'text-primary-600' : 'text-ink-900'
|
||||
// }
|
||||
/>
|
||||
}
|
||||
>
|
||||
@@ -577,13 +577,13 @@ function Filters(props: {
|
||||
<DrinksFilterText
|
||||
drinks_min={filters.drinks_min}
|
||||
drinks_max={filters.drinks_max}
|
||||
highlightedClass={(() => {
|
||||
const [noMinDrinks, noMaxDrinks] = getNoMinMaxDrinks(
|
||||
filters.drinks_min,
|
||||
filters.drinks_max,
|
||||
)
|
||||
return noMinDrinks && noMaxDrinks ? 'text-ink-900' : 'text-primary-600'
|
||||
})()}
|
||||
// highlightedClass={(() => {
|
||||
// const [noMinDrinks, noMaxDrinks] = getNoMinMaxDrinks(
|
||||
// filters.drinks_min,
|
||||
// filters.drinks_max,
|
||||
// )
|
||||
// return noMinDrinks && noMaxDrinks ? 'text-ink-900' : 'text-primary-600'
|
||||
// })()}
|
||||
/>
|
||||
}
|
||||
>
|
||||
@@ -598,7 +598,7 @@ function Filters(props: {
|
||||
selection={
|
||||
<SmokerFilterText
|
||||
is_smoker={filters.is_smoker}
|
||||
highlightedClass={filters.is_smoker == null ? 'text-ink-900' : 'text-primary-600'}
|
||||
// highlightedClass={filters.is_smoker == null ? 'text-ink-900' : 'text-primary-600'}
|
||||
/>
|
||||
}
|
||||
>
|
||||
@@ -613,9 +613,9 @@ function Filters(props: {
|
||||
selection={
|
||||
<PsychedelicsFilterText
|
||||
options={filters.psychedelics as string[] | undefined}
|
||||
highlightedClass={
|
||||
hasAny(filters.psychedelics || undefined) ? 'text-primary-600' : 'text-ink-900'
|
||||
}
|
||||
// highlightedClass={
|
||||
// hasAny(filters.psychedelics || undefined) ? 'text-primary-600' : 'text-ink-900'
|
||||
// }
|
||||
/>
|
||||
}
|
||||
>
|
||||
@@ -630,9 +630,9 @@ function Filters(props: {
|
||||
selection={
|
||||
<CannabisFilterText
|
||||
options={filters.cannabis as string[] | undefined}
|
||||
highlightedClass={
|
||||
hasAny(filters.cannabis || undefined) ? 'text-primary-600' : 'text-ink-900'
|
||||
}
|
||||
// highlightedClass={
|
||||
// hasAny(filters.cannabis || undefined) ? 'text-primary-600' : 'text-ink-900'
|
||||
// }
|
||||
/>
|
||||
}
|
||||
>
|
||||
@@ -647,9 +647,9 @@ function Filters(props: {
|
||||
selection={
|
||||
<LanguageFilterText
|
||||
options={filters.languages as string[] | undefined}
|
||||
highlightedClass={
|
||||
hasAny(filters.languages || undefined) ? 'text-primary-600' : 'text-ink-900'
|
||||
}
|
||||
// highlightedClass={
|
||||
// hasAny(filters.languages || undefined) ? 'text-primary-600' : 'text-ink-900'
|
||||
// }
|
||||
/>
|
||||
}
|
||||
>
|
||||
@@ -672,9 +672,9 @@ function Filters(props: {
|
||||
selection={
|
||||
<PoliticalFilterText
|
||||
options={filters.political_beliefs as string[] | undefined}
|
||||
highlightedClass={
|
||||
hasAny(filters.political_beliefs || undefined) ? 'text-primary-600' : 'text-ink-900'
|
||||
}
|
||||
// highlightedClass={
|
||||
// hasAny(filters.political_beliefs || undefined) ? 'text-primary-600' : 'text-ink-900'
|
||||
// }
|
||||
/>
|
||||
}
|
||||
>
|
||||
@@ -689,9 +689,9 @@ function Filters(props: {
|
||||
selection={
|
||||
<ReligionFilterText
|
||||
options={filters.religion as string[] | undefined}
|
||||
highlightedClass={
|
||||
hasAny(filters.religion || undefined) ? 'text-primary-600' : 'text-ink-900'
|
||||
}
|
||||
// highlightedClass={
|
||||
// hasAny(filters.religion || undefined) ? 'text-primary-600' : 'text-ink-900'
|
||||
// }
|
||||
/>
|
||||
}
|
||||
>
|
||||
@@ -715,7 +715,7 @@ function Filters(props: {
|
||||
<MbtiFilterText
|
||||
options={filters.mbti as string[] | undefined}
|
||||
defaultLabel={t('filter.any_mbti', 'Any MBTI')}
|
||||
highlightedClass={hasAny(filters.mbti) ? 'text-primary-600' : 'text-ink-900'}
|
||||
// highlightedClass={hasAny(filters.mbti) ? 'text-primary-600' : 'text-ink-900'}
|
||||
/>
|
||||
}
|
||||
>
|
||||
@@ -730,7 +730,7 @@ function Filters(props: {
|
||||
selection={
|
||||
<Big5FilterText
|
||||
filters={filters}
|
||||
highlightedClass={hasAnyBig5Filter(filters) ? 'text-primary-600' : 'text-ink-900'}
|
||||
// highlightedClass={hasAnyBig5Filter(filters) ? 'text-primary-600' : 'text-ink-900'}
|
||||
/>
|
||||
}
|
||||
>
|
||||
@@ -753,7 +753,7 @@ function Filters(props: {
|
||||
selection={
|
||||
<LastActiveFilterText
|
||||
last_active={filters.last_active}
|
||||
highlightedClass={!filters.last_active ? 'text-ink-900' : 'text-primary-600'}
|
||||
// highlightedClass={!filters.last_active ? 'text-ink-900' : 'text-primary-600'}
|
||||
/>
|
||||
}
|
||||
>
|
||||
@@ -767,7 +767,7 @@ function Filters(props: {
|
||||
setOpenFilter={setOpenFilter}
|
||||
isActive={!!filters.hasPhoto}
|
||||
selection={
|
||||
<span className={clsx(!filters.hasPhoto ? 'text-ink-900' : 'text-primary-600')}>
|
||||
<span>
|
||||
{filters.hasPhoto
|
||||
? t('filter.has_photo', 'Has photos')
|
||||
: t('filter.has_photo.photos', 'Photos')}
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import {ReactNode} from 'react'
|
||||
import {Row} from 'web/components/layout/row'
|
||||
|
||||
export function IconWithInfo(props: {text: string; icon: ReactNode}) {
|
||||
const {text, icon} = props
|
||||
export function IconWithInfo(props: {text?: string; icon: ReactNode; children?: ReactNode}) {
|
||||
const {text, icon, children} = props
|
||||
return (
|
||||
<Row className="items-start gap-1">
|
||||
<div className="text-ink-500 mt-1">{icon}</div>
|
||||
{text}
|
||||
<Row className="items-center gap-1" style={{gap: '5px'}}>
|
||||
<div className="mt-0.5" style={{width: '14px', height: '14px'}}>
|
||||
{icon}
|
||||
</div>
|
||||
<span style={{fontSize: '13.5px'}}>
|
||||
{text}
|
||||
{children}
|
||||
</span>
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,16 +1,28 @@
|
||||
import Link from 'next/link'
|
||||
|
||||
export const CustomLink = ({href, children}: {href?: string; children: React.ReactNode}) => {
|
||||
export const CustomLink = ({
|
||||
href,
|
||||
children,
|
||||
className,
|
||||
}: {
|
||||
href?: string
|
||||
children: React.ReactNode
|
||||
className?: string
|
||||
}) => {
|
||||
if (!href) return <>{children}</>
|
||||
|
||||
// If href is internal, use Next.js Link
|
||||
if (href.startsWith('/')) {
|
||||
return <Link href={href}>{children}</Link>
|
||||
return (
|
||||
<Link href={href} className={className}>
|
||||
{children}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
// For external links, fall back to <a>
|
||||
return (
|
||||
<a href={href} target="_blank" rel="noopener noreferrer">
|
||||
<a href={href} target="_blank" rel="noopener noreferrer" className={className}>
|
||||
{children}
|
||||
</a>
|
||||
)
|
||||
|
||||
@@ -142,7 +142,7 @@ export const SendMessageButton = (props: {
|
||||
{text ? (
|
||||
<Button
|
||||
className={clsx('h-fit gap-1', disabled && 'opacity-50 cursor-not-allowed')}
|
||||
color={'gray-outline'}
|
||||
color={'primary'}
|
||||
onClick={messageButtonClicked}
|
||||
disabled={disabled}
|
||||
>
|
||||
@@ -162,19 +162,17 @@ export const SendMessageButton = (props: {
|
||||
/>
|
||||
</button>
|
||||
) : (
|
||||
<Button
|
||||
size={'sm'}
|
||||
<button
|
||||
onClick={messageButtonClicked}
|
||||
color={'none'}
|
||||
disabled={disabled}
|
||||
className={clsx(
|
||||
'bg-canvas-200 hover:bg-canvas-300',
|
||||
'border-canvas-300 flex items-center gap-1.5 rounded-lg border px-2 py-2 text-sm text-primary-700 transition-colors hover:border-primary-400 hover:bg-primary-50',
|
||||
disabled && 'opacity-50 cursor-not-allowed',
|
||||
)}
|
||||
disabled={disabled}
|
||||
>
|
||||
<BiEnvelope className={clsx('h-5 w-5', includeLabel && 'mr-2')} />{' '}
|
||||
{includeLabel && <>{t('send_message.button_label', 'Message')}</>}
|
||||
</Button>
|
||||
</button>
|
||||
)}
|
||||
</Tooltip>
|
||||
|
||||
|
||||
@@ -311,7 +311,7 @@ export const OptionalProfileUserForm = (props: {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Col className={'gap-8'}>
|
||||
<Col className={'gap-8 max-w-3xl'}>
|
||||
<p className={'guidance'}>
|
||||
{t(
|
||||
'profile.optional.subtitle',
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import {Profile} from 'common/profiles/profile'
|
||||
import {User} from 'common/user'
|
||||
import {useState} from 'react'
|
||||
import {Button} from 'web/components/buttons/button'
|
||||
import {Col} from 'web/components/layout/col'
|
||||
import {Modal} from 'web/components/layout/modal'
|
||||
import {ShareProfileButtons} from 'web/components/widgets/share-profile-button'
|
||||
@@ -75,9 +74,25 @@ export const ViewProfileCardButton = (props: {
|
||||
const username = user.username
|
||||
return (
|
||||
<>
|
||||
<Button onClick={() => setOpen(true)} className={'bg-canvas-50 '}>
|
||||
{t('share_profile.view_profile_card', 'View Profile Card')}
|
||||
</Button>
|
||||
<button
|
||||
onClick={() => setOpen(true)}
|
||||
className="border-canvas-300 flex items-center gap-1.5 rounded-lg border px-3 py-2 text-sm text-ink-500 transition-colors hover:border-primary-400 hover:bg-primary-50"
|
||||
style={{
|
||||
fontSize: '13.5px',
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
className="h-3.5 w-3.5 hidden sm:block"
|
||||
>
|
||||
<rect x="2.5" y="5" width="15" height="11" rx="2" />
|
||||
<path d="M2.5 9h15" />
|
||||
</svg>
|
||||
{t('share_profile.view_profile_card', 'Profile Card')}
|
||||
</button>
|
||||
<Modal open={open} setOpen={setOpen} size={'lg'} className={''}>
|
||||
<Col className="gap-4 bg-canvas-100/75 rounded-2xl justify-center">
|
||||
<ProfileCardViewer user={user} profile={profile} width={width} height={height} />
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -65,7 +65,7 @@ export default function ProfileCarousel(props: {profile: Profile; refreshProfile
|
||||
src={url}
|
||||
height={300}
|
||||
width={300}
|
||||
className="h-full w-full cursor-pointer rounded object-cover"
|
||||
className="h-full w-full cursor-pointer rounded-xl object-cover"
|
||||
autoPlay
|
||||
muted
|
||||
loop
|
||||
@@ -83,7 +83,7 @@ export default function ProfileCarousel(props: {profile: Profile; refreshProfile
|
||||
width={300}
|
||||
sizes="(max-width: 640px) 100vw, 300px"
|
||||
alt=""
|
||||
className="h-full cursor-pointer rounded object-cover"
|
||||
className="h-full cursor-pointer rounded-xl object-cover"
|
||||
onClick={() => {
|
||||
setLightboxUrl(url)
|
||||
setLightboxOpen(true)
|
||||
@@ -91,9 +91,11 @@ export default function ProfileCarousel(props: {profile: Profile; refreshProfile
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<p className="mt-2 px-4 py-1 text-sm w-[300px] whitespace-pre-wrap">
|
||||
{(profile.image_descriptions as Record<string, string>)?.[url]}
|
||||
</p>
|
||||
{(profile.image_descriptions as Record<string, string>)?.[url] && (
|
||||
<p className="mt-2 px-4 py-1 text-sm w-[300px] whitespace-pre-wrap">
|
||||
{(profile.image_descriptions as Record<string, string>)?.[url]}
|
||||
</p>
|
||||
)}
|
||||
</Col>
|
||||
))}
|
||||
</Carousel>
|
||||
|
||||
@@ -12,8 +12,6 @@ import {useLiveCommentsOnProfile} from 'web/hooks/use-comments-on-profile'
|
||||
import {updateProfile} from 'web/lib/api'
|
||||
import {useT} from 'web/lib/locale'
|
||||
|
||||
import {Subtitle} from './widgets/profile-subtitle'
|
||||
|
||||
export const ProfileCommentSection = (props: {
|
||||
onUser: User
|
||||
profile: Profile
|
||||
@@ -26,14 +24,14 @@ export const ProfileCommentSection = (props: {
|
||||
const parentComments = comments.filter((c) => !c.replyToCommentId)
|
||||
const commentsByParent = groupBy(comments, (c) => c.replyToCommentId ?? '_')
|
||||
const [profile, setProfile] = useState<Profile>(props.profile)
|
||||
const [showCommentInput, setShowCommentInput] = useState(false)
|
||||
const isCurrentUser = currentUser?.id === onUser.id
|
||||
|
||||
if (!currentUser && (!profile.comments_enabled || parentComments.length == 0)) return null
|
||||
|
||||
return (
|
||||
<Col className={'rounded'}>
|
||||
<Row className={'mb-4 justify-between'}>
|
||||
<Subtitle>{t('profile.comments.section_title', 'Endorsements')}</Subtitle>
|
||||
<Row className={'justify-between'}>
|
||||
{isCurrentUser && !simpleView && (
|
||||
<Tooltip
|
||||
text={(profile.comments_enabled ? 'Disable' : 'Enable') + ' endorsements from others'}
|
||||
@@ -61,7 +59,7 @@ export const ProfileCommentSection = (props: {
|
||||
<>
|
||||
{currentUser && profile.comments_enabled && (
|
||||
<>
|
||||
<div className="mb-4">
|
||||
<div className="mb-4 text-ink-600">
|
||||
{isCurrentUser ? (
|
||||
<>
|
||||
{t(
|
||||
@@ -79,11 +77,30 @@ export const ProfileCommentSection = (props: {
|
||||
)}
|
||||
</div>
|
||||
{!isCurrentUser && (
|
||||
<ProfileCommentInput
|
||||
className="mb-4 mr-px mt-px"
|
||||
onUserId={onUser.id}
|
||||
trackingLocation={'contract page'}
|
||||
/>
|
||||
<>
|
||||
{!showCommentInput ? (
|
||||
<button
|
||||
onClick={() => setShowCommentInput(true)}
|
||||
className="w-fit border-canvas-300 text-primary-700 bg-canvas-200 hover:bg-canvas-300 mb-4 rounded-full border px-4 py-2 text-sm transition-colors"
|
||||
style={{
|
||||
padding: '6px 16px',
|
||||
borderRadius: '100px',
|
||||
fontSize: '13px',
|
||||
fontWeight: '400',
|
||||
letterSpacing: '0.01em',
|
||||
borderWidth: '1px',
|
||||
}}
|
||||
>
|
||||
{t('profile.comments.write_button', 'Write public endorsement')}
|
||||
</button>
|
||||
) : (
|
||||
<ProfileCommentInput
|
||||
className="mb-4 mr-px mt-px"
|
||||
onUserId={onUser.id}
|
||||
trackingLocation={'contract page'}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -334,6 +334,7 @@ function ProfilePreview(props: {
|
||||
'hover:before:opacity-100',
|
||||
'hover:bg-canvas-100',
|
||||
'relative z-10 cursor-pointer group block rounded-lg overflow-hidden bg-transparent h-full border border-canvas-300',
|
||||
'text-ink-600',
|
||||
// hover,
|
||||
)}
|
||||
>
|
||||
@@ -405,7 +406,12 @@ function ProfilePreview(props: {
|
||||
?.slice(0, 10)
|
||||
?.map(capitalizePure)
|
||||
?.map((tag, i) => (
|
||||
<span key={i} className={'bg-canvas-200 text-sm px-3 py-2 rounded-full'}>
|
||||
<span
|
||||
key={i}
|
||||
className={
|
||||
'bg-canvas-200 text-primary-700 text-sm px-3 p-1 rounded-full border border-canvas-300'
|
||||
}
|
||||
>
|
||||
{tag.trim()}
|
||||
</span>
|
||||
))}
|
||||
|
||||
@@ -6,7 +6,6 @@ import ReactMarkdown from 'react-markdown'
|
||||
import {Col} from 'web/components/layout/col'
|
||||
import {Row} from 'web/components/layout/row'
|
||||
import {SendMessageButton} from 'web/components/messaging/send-message-button'
|
||||
import {Subtitle} from 'web/components/widgets/profile-subtitle'
|
||||
import {Tooltip} from 'web/components/widgets/tooltip'
|
||||
import {useProfile} from 'web/hooks/use-profile'
|
||||
import {useUser} from 'web/hooks/use-user'
|
||||
@@ -83,10 +82,8 @@ export function ConnectActions(props: {profile: Profile; user: User}) {
|
||||
if (isCurrentUser || !currentUser) return null
|
||||
|
||||
return (
|
||||
<Col className="w-full gap-6 rounded-xl shadow-sm">
|
||||
<div className="border-y border-canvas-200 p-2 pb-8">
|
||||
<Subtitle className="mb-4">{t('profile.connect.title', 'Connect')}</Subtitle>
|
||||
|
||||
<Col className="w-full gap-6 rounded-xl">
|
||||
<div className="py-2">
|
||||
{/* Primary Action */}
|
||||
<div className="mb-6">
|
||||
{profile.allow_direct_messaging || matches.length > 0 ? (
|
||||
@@ -109,9 +106,9 @@ export function ConnectActions(props: {profile: Profile; user: User}) {
|
||||
|
||||
{/* Interest Section */}
|
||||
<div className="prose prose-neutral dark:prose-invert">
|
||||
<div className="text-ink-700 font-medium text-lg">
|
||||
<h2 className="font-medium text-xl">
|
||||
{t('profile.connect.private_connection_signal', 'Private connection signal')}
|
||||
</div>
|
||||
</h2>
|
||||
|
||||
{profile.allow_interest_indicating ? (
|
||||
<>
|
||||
|
||||
@@ -8,11 +8,11 @@ import clsx from 'clsx'
|
||||
import {debug} from 'common/logger'
|
||||
import {Profile} from 'common/profiles/profile'
|
||||
import {User, UserActivity} from 'common/user'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import Router from 'next/router'
|
||||
import React from 'react'
|
||||
import toast from 'react-hot-toast'
|
||||
import {Button} from 'web/components/buttons/button'
|
||||
import {MoreOptionsUserButton} from 'web/components/buttons/more-options-user-button'
|
||||
import DropdownMenu from 'web/components/comments/dropdown-menu'
|
||||
import {Col} from 'web/components/layout/col'
|
||||
@@ -52,52 +52,82 @@ export default function ProfileHeader(props: {
|
||||
})
|
||||
|
||||
return (
|
||||
<Row className="w-full">
|
||||
<Col className="w-full">
|
||||
{currentUser && !isCurrentUser && isHiddenFromMe && (
|
||||
<div className="guidance">
|
||||
{t(
|
||||
'profile_grid.hidden_notice',
|
||||
"You hid this person, so they don't appear in your search results.",
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{currentUser && isCurrentUser && disabled && (
|
||||
<div className="text-red-500">
|
||||
{t(
|
||||
'profile.header.disabled_notice',
|
||||
'You disabled your profile, so no one else can access it.',
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<Row className={clsx('flex-wrap justify-between gap-2 py-1')}>
|
||||
<Row className="items-center gap-1">
|
||||
<Col className="gap-1">
|
||||
<Row className="items-center gap-1 text-xl" data-testid="profile-display-name-age">
|
||||
{/*{!isCurrentUser && <OnlineIcon last_online_time={userActivity?.last_online_time}/>}*/}
|
||||
<span>
|
||||
{simpleView ? (
|
||||
<Link className={linkClass} href={`/${user.username}`}>
|
||||
<span className="font-semibold">{user.name}</span>
|
||||
</Link>
|
||||
) : (
|
||||
<span className="font-semibold">{user.name}</span>
|
||||
)}
|
||||
</span>
|
||||
<Row className={'flex-wrap gap-4'}>
|
||||
{currentUser && !isCurrentUser && isHiddenFromMe && (
|
||||
<div className="guidance">
|
||||
{t(
|
||||
'profile_grid.hidden_notice',
|
||||
"You hid this person, so they don't appear in your search results.",
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{currentUser && isCurrentUser && disabled && (
|
||||
<div className="text-red-500">
|
||||
{t(
|
||||
'profile.header.disabled_notice',
|
||||
'You disabled your profile, so no one else can access it.',
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<Col>
|
||||
<Row className="w-full gap-6 flex-wrap">
|
||||
{profile.pinned_url && (
|
||||
<div className="h-[108px] w-[108px] flex-none">
|
||||
<Image
|
||||
priority={true}
|
||||
src={profile.pinned_url}
|
||||
height={300}
|
||||
width={300}
|
||||
sizes="(max-width: 640px) 100vw, 300px"
|
||||
alt=""
|
||||
className="h-full w-full rounded-2xl object-cover"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<Col className="max-w-[160px] sm:max-w-[300px]">
|
||||
<Row className={clsx('flex-wrap justify-between gap-2 py-1')}>
|
||||
<Row className="items-center gap-1">
|
||||
<Col className="gap-1">
|
||||
<Row className="items-center gap-1" data-testid="profile-display-name-age">
|
||||
{/*{!isCurrentUser && <OnlineIcon last_online_time={userActivity?.last_online_time}/>}*/}
|
||||
<span>
|
||||
{simpleView ? (
|
||||
<Link className={linkClass} href={`/${user.username}`}>
|
||||
<span
|
||||
className="font-cormorant text-4xl font-medium"
|
||||
style={{lineHeight: '1.1', letterSpacing: '-0.01em'}}
|
||||
>
|
||||
{user.name}
|
||||
</span>
|
||||
</Link>
|
||||
) : (
|
||||
<span
|
||||
className="font-cormorant text-4xl font-medium"
|
||||
style={{lineHeight: '1.1', letterSpacing: '-0.01em'}}
|
||||
>
|
||||
{user.name}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
</Row>
|
||||
<ProfilePrimaryInfo profile={profile} />
|
||||
</Col>
|
||||
</Row>
|
||||
<ProfilePrimaryInfo profile={profile} />
|
||||
</Col>
|
||||
</Row>
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row className={'px-4 gap-2 flex-wrap py-2'} data-testid="profile-keywords">
|
||||
<Row className={'gap-2 flex-wrap py-2'} data-testid="profile-keywords">
|
||||
{profile.keywords?.map(capitalizePure)?.map((tag, i) => (
|
||||
<span
|
||||
key={i}
|
||||
className={'bg-canvas-200'}
|
||||
className={'border-canvas-300 text-primary-700 bg-canvas-200'}
|
||||
style={{
|
||||
padding: '6px 16px',
|
||||
borderRadius: '20px',
|
||||
padding: '5px 13px',
|
||||
borderRadius: '100px',
|
||||
fontSize: '13px',
|
||||
fontWeight: '400',
|
||||
letterSpacing: '0.01em',
|
||||
borderWidth: '1px',
|
||||
}}
|
||||
>
|
||||
{tag.trim()}
|
||||
@@ -106,8 +136,31 @@ export default function ProfileHeader(props: {
|
||||
</Row>
|
||||
</Col>
|
||||
{profile.headline && (
|
||||
<div className="italic max-w-3xl px-4 py-3" data-testid="profile-headline">
|
||||
"{profile.headline}"
|
||||
<div
|
||||
className="relative max-w-xl px-4 py-3 text-ink-600 flex items-center justify-center"
|
||||
data-testid="profile-headline"
|
||||
style={{
|
||||
fontSize: '15px',
|
||||
lineHeight: '1.65',
|
||||
borderLeft: '1.5px solid rgb(var(--color-primary-300))',
|
||||
paddingLeft: '40px',
|
||||
}}
|
||||
>
|
||||
<div className="h-fit relative">
|
||||
<span
|
||||
className="absolute -mt-6 text-3xl text-primary-300"
|
||||
style={{fontFamily: 'serif'}}
|
||||
>
|
||||
"
|
||||
</span>
|
||||
<span className="italic">{profile.headline}</span>
|
||||
<span
|
||||
className="absolute -bottom-8 text-3xl text-primary-300"
|
||||
style={{fontFamily: 'serif'}}
|
||||
>
|
||||
"
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Row>
|
||||
@@ -159,21 +212,20 @@ export function ProfileHeaderActions(props: {
|
||||
|
||||
if (currentUser && isCurrentUser) {
|
||||
return (
|
||||
<Row className={'items-center gap-4'}>
|
||||
<Row className={'items-center gap-2'}>
|
||||
<ViewProfileCardButton user={user} profile={profile} />
|
||||
<ShareProfileButton className="sm:flex" username={user.username} />
|
||||
<Tooltip text={t('more_options_user.edit_profile', 'Edit profile')} noTap>
|
||||
<Button
|
||||
<button
|
||||
data-testid="profile-edit"
|
||||
color={'gray-outline'}
|
||||
onClick={() => {
|
||||
track('editprofile', {userId: user.id})
|
||||
Router.push('profile')
|
||||
}}
|
||||
size="sm"
|
||||
className="border-canvas-300 flex items-center gap-1.5 rounded-lg border px-2 py-2 text-sm text-primary-700 transition-colors hover:border-primary-400 hover:bg-primary-50"
|
||||
>
|
||||
<PencilIcon className=" h-4 w-4" />
|
||||
</Button>
|
||||
<PencilIcon className="h-4 w-4 text-ink-500" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip
|
||||
@@ -183,7 +235,11 @@ export function ProfileHeaderActions(props: {
|
||||
>
|
||||
<DropdownMenu
|
||||
menuWidth={'w-52'}
|
||||
icon={<EllipsisHorizontalIcon className="h-5 w-5" aria-hidden="true" />}
|
||||
icon={
|
||||
<button className="border-canvas-300 flex items-center gap-1.5 rounded-lg border px-2 py-2 text-sm text-primary-700 transition-colors hover:border-primary-400 hover:bg-primary-50">
|
||||
<EllipsisHorizontalIcon className="h-4 w-4 text-ink-500" aria-hidden="true" />
|
||||
</button>
|
||||
}
|
||||
items={[
|
||||
{
|
||||
name:
|
||||
@@ -239,7 +295,7 @@ export function ProfileHeaderActions(props: {
|
||||
}
|
||||
|
||||
return (
|
||||
<Row className="items-center gap-1 sm:gap-2">
|
||||
<Row className="items-center gap-2">
|
||||
<ViewProfileCardButton user={user} profile={profile} />
|
||||
<ShareProfileButton className="sm:flex" username={user.username} />
|
||||
{currentUser && (
|
||||
|
||||
@@ -3,7 +3,6 @@ import clsx from 'clsx'
|
||||
import {debug} from 'common/logger'
|
||||
import {Profile} from 'common/profiles/profile'
|
||||
import {UserActivity} from 'common/user'
|
||||
import Image from 'next/image'
|
||||
import React, {ReactNode} from 'react'
|
||||
import {ProfileAnswers} from 'web/components/answers/profile-answers'
|
||||
import {ProfileBio} from 'web/components/bio/profile-bio'
|
||||
@@ -12,10 +11,16 @@ import {Row} from 'web/components/layout/row'
|
||||
import {SignUpButton} from 'web/components/nav/sidebar'
|
||||
import {ConnectActions} from 'web/components/profile/connect-actions'
|
||||
import ProfileHeader, {ProfileHeaderActions} from 'web/components/profile/profile-header'
|
||||
import ProfileAbout from 'web/components/profile-about'
|
||||
import ProfileAbout, {
|
||||
ProfileInterestsAndCauses,
|
||||
ProfileLinks,
|
||||
ProfilePersonality,
|
||||
} from 'web/components/profile-about'
|
||||
import ProfileCarousel from 'web/components/profile-carousel'
|
||||
import {ProfileCommentSection} from 'web/components/profile-comment-section'
|
||||
import {Content} from 'web/components/widgets/editor'
|
||||
import {Subtitle} from 'web/components/widgets/subtitle'
|
||||
import {shortenName} from 'web/components/widgets/user-link'
|
||||
import {useGetter} from 'web/hooks/use-getter'
|
||||
import {useHiddenProfiles} from 'web/hooks/use-hidden-profiles'
|
||||
import {useUser} from 'web/hooks/use-user'
|
||||
@@ -74,11 +79,19 @@ export function ProfileInfo(props: {
|
||||
|
||||
const {data: userActivity} = useUserActivity(user?.id)
|
||||
|
||||
// const isCurrentUser = currentUser?.id === user.id
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="bg-canvas-50 border-canvas-300 mb-6 flex items-center gap-2 border-b px-8 py-3">
|
||||
<div
|
||||
className="bg-canvas-50 border-canvas-300 mb-6 flex items-center border-b px-4 sm:px-9 py-4"
|
||||
style={{
|
||||
borderBottomWidth: '1px',
|
||||
gap: '10px',
|
||||
}}
|
||||
>
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<BackButton />
|
||||
<BackButton className={'hidden sm:flex'} />
|
||||
|
||||
<div className="ml-auto flex items-center gap-2">
|
||||
<ProfileHeaderActions
|
||||
@@ -96,22 +109,36 @@ export function ProfileInfo(props: {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mx-auto w-full px-8 pb-6 pt-0">
|
||||
<Row className="items-start gap-6">
|
||||
{profile.pinned_url && (
|
||||
<div className="h-[108px] w-[108px] flex-none">
|
||||
<Image
|
||||
priority={true}
|
||||
src={profile.pinned_url}
|
||||
height={300}
|
||||
width={300}
|
||||
sizes="(max-width: 640px) 100vw, 300px"
|
||||
alt=""
|
||||
className="h-full w-full cursor-pointer rounded-2xl object-cover"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="mx-auto w-full px-4 sm:px-8 pb-6 pt-0">
|
||||
<Row className="relative items-start gap-6">
|
||||
{/* Gradient overlay */}
|
||||
{/*<div*/}
|
||||
{/* style={{*/}
|
||||
{/* position: 'absolute',*/}
|
||||
{/* inset: 0,*/}
|
||||
{/* pointerEvents: 'none',*/}
|
||||
{/* background: `radial-gradient(ellipse 55% 70% at 100% 30%, rgba(193,127,62,0.07) 0%, transparent 65%), radial-gradient(ellipse 30% 40% at 0% 90%, rgba(193,127,62,0.05) 0%, transparent 55%)`,*/}
|
||||
{/* }}*/}
|
||||
{/*/>*/}
|
||||
{/* First letter of name */}
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
right: '0%',
|
||||
top: '40%',
|
||||
transform: 'translateY(-50%)',
|
||||
fontFamily: "'Cormorant Garamond', serif",
|
||||
fontSize: 'clamp(7rem, 14vw, 16rem)',
|
||||
fontWeight: 500,
|
||||
color: 'rgba(193,127,62,0.04)',
|
||||
lineHeight: 1,
|
||||
userSelect: 'none',
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
>
|
||||
{user.name?.charAt(0).toUpperCase()}
|
||||
</div>
|
||||
<div className={clsx('min-w-0 flex-1', !profile.pinned_url && 'ml-6')}>
|
||||
<ProfileHeader
|
||||
user={user}
|
||||
userActivity={userActivity}
|
||||
@@ -177,8 +204,15 @@ export function ProfileInfo(props: {
|
||||
|
||||
function ProfileCard(props: {title?: ReactNode; children: ReactNode; className?: string}) {
|
||||
const {title, children, className} = props
|
||||
// Check if children is null or undefined
|
||||
if (children == null) {
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<div className={clsx('bg-canvas-50 border-canvas-300 rounded-2xl border p-6', className)}>
|
||||
<div
|
||||
className={clsx('bg-canvas-50 border-canvas-300 border', className)}
|
||||
style={{borderRadius: '14px', padding: '22px 24px'}}
|
||||
>
|
||||
{title != null && <CardTitle>{title}</CardTitle>}
|
||||
{children}
|
||||
</div>
|
||||
@@ -188,9 +222,20 @@ function ProfileCard(props: {title?: ReactNode; children: ReactNode; className?:
|
||||
function CardTitle(props: {children: ReactNode; className?: string}) {
|
||||
const {children, className} = props
|
||||
return (
|
||||
<div className={clsx('text-ink-900 mb-4 text-lg font-semibold tracking-tight', className)}>
|
||||
// <div
|
||||
// className={clsx(
|
||||
// 'text-ink-900 mb-3.5 font-cormorant text-xl font-medium tracking-wide',
|
||||
// className,
|
||||
// )}
|
||||
// style={{letterSpacing: '0.01em'}}
|
||||
// >
|
||||
// {children}
|
||||
// </div>
|
||||
<Subtitle
|
||||
className={clsx('!mt-0 !mb-4 font-cormorant text-xl font-medium tracking-wide', className)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</Subtitle>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -223,12 +268,38 @@ function ProfileContent(props: {
|
||||
|
||||
const currentUser = useUser()
|
||||
const isCurrentUser = currentUser?.id === user.id
|
||||
const t = useT()
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mt-4 grid grid-cols-1 items-start gap-6 lg:grid-cols-[1fr_480px]">
|
||||
<div className="mt-4 grid grid-cols-1 items-start gap-6 lg:grid-cols-[1fr_800px]">
|
||||
<Col className="gap-6">
|
||||
<ProfileCard className="p-5">
|
||||
<ProfileCard title="Details" className="p-5">
|
||||
<ProfileAbout
|
||||
profile={profile}
|
||||
userActivity={userActivity}
|
||||
isCurrentUser={isCurrentUser}
|
||||
/>
|
||||
</ProfileCard>
|
||||
|
||||
<ProfileCard title={t('profile.interests_and_causes', 'Interests')} className="p-5">
|
||||
<ProfileInterestsAndCauses profile={profile} />
|
||||
</ProfileCard>
|
||||
|
||||
{(profile.mbti || profile.big5_agreeableness) && (
|
||||
<ProfileCard title={t('profile.personality', 'Personality')} className="p-5">
|
||||
<ProfilePersonality profile={profile} />
|
||||
</ProfileCard>
|
||||
)}
|
||||
|
||||
{profile.links && Object.keys(profile.links).length > 0 && (
|
||||
<ProfileCard title={t('profile.links', 'Links')} className="p-5">
|
||||
<ProfileLinks profile={profile} />
|
||||
</ProfileCard>
|
||||
)}
|
||||
</Col>
|
||||
<Col className="gap-6">
|
||||
<ProfileCard title={t('profile.bio.about_me', 'About Me')} className="p-0">
|
||||
<ProfileBio
|
||||
isCurrentUser={isCurrentUser}
|
||||
profile={profile}
|
||||
@@ -238,12 +309,21 @@ function ProfileContent(props: {
|
||||
</ProfileCard>
|
||||
|
||||
{isProfileVisible && (
|
||||
<ProfileCard className="p-5">
|
||||
<ProfileCarousel profile={profile} refreshProfile={refreshProfile} />
|
||||
</ProfileCard>
|
||||
// <ProfileCard className="!p-0">
|
||||
<ProfileCarousel profile={profile} refreshProfile={refreshProfile} />
|
||||
// </ProfileCard>
|
||||
)}
|
||||
|
||||
<ProfileCard className="p-5">
|
||||
<ProfileCard
|
||||
className="p-5"
|
||||
title={
|
||||
isCurrentUser
|
||||
? t('answers.display.your_prompts', 'Compatibility Prompts')
|
||||
: t('answers.display.user_prompts', 'Compatibility Prompts', {
|
||||
name: shortenName(user.name),
|
||||
})
|
||||
}
|
||||
>
|
||||
<ProfileAnswers
|
||||
isCurrentUser={isCurrentUser}
|
||||
user={user}
|
||||
@@ -253,11 +333,7 @@ function ProfileContent(props: {
|
||||
/>
|
||||
</ProfileCard>
|
||||
|
||||
<ProfileCard className="p-5">
|
||||
<ConnectActions user={user} profile={profile} />
|
||||
</ProfileCard>
|
||||
|
||||
<ProfileCard className="p-5">
|
||||
<ProfileCard className="p-5" title={t('profile.comments.section_title', 'Endorsements')}>
|
||||
<ProfileCommentSection
|
||||
onUser={user}
|
||||
profile={profile}
|
||||
@@ -265,16 +341,12 @@ function ProfileContent(props: {
|
||||
simpleView={!!fromProfilePage}
|
||||
/>
|
||||
</ProfileCard>
|
||||
</Col>
|
||||
|
||||
<Col className="gap-6">
|
||||
<ProfileCard title="Details" className="p-5">
|
||||
<ProfileAbout
|
||||
profile={profile}
|
||||
userActivity={userActivity}
|
||||
isCurrentUser={isCurrentUser}
|
||||
/>
|
||||
</ProfileCard>
|
||||
{!isCurrentUser && currentUser && (
|
||||
<ProfileCard title={t('profile.connect.title', 'Connect')}>
|
||||
<ConnectActions user={user} profile={profile} />
|
||||
</ProfileCard>
|
||||
)}
|
||||
</Col>
|
||||
</div>
|
||||
{/*<LikesDisplay*/}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {getLocationText} from 'common/geodb'
|
||||
import {getGoogleMapsUrl, getLocationText} from 'common/geodb'
|
||||
import {Profile} from 'common/profiles/profile'
|
||||
import React from 'react'
|
||||
import {IoLocationOutline} from 'react-icons/io5'
|
||||
import {IconWithInfo} from 'web/components/icons'
|
||||
import {CustomLink} from 'web/components/links'
|
||||
|
||||
export function ProfileLocation(props: {profile: Profile; prefix?: string}) {
|
||||
const {profile, prefix} = props
|
||||
@@ -12,5 +14,13 @@ export function ProfileLocation(props: {profile: Profile; prefix?: string}) {
|
||||
return null
|
||||
}
|
||||
|
||||
return <IconWithInfo text={text} icon={<IoLocationOutline className="h-4 w-4" />} />
|
||||
return (
|
||||
<IconWithInfo
|
||||
icon={<IoLocationOutline className="text-ink-300" style={{width: '14px', height: '14px'}} />}
|
||||
>
|
||||
<CustomLink href={getGoogleMapsUrl(text)} className={'hover:text-primary-500'}>
|
||||
{text}
|
||||
</CustomLink>
|
||||
</IconWithInfo>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -17,26 +17,36 @@ export default function ProfilePrimaryInfo(props: {profile: Profile; short?: boo
|
||||
const t = useT()
|
||||
const {measurementSystem} = useMeasurementSystem()
|
||||
return (
|
||||
<Row className="text-ink-700 gap-4 text-sm" data-testid="profile-gender-location-height-inches">
|
||||
<Row
|
||||
className="text-ink-500 gap-4 flex-wrap"
|
||||
data-testid="profile-gender-location-height-inches"
|
||||
style={{fontSize: '13.5px', gap: '6px 18px'}}
|
||||
>
|
||||
<ProfileLocation profile={profile} />
|
||||
{!short && profile.gender && (
|
||||
{profile.age && (
|
||||
<IconWithInfo
|
||||
text={capitalize(
|
||||
t(`profile.gender.${profile.gender}`, convertGender(profile.gender as Gender)),
|
||||
)}
|
||||
icon={<GenderIcon gender={profile.gender as Gender} className="h-4 w-4 " />}
|
||||
text={t('profile.header.age', '{age} years old', {age: profile.age})}
|
||||
icon={<Calendar className="text-ink-300" style={{width: '14px', height: '14px'}} />}
|
||||
/>
|
||||
)}
|
||||
{!short && profile.height_in_inches != null && (
|
||||
<IconWithInfo
|
||||
text={formatProfileValue('height_in_inches', profile.height_in_inches, measurementSystem)}
|
||||
icon={<MdHeight className="h-4 w-4 " />}
|
||||
icon={<MdHeight className="text-ink-300" style={{width: '14px', height: '14px'}} />}
|
||||
/>
|
||||
)}
|
||||
{profile.age && (
|
||||
{!short && profile.gender && (
|
||||
<IconWithInfo
|
||||
text={t('profile.header.age', '{age} years old', {age: profile.age})}
|
||||
icon={<Calendar className="h-4 w-4 " />}
|
||||
text={capitalize(
|
||||
t(`profile.gender.${profile.gender}`, convertGender(profile.gender as Gender)),
|
||||
)}
|
||||
icon={
|
||||
<GenderIcon
|
||||
gender={profile.gender as Gender}
|
||||
className="text-ink-300"
|
||||
// style={{width: '14px', height: '14px'}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Row>
|
||||
|
||||
@@ -9,7 +9,15 @@ export default function SiteLogo(props: {noLink?: boolean; className?: string})
|
||||
const inner = (
|
||||
<>
|
||||
<FavIconBlack className={className?.includes('invert') ? '' : 'dark:invert'} />
|
||||
<div className={clsx('my-auto text-xl font-thin logo')}>
|
||||
<div
|
||||
className={clsx('my-auto logo')}
|
||||
style={{
|
||||
fontFamily: 'Cormorant Garamond',
|
||||
fontSize: '22px',
|
||||
fontWeight: 500,
|
||||
letterSpacing: '0.03em',
|
||||
}}
|
||||
>
|
||||
{IS_PROD ? 'Compass' : 'Compass dev'}
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -41,15 +41,18 @@ export function UserHandles(props: {links: Socials; className?: string}) {
|
||||
|
||||
return (
|
||||
<Row
|
||||
className={clsx('text-ink-400 flex-wrap items-center gap-2 sm:gap-x-4', className)}
|
||||
className={clsx('flex-wrap items-center gap-2', className)}
|
||||
data-testid="profile-social-media-accounts"
|
||||
>
|
||||
{display.map(({platform, label, url}) => (
|
||||
<a key={platform} target="_blank" href={url}>
|
||||
<Row className="items-center gap-1">
|
||||
<SocialIcon site={platform as any} className="text-primary-900 h-4 w-4" />
|
||||
<span className="text-primary-700 text-sm hover:text-primary-500">{label}</span>
|
||||
</Row>
|
||||
<a
|
||||
key={platform}
|
||||
target="_blank"
|
||||
href={url}
|
||||
className="border-canvas-300 bg-canvas-0 flex items-center gap-1.5 rounded-lg border px-3 py-1.5 text-[12.5px] text-ink-500 transition-colors hover:border-primary-300 hover:text-primary-600"
|
||||
>
|
||||
<SocialIcon site={platform as any} className="text-ink-500 h-[16px] w-[16px]" />
|
||||
<span>{label}</span>
|
||||
</a>
|
||||
))}
|
||||
</Row>
|
||||
|
||||
@@ -36,7 +36,7 @@ export function VoteItem(props: {vote: Vote; onVoted?: () => void | Promise<void
|
||||
const t = useT()
|
||||
// console.debug('creator', creator, vote)
|
||||
return (
|
||||
<Col className={'mb-4 rounded-lg border border-canvas-200 p-4'}>
|
||||
<Col className={'mb-4 rounded-lg border border-canvas-200 p-4 bg-canvas-50'}>
|
||||
<Row className={'mb-2'}>
|
||||
<Col className={'flex-grow'}>
|
||||
<p className={'text-2xl'}>{vote.title}</p>
|
||||
|
||||
@@ -25,7 +25,7 @@ export function Carousel(props: {
|
||||
<Row
|
||||
className={clsx(
|
||||
'scrollbar-hide w-full snap-x overflow-x-auto scroll-smooth',
|
||||
labelsParentClassName ?? 'gap-4',
|
||||
labelsParentClassName ?? 'gap-2',
|
||||
)}
|
||||
ref={ref}
|
||||
onScroll={onScroll}
|
||||
|
||||
@@ -69,7 +69,8 @@ const proseClass = (size: 'sm' | 'md' | 'lg') =>
|
||||
size === 'sm' ? 'prose-sm' : 'text-md',
|
||||
size !== 'lg' && 'prose-p:my-0 prose-ul:my-0 prose-ol:my-0 prose-li:my-0',
|
||||
'[&>p]:prose-li:my-0',
|
||||
'text-ink-900 prose-blockquote:text-teal-700',
|
||||
'prose-h1:text-ink-900 prose-h2:text-ink-900 prose-h3:text-ink-900',
|
||||
'text-ink-500 prose-blockquote:text-teal-700 ',
|
||||
'break-anywhere',
|
||||
)
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@ import clsx from 'clsx'
|
||||
|
||||
export function Subtitle(props: {children: string; className?: string}) {
|
||||
const {children: text, className} = props
|
||||
return <h2 className={clsx('text-ink-600 inline-block font-semibold', className)}>{text}</h2>
|
||||
return <h2 className={clsx('text-ink-900 inline-block font-semibold', className)}>{text}</h2>
|
||||
}
|
||||
|
||||
@@ -25,12 +25,17 @@ export const ShareProfileButton = (props: {
|
||||
|
||||
return (
|
||||
<CopyLinkOrShareButton
|
||||
className={className}
|
||||
className={clsx(
|
||||
className,
|
||||
'border-canvas-300 flex items-center gap-1.5 rounded-lg border px-3 py-2 text-sm text-ink-500 transition-colors hover:border-primary-400 hover:bg-primary-50',
|
||||
)}
|
||||
url={shareUrl}
|
||||
eventTrackingName="shareprofile"
|
||||
color={color}
|
||||
size="sm"
|
||||
iconClassName={'hidden sm:inline'}
|
||||
>
|
||||
<div className="ml-2 text-sm">{t('button.share.label', 'Copy profile link')}</div>
|
||||
<div className="text-sm">{t('button.share.label', 'Copy Link')}</div>
|
||||
</CopyLinkOrShareButton>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ export const StarButton = (props: {
|
||||
>
|
||||
<StarIcon
|
||||
className={clsx(
|
||||
'h-8 w-8 transition-colors group-hover:fill-yellow-400/70',
|
||||
'h-7 w-7 transition-colors group-hover:fill-yellow-400/70',
|
||||
isStarred && 'fill-yellow-400 stroke-yellow-500 dark:stroke-yellow-600',
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -5,7 +5,7 @@ export function Subtitle(props: {children: React.ReactNode; className?: string})
|
||||
return (
|
||||
<h2
|
||||
className={clsx(
|
||||
'text-primary-700 mb-2 mt-6 inline-block text-lg sm:mb-2 sm:mt-6 sm:text-xl',
|
||||
'text-ink-700 mb-2 mt-6 inline-block text-lg sm:mb-2 sm:mt-6 sm:text-2xl',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -4,7 +4,7 @@ import {capitalize} from 'lodash'
|
||||
import {convertRelationshipType, RelationshipType} from 'web/lib/util/convert-types'
|
||||
import stringOrStringArrayToText from 'web/lib/util/string-or-string-array-to-text'
|
||||
|
||||
export function getSeekingConnectionText(profile: Profile, t: any, short?: boolean) {
|
||||
export function getSeekingConnectionText(profile: Profile, t: any, _short?: boolean) {
|
||||
const relationshipTypes = profile.pref_relation_styles
|
||||
let seekingGenderText = stringOrStringArrayToText({
|
||||
text: relationshipTypes?.length
|
||||
@@ -17,7 +17,7 @@ export function getSeekingConnectionText(profile: Profile, t: any, short?: boole
|
||||
)
|
||||
.sort()
|
||||
: [t('profile.connection.default', 'connection')],
|
||||
preText: !short ? t('profile.seeking', 'Seeking') : undefined,
|
||||
// preText: !short ? t('profile.seeking', 'Seeking') : undefined,
|
||||
asSentence: true,
|
||||
capitalizeFirstLetterOption: false,
|
||||
t: t,
|
||||
|
||||
@@ -25,6 +25,10 @@ export default function Document() {
|
||||
// href="https://fonts.googleapis.com/css2?family=EB+Garamond:wght@400;500;600;700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,500;0,600;1,300;1,400&family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;1,9..40,300&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
|
||||
{/* PWA primary color */}
|
||||
<meta name="theme-color" media="(prefers-color-scheme: light)" content="#ffffff" />
|
||||
|
||||
@@ -10,156 +10,303 @@ export const config = {runtime: 'edge'}
|
||||
const COMPASS_LOGO =
|
||||
'https://firebasestorage.googleapis.com/v0/b/compass-130ba.firebasestorage.app/o/misc%2Fcompass-512.png?alt=media&token=d2fa566f-f443-4a94-90be-e50403f1805a'
|
||||
|
||||
export const getCardOptions = async () => ({
|
||||
width: 1200,
|
||||
height: 630,
|
||||
})
|
||||
export const getCardOptions = async () => ({width: 1200, height: 630})
|
||||
|
||||
// Edge-safe capitalize
|
||||
function capitalize(str: string) {
|
||||
if (!str) return ''
|
||||
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase()
|
||||
}
|
||||
|
||||
// Palette
|
||||
const C = {
|
||||
ink900: '#1E1A14',
|
||||
ink600: '#786C5C',
|
||||
ink500: '#8C8070',
|
||||
ink300: '#BEB2A2',
|
||||
canvas50: '#F7F4EF',
|
||||
canvas100: '#EDE8E0',
|
||||
canvas200: '#E8D5BC',
|
||||
canvas300: '#DECBB2',
|
||||
canvas950: '#2C2416',
|
||||
primary50: '#FAF3E9',
|
||||
primary100: '#F3E4CE',
|
||||
primary200: '#E8C99D',
|
||||
primary300: '#DCAB71',
|
||||
primary400: '#D09352',
|
||||
primary500: '#C17F3E',
|
||||
primary600: '#A6682E',
|
||||
primary700: '#855022',
|
||||
primary800: '#653A18',
|
||||
}
|
||||
|
||||
function OgProfile(props: ogProps) {
|
||||
console.log(props)
|
||||
const {avatarUrl, name, city, country, age, interests, keywords} = props
|
||||
let headline = props.headline
|
||||
|
||||
const _interestsList =
|
||||
typeof interests === 'string' ? (interests ? interests.split(',') : []) : (interests ?? [])
|
||||
const keywordsList =
|
||||
typeof keywords === 'string' ? (keywords ? keywords.split(',') : []) : (keywords ?? [])
|
||||
const allTags = [...keywordsList].filter(Boolean).slice(0, 8)
|
||||
const interestsList =
|
||||
typeof interests === 'string' ? (interests ? interests.split(',') : []) : (interests ?? [])
|
||||
const allTags = [...keywordsList, ...interestsList].filter(Boolean).slice(0, 6)
|
||||
|
||||
const maxChars = 220
|
||||
const maxChars = 250
|
||||
if (headline && headline.length > maxChars) {
|
||||
headline = headline.slice(0, maxChars) + '...'
|
||||
headline = headline.slice(0, maxChars) + '…'
|
||||
}
|
||||
|
||||
const totalChars = (headline?.length || 0) + (allTags?.join(' ')?.length || 0) + name.length * 3
|
||||
const hasLongContent = (headline?.length || 0) > 80 || allTags.length > 4
|
||||
const imgSize = 300
|
||||
|
||||
const isLargerPicLayout = totalChars < maxChars
|
||||
|
||||
const imgSize = isLargerPicLayout ? 400 : 250
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
width: '1200px',
|
||||
height: '630px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
padding: '50px',
|
||||
fontFamily: 'sans-serif',
|
||||
backgroundColor: '#f5f5f5',
|
||||
backgroundColor: C.canvas100,
|
||||
fontFamily: 'Georgia, serif',
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
<div style={{display: 'flex', flex: isLargerPicLayout ? 1 : 3}}>
|
||||
{/* Left Column: Text */}
|
||||
{/* Left dark panel */}
|
||||
<div
|
||||
style={{
|
||||
width: '340px',
|
||||
height: '630px',
|
||||
backgroundColor: C.canvas950,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: '20px',
|
||||
flexShrink: 0,
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
{/* Amber ring behind avatar */}
|
||||
<div
|
||||
style={{
|
||||
flex: isLargerPicLayout ? 1 : 3,
|
||||
width: `${imgSize + 10}px`,
|
||||
height: `${imgSize + 10}px`,
|
||||
borderRadius: '50%',
|
||||
backgroundColor: C.primary700,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '10px',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<div style={{display: 'flex', fontSize: '64px', fontWeight: 'bold'}}>
|
||||
{name}
|
||||
{age && `, ${age}`}
|
||||
</div>
|
||||
{city && (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
fontSize: '28px',
|
||||
marginBottom: '20px',
|
||||
marginTop: '20px',
|
||||
opacity: 0.85,
|
||||
}}
|
||||
>
|
||||
{city}
|
||||
{country && `, ${country}`}
|
||||
</div>
|
||||
)}
|
||||
{/*<div style={{display: 'flex', justifyContent: 'flex-end', alignItems: 'flex-start'}}>*/}
|
||||
{/* <img src={'https://www.compassmeet.com/favicon-black.svg'} width={100} height={100} />*/}
|
||||
{/*</div>*/}
|
||||
{allTags && (
|
||||
<div style={{display: 'flex', gap: '10px', flexWrap: 'wrap'}}>
|
||||
{allTags?.map(capitalize).map((tag, i) => (
|
||||
<span
|
||||
key={i}
|
||||
style={{
|
||||
padding: '8px 16px',
|
||||
backgroundColor: '#ddd',
|
||||
borderRadius: '20px',
|
||||
fontSize: '24px',
|
||||
}}
|
||||
>
|
||||
{tag.trim()}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{isLargerPicLayout && (
|
||||
<div style={{display: 'flex'}}>
|
||||
{headline && (
|
||||
<div style={{display: 'flex', fontSize: '36px', marginTop: '40px'}}>{headline}</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Right Column: Avatar */}
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={avatarUrl || COMPASS_LOGO}
|
||||
width={imgSize}
|
||||
height={imgSize}
|
||||
style={{borderRadius: 50, objectFit: 'cover'}}
|
||||
style={{borderRadius: '50%', objectFit: 'cover', display: 'flex'}}
|
||||
alt="Avatar"
|
||||
/>
|
||||
{isLargerPicLayout && (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
fontSize: '48px',
|
||||
fontWeight: 'semibold',
|
||||
fontFamily: 'Georgia',
|
||||
marginTop: '20px',
|
||||
fontStyle: 'italic',
|
||||
}}
|
||||
>
|
||||
compassmeet.com
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!isLargerPicLayout && (
|
||||
{/* Compass URL */}
|
||||
<div
|
||||
style={{
|
||||
flex: 2,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
marginBottom: '40px',
|
||||
alignItems: 'center',
|
||||
gap: '8px',
|
||||
marginTop: '8px',
|
||||
}}
|
||||
>
|
||||
{headline && (
|
||||
<div style={{display: 'flex', fontSize: '36px', marginTop: '40px'}}>{headline}</div>
|
||||
<div
|
||||
style={{
|
||||
width: '6px',
|
||||
height: '6px',
|
||||
borderRadius: '50%',
|
||||
backgroundColor: C.primary400,
|
||||
display: 'flex',
|
||||
}}
|
||||
/>
|
||||
<span
|
||||
style={{
|
||||
fontFamily: 'Georgia, serif',
|
||||
fontStyle: 'italic',
|
||||
fontSize: '22px',
|
||||
color: C.primary300,
|
||||
display: 'flex',
|
||||
}}
|
||||
>
|
||||
compassmeet.com
|
||||
</span>
|
||||
<div
|
||||
style={{
|
||||
width: '6px',
|
||||
height: '6px',
|
||||
borderRadius: '50%',
|
||||
backgroundColor: C.primary400,
|
||||
display: 'flex',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Subtle bottom accent line */}
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: '0',
|
||||
left: '0',
|
||||
right: '0',
|
||||
height: '4px',
|
||||
backgroundColor: C.primary500,
|
||||
display: 'flex',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Right content area */}
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
padding: '52px 56px',
|
||||
gap: '0px',
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
{/* Top accent line */}
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '0',
|
||||
left: '0',
|
||||
right: '0',
|
||||
height: '4px',
|
||||
backgroundColor: C.primary500,
|
||||
display: 'flex',
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Name + age */}
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'baseline',
|
||||
gap: '16px',
|
||||
marginBottom: '10px',
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
fontFamily: 'Georgia, serif',
|
||||
fontSize: hasLongContent ? '70px' : '80px',
|
||||
fontWeight: 'bold',
|
||||
color: C.ink900,
|
||||
lineHeight: 1.05,
|
||||
display: 'flex',
|
||||
}}
|
||||
>
|
||||
{name}
|
||||
</span>
|
||||
{age && (
|
||||
<span
|
||||
style={{
|
||||
fontSize: hasLongContent ? '38px' : '44px',
|
||||
color: C.ink500,
|
||||
fontFamily: 'Georgia, serif',
|
||||
display: 'flex',
|
||||
}}
|
||||
>
|
||||
{age}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Location */}
|
||||
{(city || country) && (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px',
|
||||
marginBottom: '24px',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: '5px',
|
||||
height: '5px',
|
||||
borderRadius: '50%',
|
||||
backgroundColor: C.primary400,
|
||||
display: 'flex',
|
||||
flexShrink: 0,
|
||||
marginTop: '2px',
|
||||
}}
|
||||
/>
|
||||
<span
|
||||
style={{
|
||||
fontSize: '26px',
|
||||
color: C.ink500,
|
||||
fontFamily: 'Georgia, serif',
|
||||
fontStyle: 'italic',
|
||||
display: 'flex',
|
||||
}}
|
||||
>
|
||||
{[city, country].filter(Boolean).join(', ')}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Tags */}
|
||||
{allTags.length > 0 && (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
gap: '10px',
|
||||
flexWrap: 'wrap',
|
||||
marginBottom: '24px',
|
||||
}}
|
||||
>
|
||||
{allTags.map(capitalize).map((tag, i) => (
|
||||
<span
|
||||
key={i}
|
||||
style={{
|
||||
padding: '6px 18px',
|
||||
backgroundColor: C.canvas200,
|
||||
color: C.primary700,
|
||||
borderRadius: '100px',
|
||||
fontSize: '22px',
|
||||
fontFamily: 'Georgia, serif',
|
||||
display: 'flex',
|
||||
}}
|
||||
>
|
||||
{tag.trim()}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Headline */}
|
||||
{headline && (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
borderLeft: `3px solid ${C.primary300}`,
|
||||
paddingLeft: '20px',
|
||||
marginTop: '4px',
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
fontSize: '28px',
|
||||
color: C.ink600,
|
||||
fontFamily: 'Georgia, serif',
|
||||
fontStyle: 'italic',
|
||||
lineHeight: 1.5,
|
||||
display: 'flex',
|
||||
}}
|
||||
>
|
||||
{headline}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -169,7 +316,6 @@ export default async function handler(req: NextRequest) {
|
||||
const {searchParams} = new URL(req.url)
|
||||
const options = await getCardOptions()
|
||||
|
||||
// Clean search params by removing 'amp;' prefixes that occur due to URL encoding
|
||||
const cleanedEntries = Array.from(searchParams.entries()).map(([key, value]) => [
|
||||
key.replace(/^amp;/, ''),
|
||||
value,
|
||||
|
||||
@@ -113,8 +113,8 @@ export default function EventsPage() {
|
||||
</div>
|
||||
|
||||
{/* Event Ideas Section */}
|
||||
<div className="mt-6 bg-canvas-100 rounded-lg p-4">
|
||||
<h2 className="text-lg font-semibold mb-2 mt-0">
|
||||
<div className="mt-6 bg-canvas-50 border border-canvas-200 rounded-lg p-4">
|
||||
<h2 className="text-xl font-semibold mb-2 mt-0">
|
||||
{t('events.why_organize', 'Why organize events?')}
|
||||
</h2>
|
||||
<p className="text-ink-600 text-sm mb-3">
|
||||
@@ -124,28 +124,28 @@ export default function EventsPage() {
|
||||
)}
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<span className="bg-canvas-50 text-ink-700 px-3 py-1 rounded-full text-xs">
|
||||
<span className="bg-canvas-100 border border-canvas-200 text-ink-700 px-3 py-1 rounded-full text-xs">
|
||||
📚 {t('events.book_clubs', 'Book clubs')}
|
||||
</span>
|
||||
<span className="bg-canvas-50 text-ink-700 px-3 py-1 rounded-full text-xs">
|
||||
<span className="bg-canvas-100 border border-canvas-200 text-ink-700 px-3 py-1 rounded-full text-xs">
|
||||
🎮 {t('events.game_nights', 'Game nights')}
|
||||
</span>
|
||||
<span className="bg-canvas-50 text-ink-700 px-3 py-1 rounded-full text-xs">
|
||||
<span className="bg-canvas-100 border border-canvas-200 text-ink-700 px-3 py-1 rounded-full text-xs">
|
||||
🚶 {t('events.walking_groups', 'Walking groups')}
|
||||
</span>
|
||||
<span className="bg-canvas-50 text-ink-700 px-3 py-1 rounded-full text-xs">
|
||||
<span className="bg-canvas-100 border border-canvas-200 text-ink-700 px-3 py-1 rounded-full text-xs">
|
||||
☕ {t('events.coffee_chats', 'Coffee chats')}
|
||||
</span>
|
||||
<span className="bg-canvas-50 text-ink-700 px-3 py-1 rounded-full text-xs">
|
||||
<span className="bg-canvas-100 border border-canvas-200 text-ink-700 px-3 py-1 rounded-full text-xs">
|
||||
🎨 {t('events.creative_workshops', 'Creative workshops')}
|
||||
</span>
|
||||
<span className="bg-canvas-50 text-ink-700 px-3 py-1 rounded-full text-xs">
|
||||
<span className="bg-canvas-100 border border-canvas-200 text-ink-700 px-3 py-1 rounded-full text-xs">
|
||||
🤔 {t('events.philosophy_discussions', 'Philosophy discussions')}
|
||||
</span>
|
||||
<span className="bg-canvas-50 text-ink-700 px-3 py-1 rounded-full text-xs">
|
||||
<span className="bg-canvas-100 border border-canvas-200 text-ink-700 px-3 py-1 rounded-full text-xs">
|
||||
🌱 {t('events.sustainability_meetups', 'Sustainability meetups')}
|
||||
</span>
|
||||
<span className="bg-canvas-50 text-ink-700 px-3 py-1 rounded-full text-xs">
|
||||
<span className="bg-canvas-100 border border-canvas-200 text-ink-700 px-3 py-1 rounded-full text-xs">
|
||||
🎯 {t('events.hobby_exchanges', 'Hobby exchanges')}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -38,7 +38,7 @@ function SectionCard({icon, title, description, links}: SectionCardProps) {
|
||||
</div>
|
||||
|
||||
{/* Title & description */}
|
||||
<h2 className="text-base font-bold text-ink-900 mb-2">{title}</h2>
|
||||
<h2 className="font-bold text-ink-900 mb-2">{title}</h2>
|
||||
<p className="text-sm text-ink-500 leading-relaxed mb-6">{description}</p>
|
||||
|
||||
{/* Links */}
|
||||
|
||||
@@ -111,7 +111,7 @@ function ProfilePageInner(props: {user: User; profile: Profile}) {
|
||||
url={`/profile`}
|
||||
/>
|
||||
<Col className="items-center">
|
||||
<BackButton className="-ml-2 mb-2 self-start" />
|
||||
<BackButton className="ml-2 mb-2 self-start" />
|
||||
<Col className={'w-full px-6 py-4'}>
|
||||
<RequiredProfileUserForm
|
||||
data={baseUser}
|
||||
|
||||
@@ -265,7 +265,7 @@ const DataPrivacySettings = () => {
|
||||
>
|
||||
{isDownloading
|
||||
? t('settings.data_privacy.downloading', 'Downloading...')
|
||||
: t('settings.data_privacy.download', 'Download all my data (JSON)')}
|
||||
: t('settings.data_privacy.download', 'Download all my data')}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -139,7 +139,7 @@ export default function SignupPage() {
|
||||
onSubmit={async () => advanceToStep(1)}
|
||||
/>
|
||||
) : step === 1 ? (
|
||||
<Col className={'w-full px-2 sm:px-6 py-4 mb-2'}>
|
||||
<Col className={'w-full px-2 sm:px-6 py-4 mb-2 '}>
|
||||
<OptionalProfileUserForm
|
||||
profile={profileForm}
|
||||
setProfile={setProfileState}
|
||||
|
||||
@@ -72,7 +72,7 @@ function SectionCard({icon, title, description, links}: SectionCardProps) {
|
||||
</div>
|
||||
|
||||
{/* Title & description */}
|
||||
<h2 className="text-base font-bold text-ink-900 mb-1.5">{title}</h2>
|
||||
<h2 className="font-bold text-ink-900 mb-1.5">{title}</h2>
|
||||
<p className="text-sm text-ink-500 leading-relaxed mb-5">{description}</p>
|
||||
|
||||
{/* Links */}
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-family: var(--font-main), serif;
|
||||
font-family: 'Cormorant Garamond', serif;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
@@ -103,6 +103,7 @@
|
||||
/* Ink - Text Colors */
|
||||
--color-ink-900: 30 26 20; /* Deep Warm Black (#1E1A14) */
|
||||
--color-ink-500: 140 128 112; /* Muted Warm Gray (#8C8070) */
|
||||
--color-ink-600: 120 108 92;
|
||||
|
||||
/* Green - Accents */
|
||||
--color-green-500: 107 143 113; /* Accent - Sage Green (#6B8F71) */
|
||||
@@ -138,7 +139,7 @@
|
||||
--color-ink-300: 160 160 160;
|
||||
--color-ink-400: 160 160 160;
|
||||
/*--color-ink-500: 80 80 80;*/
|
||||
--color-ink-600: 0 0 0;
|
||||
/*--color-ink-600: 0 0 0;*/
|
||||
--color-ink-700: 0 0 0;
|
||||
--color-ink-800: 0 0 0;
|
||||
/*--color-ink-900: 0 0 0;*/
|
||||
@@ -219,6 +220,7 @@
|
||||
--color-ink-950: 255 255 255; /* Purest highlight */
|
||||
--color-ink-900: 247 244 239; /* Main Body Text (Old Card color) */
|
||||
--color-ink-500: 176 160 140; /* Muted Text - shifted to warm tan */
|
||||
--color-ink-600: 156 140 120;
|
||||
|
||||
/* Green - Sage looks great on dark brown */
|
||||
--color-green-500: 125 160 131; /* Lightened Sage */
|
||||
@@ -229,7 +231,7 @@
|
||||
/*--color-ink-900: 255 255 255;*/
|
||||
--color-ink-800: 255 255 255;
|
||||
--color-ink-700: 255 255 255;
|
||||
--color-ink-600: 255 255 255;
|
||||
/*--color-ink-600: 255 255 255;*/
|
||||
/*--color-ink-500: 200 200 200;*/
|
||||
--color-ink-400: 100 100 100;
|
||||
--color-ink-300: 100 100 100;
|
||||
@@ -300,29 +302,29 @@
|
||||
--color-green-900: 20 83 45;
|
||||
--color-green-950: 5 46 22; /* darkest green */
|
||||
|
||||
--color-yellow-50: 255 251 235; /* lightest yellow */
|
||||
--color-yellow-100: 254 243 199;
|
||||
--color-yellow-200: 253 230 138;
|
||||
--color-yellow-300: 252 211 77;
|
||||
--color-yellow-400: 251 191 36;
|
||||
--color-yellow-950: 255 251 235; /* lightest yellow */
|
||||
--color-yellow-900: 254 243 199;
|
||||
--color-yellow-800: 253 230 138;
|
||||
--color-yellow-700: 252 211 77;
|
||||
--color-yellow-600: 251 191 36;
|
||||
--color-yellow-500: 245 158 11; /* standard yellow */
|
||||
--color-yellow-600: 217 119 6;
|
||||
--color-yellow-700: 180 83 9;
|
||||
--color-yellow-800: 146 64 14;
|
||||
--color-yellow-900: 113 63 18;
|
||||
--color-yellow-950: 66 50 3; /* darkest yellow */
|
||||
--color-yellow-400: 217 119 6;
|
||||
--color-yellow-300: 180 83 9;
|
||||
--color-yellow-200: 146 64 14;
|
||||
--color-yellow-100: 113 63 18;
|
||||
--color-yellow-50: 66 50 3; /* darkest yellow */
|
||||
|
||||
--color-red-50: 254 242 242; /* lightest red */
|
||||
--color-red-100: 254 226 226;
|
||||
--color-red-200: 254 202 202;
|
||||
--color-red-300: 252 165 165;
|
||||
--color-red-400: 248 113 113;
|
||||
--color-red-950: 254 242 242; /* lightest red */
|
||||
--color-red-900: 254 226 226;
|
||||
--color-red-800: 254 202 202;
|
||||
--color-red-700: 252 165 165;
|
||||
--color-red-600: 248 113 113;
|
||||
--color-red-500: 239 68 68; /* standard red */
|
||||
--color-red-600: 220 38 38;
|
||||
--color-red-700: 185 28 28;
|
||||
--color-red-800: 153 27 27;
|
||||
--color-red-900: 127 29 29;
|
||||
--color-red-950: 69 10 10; /* darkest red */
|
||||
--color-red-400: 220 38 38;
|
||||
--color-red-300: 185 28 28;
|
||||
--color-red-200: 153 27 27;
|
||||
--color-red-100: 127 29 29;
|
||||
--color-red-50: 69 10 10; /* darkest red */
|
||||
|
||||
touch-action: pan-y;
|
||||
}
|
||||
@@ -411,7 +413,7 @@ h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
/*font-family: 'Inter', sans-serif; !* Clean modern font *!*/
|
||||
font-family: 'Cormorant Garamond', Inter, sans-serif; /* Clean modern font */
|
||||
font-weight: 900; /* Semi-bold for clarity */
|
||||
/*color: #111827; !* Near-black text for readability *!*/
|
||||
line-height: 1.25;
|
||||
|
||||
@@ -27,6 +27,8 @@ module.exports = {
|
||||
'major-mono': ['var(--font-logo)', 'monospace'],
|
||||
figtree: ['icomoon', 'var(--font-main)', 'emoji', 'sans-serif'],
|
||||
'grenze-gotisch': ['var(--font-match-cards)', 'cursive'], // just for match card game
|
||||
cormorant: ['Cormorant Garamond', 'serif'],
|
||||
'dm-sans': ['DM Sans', 'sans-serif'],
|
||||
},
|
||||
),
|
||||
extend: {
|
||||
|
||||
Reference in New Issue
Block a user