Skip to content

Commit

Permalink
feat: implement profile page (#28)
Browse files Browse the repository at this point in the history
  • Loading branch information
smeijer committed Jul 5, 2021
1 parent 155464e commit b0b07fe
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 74 deletions.
19 changes: 13 additions & 6 deletions app/components/form-elements.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import * as React from 'react'
import clsx from 'clsx'

function Label(props: JSX.IntrinsicElements['label']) {
function Label({className, ...labelProps}: JSX.IntrinsicElements['label']) {
return (
<label
{...props}
className="inline-block dark:text-blueGray-500 text-gray-500 text-lg"
{...labelProps}
className={clsx(
'inline-block dark:text-blueGray-500 text-gray-500 text-lg',
className,
)}
/>
)
}

function Input(props: JSX.IntrinsicElements['input']) {
function Input({className, ...inputProps}: JSX.IntrinsicElements['input']) {
return (
<input
{...props}
className="placeholder-gray-500 dark:disabled:text-blueGray-500 focus-ring px-11 py-8 w-full text-black disabled:text-gray-400 dark:text-white text-lg font-medium bg-gray-200 dark:bg-gray-800 rounded-lg"
{...inputProps}
className={clsx(
'placeholder-gray-500 dark:disabled:text-blueGray-500 focus-ring px-11 py-8 w-full text-black disabled:text-gray-400 dark:text-white text-lg font-medium bg-gray-200 dark:bg-gray-800 rounded-lg',
className,
)}
/>
)
}
Expand Down
31 changes: 31 additions & 0 deletions app/components/icons/logout-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import * as React from 'react'

function LogoutIcon() {
return (
<svg width="24" height="24" fill="none" viewBox="0 0 24 24">
<path
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.5"
d="M15.75 8.75L19.25 12L15.75 15.25"
/>
<path
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.5"
d="M19 12H10.75"
/>
<path
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.5"
d="M15.25 4.75H6.75C5.64543 4.75 4.75 5.64543 4.75 6.75V17.25C4.75 18.3546 5.64543 19.25 6.75 19.25H15.25"
/>
</svg>
)
}

export {LogoutIcon}
185 changes: 140 additions & 45 deletions app/routes/me.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import * as React from 'react'
import type {ActionFunction, LoaderFunction} from 'remix'
import {Form, useRouteData, json, redirect} from 'remix'
import {Form, json, redirect, useRouteData} from 'remix'
import {Link} from 'react-router-dom'
import {getQrCodeDataURL} from '../utils/qrcode.server'
import {getDiscordAuthorizeURL, getDomainUrl} from '../utils/misc'
import {useRequestInfo, useUser, useUserInfo} from '../utils/providers'
import {getMagicLink, updateUser} from '../utils/prisma.server'
import {requireUser, rootStorage, signOutSession} from '../utils/session.server'
import {H2, H6} from '../components/typography'
import {Grid} from '../components/grid'
import {Input, Label} from '../components/form-elements'
import {Button} from '../components/button'
import {CheckIcon} from '../components/icons/check-icon'
import {LogoutIcon} from '../components/icons/logout-icon'
import {TEAM_MAP} from '../utils/onboarding'

type LoaderData = {message?: string; qrLoginCode: string}
export const loader: LoaderFunction = ({request}) => {
Expand Down Expand Up @@ -61,57 +69,144 @@ function YouScreen() {
const userInfo = useUserInfo()
const requestInfo = useRequestInfo()
const authorizeURL = getDiscordAuthorizeURL(requestInfo.origin)

return (
<div>
{data.message ? <div>{data.message}</div> : null}
<h2>User: {user.email}</h2>
<div>Team: {user.team}</div>
<div>
<Form method="post" action="/me">
<input type="hidden" name="actionId" value="logout" />
<button type="submit">Logout</button>
</Form>
</div>
<details>
<summary>Change account details</summary>

<Form method="post" action="/me">
<input type="hidden" name="actionId" value="change details" />
<div>
<label htmlFor="firstName">First Name</label>
<input
id="firstName"
<Form action="/me" method="post" className="mb-64 mt-12">
<Grid>
<div className="col-span-full mb-12 lg:mb-20">
<div className="flex flex-col-reverse items-start justify-between lg:flex-row lg:items-center">
<div>
<H2 className="mb-2">Here’s your profile.</H2>
<H2 variant="secondary" as="p">
Edit as you wish.
</H2>
</div>
<Link
to="/logout"
className="flex col-span-full items-center self-end mb-12 px-11 py-6 text-black dark:text-white border-2 border-gray-600 rounded-full space-x-4 lg:col-span-8 lg:col-start-3 lg:self-auto lg:mb-0"
>
<LogoutIcon />
<H6 as="span">logout</H6>
</Link>
</div>
</div>

<div className="col-span-full mb-24 lg:col-span-5 lg:mb-0">
<Label className="mb-4" htmlFor="firstName">
First name
</Label>

<Input
className="mb-8"
name="firstName"
id="firstName"
autoComplete="firstName"
defaultValue={user.firstName}
required
/>

<Label className="mb-4" htmlFor="email-address">
Email address
</Label>

<Input
name="email"
id="email-address"
autoComplete="email"
required
defaultValue={user.email}
className="mb-8"
readOnly
disabled
/>

<div className="flex flex-wrap items-baseline justify-between mb-4">
<Label htmlFor="discord-id">Discord</Label>
<p
id="discord-message"
className="dark:text-blueGray-500 text-gray-500 text-lg"
>
{user.discordId ? (
<a
className="text-black dark:text-white"
href={`https://discord.com/users/${user.discordId}`}
>
connected
</a>
) : (
<a
className="focus-ring text-black dark:text-white rounded-lg"
href={authorizeURL}
>
Connect to Discord
</a>
)}
</p>
</div>

<Input
id="discord-id"
name="discord"
value={userInfo.discord?.username ?? user.discordId ?? ''}
placeholder="n/a"
className="mb-12 lg:mb-20"
aria-describedby="discord-message"
readOnly
disabled
/>

<Button type="submit">Save changes</Button>
</div>
<button type="submit">Submit</button>
</Form>
</details>
<div>
{user.discordId ? (
<div>
Connected to discord account:{' '}
<a href={`https://discord.com/users/${user.discordId}`}>
{userInfo.discord?.username ?? user.discordId}
</a>
</div>
) : (
<>
<div>You wanna connect your account to discord?</div>
<a href={authorizeURL}>Connect my KCD account to Discord</a>
</>
)}
</div>
<div>
<details>
<summary>Login on another device:</summary>
<div>
Scan this QR code on the other device:
<img src={data.qrLoginCode} alt="Login QR Code" />

<div className="col-span-full lg:col-span-4 lg:col-start-8">
<Label className="mb-4" htmlFor="chosen-team">
Chosen team
</Label>

<input
className="sr-only"
type="radio"
name="team"
value={user.team}
checked
readOnly
/>

<div className="relative col-span-full mb-3 bg-gray-100 dark:bg-gray-800 rounded-lg focus-within:outline-none ring-2 focus-within:ring-2 ring-team-current ring-offset-4 ring-offset-team-current ring-offset-team-current lg:col-span-4 lg:mb-0">
<span className="absolute left-9 top-9 text-team-current">
<CheckIcon />
</span>

<div className="block pb-12 pt-20 px-12 text-center">
<img
className="block mb-16"
src={TEAM_MAP[user.team].image.src}
alt={TEAM_MAP[user.team].image.alt}
/>
<H6>{TEAM_MAP[user.team].label}</H6>
</div>
</div>
</div>
</details>
</div>
</Grid>
</Form>

<Grid>
<div className="col-span-full mb-12 lg:col-span-5 lg:col-start-8 lg:mb-0">
<H2 className="mb-2">Need to login somewhere else?</H2>
<H2 variant="secondary" as="p">
Scan this QR code on the other device.
</H2>
</div>

<div className="col-span-full lg:col-span-5 lg:col-start-1 lg:row-start-1">
<img
src={data.qrLoginCode}
alt="Login QR Code"
className="w-full rounded-lg object-contain"
/>
</div>
</Grid>
</div>
)
}
Expand Down
23 changes: 1 addition & 22 deletions app/routes/signup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ import {getErrorMessage, getNonNull, teams} from '../utils/misc'
import {tagKCDSiteSubscriber} from '../utils/convertkit.server'
import {useTeam} from '../utils/providers'
import {Grid} from '../components/grid'
import {images} from '../images'
import {H2, H6, Paragraph} from '../components/typography'
import {Input, InputError, Label} from '../components/form-elements'
import {Button} from '../components/button'
import {CheckIcon} from '../components/icons/check-icon'
import {TEAM_MAP} from '../utils/onboarding'

type LoaderData = {
email: string
Expand Down Expand Up @@ -159,27 +159,6 @@ export const loader: LoaderFunction = async ({request}) => {
})
}

const TEAM_MAP: Record<
Team,
{image: {src: string; alt: string}; label: string; focusClassName: string}
> = {
BLUE: {
image: images.alexBlue,
label: 'Blue Team',
focusClassName: 'ring-team-blue',
},
RED: {
image: images.alexRed,
label: 'Red Team',
focusClassName: 'ring-team-red',
},
YELLOW: {
image: images.alexYellow,
label: 'Yellow Team',
focusClassName: 'ring-team-yellow',
},
}

interface TeamOptionProps {
team: Team
error?: string | null
Expand Down
23 changes: 23 additions & 0 deletions app/utils/onboarding.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type {Team} from '../../types'
import {images} from '../images'

export const TEAM_MAP: Record<
Team,
{image: {src: string; alt: string}; label: string; focusClassName: string}
> = {
BLUE: {
image: images.alexBlue,
label: 'Blue Team',
focusClassName: 'ring-team-blue',
},
RED: {
image: images.alexRed,
label: 'Red Team',
focusClassName: 'ring-team-red',
},
YELLOW: {
image: images.alexYellow,
label: 'Yellow Team',
focusClassName: 'ring-team-yellow',
},
}
2 changes: 1 addition & 1 deletion cypress/e2e/discord.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe('discord', () => {
cy.findByRole('main').within(() => {
// eventually this should probably be improved but it's ok for now.
// using hard coded IDs like this is not awesome.
cy.findByText(/test_discord_id/i)
cy.findByDisplayValue(/test_discord_username/i)
})
})
})

0 comments on commit b0b07fe

Please sign in to comment.