Skip to content

Commit 1dec6eb

Browse files
committed
Merge branch 'main' into task-963-create-endpoints-to-handle-org-members
2 parents 0401fc4 + 2f1d45e commit 1dec6eb

File tree

76 files changed

+2732
-1356
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+2732
-1356
lines changed

.github/CODEOWNERS

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,22 @@
3636
/jsapp/ @magicznyleszek
3737
/patches/ @p2edwards
3838
/static/ @magicznyleszek
39-
/webpack/ @magicznyleszek
40-
/.babelrc.json @magicznyleszek
41-
/.browserslistrc @magicznyleszek
42-
/.eslintignore @magicznyleszek
43-
/.eslintrc.js @magicznyleszek
44-
/.node-version @magicznyleszek
45-
/.nvmrc @magicznyleszek
46-
/.prettierrc.js @magicznyleszek
47-
/.stylelintrc.js @magicznyleszek
48-
/.swcrc @magicznyleszek
49-
/.coffeelint.json @magicznyleszek
50-
/package-lock.json @magicznyleszek
51-
/package.json @magicznyleszek
52-
/tsconfig.json @magicznyleszek
39+
/static/js/ @magicznyleszek @p2edwards
40+
/test/ @magicznyleszek @p2edwards
41+
/webpack/ @magicznyleszek @p2edwards
42+
/.babelrc.json @magicznyleszek @p2edwards
43+
/.browserslistrc @magicznyleszek @p2edwards
44+
/.eslintignore @magicznyleszek @p2edwards
45+
/.eslintrc.js @magicznyleszek @p2edwards
46+
/.node-version @magicznyleszek @p2edwards
47+
/.nvmrc @magicznyleszek @p2edwards
48+
/.prettierrc.js @magicznyleszek @p2edwards
49+
/.stylelintrc.js @magicznyleszek @p2edwards
50+
/.swcrc @magicznyleszek @p2edwards
51+
/.coffeelint.json @magicznyleszek @p2edwards
52+
/package-lock.json @magicznyleszek @p2edwards
53+
/package.json @magicznyleszek @p2edwards
54+
/tsconfig.json @magicznyleszek @p2edwards
5355

5456
# Billing
5557
/jsapp/js/account/ @jamesrkiger
@@ -64,18 +66,25 @@
6466
# Default owner
6567
/dependencies/ @jnm @noliveleger
6668
/hub/ @jnm @noliveleger
69+
/hub/tests/ @jnm @noliveleger
6770
/kobo/ @jnm @noliveleger
6871
/kobo/apps/audit_log/ @rgraber
6972
/kobo/apps/subsequences/ @Guitlle
7073
/kpi/ @jnm @noliveleger
71-
/test/ @jnm @noliveleger
74+
/kpi/tests/ @jnm @noliveleger
7275
/.coveragerc @jnm
7376
/.dockerignore @jnm @noliveleger
7477
/format-python.sh @noliveleger
7578
/manage.py @jnm @noliveleger
7679
/pip-compile.sh @jnm @noliveleger
7780
/pyproject.toml @jnm @noliveleger
7881

82+
# Django Static Templates, HTML, JS, CSS
83+
/kobo/apps/accounts/templates/ @magicznyleszek @p2edwards @jnm @noliveleger
84+
/kpi/templates/ @magicznyleszek @p2edwards @jnm @noliveleger
85+
/kpi/static/css/ @magicznyleszek @p2edwards
86+
_registration.scss @magicznyleszek @p2edwards
87+
7988

8089

8190

@@ -84,7 +93,9 @@
8493

8594
/docker/ @bufke @jnm @noliveleger
8695
/scripts/ @bufke @jnm @noliveleger
96+
/scripts/*.js @magicznyleszek @p2edwards
8797
/.github/ @bufke @jnm @noliveleger @magicznyleszek
98+
/.github/workflows/npm-test.yml @bufke @jnm @noliveleger @magicznyleszek @p2edwards
8899
/.gitlab-ci.yml @bufke
89100
/Dockerfile @bufke @jnm @noliveleger
90101

hub/models/extra_user_detail.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,5 +78,6 @@ def _sync_org_name(self):
7878
except (KeyError, AttributeError):
7979
organization_name = None
8080

81-
user_organization.name = organization_name
82-
user_organization.save(update_fields=['name'])
81+
if organization_name:
82+
user_organization.name = organization_name
83+
user_organization.save(update_fields=['name'])

jsapp/js/components/common/button.scss

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@ $button-border-radius: 6px;
102102
left: calc(50% - #{$iconAlone * 0.5});
103103
}
104104

105-
&.k-button--type-text {
105+
// We need bigger specificity here to override paddings for has-label
106+
&.k-button--type-text.k-button--type-text {
106107
// We need the text button to align better with other UI elements, so we
107108
// want to avoid the transparent space on both sides.
108109
padding-left: 0;

jsapp/js/components/header/mainHeader.component.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,10 @@ const MainHeader = class MainHeader extends React.Component<MainHeaderProps> {
160160
</React.Fragment>
161161
)}
162162

163-
<OrganizationBadge/>
164-
165-
<AccountMenu />
163+
<div className={styles.accountSection}>
164+
<OrganizationBadge />
165+
<AccountMenu />
166+
</div>
166167

167168
{!isLoggedIn && this.renderLoginButton()}
168169
</MainHeaderBase>

jsapp/js/components/header/mainHeader.module.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,9 @@
1717
display: none;
1818
}
1919
}
20+
21+
.accountSection {
22+
display: flex;
23+
margin-left: auto;
24+
position: relative;
25+
}

jsapp/js/components/header/organizationBadge.module.scss

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,5 @@
77
border-radius: 48px;
88
font-weight: 600;
99
font-size: .85em;
10-
line-height: 12px;
1110
margin-right: 20px;
1211
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Libraries
2+
import React, {useState, useEffect} from 'react';
3+
import {useSearchParams} from 'react-router-dom';
4+
5+
// Partial components
6+
import TransferProjectsInvite from './transferProjectsInvite.component';
7+
import ProjectTransferInviteBanner from './projectTransferInviteBanner';
8+
9+
// Stores, hooks and utilities
10+
import {
11+
isInviteForLoggedInUser,
12+
type TransferStatuses,
13+
} from './transferProjects.api';
14+
15+
// Constants and types
16+
import type {TransferInviteState} from './projectTransferInviteBanner';
17+
18+
/**
19+
* This is a glue component that displays a modal from `TransferProjectsInvite`
20+
* and a banner from `ProjectTransferInviteBanner` as an outcome of the modal
21+
* action.
22+
*/
23+
export default function ProjectOwnershipTransferModalWithBanner() {
24+
const [invite, setInvite] = useState<TransferInviteState>({
25+
valid: false,
26+
uid: '',
27+
status: null,
28+
name: '',
29+
currentOwner: '',
30+
});
31+
const [isBannerVisible, setIsBannerVisible] = useState(true);
32+
const [searchParams] = useSearchParams();
33+
34+
useEffect(() => {
35+
const inviteParams = searchParams.get('invite');
36+
if (inviteParams) {
37+
isInviteForLoggedInUser(inviteParams).then((data) => {
38+
setInvite({...invite, valid: data, uid: inviteParams});
39+
});
40+
} else {
41+
setInvite({...invite, valid: false, uid: ''});
42+
}
43+
}, [searchParams]);
44+
45+
const setInviteDetail = (
46+
newStatus: TransferStatuses.Accepted | TransferStatuses.Declined,
47+
name: string,
48+
currentOwner: string
49+
) => {
50+
setInvite({
51+
...invite,
52+
status: newStatus,
53+
name: name,
54+
currentOwner: currentOwner,
55+
});
56+
};
57+
58+
return (
59+
<>
60+
{isBannerVisible &&
61+
<ProjectTransferInviteBanner
62+
invite={invite}
63+
onRequestClose={() => {setIsBannerVisible(false);}}
64+
/>
65+
}
66+
67+
{invite.valid && invite.uid !== '' && (
68+
<TransferProjectsInvite
69+
setInvite={setInviteDetail}
70+
inviteUid={invite.uid}
71+
/>
72+
)}
73+
</>
74+
);
75+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
@use 'scss/colors';
2+
3+
.banner {
4+
display: flex;
5+
justify-content: space-between;
6+
margin: 24px 24px 0;
7+
padding: 12px;
8+
background-color: colors.$kobo-light-blue;
9+
border-radius: 5px;
10+
align-items: center;
11+
}
12+
13+
.bannerIcon {
14+
padding-right: 18px;
15+
}
16+
17+
.bannerButton {
18+
margin-left: auto;
19+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import React from 'react';
2+
import Icon from 'js/components/common/icon';
3+
import Button from 'js/components/common/button';
4+
import {TransferStatuses} from 'js/components/permissions/transferProjects/transferProjects.api';
5+
import styles from './projectTransferInviteBanner.module.scss';
6+
7+
export interface TransferInviteState {
8+
valid: boolean;
9+
uid: string;
10+
status: TransferStatuses.Accepted | TransferStatuses.Declined | null;
11+
name: string;
12+
currentOwner: string;
13+
}
14+
15+
interface ProjectTransferInviteBannerProps {
16+
invite: TransferInviteState;
17+
onRequestClose: () => void;
18+
}
19+
20+
/**
21+
* Displays a banner about accepting or declining project transfer invitation.
22+
*/
23+
export default function ProjectTransferInviteBanner(props: ProjectTransferInviteBannerProps) {
24+
if (props.invite.status) {
25+
return (
26+
<div className={styles.banner}>
27+
<Icon
28+
name='information'
29+
color='blue'
30+
className={styles.bannerIcon}
31+
/>
32+
33+
{props.invite.status === TransferStatuses.Declined && (
34+
<>
35+
{t('You have declined the request of transfer ownership for ##PROJECT_NAME##. ##CURRENT_OWNER_NAME## will receive a notification that the transfer was incomplete.')
36+
.replace('##PROJECT_NAME##', props.invite.name)
37+
.replace('##CURRENT_OWNER_NAME##', props.invite.currentOwner)}
38+
&nbsp;
39+
{t('##CURRENT_OWNER_NAME## will remain the project owner.')
40+
.replace('##CURRENT_OWNER_NAME##', props.invite.currentOwner)}
41+
</>
42+
)}
43+
44+
{props.invite.status === TransferStatuses.Accepted && (
45+
<>
46+
{t('You have accepted project ownership from ##CURRENT_OWNER_NAME## for ##PROJECT_NAME##. This process can take up to a few minutes to complete.')
47+
.replace('##PROJECT_NAME##', props.invite.name)
48+
.replace('##CURRENT_OWNER_NAME##', props.invite.currentOwner)}
49+
</>
50+
)}
51+
52+
<Button
53+
type='text'
54+
size='s'
55+
startIcon='close'
56+
onClick={() => {props.onRequestClose();}}
57+
className={styles.bannerButton}
58+
/>
59+
</div>
60+
);
61+
}
62+
63+
return null;
64+
}

0 commit comments

Comments
 (0)