mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
refactor: clean up workspace and template settings (#9654)
This commit is contained in:
@@ -1,4 +1,38 @@
|
|||||||
import * as API from "api/api";
|
import * as API from "api/api";
|
||||||
|
import { type Template, type AuthorizationResponse } from "api/typesGenerated";
|
||||||
|
import { type QueryOptions } from "@tanstack/react-query";
|
||||||
|
|
||||||
|
export const templateByNameKey = (orgId: string, name: string) => [
|
||||||
|
orgId,
|
||||||
|
"template",
|
||||||
|
name,
|
||||||
|
"settings",
|
||||||
|
];
|
||||||
|
|
||||||
|
export const templateByName = (
|
||||||
|
orgId: string,
|
||||||
|
name: string,
|
||||||
|
): QueryOptions<{ template: Template; permissions: AuthorizationResponse }> => {
|
||||||
|
return {
|
||||||
|
queryKey: templateByNameKey(orgId, name),
|
||||||
|
queryFn: async () => {
|
||||||
|
const template = await API.getTemplateByName(orgId, name);
|
||||||
|
const permissions = await API.checkAuthorization({
|
||||||
|
checks: {
|
||||||
|
canUpdateTemplate: {
|
||||||
|
object: {
|
||||||
|
resource_type: "template",
|
||||||
|
resource_id: template.id,
|
||||||
|
},
|
||||||
|
action: "update",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { template, permissions };
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const getTemplatesQueryKey = (orgId: string) => [orgId, "templates"];
|
const getTemplatesQueryKey = (orgId: string) => [orgId, "templates"];
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import * as API from "api/api";
|
||||||
|
import { type Workspace } from "api/typesGenerated";
|
||||||
|
import { type QueryOptions } from "@tanstack/react-query";
|
||||||
|
|
||||||
|
export const workspaceByOwnerAndNameKey = (owner: string, name: string) => [
|
||||||
|
"workspace",
|
||||||
|
owner,
|
||||||
|
name,
|
||||||
|
"settings",
|
||||||
|
];
|
||||||
|
|
||||||
|
export const workspaceByOwnerAndName = (
|
||||||
|
owner: string,
|
||||||
|
name: string,
|
||||||
|
): QueryOptions<Workspace> => {
|
||||||
|
return {
|
||||||
|
queryKey: workspaceByOwnerAndNameKey(owner, name),
|
||||||
|
queryFn: () => API.getWorkspaceByOwnerAndName(owner, name),
|
||||||
|
};
|
||||||
|
};
|
||||||
+8
-8
@@ -6,16 +6,16 @@ import { FC } from "react";
|
|||||||
import { Helmet } from "react-helmet-async";
|
import { Helmet } from "react-helmet-async";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import { pageTitle } from "utils/page";
|
import { pageTitle } from "utils/page";
|
||||||
import {
|
import { useTemplateSettings } from "../TemplateSettingsLayout";
|
||||||
getTemplateQuery,
|
|
||||||
useTemplateSettingsContext,
|
|
||||||
} from "../TemplateSettingsLayout";
|
|
||||||
import { TemplateSettingsPageView } from "./TemplateSettingsPageView";
|
import { TemplateSettingsPageView } from "./TemplateSettingsPageView";
|
||||||
|
import { templateByNameKey } from "api/queries/templates";
|
||||||
|
import { useOrganizationId } from "hooks";
|
||||||
|
|
||||||
export const TemplateSettingsPage: FC = () => {
|
export const TemplateSettingsPage: FC = () => {
|
||||||
const { template: templateName } = useParams() as { template: string };
|
const { template: templateName } = useParams() as { template: string };
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { template } = useTemplateSettingsContext();
|
const orgId = useOrganizationId();
|
||||||
|
const { template } = useTemplateSettings();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const {
|
const {
|
||||||
mutate: updateTemplate,
|
mutate: updateTemplate,
|
||||||
@@ -25,9 +25,9 @@ export const TemplateSettingsPage: FC = () => {
|
|||||||
(data: UpdateTemplateMeta) => updateTemplateMeta(template.id, data),
|
(data: UpdateTemplateMeta) => updateTemplateMeta(template.id, data),
|
||||||
{
|
{
|
||||||
onSuccess: async () => {
|
onSuccess: async () => {
|
||||||
await queryClient.invalidateQueries({
|
await queryClient.invalidateQueries(
|
||||||
queryKey: getTemplateQuery(templateName),
|
templateByNameKey(orgId, templateName),
|
||||||
});
|
);
|
||||||
displaySuccess("Template updated successfully");
|
displaySuccess("Template updated successfully");
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
+2
-2
@@ -11,7 +11,7 @@ import { FC } from "react";
|
|||||||
import { Helmet } from "react-helmet-async";
|
import { Helmet } from "react-helmet-async";
|
||||||
import { pageTitle } from "utils/page";
|
import { pageTitle } from "utils/page";
|
||||||
import { templateACLMachine } from "xServices/template/templateACLXService";
|
import { templateACLMachine } from "xServices/template/templateACLXService";
|
||||||
import { useTemplateSettingsContext } from "../TemplateSettingsLayout";
|
import { useTemplateSettings } from "../TemplateSettingsLayout";
|
||||||
import { TemplatePermissionsPageView } from "./TemplatePermissionsPageView";
|
import { TemplatePermissionsPageView } from "./TemplatePermissionsPageView";
|
||||||
import { docs } from "utils/docs";
|
import { docs } from "utils/docs";
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ export const TemplatePermissionsPage: FC<
|
|||||||
React.PropsWithChildren<unknown>
|
React.PropsWithChildren<unknown>
|
||||||
> = () => {
|
> = () => {
|
||||||
const organizationId = useOrganizationId();
|
const organizationId = useOrganizationId();
|
||||||
const { template, permissions } = useTemplateSettingsContext();
|
const { template, permissions } = useTemplateSettings();
|
||||||
const { template_rbac: isTemplateRBACEnabled } = useFeatureVisibility();
|
const { template_rbac: isTemplateRBACEnabled } = useFeatureVisibility();
|
||||||
const [state, send] = useMachine(templateACLMachine, {
|
const [state, send] = useMachine(templateACLMachine, {
|
||||||
context: { templateId: template.id },
|
context: { templateId: template.id },
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
initialTouched,
|
initialTouched,
|
||||||
|
enableReinitialize: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const getFieldHelpers = getFormHelpers<TemplateScheduleFormValues>(
|
const getFieldHelpers = getFormHelpers<TemplateScheduleFormValues>(
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useMutation } from "@tanstack/react-query";
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
import { updateTemplateMeta } from "api/api";
|
import { updateTemplateMeta } from "api/api";
|
||||||
import { UpdateTemplateMeta } from "api/typesGenerated";
|
import { UpdateTemplateMeta } from "api/typesGenerated";
|
||||||
import { useDashboard } from "components/Dashboard/DashboardProvider";
|
import { useDashboard } from "components/Dashboard/DashboardProvider";
|
||||||
@@ -7,14 +7,17 @@ import { FC } from "react";
|
|||||||
import { Helmet } from "react-helmet-async";
|
import { Helmet } from "react-helmet-async";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import { pageTitle } from "utils/page";
|
import { pageTitle } from "utils/page";
|
||||||
import { useTemplateSettingsContext } from "../TemplateSettingsLayout";
|
import { useTemplateSettings } from "../TemplateSettingsLayout";
|
||||||
import { TemplateSchedulePageView } from "./TemplateSchedulePageView";
|
import { TemplateSchedulePageView } from "./TemplateSchedulePageView";
|
||||||
import { useLocalStorage } from "hooks";
|
import { useLocalStorage, useOrganizationId } from "hooks";
|
||||||
|
import { templateByNameKey } from "api/queries/templates";
|
||||||
|
|
||||||
const TemplateSchedulePage: FC = () => {
|
const TemplateSchedulePage: FC = () => {
|
||||||
const { template: templateName } = useParams() as { template: string };
|
const { template: templateName } = useParams() as { template: string };
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { template } = useTemplateSettingsContext();
|
const queryClient = useQueryClient();
|
||||||
|
const orgId = useOrganizationId();
|
||||||
|
const { template } = useTemplateSettings();
|
||||||
const { entitlements, experiments } = useDashboard();
|
const { entitlements, experiments } = useDashboard();
|
||||||
const allowAdvancedScheduling =
|
const allowAdvancedScheduling =
|
||||||
entitlements.features["advanced_template_scheduling"].enabled;
|
entitlements.features["advanced_template_scheduling"].enabled;
|
||||||
@@ -33,7 +36,10 @@ const TemplateSchedulePage: FC = () => {
|
|||||||
} = useMutation(
|
} = useMutation(
|
||||||
(data: UpdateTemplateMeta) => updateTemplateMeta(template.id, data),
|
(data: UpdateTemplateMeta) => updateTemplateMeta(template.id, data),
|
||||||
{
|
{
|
||||||
onSuccess: () => {
|
onSuccess: async () => {
|
||||||
|
await queryClient.invalidateQueries(
|
||||||
|
templateByNameKey(orgId, templateName),
|
||||||
|
);
|
||||||
displaySuccess("Template updated successfully");
|
displaySuccess("Template updated successfully");
|
||||||
// clear browser storage of workspaces impending deletion
|
// clear browser storage of workspaces impending deletion
|
||||||
clearLocal("dismissedWorkspaceList"); // workspaces page
|
clearLocal("dismissedWorkspaceList"); // workspaces page
|
||||||
|
|||||||
@@ -7,68 +7,36 @@ import { pageTitle } from "../../utils/page";
|
|||||||
import { Loader } from "components/Loader/Loader";
|
import { Loader } from "components/Loader/Loader";
|
||||||
import { Outlet, useParams } from "react-router-dom";
|
import { Outlet, useParams } from "react-router-dom";
|
||||||
import { Margins } from "components/Margins/Margins";
|
import { Margins } from "components/Margins/Margins";
|
||||||
import { checkAuthorization, getTemplateByName } from "api/api";
|
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { useOrganizationId } from "hooks/useOrganizationId";
|
import { useOrganizationId } from "hooks/useOrganizationId";
|
||||||
|
import { templateByName } from "api/queries/templates";
|
||||||
|
import { type AuthorizationResponse, type Template } from "api/typesGenerated";
|
||||||
|
import { ErrorAlert } from "components/Alert/ErrorAlert";
|
||||||
|
|
||||||
const templatePermissions = (templateId: string) =>
|
const TemplateSettings = createContext<
|
||||||
({
|
{ template: Template; permissions: AuthorizationResponse } | undefined
|
||||||
canUpdateTemplate: {
|
|
||||||
object: {
|
|
||||||
resource_type: "template",
|
|
||||||
resource_id: templateId,
|
|
||||||
},
|
|
||||||
action: "update",
|
|
||||||
},
|
|
||||||
}) as const;
|
|
||||||
|
|
||||||
const fetchTemplateSettings = async (orgId: string, name: string) => {
|
|
||||||
const template = await getTemplateByName(orgId, name);
|
|
||||||
const permissions = await checkAuthorization({
|
|
||||||
checks: templatePermissions(template.id),
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
template,
|
|
||||||
permissions,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getTemplateQuery = (name: string) => [
|
|
||||||
"template",
|
|
||||||
name,
|
|
||||||
"settings",
|
|
||||||
];
|
|
||||||
|
|
||||||
const useTemplate = (orgId: string, name: string) => {
|
|
||||||
return useQuery({
|
|
||||||
queryKey: getTemplateQuery(name),
|
|
||||||
queryFn: () => fetchTemplateSettings(orgId, name),
|
|
||||||
keepPreviousData: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const TemplateSettingsContext = createContext<
|
|
||||||
Awaited<ReturnType<typeof fetchTemplateSettings>> | undefined
|
|
||||||
>(undefined);
|
>(undefined);
|
||||||
|
|
||||||
export const useTemplateSettingsContext = () => {
|
export function useTemplateSettings() {
|
||||||
const context = useContext(TemplateSettingsContext);
|
const value = useContext(TemplateSettings);
|
||||||
|
if (!value) {
|
||||||
if (!context) {
|
throw new Error("This hook can only be used from a template settings page");
|
||||||
throw new Error(
|
|
||||||
"useTemplateSettingsContext must be used within a TemplateSettingsContext.Provider",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return context;
|
return value;
|
||||||
};
|
}
|
||||||
|
|
||||||
export const TemplateSettingsLayout: FC = () => {
|
export const TemplateSettingsLayout: FC = () => {
|
||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
const orgId = useOrganizationId();
|
const orgId = useOrganizationId();
|
||||||
const { template: templateName } = useParams() as { template: string };
|
const { template: templateName } = useParams() as { template: string };
|
||||||
const { data: settings } = useTemplate(orgId, templateName);
|
const { data, error, isLoading, isError } = useQuery(
|
||||||
|
templateByName(orgId, templateName),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return <Loader />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -76,22 +44,22 @@ export const TemplateSettingsLayout: FC = () => {
|
|||||||
<title>{pageTitle([templateName, "Settings"])}</title>
|
<title>{pageTitle([templateName, "Settings"])}</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
|
|
||||||
{settings ? (
|
<Margins>
|
||||||
<TemplateSettingsContext.Provider value={settings}>
|
<Stack className={styles.wrapper} direction="row" spacing={10}>
|
||||||
<Margins>
|
{isError ? (
|
||||||
<Stack className={styles.wrapper} direction="row" spacing={10}>
|
<ErrorAlert error={error} />
|
||||||
<Sidebar template={settings.template} />
|
) : (
|
||||||
|
<TemplateSettings.Provider value={data}>
|
||||||
|
<Sidebar template={data.template} />
|
||||||
<Suspense fallback={<Loader />}>
|
<Suspense fallback={<Loader />}>
|
||||||
<main className={styles.content}>
|
<main className={styles.content}>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</main>
|
</main>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</Stack>
|
</TemplateSettings.Provider>
|
||||||
</Margins>
|
)}
|
||||||
</TemplateSettingsContext.Provider>
|
</Stack>
|
||||||
) : (
|
</Margins>
|
||||||
<Loader />
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { Helmet } from "react-helmet-async";
|
|||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import { templateVariablesMachine } from "xServices/template/templateVariablesXService";
|
import { templateVariablesMachine } from "xServices/template/templateVariablesXService";
|
||||||
import { pageTitle } from "../../../utils/page";
|
import { pageTitle } from "../../../utils/page";
|
||||||
import { useTemplateSettingsContext } from "../TemplateSettingsLayout";
|
import { useTemplateSettings } from "../TemplateSettingsLayout";
|
||||||
import { TemplateVariablesPageView } from "./TemplateVariablesPageView";
|
import { TemplateVariablesPageView } from "./TemplateVariablesPageView";
|
||||||
|
|
||||||
export const TemplateVariablesPage: FC = () => {
|
export const TemplateVariablesPage: FC = () => {
|
||||||
@@ -20,7 +20,7 @@ export const TemplateVariablesPage: FC = () => {
|
|||||||
template: string;
|
template: string;
|
||||||
};
|
};
|
||||||
const organizationId = useOrganizationId();
|
const organizationId = useOrganizationId();
|
||||||
const { template } = useTemplateSettingsContext();
|
const { template } = useTemplateSettings();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [state, send] = useMachine(templateVariablesMachine, {
|
const [state, send] = useMachine(templateVariablesMachine, {
|
||||||
context: {
|
context: {
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
import { getWorkspaceParameters, postWorkspaceBuild } from "api/api";
|
import { getWorkspaceParameters, postWorkspaceBuild } from "api/api";
|
||||||
import { Helmet } from "react-helmet-async";
|
import { Helmet } from "react-helmet-async";
|
||||||
import { pageTitle } from "utils/page";
|
import { pageTitle } from "utils/page";
|
||||||
import { useWorkspaceSettingsContext } from "../WorkspaceSettingsLayout";
|
import { useWorkspaceSettings } from "../WorkspaceSettingsLayout";
|
||||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||||
import { Loader } from "components/Loader/Loader";
|
import { Loader } from "components/Loader/Loader";
|
||||||
import {
|
import {
|
||||||
@@ -17,7 +17,7 @@ import { ErrorAlert } from "components/Alert/ErrorAlert";
|
|||||||
import { WorkspaceBuildParameter } from "api/typesGenerated";
|
import { WorkspaceBuildParameter } from "api/typesGenerated";
|
||||||
|
|
||||||
const WorkspaceParametersPage = () => {
|
const WorkspaceParametersPage = () => {
|
||||||
const { workspace } = useWorkspaceSettingsContext();
|
const workspace = useWorkspaceSettings();
|
||||||
const parameters = useQuery({
|
const parameters = useQuery({
|
||||||
queryKey: ["workspace", workspace.id, "parameters"],
|
queryKey: ["workspace", workspace.id, "parameters"],
|
||||||
queryFn: () => getWorkspaceParameters(workspace),
|
queryFn: () => getWorkspaceParameters(workspace),
|
||||||
|
|||||||
@@ -201,6 +201,7 @@ export const WorkspaceScheduleForm: FC<
|
|||||||
onSubmit,
|
onSubmit,
|
||||||
validationSchema,
|
validationSchema,
|
||||||
initialTouched,
|
initialTouched,
|
||||||
|
enableReinitialize: true,
|
||||||
});
|
});
|
||||||
const formHelpers = getFormHelpers<WorkspaceScheduleFormValues>(
|
const formHelpers = getFormHelpers<WorkspaceScheduleFormValues>(
|
||||||
form,
|
form,
|
||||||
|
|||||||
+10
-3
@@ -10,12 +10,13 @@ import {
|
|||||||
scheduleChanged,
|
scheduleChanged,
|
||||||
} from "pages/WorkspaceSettingsPage/WorkspaceSchedulePage/schedule";
|
} from "pages/WorkspaceSettingsPage/WorkspaceSchedulePage/schedule";
|
||||||
import { ttlMsToAutostop } from "pages/WorkspaceSettingsPage/WorkspaceSchedulePage/ttl";
|
import { ttlMsToAutostop } from "pages/WorkspaceSettingsPage/WorkspaceSchedulePage/ttl";
|
||||||
import { useWorkspaceSettingsContext } from "pages/WorkspaceSettingsPage/WorkspaceSettingsLayout";
|
import { useWorkspaceSettings } from "pages/WorkspaceSettingsPage/WorkspaceSettingsLayout";
|
||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { Helmet } from "react-helmet-async";
|
import { Helmet } from "react-helmet-async";
|
||||||
import { Navigate, useNavigate, useParams } from "react-router-dom";
|
import { Navigate, useNavigate, useParams } from "react-router-dom";
|
||||||
import { pageTitle } from "utils/page";
|
import { pageTitle } from "utils/page";
|
||||||
import * as TypesGen from "api/typesGenerated";
|
import * as TypesGen from "api/typesGenerated";
|
||||||
|
import { workspaceByOwnerAndNameKey } from "api/queries/workspace";
|
||||||
import { WorkspaceScheduleForm } from "./WorkspaceScheduleForm";
|
import { WorkspaceScheduleForm } from "./WorkspaceScheduleForm";
|
||||||
import { workspaceSchedule } from "xServices/workspaceSchedule/workspaceScheduleXService";
|
import { workspaceSchedule } from "xServices/workspaceSchedule/workspaceScheduleXService";
|
||||||
import {
|
import {
|
||||||
@@ -23,6 +24,7 @@ import {
|
|||||||
formValuesToTTLRequest,
|
formValuesToTTLRequest,
|
||||||
} from "./formToRequest";
|
} from "./formToRequest";
|
||||||
import { ErrorAlert } from "components/Alert/ErrorAlert";
|
import { ErrorAlert } from "components/Alert/ErrorAlert";
|
||||||
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
|
|
||||||
const getAutostart = (workspace: TypesGen.Workspace) =>
|
const getAutostart = (workspace: TypesGen.Workspace) =>
|
||||||
scheduleToAutostart(workspace.autostart_schedule);
|
scheduleToAutostart(workspace.autostart_schedule);
|
||||||
@@ -44,7 +46,8 @@ export const WorkspaceSchedulePage: FC = () => {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const username = params.username.replace("@", "");
|
const username = params.username.replace("@", "");
|
||||||
const workspaceName = params.workspace;
|
const workspaceName = params.workspace;
|
||||||
const { workspace } = useWorkspaceSettingsContext();
|
const queryClient = useQueryClient();
|
||||||
|
const workspace = useWorkspaceSettings();
|
||||||
const [scheduleState, scheduleSend] = useMachine(workspaceSchedule, {
|
const [scheduleState, scheduleSend] = useMachine(workspaceSchedule, {
|
||||||
context: { workspace },
|
context: { workspace },
|
||||||
});
|
});
|
||||||
@@ -97,7 +100,7 @@ export const WorkspaceSchedulePage: FC = () => {
|
|||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
navigate(`/@${username}/${workspaceName}`);
|
navigate(`/@${username}/${workspaceName}`);
|
||||||
}}
|
}}
|
||||||
onSubmit={(values) => {
|
onSubmit={async (values) => {
|
||||||
scheduleSend({
|
scheduleSend({
|
||||||
type: "SUBMIT_SCHEDULE",
|
type: "SUBMIT_SCHEDULE",
|
||||||
autostart: formValuesToAutostartRequest(values),
|
autostart: formValuesToAutostartRequest(values),
|
||||||
@@ -111,6 +114,10 @@ export const WorkspaceSchedulePage: FC = () => {
|
|||||||
values,
|
values,
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await queryClient.invalidateQueries(
|
||||||
|
workspaceByOwnerAndNameKey(params.username, params.workspace),
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -7,39 +7,23 @@ import { pageTitle } from "../../utils/page";
|
|||||||
import { Loader } from "components/Loader/Loader";
|
import { Loader } from "components/Loader/Loader";
|
||||||
import { Outlet, useParams } from "react-router-dom";
|
import { Outlet, useParams } from "react-router-dom";
|
||||||
import { Margins } from "components/Margins/Margins";
|
import { Margins } from "components/Margins/Margins";
|
||||||
import { getWorkspaceByOwnerAndName } from "api/api";
|
import { workspaceByOwnerAndName } from "api/queries/workspace";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { ErrorAlert } from "components/Alert/ErrorAlert";
|
||||||
|
import { type Workspace } from "api/typesGenerated";
|
||||||
|
|
||||||
const fetchWorkspaceSettings = async (owner: string, name: string) => {
|
const WorkspaceSettings = createContext<Workspace | undefined>(undefined);
|
||||||
const workspace = await getWorkspaceByOwnerAndName(owner, name);
|
|
||||||
|
|
||||||
return {
|
export function useWorkspaceSettings() {
|
||||||
workspace,
|
const value = useContext(WorkspaceSettings);
|
||||||
};
|
if (!value) {
|
||||||
};
|
|
||||||
|
|
||||||
const useWorkspace = (owner: string, name: string) => {
|
|
||||||
return useQuery({
|
|
||||||
queryKey: ["workspace", name, "settings"],
|
|
||||||
queryFn: () => fetchWorkspaceSettings(owner, name),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const WorkspaceSettingsContext = createContext<
|
|
||||||
Awaited<ReturnType<typeof fetchWorkspaceSettings>> | undefined
|
|
||||||
>(undefined);
|
|
||||||
|
|
||||||
export const useWorkspaceSettingsContext = () => {
|
|
||||||
const context = useContext(WorkspaceSettingsContext);
|
|
||||||
|
|
||||||
if (!context) {
|
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"useWorkspaceSettingsContext must be used within a WorkspaceSettingsContext.Provider",
|
"This hook can only be used from a workspace settings page",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return context;
|
return value;
|
||||||
};
|
}
|
||||||
|
|
||||||
export const WorkspaceSettingsLayout: FC = () => {
|
export const WorkspaceSettingsLayout: FC = () => {
|
||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
@@ -49,7 +33,16 @@ export const WorkspaceSettingsLayout: FC = () => {
|
|||||||
};
|
};
|
||||||
const workspaceName = params.workspace;
|
const workspaceName = params.workspace;
|
||||||
const username = params.username.replace("@", "");
|
const username = params.username.replace("@", "");
|
||||||
const { data: settings } = useWorkspace(username, workspaceName);
|
const {
|
||||||
|
data: workspace,
|
||||||
|
error,
|
||||||
|
isLoading,
|
||||||
|
isError,
|
||||||
|
} = useQuery(workspaceByOwnerAndName(username, workspaceName));
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return <Loader />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -57,22 +50,22 @@ export const WorkspaceSettingsLayout: FC = () => {
|
|||||||
<title>{pageTitle([workspaceName, "Settings"])}</title>
|
<title>{pageTitle([workspaceName, "Settings"])}</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
|
|
||||||
{settings ? (
|
<Margins>
|
||||||
<WorkspaceSettingsContext.Provider value={settings}>
|
<Stack className={styles.wrapper} direction="row" spacing={10}>
|
||||||
<Margins>
|
{isError ? (
|
||||||
<Stack className={styles.wrapper} direction="row" spacing={10}>
|
<ErrorAlert error={error} />
|
||||||
<Sidebar workspace={settings.workspace} username={username} />
|
) : (
|
||||||
|
<WorkspaceSettings.Provider value={workspace}>
|
||||||
|
<Sidebar workspace={workspace} username={username} />
|
||||||
<Suspense fallback={<Loader />}>
|
<Suspense fallback={<Loader />}>
|
||||||
<main className={styles.content}>
|
<main className={styles.content}>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</main>
|
</main>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</Stack>
|
</WorkspaceSettings.Provider>
|
||||||
</Margins>
|
)}
|
||||||
</WorkspaceSettingsContext.Provider>
|
</Stack>
|
||||||
) : (
|
</Margins>
|
||||||
<Loader />
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Helmet } from "react-helmet-async";
|
import { Helmet } from "react-helmet-async";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import { pageTitle } from "utils/page";
|
import { pageTitle } from "utils/page";
|
||||||
import { useWorkspaceSettingsContext } from "./WorkspaceSettingsLayout";
|
import { useWorkspaceSettings } from "./WorkspaceSettingsLayout";
|
||||||
import { WorkspaceSettingsPageView } from "./WorkspaceSettingsPageView";
|
import { WorkspaceSettingsPageView } from "./WorkspaceSettingsPageView";
|
||||||
import { useMutation } from "@tanstack/react-query";
|
import { useMutation } from "@tanstack/react-query";
|
||||||
import { displaySuccess } from "components/GlobalSnackbar/utils";
|
import { displaySuccess } from "components/GlobalSnackbar/utils";
|
||||||
@@ -15,7 +15,7 @@ const WorkspaceSettingsPage = () => {
|
|||||||
};
|
};
|
||||||
const workspaceName = params.workspace;
|
const workspaceName = params.workspace;
|
||||||
const username = params.username.replace("@", "");
|
const username = params.username.replace("@", "");
|
||||||
const { workspace } = useWorkspaceSettingsContext();
|
const workspace = useWorkspaceSettings();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationFn: (formValues: WorkspaceSettingsFormValues) =>
|
mutationFn: (formValues: WorkspaceSettingsFormValues) =>
|
||||||
|
|||||||
Reference in New Issue
Block a user