Skip to content

Commit

Permalink
Merge 66c3b65 into c8e4440
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesrkiger committed Jun 12, 2024
2 parents c8e4440 + 66c3b65 commit 4445e40
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 62 deletions.
2 changes: 2 additions & 0 deletions jsapp/js/components/anonymousSubmission.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import styles from './anonymousSubmission.module.scss';

interface AnonymousSubmissionProps {
checked: boolean;
disabled: boolean;
onChange: (isChecked: boolean) => void;
}

Expand All @@ -15,6 +16,7 @@ export default function AnonymousSubmission(props: AnonymousSubmissionProps) {
<div className={styles.root}>
<ToggleSwitch
checked={props.checked}
disabled={props.disabled}
onChange={props.onChange}
label={t(
'Allow submissions to this form without a username and password'
Expand Down
8 changes: 6 additions & 2 deletions jsapp/js/components/common/toggleSwitch.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@use '~kobo-common/src/styles/colors';
@use "scss/_variables";
@use 'scss/_variables';

.toggle-switch {
position: relative;
Expand All @@ -10,6 +10,10 @@
cursor: pointer;
}

.toggle-switch__wrapper.toggle-switch__wrapper--is-disabled {
cursor: default;
}

input {
visibility: hidden;
position: absolute;
Expand All @@ -31,7 +35,7 @@

&::before {
position: absolute;
content: "";
content: '';
height: 16px;
width: 16px;
left: 2px;
Expand Down
15 changes: 9 additions & 6 deletions jsapp/js/components/common/toggleSwitch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,14 @@ class ToggleSwitch extends React.Component<ToggleSwitchProps, {}> {
}

render() {
const modifiers = [];
if (this.props.disabled) {
modifiers.push('is-disabled');
}

return (
<bem.ToggleSwitch>
<bem.ToggleSwitch__wrapper>
<bem.ToggleSwitch__wrapper m={this.props.disabled ? 'is-disabled' : ''}>
<bem.ToggleSwitch__input
type='checkbox'
name={this.props.name}
Expand All @@ -33,15 +38,13 @@ class ToggleSwitch extends React.Component<ToggleSwitchProps, {}> {
disabled={this.props.disabled}
data-cy={this.props['data-cy']}
/>
<bem.ToggleSwitch__slider
disabled={this.props.disabled}
/>
<bem.ToggleSwitch__slider disabled={this.props.disabled} />

{this.props.label &&
{this.props.label && (
<bem.ToggleSwitch__label htmlFor={this.props.id}>
{this.props.label}
</bem.ToggleSwitch__label>
}
)}
</bem.ToggleSwitch__wrapper>
</bem.ToggleSwitch>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ interface PublicShareSettingsProps {
publicPerms: PermissionResponse[];
assetUid: string;
deploymentActive: boolean;
disableControls: boolean;
}

class PublicShareSettings extends React.Component<PublicShareSettingsProps> {
Expand Down Expand Up @@ -87,6 +88,7 @@ class PublicShareSettings extends React.Component<PublicShareSettingsProps> {
>
<AnonymousSubmission
checked={anonCanAddData}
disabled={this.props.disableControls}
onChange={this.togglePerms.bind(this, 'add_submissions')}
/>
</NewFeatureDialog>
Expand All @@ -99,6 +101,7 @@ class PublicShareSettings extends React.Component<PublicShareSettingsProps> {
<bem.FormModal__item>
<Checkbox
checked={anonCanView}
disabled={this.props.disableControls}
onChange={this.togglePerms.bind(this, 'view_asset')}
label={t('Anyone can view this form')}
/>
Expand All @@ -108,6 +111,7 @@ class PublicShareSettings extends React.Component<PublicShareSettingsProps> {
<bem.FormModal__item>
<Checkbox
checked={anonCanViewData}
disabled={this.props.disableControls}
onChange={this.togglePerms.bind(this, 'view_submissions')}
label={t('Anyone can view submissions made to this form')}
/>
Expand Down
42 changes: 26 additions & 16 deletions jsapp/js/components/permissions/sharingForm.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import bem from 'js/bem';
import LoadingSpinner from 'js/components/common/loadingSpinner';
import Button from 'js/components/common/button';
import InlineMessage from 'js/components/common/inlineMessage';
import {userCan} from 'js/components/permissions/utils';
import {replaceBracketsWithLink} from 'js/textUtils';
import {ANON_USERNAME, ANON_USERNAME_URL} from 'js/users/utils';
import {ASSET_TYPES} from 'js/constants';
Expand Down Expand Up @@ -173,7 +174,7 @@ export default class SharingForm extends React.Component<
}

/** Display pending owner if not already included in list of user permissions */
renderPendingOwner() {
renderPendingOwner(displayControls: boolean) {
if (
this.state.asset?.project_ownership?.status ===
TransferStatuses.Pending &&
Expand All @@ -185,6 +186,7 @@ export default class SharingForm extends React.Component<
return (
<UserPermissionRow
assetUid={this.props.assetUid}
displayControls={displayControls}
nonOwnerPerms={this.state.nonOwnerPerms}
assignablePerms={this.state.assignablePerms}
permissions={[]}
Expand All @@ -204,6 +206,7 @@ export default class SharingForm extends React.Component<
}

const assetType = this.state.asset.asset_type;
const isManagingPossible = userCan('manage_asset', this.state.asset);

const isRequireAuthWarningVisible =
'extra_details' in sessionStore.currentAccount &&
Expand Down Expand Up @@ -249,6 +252,7 @@ export default class SharingForm extends React.Component<
<UserPermissionRow
key={`perm.${this.props.assetUid}.${perm.user.name}`}
assetUid={this.props.assetUid}
displayControls={isManagingPossible}
nonOwnerPerms={this.state.nonOwnerPerms}
assignablePerms={this.state.assignablePerms}
permissions={perm.permissions}
Expand All @@ -258,11 +262,12 @@ export default class SharingForm extends React.Component<
/>
);
})}
{this.renderPendingOwner()}
{this.renderPendingOwner(isManagingPossible)}

{!this.state.isAddUserEditorVisible && (
<Button
color='blue'
isDisabled={!isManagingPossible}
type='full'
size='l'
onClick={this.toggleAddUserEditor.bind(this)}
Expand Down Expand Up @@ -298,26 +303,31 @@ export default class SharingForm extends React.Component<
publicPerms={this.state.publicPerms}
assetUid={this.props.assetUid}
deploymentActive={this.state.asset.deployment__active}
disableControls={!isManagingPossible}
/>
</bem.FormModal__item>
</>
)}

{/* copying permissions from other assets */}
{assetType !== ASSET_TYPES.collection.id &&
this.state.allAssetsCount === 0 && (
<>
<bem.Modal__hr />
{t('Waiting for all projects to load…')}
</>
)}
{assetType !== ASSET_TYPES.collection.id &&
this.state.allAssetsCount >= 2 && (
<>
<bem.Modal__hr />
<CopyTeamPermissions assetUid={this.props.assetUid} />
</>
)}
{isManagingPossible && (
<>
{assetType !== ASSET_TYPES.collection.id &&
this.state.allAssetsCount === 0 && (
<>
<bem.Modal__hr />
{t('Waiting for all projects to load…')}
</>
)}
{assetType !== ASSET_TYPES.collection.id &&
this.state.allAssetsCount >= 2 && (
<>
<bem.Modal__hr />
<CopyTeamPermissions assetUid={this.props.assetUid} />
</>
)}
</>
)}
</bem.FormModal>
);
}
Expand Down
45 changes: 24 additions & 21 deletions jsapp/js/components/permissions/userPermissionRow.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {getPermLabel, getFriendlyPermName} from './utils';

interface UserPermissionRowProps {
assetUid: string;
displayControls: boolean;
nonOwnerPerms: PermissionBase[];
assignablePerms: AssignablePermsMap;
permissions: PermissionResponse[];
Expand Down Expand Up @@ -163,27 +164,29 @@ export default class UserPermissionRow extends React.Component<
<bem.UserRow__perms>{t('Pending owner')}</bem.UserRow__perms>
)}

{!this.props.isUserOwner && !this.props.isPendingOwner && (
<React.Fragment>
{this.renderPermissions(this.props.permissions)}

<bem.Button m='icon' onClick={this.toggleEditForm.bind(this)}>
{this.state.isEditFormVisible && (
<i className='k-icon k-icon-close' />
)}
{!this.state.isEditFormVisible && (
<i className='k-icon k-icon-edit' />
)}
</bem.Button>

<bem.Button
m='icon'
onClick={this.showRemovePermissionsPrompt.bind(this)}
>
<i className='k-icon k-icon-trash' />
</bem.Button>
</React.Fragment>
)}
{this.props.displayControls &&
!this.props.isUserOwner &&
!this.props.isPendingOwner && (
<React.Fragment>
{this.renderPermissions(this.props.permissions)}

<bem.Button m='icon' onClick={this.toggleEditForm.bind(this)}>
{this.state.isEditFormVisible && (
<i className='k-icon k-icon-close' />
)}
{!this.state.isEditFormVisible && (
<i className='k-icon k-icon-edit' />
)}
</bem.Button>

<bem.Button
m='icon'
onClick={this.showRemovePermissionsPrompt.bind(this)}
>
<i className='k-icon k-icon-trash' />
</bem.Button>
</React.Fragment>
)}
</bem.UserRow__info>

{this.state.isEditFormVisible && (
Expand Down
5 changes: 4 additions & 1 deletion jsapp/js/projects/customViewRoute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,10 @@ function CustomViewRoute() {

{selectedAssets.length === 1 && (
<div className={styles.actions}>
<ProjectQuickActions asset={selectedAssets[0]} />
<ProjectQuickActions
asset={selectedAssets[0]}
isProjectView={true}
/>
</div>
)}

Expand Down
35 changes: 19 additions & 16 deletions jsapp/js/projects/projectsTable/projectQuickActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,37 +19,38 @@ import customViewStore from 'js/projects/customViewStore';

interface ProjectQuickActionsProps {
asset: AssetResponse | ProjectViewAsset;
isProjectView?: boolean;
}

/**
* Quick Actions (Archive, Share, Delete) buttons. Use these when a single
* project is selected in the Project Table.
*/
export default function ProjectQuickActions(props: ProjectQuickActionsProps) {
const ProjectQuickActions = ({asset, isProjectView = false}: ProjectQuickActionsProps) => {
// The `userCan` method requires `permissions` property to be present in the
// `asset` object. For performance reasons `ProjectViewAsset` doesn't have
// that property, and it is fine, as we don't expect Project View to have
// a lot of options available.
const isChangingPossible = userCan('change_asset', props.asset);
const isManagingPossible = userCan('manage_asset', props.asset);
const isChangingPossible = userCan('change_asset', asset);
const isManagingPossible = userCan('manage_asset', asset);

return (
<div className={styles.root}>
{/* Archive / Unarchive */}
{/* Archive a deployed project */}
{props.asset.deployment_status === 'deployed' && (
{asset.deployment_status === 'deployed' && (
<Button
isDisabled={
!isChangingPossible ||
props.asset.asset_type !== ASSET_TYPES.survey.id ||
!props.asset.has_deployment
asset.asset_type !== ASSET_TYPES.survey.id ||
!asset.has_deployment
}
type='bare'
color='storm'
size='s'
startIcon='archived'
onClick={() =>
archiveAsset(props.asset, (response: DeploymentResponse) => {
archiveAsset(asset, (response: DeploymentResponse) => {
customViewStore.handleAssetChanged(response.asset);
})
}
Expand All @@ -58,19 +59,19 @@ export default function ProjectQuickActions(props: ProjectQuickActionsProps) {
/>
)}
{/* Un-archive a deployed project */}
{props.asset.deployment_status === 'archived' && (
{asset.deployment_status === 'archived' && (
<Button
isDisabled={
!isChangingPossible ||
props.asset.asset_type !== ASSET_TYPES.survey.id ||
!props.asset.has_deployment
asset.asset_type !== ASSET_TYPES.survey.id ||
!asset.has_deployment
}
type='bare'
color='storm'
size='s'
startIcon='archived'
onClick={() =>
unarchiveAsset(props.asset, (response: DeploymentResponse) => {
unarchiveAsset(asset, (response: DeploymentResponse) => {
customViewStore.handleAssetChanged(response.asset);
})
}
Expand All @@ -79,7 +80,7 @@ export default function ProjectQuickActions(props: ProjectQuickActionsProps) {
/>
)}
{/* Show tooltip, since drafts can't be archived/unarchived */}
{props.asset.deployment_status === 'draft' && (
{asset.deployment_status === 'draft' && (
<Button
isDisabled
type='bare'
Expand All @@ -93,12 +94,12 @@ export default function ProjectQuickActions(props: ProjectQuickActionsProps) {

{/* Share */}
<Button
isDisabled={!isManagingPossible}
isDisabled={!isManagingPossible && !isProjectView}
type='bare'
color='storm'
size='s'
startIcon='user-share'
onClick={() => manageAssetSharing(props.asset.uid)}
onClick={() => manageAssetSharing(asset.uid)}
tooltip={t('Share project')}
tooltipPosition='right'
/>
Expand All @@ -112,8 +113,8 @@ export default function ProjectQuickActions(props: ProjectQuickActionsProps) {
startIcon='trash'
onClick={() =>
deleteAsset(
props.asset,
getAssetDisplayName(props.asset).final,
asset,
getAssetDisplayName(asset).final,
(deletedAssetUid: string) => {
customViewStore.handleAssetsDeleted([deletedAssetUid]);
}
Expand All @@ -127,3 +128,5 @@ export default function ProjectQuickActions(props: ProjectQuickActionsProps) {
</div>
);
}

export default ProjectQuickActions

0 comments on commit 4445e40

Please sign in to comment.