Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
@import '@libs/ui/styles/includes';

.linkItemWrap {
display: flex;
align-items: center;
justify-content: flex-start;
margin-bottom: $sp-2;

> svg {
width: 24px;
height: 24px;

path:not([stroke-linecap]) {
fill: $black-100;
}

}
}

.linkItem {
border-radius: 4px;
padding: $sp-2 $sp-4;
border: 1px solid $black-40;
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
margin-left: $sp-4;

button {
padding-right: 0;

&.button {
margin-left: $sp-2;
padding-left: $sp-2;
padding-right: $sp-2;
}

svg {
width: 24px;
height: 24px;
}
}
}

.linkLabelWrap {
display: flex;
flex-direction: column;
align-items: flex-start;

small {
font-size: 11px;
line-height: 11px;
font-weight: $font-weight-medium;
color: $turq-160;
margin-bottom: $sp-1;
}

p {
word-break: break-all;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { FC, useState } from 'react'

import { Button, IconOutline } from '~/libs/ui'
import { UserTrait } from '~/libs/core'

import { renderLinkIcon } from '../../MemberLinks'
import { LinkForm, UserLink } from '../LinkForm'

import styles from './LinkEntry.module.scss'

interface LinkEntryProps {
index: number
link: UserTrait
onSave: (link: UserTrait, index: number) => Promise<UserTrait | undefined>
onRemove: (link: UserTrait) => void
}

const LinkEntry: FC<LinkEntryProps> = props => {
const [isEditing, setIsEditing] = useState(!(props.link.name || props.link.url))
const isNew = !props.link.name && !props.link.url

function handleReomveClick(): void {
props.onRemove(props.link)
}

async function handleOnSave(link: UserLink): Promise<void> {
if (!await props.onSave(link, props.index)) {
return
}

setIsEditing(false)
}

function toggleIsEditing(): void {
setIsEditing(editMode => !editMode)
}

return !isEditing ? (
<div className={styles.linkItemWrap} key={`member-link-${props.link.name}`}>
{renderLinkIcon(props.link.name)}
<div className={styles.linkItem}>
<div className={styles.linkLabelWrap}>
<small>{props.link.name}</small>
<p>{props.link.url}</p>
</div>
<div>
<Button
className={styles.button}
size='lg'
icon={IconOutline.PencilIcon}
onClick={toggleIsEditing}
/>
<Button
className={styles.button}
size='lg'
icon={IconOutline.TrashIcon}
onClick={handleReomveClick}
/>
</div>
</div>
</div>
) : (
<LinkForm
link={props.link as UserLink}
onSave={handleOnSave}
onDiscard={toggleIsEditing}
isNew={isNew}
/>
)
}

export default LinkEntry
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as LinkEntry } from './LinkEntry'
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
@import '@libs/ui/styles/includes';

.formError {
color: $red-100;
}

.formWrap {
display: flex;
flex-direction: column;
margin-top: $sp-2;

@include ltelg {
:global(.input-wrapper) {
margin-bottom: $sp-2;
}
}
}

.form {
display: flex;
align-items: center;
justify-content: space-between;

&>div:nth-child(1) {
margin-right: $sp-2;
min-width: 150px;
}

&>div:nth-child(2) {
flex: 1;
margin-right: $sp-2;
}
&>button {
margin-bottom: $sp-45;
&.button {
margin-left: $sp-2;
padding-left: $sp-2;
padding-right: $sp-2;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { trim } from 'lodash'
import { FC, useEffect, useState } from 'react'
import classNames from 'classnames'

import { Button, IconOutline, InputSelect, InputText } from '~/libs/ui'

import { linkTypes } from '../link-types.config'
import { isValidURL } from '../../../../lib'

import styles from './LinkForm.module.scss'

export interface UserLink {
name: string
url: string
}

interface LinkFormProps {
isNew: boolean
link?: UserLink
onSave: (link: UserLink) => void
onDiscard: () => void
}

const LinkForm: FC<LinkFormProps> = props => {
const [formErrors, setFormErrors] = useState<{ [key: string]: string }>({})
const [selectedLinkType, setSelectedLinkType] = useState<string | undefined>()
const [selectedLinkURL, setSelectedLinkURL] = useState<string | undefined>()

function handleSelectedLinkTypeChange(event: React.ChangeEvent<HTMLInputElement>): void {
setSelectedLinkType(event.target.value)
}

function handleURLChange(event: React.ChangeEvent<HTMLInputElement>): void {
setSelectedLinkURL(event.target.value)
}

function handleFormAction(): void {
setFormErrors({})

if (!selectedLinkType) {
setFormErrors({ selectedLinkType: 'Please select a link type' })
return
}

if (!trim(selectedLinkURL)) {
setFormErrors({ url: 'Please enter a URL' })
return
}

if (!isValidURL(selectedLinkURL as string)) {
setFormErrors({ url: 'Invalid URL' })
return
}

props.onSave({
name: selectedLinkType,
url: trim(selectedLinkURL) || '',
})
}

function handleDiscardClick(): void {
setFormErrors({})
props.onDiscard()

if (!props.link) {
return
}

if (selectedLinkType !== props.link.name) {
setSelectedLinkType(props.link.name)
}

if (selectedLinkURL !== props.link.url) {
setSelectedLinkURL(props.link.url)
}
}

useEffect(() => {
if (!props.link) {
return
}

if (selectedLinkType !== props.link.name) {
setSelectedLinkType(props.link.name)
}

if (selectedLinkURL !== props.link.url) {
setSelectedLinkURL(props.link.url)
}

}, [props.link?.name, props.link?.url])

return (
<form className={classNames(styles.formWrap)}>
<div className={styles.form}>
<InputSelect
options={linkTypes}
value={selectedLinkType}
onChange={handleSelectedLinkTypeChange}
name='linkType'
label='Type'
error={formErrors.selectedLinkType}
placeholder='Select a link type'
dirty
/>

<InputText
name='url'
label='URL'
error={formErrors.url}
placeholder='Enter a URL'
dirty
tabIndex={-1}
type='text'
onChange={handleURLChange}
value={selectedLinkURL}
/>
<Button
className={styles.button}
size='lg'
icon={IconOutline.CheckIcon}
onClick={handleFormAction}
/>
{!props.isNew && (
<Button
className={styles.button}
size='lg'
icon={IconOutline.XIcon}
onClick={handleDiscardClick}
/>
)}
</div>
</form>
)
}

export default LinkForm
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as LinkForm, type UserLink } from './LinkForm'
Loading