Skip to content

Commit e8755ac

Browse files
committed
Kind of get account stuff working; IDE is throwing a fit right now
1 parent 7cea5e6 commit e8755ac

File tree

7 files changed

+176
-43
lines changed

7 files changed

+176
-43
lines changed

resources/scripts/assets/css/GlobalStylesheet.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,22 @@ export default createGlobalStyle`
1414
p {
1515
${tw`text-neutral-200 leading-snug font-sans`};
1616
}
17+
18+
form {
19+
${tw`m-0`};
20+
}
21+
22+
textarea, select, input, button, button:focus, button:focus-visible {
23+
${tw`outline-none`};
24+
}
25+
26+
input[type=number]::-webkit-outer-spin-button,
27+
input[type=number]::-webkit-inner-spin-button {
28+
-webkit-appearance: none !important;
29+
margin: 0;
30+
}
31+
32+
input[type=number] {
33+
-moz-appearance: textfield !important;
34+
}
1735
`;

resources/scripts/components/dashboard/forms/ConfigureTwoFactorForm.tsx

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { useStoreState } from 'easy-peasy';
33
import { ApplicationStore } from '@/state';
44
import SetupTwoFactorModal from '@/components/dashboard/forms/SetupTwoFactorModal';
55
import DisableTwoFactorModal from '@/components/dashboard/forms/DisableTwoFactorModal';
6+
import tw from 'twin.macro';
7+
import Button from '@/components/elements/Button';
68

79
export default () => {
810
const user = useStoreState((state: ApplicationStore) => state.user.data!);
@@ -17,38 +19,40 @@ export default () => {
1719
onDismissed={() => setVisible(false)}
1820
/>
1921
}
20-
<p className={'text-sm'}>
22+
<p css={tw`text-sm`}>
2123
Two-factor authentication is currently enabled on your account.
2224
</p>
23-
<div className={'mt-6'}>
24-
<button
25+
<div css={tw`mt-6`}>
26+
<Button
27+
color={'red'}
28+
isSecondary
2529
onClick={() => setVisible(true)}
26-
className={'btn btn-red btn-secondary btn-sm'}
2730
>
2831
Disable
29-
</button>
32+
</Button>
3033
</div>
3134
</div>
3235
:
3336
<div>
3437
{visible &&
3538
<SetupTwoFactorModal
36-
appear={true}
39+
appear
3740
visible={visible}
3841
onDismissed={() => setVisible(false)}
3942
/>
4043
}
41-
<p className={'text-sm'}>
44+
<p css={tw`text-sm`}>
4245
You do not currently have two-factor authentication enabled on your account. Click
4346
the button below to begin configuring it.
4447
</p>
45-
<div className={'mt-6'}>
46-
<button
48+
<div css={tw`mt-6`}>
49+
<Button
50+
color={'green'}
51+
isSecondary
4752
onClick={() => setVisible(true)}
48-
className={'btn btn-green btn-secondary btn-sm'}
4953
>
5054
Begin Setup
51-
</button>
55+
</Button>
5256
</div>
5357
</div>
5458
;

resources/scripts/components/dashboard/forms/UpdateEmailAddressForm.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
66
import Field from '@/components/elements/Field';
77
import { httpErrorToHuman } from '@/api/http';
88
import { ApplicationStore } from '@/state';
9+
import tw from 'twin.macro';
10+
import Button from '@/components/elements/Button';
911

1012
interface Values {
1113
email: string;
@@ -54,25 +56,25 @@ export default () => {
5456
({ isSubmitting, isValid }) => (
5557
<React.Fragment>
5658
<SpinnerOverlay size={'large'} visible={isSubmitting}/>
57-
<Form className={'m-0'}>
59+
<Form css={tw`m-0`}>
5860
<Field
5961
id={'current_email'}
6062
type={'email'}
6163
name={'email'}
6264
label={'Email'}
6365
/>
64-
<div className={'mt-6'}>
66+
<div css={tw`mt-6`}>
6567
<Field
6668
id={'confirm_password'}
6769
type={'password'}
6870
name={'password'}
6971
label={'Confirm Password'}
7072
/>
7173
</div>
72-
<div className={'mt-6'}>
73-
<button className={'btn btn-sm btn-primary'} disabled={isSubmitting || !isValid}>
74+
<div css={tw`mt-6`}>
75+
<Button size={'small'} disabled={isSubmitting || !isValid}>
7476
Update Email
75-
</button>
77+
</Button>
7678
</div>
7779
</Form>
7880
</React.Fragment>

resources/scripts/components/dashboard/forms/UpdatePasswordForm.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
77
import updateAccountPassword from '@/api/account/updateAccountPassword';
88
import { httpErrorToHuman } from '@/api/http';
99
import { ApplicationStore } from '@/state';
10+
import tw from 'twin.macro';
11+
import Button from '@/components/elements/Button';
1012

1113
interface Values {
1214
current: string;
@@ -34,7 +36,6 @@ export default () => {
3436
clearFlashes('account:password');
3537
updateAccountPassword({ ...values })
3638
.then(() => {
37-
// @ts-ignore
3839
window.location = '/auth/login';
3940
})
4041
.catch(error => addFlash({
@@ -57,14 +58,14 @@ export default () => {
5758
({ isSubmitting, isValid }) => (
5859
<React.Fragment>
5960
<SpinnerOverlay size={'large'} visible={isSubmitting}/>
60-
<Form className={'m-0'}>
61+
<Form css={tw`m-0`}>
6162
<Field
6263
id={'current_password'}
6364
type={'password'}
6465
name={'current'}
6566
label={'Current Password'}
6667
/>
67-
<div className={'mt-6'}>
68+
<div css={tw`mt-6`}>
6869
<Field
6970
id={'new_password'}
7071
type={'password'}
@@ -73,18 +74,18 @@ export default () => {
7374
description={'Your new password should be at least 8 characters in length and unique to this website.'}
7475
/>
7576
</div>
76-
<div className={'mt-6'}>
77+
<div css={tw`mt-6`}>
7778
<Field
7879
id={'confirm_password'}
7980
type={'password'}
8081
name={'confirmPassword'}
8182
label={'Confirm New Password'}
8283
/>
8384
</div>
84-
<div className={'mt-6'}>
85-
<button className={'btn btn-primary btn-sm'} disabled={isSubmitting || !isValid}>
85+
<div css={tw`mt-6`}>
86+
<Button size={'small'} disabled={isSubmitting || !isValid}>
8687
Update Password
87-
</button>
88+
</Button>
8889
</div>
8990
</Form>
9091
</React.Fragment>
Lines changed: 87 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,97 @@
11
import React from 'react';
2-
import classNames from 'classnames';
2+
import styled, { css } from 'styled-components/macro';
3+
import tw from 'twin.macro';
34

4-
type Props = { isLoading?: boolean } & React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>;
5+
interface Props {
6+
isLoading?: boolean;
7+
size?: 'xsmall' | 'small' | 'large' | 'xlarge';
8+
color?: 'green' | 'red' | 'primary' | 'grey';
9+
isSecondary?: boolean;
10+
disabled?: boolean;
11+
}
512

6-
export default ({ isLoading, children, className, ...props }: Props) => (
7-
<button
8-
{...props}
9-
className={classNames('btn btn-sm relative', className)}
10-
>
13+
const StyledButton = styled.button<Props>`
14+
${tw`rounded p-2 uppercase tracking-wide text-sm transition-all duration-150`};
15+
16+
${props => props.isSecondary && css<Props>`
17+
${tw`border border-neutral-600 bg-transparent text-neutral-200`};
18+
19+
&:hover:not(:disabled) {
20+
${tw`border-neutral-500 text-neutral-100`};
21+
${props => props.color === 'red' && tw`bg-red-500 border-red-600 text-red-50`};
22+
${props => props.color === 'green' && tw`bg-green-500 border-green-600 text-green-50`};
23+
}
24+
`};
25+
26+
${props => (!props.color || props.color === 'primary') && css<Props>`
27+
${props => !props.isSecondary && tw`bg-primary-500 border-primary-600 border text-primary-50`};
28+
29+
&:hover:not(:disabled) {
30+
${tw`bg-primary-600 border-primary-700`};
31+
}
32+
`};
33+
34+
${props => props.color === 'grey' && css`
35+
${tw`border border-neutral-600 bg-neutral-500 text-neutral-50`};
36+
37+
&:hover:not(:disabled) {
38+
${tw`bg-neutral-600 border-neutral-700`};
39+
}
40+
`};
41+
42+
${props => props.color === 'green' && css<Props>`
43+
${tw`border border-green-600 bg-green-500 text-green-50`};
44+
45+
&:hover:not(:disabled) {
46+
${tw`bg-green-600 border-green-700`};
47+
}
48+
49+
${props => props.isSecondary && css`
50+
&:active:not(:disabled) {
51+
${tw`bg-green-600 border-green-700`};
52+
}
53+
`};
54+
`};
55+
56+
${props => props.color === 'red' && css<Props>`
57+
${tw`border border-red-600 bg-red-500 text-red-50`};
58+
59+
&:hover:not(:disabled) {
60+
${tw`bg-red-600 border-red-700`};
61+
}
62+
63+
${props => props.isSecondary && css`
64+
&:active:not(:disabled) {
65+
${tw`bg-red-600 border-red-700`};
66+
}
67+
`};
68+
`};
69+
70+
${props => props.size === 'xsmall' && tw`p-2 text-xs`};
71+
${props => (!props.size || props.size === 'small') && tw`p-3`};
72+
${props => props.size === 'large' && tw`p-4 text-sm`};
73+
${props => props.size === 'xlarge' && tw`p-4 w-full`};
74+
75+
&:disabled { opacity: 0.55; cursor: default }
76+
77+
${props => props.disabled && css`opacity: 0.55; cursor: default`};
78+
79+
`;
80+
81+
type ComponentProps = Props &
82+
Omit<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, keyof Props>;
83+
84+
const Button: React.FC<ComponentProps> = ({ children, isLoading, ...props }) => (
85+
<StyledButton {...props}>
1186
{isLoading &&
12-
<div className={'w-full flex absolute justify-center'} style={{ marginLeft: '-0.75rem' }}>
87+
<div css={tw`w-full flex absolute justify-center`} style={{ marginLeft: '-0.75rem' }}>
1388
<div className={'spinner-circle spinner-white spinner-sm'}/>
1489
</div>
1590
}
16-
<span className={isLoading ? 'text-transparent' : undefined}>
91+
<span css={isLoading && tw`text-transparent`}>
1792
{children}
1893
</span>
19-
</button>
94+
</StyledButton>
2095
);
96+
97+
export default Button;

resources/scripts/components/elements/ContentBox.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import * as React from 'react';
2-
import classNames from 'classnames';
1+
import React from 'react';
32
import FlashMessageRender from '@/components/FlashMessageRender';
43
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
4+
import tw from 'twin.macro';
55

66
type Props = Readonly<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> & {
77
title?: string;
@@ -12,16 +12,19 @@ type Props = Readonly<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElemen
1212

1313
const ContentBox = ({ title, borderColor, showFlashes, showLoadingOverlay, children, ...props }: Props) => (
1414
<div {...props}>
15-
{title && <h2 className={'text-neutral-300 mb-4 px-4'}>{title}</h2>}
15+
{title && <h2 css={tw`text-neutral-300 mb-4 px-4 text-2xl`}>{title}</h2>}
1616
{showFlashes &&
1717
<FlashMessageRender
1818
byKey={typeof showFlashes === 'string' ? showFlashes : undefined}
19-
className={'mb-4'}
19+
css={tw`mb-4`}
2020
/>
2121
}
22-
<div className={classNames('bg-neutral-700 p-4 rounded shadow-lg relative', borderColor, {
23-
'border-t-4': !!borderColor,
24-
})}>
22+
<div
23+
css={[
24+
tw`bg-neutral-700 p-4 rounded shadow-lg relative`,
25+
!!borderColor && tw`border-t-4`,
26+
]}
27+
>
2528
<SpinnerOverlay visible={showLoadingOverlay || false}/>
2629
{children}
2730
</div>

resources/scripts/routers/DashboardRouter.tsx

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,45 @@ import DashboardContainer from '@/components/dashboard/DashboardContainer';
66
import TransitionRouter from '@/TransitionRouter';
77
import AccountApiContainer from '@/components/dashboard/AccountApiContainer';
88
import NotFound from '@/components/screens/NotFound';
9+
import styled from 'styled-components/macro';
10+
import tw from 'twin.macro';
11+
import config from '@/../../tailwind.config.js';
12+
13+
const SubNavigation = styled.div`
14+
${tw`w-full bg-neutral-700 shadow`};
15+
16+
& > div {
17+
${tw`flex items-center text-sm mx-auto px-2`};
18+
max-width: 1200px;
19+
20+
& > a, & > div {
21+
${tw`inline-block py-3 px-4 text-neutral-300 no-underline transition-all duration-150`};
22+
23+
&:not(:first-of-type) {
24+
${tw`ml-2`};
25+
}
26+
27+
&:active, &:hover {
28+
${tw`text-neutral-100`};
29+
}
30+
31+
&:active, &:hover, &.active {
32+
box-shadow: inset 0 -2px ${config.theme.colors.cyan['500']};
33+
}
34+
}
35+
}
36+
`;
937

1038
export default ({ location }: RouteComponentProps) => (
1139
<>
1240
<NavigationBar/>
1341
{location.pathname.startsWith('/account') &&
14-
<div id={'sub-navigation'}>
15-
<div className={'items'}>
42+
<SubNavigation>
43+
<div>
1644
<NavLink to={'/account'} exact>Settings</NavLink>
1745
<NavLink to={'/account/api'}>API Credentials</NavLink>
1846
</div>
19-
</div>
47+
</SubNavigation>
2048
}
2149
<TransitionRouter>
2250
<Switch location={location}>

0 commit comments

Comments
 (0)