Skip to content

Commit

Permalink
Hosting Panel: Update UI for cards and add clipboard button (#36864)
Browse files Browse the repository at this point in the history
  • Loading branch information
Olivia Wolski authored Oct 22, 2019
1 parent d8e91bd commit 11dcffe
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 55 deletions.
7 changes: 4 additions & 3 deletions client/my-sites/hosting/phpmyadmin-card/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ const PhpMyAdminCard = ( { translate, siteId, pmaLink, loading } ) => {
}, [ pmaLink, loading ] );

return (
<Card>
<div className="phpmyadmin-card__icon-col">
<Card className="phpmyadmin-card">
<div className="phpmyadmin-card__icon">
<MaterialIcon icon="dns" size={ 32 } />
</div>
<div>
Expand All @@ -59,7 +59,8 @@ const PhpMyAdminCard = ( { translate, siteId, pmaLink, loading } ) => {
) }
</p>
<Button onClick={ () => requestPmaLink( siteId ) } busy={ loading }>
{ translate( 'Access PHPMyAdmin' ) }
<span>{ translate( 'Open PHPMyAdmin' ) }</span>
<MaterialIcon icon="launch" size={ 16 } />
</Button>
</div>
</Card>
Expand Down
117 changes: 81 additions & 36 deletions client/my-sites/hosting/sftp-card/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import React, { useState } from 'react';
import { connect } from 'react-redux';
import { localize } from 'i18n-calypso';
import { get, map } from 'lodash';
import classNames from 'classnames';

/**
* Internal dependencies
Expand All @@ -16,10 +15,17 @@ import Card from 'components/card';
import CardHeading from 'components/card-heading';
import MaterialIcon from 'components/material-icon';
import Button from 'components/button';
import ClipboardButton from 'components/forms/clipboard-button';
import Spinner from 'components/spinner';
import { getSelectedSiteId } from 'state/ui/selectors';

// @TODO derive API request details from props when API is merged & remove component state
// @TODO derive API request details from props when API is merged & remove component state for dummy data
const SFTPCard = ( { translate, siteId } ) => {
// State for clipboard copy button for both username and password data
const [ isCopied, setIsCopied ] = useState( false );
const usernameIsCopied = isCopied === 'username';
const passwordIsCopied = isCopied === 'password';

// Begin dummy API data/methods
const [ dummyApiRequest, setDummyApiRequest ] = useState( {
status: 'error',
Expand All @@ -31,14 +37,17 @@ const SFTPCard = ( { translate, siteId } ) => {
const username = get( dummyApiRequest, 'data.username', null );
const password = get( dummyApiRequest, 'data.password', null );
const loading = dummyApiRequest.status === 'pending';
const noSftpUser = get( dummyApiRequest, 'error.status', 0 ) === 404;
const noSftpUser = dummyApiRequest.error.status === 404;

const createAtomicSFTPUser = () => {
setDummyApiRequest( {
status: 'success',
data: {
username: 'test_user_testsite.wordpress.com_1234',
},
error: {
status: 0,
},
} );
};

Expand All @@ -49,71 +58,107 @@ const SFTPCard = ( { translate, siteId } ) => {
username: 'test_user_testsite.wordpress.com_1234',
password: 'a.reset.p.a.s.s.word',
},
error: {
status: 0,
},
} );
};
// End of dummy API data/methods

const sftpData = {
[ translate( 'URL' ) ]: 'sftp1.wordpress.com',
[ translate( 'Port' ) ]: 22,
[ translate( 'Username' ) ]: username,
};

return (
<Card>
<div className="sftp-card__icon-col">
<Card className="sftp-card">
<div className="sftp-card__icon">
<MaterialIcon icon="cloud" size={ 32 } />
</div>
<div>
<div className="sftp-card__body">
<CardHeading>{ translate( 'SFTP Information' ) }</CardHeading>
<p>{ translate( "Access and edit your website's files directly using an FTP client." ) }</p>
{ password && (
<div className="sftp-card__callout-box">
<p>{ translate( 'Your new password for your sftp user is:' ) }</p>
{ noSftpUser ? (
<>
<p>
<code>{ password }</code>
</p>
<strong>
{ translate(
'Make sure to save this password in a safe place! You will need to reset this password if you lose it.'
"Enable SFTP access to generate a username and password so you can access your website's files."
) }
</strong>
</div>
) }
{ noSftpUser && (
<Button onClick={ () => createAtomicSFTPUser( siteId ) } primary>
Create SFTP User
</Button>
</p>
<Button onClick={ () => createAtomicSFTPUser( siteId ) } primary>
{ translate( 'Enable SFTP' ) }
</Button>
</>
) : (
<p>
{ translate( "Access and edit your website's files directly using an FTP client." ) }
</p>
) }
<table
className={ classNames( 'sftp-card__info-table', {
[ 'is-placeholder' ]: loading || noSftpUser,
} ) }
>
</div>
{ username && (
<table className="sftp-card__info-table">
<tbody>
{ map( sftpData, ( data, title ) => (
<tr key={ title }>
<th>{ title }:</th>
<td>
<span>{ ! loading && ! noSftpUser && data }</span>
<span>{ data }</span>
</td>
</tr>
) ) }
<tr>
<th>{ translate( 'Password' ) }:</th>
<th>{ translate( 'Username' ) }:</th>
<td>
<Button
onClick={ () => resetAtomicSFTPUserPassword( siteId ) }
disabled={ loading || noSftpUser }
busy={ loading && ! noSftpUser }
<p className="sftp-card__hidden-overflow">{ username }</p>
<ClipboardButton
text={ username }
onCopy={ () => setIsCopied( 'username' ) }
compact
>
{ translate( 'Reset Password' ) }
</Button>
{ usernameIsCopied
? translate( 'Copied!' )
: translate( 'Copy', { context: 'verb' } ) }
</ClipboardButton>
</td>
</tr>
<tr>
<th>{ translate( 'Password' ) }:</th>
<td>
{ password ? (
<>
<p className="sftp-card__hidden-overflow">{ password }</p>
<ClipboardButton
text={ password }
onCopy={ () => setIsCopied( 'password' ) }
compact
>
{ passwordIsCopied
? translate( 'Copied!' )
: translate( 'Copy', { context: 'verb' } ) }
</ClipboardButton>
<p className="sftp-card__password-warning">
{ translate(
"Be sure to save your password somewhere safe. You won't be able to view it again without resetting."
) }
</p>
</>
) : (
<>
<p>{ translate( 'You must reset your password to view it.' ) }</p>
<Button
onClick={ () => resetAtomicSFTPUserPassword( siteId ) }
disabled={ loading }
compact
>
{ translate( 'Reset Password' ) }
</Button>
</>
) }
</td>
</tr>
</tbody>
</table>
</div>
) }
{ loading && ! noSftpUser && <Spinner /> }
</Card>
);
};
Expand Down
50 changes: 38 additions & 12 deletions client/my-sites/hosting/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -23,36 +23,62 @@
&:first-child {
width: calc( 60% - 10px );
}
}
}
}

.spinner {
width: 100%;
}
}

.sftp-card {
flex-wrap: wrap;

.sftp-card__body {
width: calc( 100% - 24px - 32px );
}
}
}

.phpmyadmin-card__icon-col, .sftp-card__icon-col {
.phpmyadmin-card__icon, .sftp-card__icon {
margin-right: 24px;
height: 100%;
}

.sftp-card__info-table {
border: 1px solid var( --color-border-subtle );
margin-top: 15px;
table-layout: fixed;

th {
text-align: right;
width: 20%;
background-color: var( --color-surface-backdrop );

@include breakpoint( '<960px' ) {
width: 100px;
}
}

th, td {
padding: 10px;
}

&.is-placeholder span {
padding: 0 35%;
background-color: var( --color-surface-backdrop );
p {
margin-bottom: 0.5em;
}
}

.sftp-card__callout-box {
border: 1px solid var( --color-accent );
padding: 10px;
margin: 10px 0;
.sftp-card__hidden-overflow {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

.sftp-card__password-warning {
color: var( --color-text-subtle );
margin-top: 1.5em;
}
}

.phpmyadmin-card button svg {
vertical-align: text-bottom;
margin-left: 10px;
}
6 changes: 3 additions & 3 deletions client/state/data-getters/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ export const requestAtomicSFTPDetails = siteId =>
http(
{
method: 'GET',
path: `/sites/${ siteId }/hosting/ssh`,
path: `/sites/${ siteId }/hosting/ssh-user`,
apiNamespace: 'wpcom/v2',
},
{}
Expand All @@ -235,7 +235,7 @@ export const resetAtomicSFTPUserPassword = siteId =>
http(
{
method: 'POST',
path: `/sites/${ siteId }/hosting/ssh/reset-password`,
path: `/sites/${ siteId }/hosting/ssh-user/reset-password`,
apiNamespace: 'wpcom/v2',
body: {},
},
Expand All @@ -255,7 +255,7 @@ export const createAtomicSFTPUser = siteId =>
http(
{
method: 'POST',
path: `/sites/${ siteId }/hosting/ssh`,
path: `/sites/${ siteId }/hosting/ssh-user`,
apiNamespace: 'wpcom/v2',
body: {},
},
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 11dcffe

Please sign in to comment.