diff --git a/apps/cyberstorm-remix/app/settings/teams/team/tabs/Members/Members.tsx b/apps/cyberstorm-remix/app/settings/teams/team/tabs/Members/Members.tsx
index 477c406eb..2b544061b 100644
--- a/apps/cyberstorm-remix/app/settings/teams/team/tabs/Members/Members.tsx
+++ b/apps/cyberstorm-remix/app/settings/teams/team/tabs/Members/Members.tsx
@@ -1,6 +1,8 @@
-import "./Members.css";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faPlus, faTrashCan } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { useReducer, useState } from "react";
+import { useLoaderData, useOutletContext, useRevalidator } from "react-router";
+
import {
Modal,
NewAvatar,
@@ -13,50 +15,26 @@ import {
type SelectOption,
useToast,
} from "@thunderstore/cyberstorm";
-import { type LoaderFunctionArgs } from "react-router";
-import { useLoaderData, useOutletContext, useRevalidator } from "react-router";
+import { TableSort } from "@thunderstore/cyberstorm/src/newComponents/Table/Table";
import {
- ApiError,
type RequestConfig,
teamAddMember,
type TeamAddMemberRequestData,
teamEditMember,
teamRemoveMember,
} from "@thunderstore/thunderstore-api";
-import { type OutletContextShape } from "../../../../../root";
-import { TableSort } from "@thunderstore/cyberstorm/src/newComponents/Table/Table";
import { ApiAction } from "@thunderstore/ts-api-react-actions";
-import { DapperTs } from "@thunderstore/dapper-ts";
-import { getSessionTools } from "cyberstorm/security/publicEnvVariables";
+
+import { type OutletContextShape } from "app/root";
+import { makeTeamSettingsTabLoader } from "cyberstorm/utils/dapperClientLoaders";
import { useStrongForm } from "cyberstorm/utils/StrongForm/useStrongForm";
-import { useReducer, useState } from "react";
+import "./Members.css";
-// REMIX TODO: Add check for "user has permission to see this page"
-export async function clientLoader({ params }: LoaderFunctionArgs) {
- if (params.namespaceId) {
- try {
- const tools = getSessionTools();
- const config = tools?.getConfig();
- const dapper = new DapperTs(() => {
- return {
- apiHost: config.apiHost,
- sessionId: config.sessionId,
- };
- });
- return {
- teamName: params.namespaceId,
- members: await dapper.getTeamMembers(params.namespaceId),
- };
- } catch (error) {
- if (error instanceof ApiError) {
- throw new Response("Team members not found", { status: 404 });
- } else {
- throw error;
- }
- }
- }
- throw new Response("Team not found", { status: 404 });
-}
+export const clientLoader = makeTeamSettingsTabLoader(
+ async (dapper, teamName) => ({
+ members: await dapper.getTeamMembers(teamName),
+ })
+);
export function HydrateFallback() {
return
Loading...
;
diff --git a/apps/cyberstorm-remix/app/settings/teams/team/tabs/Profile/Profile.tsx b/apps/cyberstorm-remix/app/settings/teams/team/tabs/Profile/Profile.tsx
index 18252986b..ef26cd41e 100644
--- a/apps/cyberstorm-remix/app/settings/teams/team/tabs/Profile/Profile.tsx
+++ b/apps/cyberstorm-remix/app/settings/teams/team/tabs/Profile/Profile.tsx
@@ -1,42 +1,25 @@
-import { type LoaderFunctionArgs } from "react-router";
+import { useReducer } from "react";
import { useLoaderData, useOutletContext, useRevalidator } from "react-router";
+
+import { NewButton, NewTextInput, useToast } from "@thunderstore/cyberstorm";
import {
- ApiError,
teamDetailsEdit,
type TeamDetailsEditRequestData,
} from "@thunderstore/thunderstore-api";
-import { type OutletContextShape } from "~/root";
-import "./Profile.css";
-import { DapperTs } from "@thunderstore/dapper-ts";
-import { getSessionTools } from "cyberstorm/security/publicEnvVariables";
+
+import { type OutletContextShape } from "app/root";
+import { makeTeamSettingsTabLoader } from "cyberstorm/utils/dapperClientLoaders";
import { useStrongForm } from "cyberstorm/utils/StrongForm/useStrongForm";
-import { useReducer } from "react";
-import { NewButton, NewTextInput, useToast } from "@thunderstore/cyberstorm";
+import "./Profile.css";
-export async function clientLoader({ params }: LoaderFunctionArgs) {
- if (params.namespaceId) {
- try {
- const tools = getSessionTools();
- const dapper = new DapperTs(() => {
- return {
- apiHost: tools?.getConfig().apiHost,
- sessionId: tools?.getConfig().sessionId,
- };
- });
- return {
- team: await dapper.getTeamDetails(params.namespaceId),
- };
- } catch (error) {
- if (error instanceof ApiError) {
- throw new Response("Team not found", { status: 404 });
- } else {
- // REMIX TODO: Add sentry
- throw error;
- }
- }
- }
- throw new Response("Team not found", { status: 404 });
-}
+export const clientLoader = makeTeamSettingsTabLoader(
+ async (dapper, teamName) => ({
+ // TODO: for hygienie we shouldn't use this public endpoint but
+ // have an endpoint that confirms user permissions and returns
+ // possibly sensitive information.
+ team: await dapper.getTeamDetails(teamName),
+ })
+);
export function HydrateFallback() {
return Loading...
;
diff --git a/apps/cyberstorm-remix/app/settings/teams/team/tabs/ServiceAccounts/ServiceAccounts.tsx b/apps/cyberstorm-remix/app/settings/teams/team/tabs/ServiceAccounts/ServiceAccounts.tsx
index fa5467811..81c822ab4 100644
--- a/apps/cyberstorm-remix/app/settings/teams/team/tabs/ServiceAccounts/ServiceAccounts.tsx
+++ b/apps/cyberstorm-remix/app/settings/teams/team/tabs/ServiceAccounts/ServiceAccounts.tsx
@@ -1,4 +1,8 @@
-import "./ServiceAccounts.css";
+import { faPlus } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { useReducer, useState } from "react";
+import { useLoaderData, useOutletContext, useRevalidator } from "react-router";
+
import {
NewAlert,
NewButton,
@@ -9,52 +13,24 @@ import {
Heading,
CodeBox,
} from "@thunderstore/cyberstorm";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { faPlus } from "@fortawesome/free-solid-svg-icons";
-import { type LoaderFunctionArgs } from "react-router";
-import { useLoaderData, useOutletContext, useRevalidator } from "react-router";
+import { TableSort } from "@thunderstore/cyberstorm/src/newComponents/Table/Table";
import {
- ApiError,
type RequestConfig,
teamAddServiceAccount,
type TeamServiceAccountAddRequestData,
} from "@thunderstore/thunderstore-api";
-import { TableSort } from "@thunderstore/cyberstorm/src/newComponents/Table/Table";
-import { type OutletContextShape } from "../../../../../root";
-import { useReducer, useState } from "react";
-import { DapperTs } from "@thunderstore/dapper-ts";
-import { getSessionTools } from "cyberstorm/security/publicEnvVariables";
+
+import { type OutletContextShape } from "app/root";
+import { makeTeamSettingsTabLoader } from "cyberstorm/utils/dapperClientLoaders";
import { useStrongForm } from "cyberstorm/utils/StrongForm/useStrongForm";
import { ServiceAccountRemoveModal } from "./ServiceAccountRemoveModal";
+import "./ServiceAccounts.css";
-// REMIX TODO: Add check for "user has permission to see this page"
-export async function clientLoader({ params }: LoaderFunctionArgs) {
- if (params.namespaceId) {
- try {
- const tools = getSessionTools();
- const config = tools?.getConfig();
- const dapper = new DapperTs(() => {
- return {
- apiHost: config?.apiHost,
- sessionId: config?.sessionId,
- };
- });
- return {
- teamName: params.namespaceId,
- serviceAccounts: await dapper.getTeamServiceAccounts(
- params.namespaceId
- ),
- };
- } catch (error) {
- if (error instanceof ApiError) {
- throw new Response("Team not found", { status: 404 });
- } else {
- throw error;
- }
- }
- }
- throw new Response("Team not found", { status: 404 });
-}
+export const clientLoader = makeTeamSettingsTabLoader(
+ async (dapper, teamName) => ({
+ serviceAccounts: await dapper.getTeamServiceAccounts(teamName),
+ })
+);
export function HydrateFallback() {
return Loading...
;
diff --git a/apps/cyberstorm-remix/app/settings/teams/team/tabs/Settings/Settings.tsx b/apps/cyberstorm-remix/app/settings/teams/team/tabs/Settings/Settings.tsx
index bd1cb6879..c43c1ec15 100644
--- a/apps/cyberstorm-remix/app/settings/teams/team/tabs/Settings/Settings.tsx
+++ b/apps/cyberstorm-remix/app/settings/teams/team/tabs/Settings/Settings.tsx
@@ -1,3 +1,8 @@
+import { faTrashCan } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { useReducer, useState } from "react";
+import { useLoaderData, useNavigate, useOutletContext } from "react-router";
+
import "./Settings.css";
import {
NewAlert,
@@ -8,11 +13,6 @@ import {
NewTextInput,
useToast,
} from "@thunderstore/cyberstorm";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { faTrashCan } from "@fortawesome/free-solid-svg-icons";
-import { useNavigate, useOutletContext, useParams } from "react-router";
-
-import { type OutletContextShape } from "~/root";
import {
type RequestConfig,
teamDisband,
@@ -20,24 +20,24 @@ import {
teamRemoveMember,
} from "@thunderstore/thunderstore-api";
import { ApiAction } from "@thunderstore/ts-api-react-actions";
-import { NotLoggedIn } from "~/commonComponents/NotLoggedIn/NotLoggedIn";
+
+import { NotLoggedIn } from "app/commonComponents/NotLoggedIn/NotLoggedIn";
+import { type OutletContextShape } from "app/root";
+import { makeTeamSettingsTabLoader } from "cyberstorm/utils/dapperClientLoaders";
import { useStrongForm } from "cyberstorm/utils/StrongForm/useStrongForm";
-import { useReducer, useState } from "react";
-// REMIX TODO: Make sure user is redirected of this page, if the user is not logged in
+export const clientLoader = makeTeamSettingsTabLoader(
+ // TODO: add end point for checking can leave/disband status.
+ async (dapper, teamName) => ({})
+);
+
export default function Settings() {
- const params = useParams();
+ const { teamName } = useLoaderData();
const outletContext = useOutletContext() as OutletContextShape;
- if (
- !outletContext.currentUser ||
- !outletContext.currentUser.username ||
- !params.namespaceId
- )
+ if (!outletContext.currentUser || !outletContext.currentUser.username)
return ;
- if (!params.namespaceId) return Team not found
;
-
const toast = useToast();
const navigate = useNavigate();
@@ -69,7 +69,7 @@ export default function Settings() {
You cannot currently disband this team as it has packages.
- You are about to disband the team {params.namespaceId}.
+ You are about to disband the team {teamName}.
Be aware you can currently only disband teams with no packages. If
you need to archive a team with existing pages, contact Mythic#0001
on the Thunderstore Discord.
(
+ dataFetcher: (dapper: DapperTs, teamName: string) => Promise
+) {
+ return async function clientLoader({ params }: LoaderFunctionArgs) {
+ const teamName = params.namespaceId!;
+
+ try {
+ const dapper = setupDapper();
+ const data = await dataFetcher(dapper, teamName);
+ return { teamName, ...data };
+ } catch (error) {
+ if (error instanceof ApiError) {
+ const status = error.response.status;
+ const statusText =
+ (error.responseJson as GenericApiError)?.detail ??
+ error.response.statusText;
+ throw new Response(statusText, { status, statusText });
+ }
+ throw error;
+ }
+ };
+}
+
+const setupDapper = () => {
+ const tools = getSessionTools();
+ const config = tools?.getConfig();
+ return new DapperTs(() => ({
+ apiHost: config?.apiHost,
+ sessionId: config?.sessionId,
+ }));
+};