Skip to content

Commit

Permalink
Merge pull request #5028 from systeminit/feat-auth-portal
Browse files Browse the repository at this point in the history
feat(auth-portal, auth-api): Set ability to make a workspace the default
  • Loading branch information
stack72 authored Nov 26, 2024
2 parents 83151ef + 6ed1e67 commit 50d4929
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 26 deletions.
4 changes: 2 additions & 2 deletions app/auth-portal/src/components/WorkspaceLinkWidget.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
</a>
</TruncateWithTooltip>
<IconButton
:tooltip="workspace.isFavourite ? 'Remove Star' : 'Add Star'"
:tooltip="workspace.isFavourite ? 'Remove Favourite' : 'Set Favourite'"
tooltipPlacement="top"
:icon="workspace.isFavourite ? 'star' : 'starOutline'"
size="sm"
Expand Down Expand Up @@ -144,7 +144,7 @@ function clickHandler(e: MouseEvent) {
const starWorkspace = async () => {
if (!props.workspaceId || !workspace.value) return;
await workspacesStore.SET_FAVOURITE_QUARANTINE(
await workspacesStore.SET_FAVOURITE(
props.workspaceId,
!workspace.value.isFavourite,
);
Expand Down
96 changes: 75 additions & 21 deletions app/auth-portal/src/pages/WorkspaceDetailsPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,25 @@
}}
</div>
</div>
<template v-if="isDefaultWorkspace">
<div
:class="
clsx(
'rounded text-sm px-xs py-2xs my-2xs w-fit',
themeClasses(
'bg-success-600 text-shade-0',
'bg-success-500 text-shade-100',
),
)
"
>
DEFAULT
</div>
</template>
<IconButton
:tooltip="draftWorkspace.isFavourite ? 'Remove Star' : 'Add Star'"
:tooltip="
draftWorkspace.isFavourite ? 'Remove Favourite' : 'Add Favourite'
"
tooltipPlacement="top"
:icon="draftWorkspace.isFavourite ? 'star' : 'starOutline'"
size="lg"
Expand Down Expand Up @@ -84,22 +101,36 @@
placeholder="A description for this workspace"
/>

<VButton
v-if="!createMode || createWorkspaceType"
:disabled="
validationState.isError || (!isWorkspaceOwner && !createMode)
"
:loadingText="createMode ? 'Creating...' : 'Applying...'"
:requestStatus="
createMode ? createWorkspaceReqStatus : editWorkspaceReqStatus
"
iconRight="chevron--right"
tone="action"
variant="solid"
@click="() => (createMode ? createWorkspace() : editWorkspace())"
>
{{ createMode ? "Create Workspace" : "Save" }}
</VButton>
<div class="flex flex-row flex-wrap items-center w-full gap-xs">
<VButton
v-if="!createMode || createWorkspaceType"
:disabled="
validationState.isError || (!isWorkspaceOwner && !createMode)
"
:loadingText="createMode ? 'Creating...' : 'Applying...'"
:requestStatus="
createMode ? createWorkspaceReqStatus : editWorkspaceReqStatus
"
class="basis-[calc(75%-0.5rem)] flex-grow-0"
iconRight="chevron--right"
tone="action"
variant="solid"
@click="() => (createMode ? createWorkspace() : editWorkspace())"
>
{{ createMode ? "Create Workspace" : "Save Workspace" }}
</VButton>
<VButton
v-if="!createMode"
:disabled="isDefaultWorkspace"
class="basis-[calc(25%-0.5rem)] flex-grow-0"
loadingText="Setting as default..."
iconRight="chevron--right"
tone="action"
variant="solid"
@click="setDefaultWorkspace()"
>Set as Default</VButton
>
</div>
</Stack>
<div v-if="!createMode" class="mt-sm">
<template v-if="loadWorkspaceMembersReqStatus.isPending">
Expand Down Expand Up @@ -208,7 +239,9 @@ import {
VButton,
useValidatedInputGroup,
IconButton,
themeClasses,
} from "@si/vue-lib/design-system";
import clsx from "clsx";
import { useHead } from "@vueuse/head";
import { useRouter } from "vue-router";
import { useAuthStore } from "@/store/auth.store";
Expand Down Expand Up @@ -272,6 +305,16 @@ const isWorkspaceOwner = computed(
workspacesStore.workspacesById[props.workspaceId]?.role === "OWNER",
);
const isDefaultWorkspace = computed(
() =>
props.workspaceId === "new" ||
workspacesStore.workspacesById[props.workspaceId]?.isDefault,
);
const setDefaultReqStatus = workspacesStore.getRequestStatus(
"SET_DEFAULT_WORKSPACE",
);
const loadWorkspacesReqStatus =
workspacesStore.getRequestStatus("LOAD_WORKSPACES");
Expand All @@ -284,6 +327,15 @@ function reloadWorkspaces() {
}
watch(() => authStore.userIsLoggedIn, reloadWorkspaces, { immediate: true });
watch(
[() => props.workspaceId, setDefaultReqStatus],
() => {
if (!setDefaultReqStatus.value.isSuccess) return;
reloadWorkspaces();
},
{ immediate: true },
);
watch(
[() => props.workspaceId, loadWorkspacesReqStatus],
() => {
Expand All @@ -303,6 +355,11 @@ watch(
},
{ immediate: true },
);
const setDefaultWorkspace = async () => {
if (!props.workspaceId) return;
await workspacesStore.SET_DEFAULT_WORKSPACE(props.workspaceId);
};
const createWorkspace = async () => {
if (!draftWorkspace.description) {
draftWorkspace.description = "";
Expand Down Expand Up @@ -361,10 +418,7 @@ const editWorkspace = async () => {
const favouriteWorkspace = async (isFavourite: boolean) => {
if (!props.workspaceId) return;
await workspacesStore.SET_FAVOURITE_QUARANTINE(
props.workspaceId,
isFavourite,
);
await workspacesStore.SET_FAVOURITE(props.workspaceId, isFavourite);
draftWorkspace.isFavourite = isFavourite;
};
Expand Down
7 changes: 5 additions & 2 deletions app/auth-portal/src/pages/WorkspacesPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -169,12 +169,15 @@ const workspaces = computed(() => workspacesStore.workspaces);
function sortedWorkspaces(workspaces: Workspace[]): Workspace[] {
return workspaces.sort((a, b) => {
// 1. Sort by isDefault (true comes first)
if (a.isDefault !== b.isDefault) {
if (a.isDefault !== b.isDefault && a.creatorUserId === authStore.user?.id) {
return a.isDefault ? -1 : 1;
}
// 2. Sort by isFavourite (true comes first)
if (a.isFavourite !== b.isFavourite) {
if (
a.isFavourite !== b.isFavourite &&
a.creatorUserId === authStore.user?.id
) {
return a.isFavourite ? -1 : 1;
}
Expand Down
8 changes: 7 additions & 1 deletion app/auth-portal/src/store/workspaces.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ export const useWorkspacesStore = defineStore("workspaces", {
},
});
},
async SET_FAVOURITE_QUARANTINE(workspaceId: string, isFavourite: boolean) {
async SET_FAVOURITE(workspaceId: string, isFavourite: boolean) {
return new ApiRequest<{ user: User }>({
method: "patch",
url: `/workspaces/${workspaceId}/favourite`,
Expand All @@ -193,6 +193,12 @@ export const useWorkspacesStore = defineStore("workspaces", {
},
});
},
async SET_DEFAULT_WORKSPACE(workspaceId: string) {
return new ApiRequest<{ user: User }>({
method: "patch",
url: `/workspaces/${workspaceId}/setDefault`,
});
},

async INVITE_USER(
userinfo: { email: string; role: string },
Expand Down
18 changes: 18 additions & 0 deletions bin/auth-api/src/routes/workspace.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
LOCAL_WORKSPACE_URL,
SAAS_WORKSPACE_URL,
changeWorkspaceMembership,
setUpdatedDefaultWorkspace,
} from "../services/workspaces.service";
import { validate } from "../lib/validation-helpers";

Expand Down Expand Up @@ -308,6 +309,23 @@ router.delete("/workspace/:workspaceId/members", async (ctx) => {
ctx.body = members;
});

router.patch("/workspaces/:workspaceId/setDefault", async (ctx) => {
const authUser = extractAuthUser(ctx);

const workspace = await extractWorkspaceIdParam(ctx);

tracker.trackEvent(authUser, "set_default_workspace", {
defaultWorkspaceSetBy: authUser.email,
workspaceId: workspace.id,
});

// update all existing workspaces to not be default
await setUpdatedDefaultWorkspace(authUser.id, workspace.id);

// Return the updated workspace list
ctx.body = await getUserWorkspaces(authUser.id);
});

router.patch("/workspaces/:workspaceId/favourite", async (ctx) => {
const authUser = extractAuthUser(ctx);

Expand Down
31 changes: 31 additions & 0 deletions bin/auth-api/src/services/workspaces.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,3 +266,34 @@ export async function createProductionWorkspaceForUser(userId: UserId) {

return null;
}

async function resetDefaultWorkspaces(userId: UserId) {
await prisma.workspace.updateMany({
where: {
deletedAt: null,
creatorUserId: userId,
},
data: {
isDefault: false,
},
});
}

async function setDefaultWorkspace(workspaceId: WorkspaceId) {
await prisma.workspace.update({
where: {
id: workspaceId,
},
data: {
isDefault: true,
},
});
}

export async function setUpdatedDefaultWorkspace(
userId: UserId,
workspaceId: WorkspaceId,
) {
await resetDefaultWorkspaces(userId);
await setDefaultWorkspace(workspaceId);
}

0 comments on commit 50d4929

Please sign in to comment.