Skip to content

Commit

Permalink
add team transfer option for users who want to delete account but kee…
Browse files Browse the repository at this point in the history
…p team
  • Loading branch information
CADawg committed Mar 16, 2024
1 parent 78d5d17 commit 0f08b10
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 19 deletions.
10 changes: 5 additions & 5 deletions ui/src/components/navbar/NavAuthLinks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,23 @@ export default function NavAuthLinks() {
const authValid = useAuthValid();

if (authValid) {
return <NavBarDropdownLink>
return <>
<NavBarLink href={URLS.DASHBOARD}>Dashboard</NavBarLink>
<NavBarDropdownLink>
<NavBarDropdownLinkText>
<FontAwesomeIcon
icon={faUserCircle}/>&nbsp;{pocketbase.authStore?.model?.username}
</NavBarDropdownLinkText>
<NavBarDropdownItemContainer>
<NavBarDropdownItem>
<NavBarLink href={URLS.DASHBOARD}>Dashboard</NavBarLink>
</NavBarDropdownItem>
<NavBarDropdownItem>
<NavBarLink href={URLS.USER_SETTINGS}>Account</NavBarLink>
</NavBarDropdownItem>
<NavBarDropdownItem>
<NavBarLink href={URLS.LOGOUT}>Logout</NavBarLink>
</NavBarDropdownItem>
</NavBarDropdownItemContainer>
</NavBarDropdownLink>;
</NavBarDropdownLink>
</>;
}

return <>
Expand Down
5 changes: 5 additions & 0 deletions ui/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import OAuth from "./routes/OAuth";
import ForgotPassword from "./routes/ForgotPassword";
import ResetPassword from "./routes/ResetPassword";
import ChangePassword from "./routes/ChangePassword";
import User from "./routes/Dashboards/User";

const router = createBrowserRouter([
{
Expand Down Expand Up @@ -82,6 +83,10 @@ const router = createBrowserRouter([
element: <Config/>,
loader: configLoader,
errorElement: <DashboardError/>,
},
{
path: "user",
element: <User />,
}
]
},
Expand Down
4 changes: 2 additions & 2 deletions ui/src/routes/ChangePassword.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ export default function ChangePassword() {
{message ? message : ""}
</div>

<p className="auth-form-text">Want to go back? <Link to={URLS.DASHBOARD}
class="auth-form-link">Dashboard</Link></p>
<p className="auth-form-text">Want to go back? <Link to={URLS.USER_SETTINGS}
class="auth-form-link">Account Settings</Link></p>

</div>
</form>
Expand Down
75 changes: 64 additions & 11 deletions ui/src/routes/Dashboards/Team.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,23 +44,42 @@ const userRoles: DashboardSelectItem[] = [
title: "Admin",
description: "Can manage, create and delete all projects, configs, environments, flags and values.",
},
{
value: "owner",
title: "Owner",
description: "Owns this team and can change it in any way. Selecting this option will change the ownership of this team."
}
];

export default function Team() {
const [teamData, projectsData, userData] = useLoaderData() as TeamLoaderData;
const [projects, setProjects] = useState<ProjectRecord[]>(projectsData);
const [newProjectName, setNewProjectName] = useState<string>("");
const [userToAdd, setUserToAdd] = useState<UserRecord | null>(null);
const [userRole, setUserRole] = useState<DashboardSelectItem | null>(userRoles[0]);
const [userToAdd, setUserToAddInner] = useState<UserRecord | null>(null);
const [userRole, setUserRoleInner] = useState<DashboardSelectItem | null>(userRoles[0]);
const [error, setError] = useState<string>("");
const [memberError, setMemberError] = useState<string>("");
const [team, setTeam] = useState<TeamRecord>(teamData);
const [reset, setReset] = useState<boolean>(false);
const [terribleActionConfirm, setTerribleActionConfirm] = useState<boolean>(false);

const [deleteObjectType, setDeleteObjectType] = useState<string>("");
const [deleteObject, setDeleteObject] = useState<ProjectRecord | UserRecord | null>(null);
const [deleteObjectError, setDeleteObjectError] = useState<string>("");

// reset the terrible action confirmed if either input is changed.
function setUserRole(item: DashboardSelectItem | null) {
setTerribleActionConfirm(false);

setUserRoleInner(item);
}

function setUserToAdd(item: UserRecord | null) {
setTerribleActionConfirm(false);

setUserToAddInner(item);
}

const navigate = useNavigate();

useEffect(() => {
Expand Down Expand Up @@ -135,8 +154,8 @@ export default function Team() {
<SelectInput items={userRoles} onSelectedItemChange={setUserRole} defaultValue={userRole}/>
</DialogBody>
<DialogFooter>
<button className="dialog-action dialog-action__save"
onClick={() => addTeamMember()}>Add Member
<button className={"dialog-action " + (userRole?.value === "owner" ? " dialog-action__delete" : " dialog-action__save")}
onClick={() => addTeamMember()}>{userRole?.value === "owner" ? "Transfer Ownership" : "Add Member"}
</button>
<button className="dialog-action dialog-action__cancel"
onClick={() => setUserAddDialogShowing(false)}>Cancel
Expand All @@ -148,6 +167,7 @@ export default function Team() {
if (!showing) {
setUserToAdd(null);
setReset(true);
setTerribleActionConfirm(false);
} else {
setReset(false);
}
Expand Down Expand Up @@ -228,6 +248,26 @@ export default function Team() {
return;
}

if (userRole?.value === "owner") {
if (!terribleActionConfirm) {
setMemberError("Please click again to confirm that you want to transfer ownership");
setTimeout(() => setMemberError(''), 5000);
setTerribleActionConfirm(true);
return;
}
} else {
setTerribleActionConfirm(false);
}

// check if the userRole is owner and warn them that they are about to transfer ownership
if (userRole?.value === "owner") {
if (team.owner === userToAdd.id) {
setMemberError("User is already the owner of the team");
setTimeout(() => setMemberError(''), 5000);
return;
}
}

if (userRole === null) {
setMemberError("Please select a role for the user");
setTimeout(() => setMemberError(''), 5000);
Expand Down Expand Up @@ -259,22 +299,35 @@ export default function Team() {
return;
}

// add user to team
pocketbase.collection('team').update(team.id, {
let bodyParams: any = {
[userRole.value]: [...team[userRole.value], userToAdd.id],
}).then(() => {
}

if (userRole.value === "owner") {
bodyParams = {
...bodyParams,
owner: userToAdd.id,
}
}

// add user to team (or replace owner)
pocketbase.collection('team').update(team.id, bodyParams).then(() => {
// need to add it manually because otherwise we lose the extra data
const userObjectClone = {...userToAdd} as UserRecord;

setTeam(team => {
let teamClone = {...team} as TeamRecord;

teamClone[userRole.value].push(userObjectClone.id);
if (userRole.value === "owner") {
teamClone.owner = userObjectClone.id;
} else {
teamClone[userRole.value].push(userObjectClone.id);

// @ts-ignore
if (teamClone.expand[userRole.value]) {
// @ts-ignore
teamClone.expand[userRole.value].push(userObjectClone);
if (teamClone.expand[userRole.value]) {
// @ts-ignore
teamClone.expand[userRole.value].push(userObjectClone);
}
}

return teamClone;
Expand Down
54 changes: 54 additions & 0 deletions ui/src/routes/Dashboards/User.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import {Link, useNavigate} from "react-router-dom";
import Content from "../../components/general/Content";
import DashboardSpacer from "../../components/dashboard/DashboardSpacer";
import SettingButtons from "../../components/dashboard/config/SettingButtons";
import SettingButton from "../../components/dashboard/config/SettingButton";
import DashboardNavbar from "../../components/navbar/DashboardNavbar";
import {useAuthValidWithModel} from "../../hooks/useAuthValid";
import useAuthRedirect from "../../hooks/useAuthRedirect";
import URLS from "../../helpers/URLS";

export default function User() {
const [, model] = useAuthValidWithModel();
const navigate = useNavigate();
useAuthRedirect(URLS.LOGIN, false);

return <>
<DashboardNavbar>
{model ? <div className={"navbar-links-breadcrumb"}>
<Link class="breadcrumb-page"
to={`/dashboard/user`}><p>Account Settings</p></Link>
</div>
: null}
</DashboardNavbar>
<Content pageName="dashboard dashboard-user-settings">

<h1 className="action-header">Change your password</h1>

<p>You can do so using the button below if you know your current password. If you have forgotten it, please
log out and use the forgotten password process.</p>

<SettingButtons>
<SettingButton onClick={() => navigate(URLS.CHANGE_PASSWORD)}
type={"Change Password"}/>
</SettingButtons>

<h1 className="action-header">Delete account</h1>

<p>If you want to, you can delete your ConfigDN account. This actions is irreversible! Any teams you are the owner of will also be deleted.</p>

<p>You currently own the following teams:</p>

<ul>
<li>Test</li>
</ul>

<SettingButtons>
<SettingButton onClick={() => navigate(URLS.CHANGE_PASSWORD)}
type={"Change Password"}/>
</SettingButtons>

<DashboardSpacer/>
</Content>
</>;
}
17 changes: 16 additions & 1 deletion ui/src/styles/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,9 @@ nav.navbar {
.navbar-link__text {
margin-top: auto;
margin-bottom: auto;


font-size: 1.15em;
}

&:hover, & {
Expand Down Expand Up @@ -740,7 +743,7 @@ div.page-transition {
}

// Dashboard (Config)
.page .content.dashboard.dashboard-config {
.page .content.dashboard.dashboard-config, .page .content.dashboard.dashboard-user-settings {
display: flex;
flex-direction: column;

Expand Down Expand Up @@ -1226,6 +1229,18 @@ div.page-transition {
}
}

.page .dashboard.dashboard-user-settings {
.action-header {
font-size: 2em;
margin-top: 50px;
margin-bottom: 10px;

&:first-of-type {
margin-top: 20px;
}
}
}

// Auth pages (login, register)
.page.auth-page {
.content.auth-content {
Expand Down

0 comments on commit 0f08b10

Please sign in to comment.