Skip to content

Commit 4598b01

Browse files
authored
fix(front): add missing sync button to people page (#107)
## 📝 Description - Added missing `sync` button to refresh collaborators on people page. - Refactored adding people component for clarity ## ✅ Checklist - [x] I have tested this change - [x] ~~This change requires documentation update~~
1 parent 5025367 commit 4598b01

File tree

9 files changed

+370
-203
lines changed

9 files changed

+370
-203
lines changed

front/assets/js/app.js

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ import { default as GetStarted } from "./get_started"
6363
import { default as Agents} from "./agents";
6464
import { default as AddPeople } from "./people/add_people";
6565
import { default as EditPerson } from "./people/edit_person";
66+
import { default as SyncPeople } from "./people/sync_people";
6667

6768
import { InitializingScreen } from "./project_onboarding/initializing";
6869
import { AccountInitializingScreen } from "./me/initialization/initializing";
@@ -276,18 +277,25 @@ export var App = {
276277
GroupManagement.init();
277278
new Star();
278279

279-
const addPeopleApp = document.getElementById("add-people");
280-
if (addPeopleApp) {
280+
const addPeopleAppRoot = document.getElementById("add-people");
281+
if (addPeopleAppRoot) {
281282
AddPeople({
282-
dom: addPeopleApp,
283-
config: addPeopleApp.dataset,
283+
dom: addPeopleAppRoot,
284+
config: addPeopleAppRoot.dataset,
284285
});
285286
}
286287

287-
document.querySelectorAll(".app-edit-person").forEach((person) => {
288+
document.querySelectorAll(".app-edit-person").forEach((editPersonAppRoot) => {
288289
EditPerson({
289-
dom: person,
290-
config: person.dataset
290+
dom: editPersonAppRoot,
291+
config: editPersonAppRoot.dataset
292+
})
293+
});
294+
295+
document.querySelectorAll(".app-sync-people").forEach((syncPeopleAppRoot) => {
296+
SyncPeople({
297+
dom: syncPeopleAppRoot,
298+
config: syncPeopleAppRoot.dataset
291299
})
292300
});
293301
},
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { createContext } from "preact";
2+
import { APIRequest } from "js/toolbox";
3+
import { UserProvider } from "./types";
4+
5+
export interface RawConfig {
6+
config: string;
7+
}
8+
9+
interface ParsedConfig {
10+
users: {
11+
collaborators_url: string;
12+
create_url: string;
13+
invite_url: string;
14+
sync_url: string;
15+
providers: string[];
16+
};
17+
}
18+
19+
export class AppConfig {
20+
collaboratorListUrl: APIRequest.Url<{ collaborators: any, }>;
21+
inviteMemberUrl: APIRequest.Url<{ message: string, }>;
22+
createMemberUrl: APIRequest.Url<{
23+
password: string;
24+
message: string;
25+
}>;
26+
27+
allowedProviders: UserProvider[] = [];
28+
baseUrl: string;
29+
30+
static fromJSON(rawJson: RawConfig): AppConfig {
31+
const config = AppConfig.default();
32+
const json: ParsedConfig = JSON.parse(rawJson.config);
33+
34+
config.collaboratorListUrl = APIRequest.Url.fromJSON(
35+
json.users.collaborators_url
36+
);
37+
config.createMemberUrl = APIRequest.Url.fromJSON(json.users.create_url);
38+
config.inviteMemberUrl = APIRequest.Url.fromJSON(json.users.invite_url);
39+
40+
json.users.providers
41+
.map((provider: string) => {
42+
switch (provider) {
43+
case `email`:
44+
return UserProvider.Email;
45+
case `github`:
46+
return UserProvider.GitHub;
47+
case `bitbucket`:
48+
return UserProvider.Bitbucket;
49+
case `gitlab`:
50+
return UserProvider.GitLab;
51+
default:
52+
return null;
53+
}
54+
})
55+
.filter((provider: UserProvider | null) => provider)
56+
.forEach((provider) => config.allowedProviders.push(provider));
57+
58+
return config;
59+
}
60+
61+
static default(): AppConfig {
62+
const config = new AppConfig();
63+
return config;
64+
}
65+
}
66+
67+
export const Config = createContext<AppConfig>(AppConfig.default());

front/assets/js/people/add_people/index.tsx

Lines changed: 9 additions & 191 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
1-
import { createContext, Fragment, render, VNode } from "preact";
1+
import { Fragment, render, VNode } from "preact";
22
import { Modal } from "js/toolbox";
33
import { useContext, useEffect, useReducer, useState } from "preact/hooks";
44
import * as toolbox from "js/toolbox";
55
import _ from "lodash";
66
import { ChangeEvent } from "react-dom/src";
77
import styled from "styled-components";
8+
import { Config, AppConfig, RawConfig } from "./config";
9+
import { AddPeopleState, AvailableProviderTypes, Collaborator, PeopleStateReducer, Person, PersonState, UserProvider } from "./types";
810

911
export default function ({
1012
dom,
1113
config: jsonConfig,
1214
}: {
1315
dom: HTMLElement;
14-
config: any;
16+
config: RawConfig;
1517
}) {
1618
render(
17-
<Config.Provider value={State.fromJSON(jsonConfig as DOMStringMap)}>
19+
<Config.Provider value={AppConfig.fromJSON(jsonConfig)}>
1820
<App/>
1921
</Config.Provider>,
2022
dom
@@ -63,6 +65,7 @@ const AddNewUsers = (props: { close: (reload: boolean) => void, }) => {
6365

6466
const [loading, setLoading] = useState(true);
6567

68+
6669
useEffect(() => {
6770
void config.collaboratorListUrl
6871
.call()
@@ -140,7 +143,7 @@ const AddNewUsers = (props: { close: (reload: boolean) => void, }) => {
140143
</div>
141144

142145
{userProviders.length > 1 && (
143-
<div className="mb3 button-group ph4 w-100">
146+
<div className="mb3 button-group ph4 w-100 items-center justify-center">
144147
{userProviders.map(userProviderBox)}
145148
</div>
146149
)}
@@ -193,9 +196,8 @@ const ProvideVia = (props: ProvideViaProps) => {
193196
(collaborator) => collaborator.provider === props.provider.toLowerCase()
194197
)
195198
);
196-
const [selectedCollaborators, setSelectedCollaborators] = useState<
197-
Collaborator[]
198-
>([]);
199+
200+
const [selectedCollaborators, setSelectedCollaborators] = useState<Collaborator[]>([]);
199201

200202
const toggleCollaborator = (collaborator: Collaborator) => {
201203
if (selectedCollaborators.includes(collaborator)) {
@@ -621,62 +623,6 @@ const ProvideViaEmail = (props: ProvideViaEmailProps) => {
621623
);
622624
};
623625

624-
export class State {
625-
collaboratorListUrl: toolbox.APIRequest.Url<{ collaborators: any, }>;
626-
inviteMemberUrl: toolbox.APIRequest.Url<{ message: string, }>;
627-
createMemberUrl: toolbox.APIRequest.Url<{
628-
password: string;
629-
message: string;
630-
}>;
631-
allowedProviders: UserProvider[] = [];
632-
633-
static fromJSON(rawJson: DOMStringMap): State {
634-
const config = State.default();
635-
const json = JSON.parse(rawJson.config);
636-
637-
config.collaboratorListUrl = toolbox.APIRequest.Url.fromJSON(
638-
json.users.collaborators_url
639-
);
640-
config.createMemberUrl = toolbox.APIRequest.Url.fromJSON(
641-
json.users.create_url
642-
);
643-
config.inviteMemberUrl = toolbox.APIRequest.Url.fromJSON(
644-
json.users.invite_url
645-
);
646-
647-
json.users.providers
648-
.map((provider: string) => {
649-
switch (provider) {
650-
case `email`:
651-
return config.allowedProviders.push(UserProvider.Email);
652-
case `github`:
653-
return config.allowedProviders.push(UserProvider.GitHub);
654-
case `bitbucket`:
655-
return config.allowedProviders.push(UserProvider.Bitbucket);
656-
case `gitlab`:
657-
return config.allowedProviders.push(UserProvider.GitLab);
658-
default:
659-
return null;
660-
}
661-
})
662-
.filter((provider: UserProvider | null) => provider);
663-
return config;
664-
}
665-
666-
static default(): State {
667-
const config = new State();
668-
return config;
669-
}
670-
}
671-
672-
export const Config = createContext<State>(new State());
673-
674-
enum UserProvider {
675-
Email = `email`,
676-
GitHub = `github`,
677-
Bitbucket = `bitbucket`,
678-
GitLab = `gitlab`,
679-
}
680626

681627
const ActiveShadowLink = styled.button`
682628
&:hover,
@@ -686,138 +632,10 @@ const ActiveShadowLink = styled.button`
686632
}
687633
`;
688634

689-
class Collaborator {
690-
displayName: string;
691-
uid: string;
692-
login: string;
693-
provider: string;
694-
avatarUrl: string;
695-
696-
constructor() {
697-
this.displayName = ``;
698-
this.uid = ``;
699-
this.login = ``;
700-
this.provider = ``;
701-
this.avatarUrl = ``;
702-
}
703-
704-
static fromJSON(json: any): Collaborator {
705-
const collaborator = new Collaborator();
706-
collaborator.displayName = json.display_name as string;
707-
collaborator.login = json.login as string;
708-
collaborator.uid = json.uid as string;
709-
collaborator.provider = json.provider as string;
710-
collaborator.avatarUrl = json.avatar_url as string;
711-
712-
return collaborator;
713-
}
714-
715-
hasAvatar(): boolean {
716-
return this.avatarUrl.length != 0;
717-
}
718-
}
719-
720-
enum PersonState {
721-
Empty,
722-
Loading,
723-
Invited,
724-
Error,
725-
}
726-
727-
class Person {
728-
id: string;
729-
email: string;
730-
username: string;
731-
password = ``;
732-
errorMessage = ``;
733-
state: PersonState = PersonState.Empty;
734-
wasInvited: boolean;
735-
emailValid = true;
736-
737-
constructor() {
738-
this.id = _.uniqueId(`person_`);
739-
this.email = ``;
740-
this.username = ``;
741-
this.wasInvited = false;
742-
}
743-
744-
isEmpty(): boolean {
745-
return _.isEmpty(this.email) && _.isEmpty(this.username);
746-
}
747-
748-
init() {
749-
this.password = ``;
750-
this.errorMessage = ``;
751-
this.state = PersonState.Empty;
752-
}
753-
754-
setPassword(password: string) {
755-
this.state = PersonState.Invited;
756-
this.password = password;
757-
}
758-
759-
setError(errorMessage: string) {
760-
this.state = PersonState.Error;
761-
this.errorMessage = errorMessage;
762-
}
763-
setLoading() {
764-
this.wasInvited = true;
765-
this.state = PersonState.Loading;
766-
}
767-
}
768-
769-
class AddPeopleState {
770-
people: Person[] = [new Person()];
771-
type: UserProvider;
772-
}
773-
774-
export type AddPeopleAction =
775-
| { type: `UPDATE_PERSON`, id: string, value: Person, }
776-
| { type: `REMOVE_PERSON`, id: string, }
777-
| { type: `RESET`, };
778-
779-
const PeopleStateReducer = (state: AddPeopleState, action: AddPeopleAction) => {
780-
switch (action.type) {
781-
case `UPDATE_PERSON`: {
782-
const newPeople = state.people
783-
.map((person) => {
784-
if (person.id == action.id) {
785-
return action.value;
786-
}
787-
return person;
788-
})
789-
.filter((person) => !person.isEmpty());
790-
791-
newPeople.push(new Person());
792-
793-
return { ...state, people: newPeople };
794-
}
795-
796-
case `REMOVE_PERSON`: {
797-
const newPeople = state.people.filter((person) => person.id != action.id);
798-
799-
return { ...state, people: newPeople };
800-
}
801-
802-
case `RESET`: {
803-
return { ...state, people: [new Person()] };
804-
}
805-
806-
default:
807-
return state;
808-
}
809-
};
810-
811635
const ActiveShadowDiv = styled.div`
812636
&:hover,
813637
&.active {
814638
z-index: 2;
815639
box-shadow: 0 0 0 3px #00359f !important;
816640
}
817641
`;
818-
const AvailableProviderTypes: UserProvider[] = [
819-
UserProvider.Email,
820-
UserProvider.GitHub,
821-
UserProvider.Bitbucket,
822-
UserProvider.GitLab,
823-
];

0 commit comments

Comments
 (0)